ngio 0.3.0a1__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngio/common/_array_pipe.py +50 -27
- ngio/common/_table_ops.py +1 -1
- ngio/hcs/__init__.py +1 -1
- ngio/hcs/{plate.py → _plate.py} +23 -8
- ngio/images/__init__.py +3 -3
- ngio/images/{image.py → _image.py} +26 -21
- ngio/images/{label.py → _label.py} +6 -4
- ngio/images/{masked_image.py → _masked_image.py} +2 -2
- ngio/images/{ome_zarr_container.py → _ome_zarr_container.py} +55 -20
- ngio/ome_zarr_meta/ngio_specs/_axes.py +4 -7
- ngio/ome_zarr_meta/ngio_specs/_channels.py +41 -29
- ngio/tables/__init__.py +6 -6
- ngio/tables/{tables_container.py → _tables_container.py} +29 -16
- ngio/tables/backends/_table_backends.py +34 -12
- ngio/tables/v1/_condition_table.py +1 -1
- ngio/tables/v1/_feature_table.py +2 -2
- ngio/tables/v1/_generic_table.py +1 -1
- ngio/tables/v1/_roi_table.py +1 -1
- ngio/utils/_fractal_fsspec_store.py +1 -1
- {ngio-0.3.0a1.dist-info → ngio-0.3.1.dist-info}/METADATA +68 -35
- {ngio-0.3.0a1.dist-info → ngio-0.3.1.dist-info}/RECORD +26 -26
- /ngio/images/{abstract_image.py → _abstract_image.py} +0 -0
- /ngio/images/{create.py → _create.py} +0 -0
- /ngio/tables/{abstract_table.py → _abstract_table.py} +0 -0
- {ngio-0.3.0a1.dist-info → ngio-0.3.1.dist-info}/WHEEL +0 -0
- {ngio-0.3.0a1.dist-info → ngio-0.3.1.dist-info}/licenses/LICENSE +0 -0
ngio/common/_array_pipe.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from collections.abc import Collection, Iterable
|
|
2
2
|
from typing import Literal
|
|
3
3
|
|
|
4
|
-
import dask
|
|
5
|
-
import dask.delayed
|
|
4
|
+
import dask.array as da
|
|
6
5
|
import numpy as np
|
|
7
6
|
import zarr
|
|
7
|
+
from dask.array import Array as DaskArray
|
|
8
|
+
from dask.delayed import Delayed, delayed
|
|
8
9
|
|
|
9
10
|
from ngio.common._axes_transforms import transform_dask_array, transform_numpy_array
|
|
10
11
|
from ngio.common._common_types import ArrayLike
|
|
@@ -55,26 +56,26 @@ def _numpy_get_pipe(
|
|
|
55
56
|
slices: SliceTransform,
|
|
56
57
|
transformations: tuple[AxesTransformation, ...],
|
|
57
58
|
) -> np.ndarray:
|
|
58
|
-
|
|
59
|
-
return transform_numpy_array(
|
|
59
|
+
_array = numpy_get_slice(array, slices)
|
|
60
|
+
return transform_numpy_array(_array, transformations)
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
def _delayed_numpy_get_pipe(
|
|
63
64
|
array: zarr.Array,
|
|
64
65
|
slices: SliceTransform,
|
|
65
66
|
transformations: tuple[AxesTransformation, ...],
|
|
66
|
-
) ->
|
|
67
|
-
|
|
68
|
-
return
|
|
67
|
+
) -> Delayed:
|
|
68
|
+
_array = delayed(numpy_get_slice)(array, slices)
|
|
69
|
+
return delayed(transform_numpy_array)(_array, transformations)
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
def _dask_get_pipe(
|
|
72
73
|
array: zarr.Array,
|
|
73
74
|
slices: SliceTransform,
|
|
74
75
|
transformations: tuple[AxesTransformation, ...],
|
|
75
|
-
) ->
|
|
76
|
-
|
|
77
|
-
return transform_dask_array(
|
|
76
|
+
) -> DaskArray:
|
|
77
|
+
_array = dask_get_slice(array, slices)
|
|
78
|
+
return transform_dask_array(_array, transformations)
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
def _numpy_set_pipe(
|
|
@@ -89,22 +90,22 @@ def _numpy_set_pipe(
|
|
|
89
90
|
|
|
90
91
|
def _dask_set_pipe(
|
|
91
92
|
array: zarr.Array,
|
|
92
|
-
patch:
|
|
93
|
+
patch: DaskArray,
|
|
93
94
|
slices: SliceTransform,
|
|
94
95
|
transformations: tuple[AxesTransformation, ...],
|
|
95
96
|
) -> None:
|
|
96
|
-
|
|
97
|
-
dask_set_slice(array,
|
|
97
|
+
_patch = transform_dask_array(patch, transformations)
|
|
98
|
+
dask_set_slice(array, _patch, slices)
|
|
98
99
|
|
|
99
100
|
|
|
100
101
|
def _delayed_numpy_set_pipe(
|
|
101
102
|
array: zarr.Array,
|
|
102
|
-
patch: np.ndarray,
|
|
103
|
+
patch: np.ndarray | Delayed,
|
|
103
104
|
slices: SliceTransform,
|
|
104
105
|
transformations: tuple[AxesTransformation, ...],
|
|
105
|
-
) ->
|
|
106
|
-
|
|
107
|
-
return
|
|
106
|
+
) -> Delayed:
|
|
107
|
+
_patch = delayed(transform_numpy_array)(patch, transformations)
|
|
108
|
+
return delayed(numpy_set_slice)(array, _patch, slices)
|
|
108
109
|
|
|
109
110
|
|
|
110
111
|
def get_pipe(
|
|
@@ -144,7 +145,7 @@ def set_pipe(
|
|
|
144
145
|
slices, transformations = _compute_to_disk_transforms(
|
|
145
146
|
dimensions=dimensions, axes_order=axes_order, **slice_kwargs
|
|
146
147
|
)
|
|
147
|
-
if isinstance(patch,
|
|
148
|
+
if isinstance(patch, DaskArray):
|
|
148
149
|
_dask_set_pipe(
|
|
149
150
|
array=array, patch=patch, slices=slices, transformations=transformations
|
|
150
151
|
)
|
|
@@ -152,7 +153,7 @@ def set_pipe(
|
|
|
152
153
|
_numpy_set_pipe(
|
|
153
154
|
array=array, patch=patch, slices=slices, transformations=transformations
|
|
154
155
|
)
|
|
155
|
-
elif isinstance(patch,
|
|
156
|
+
elif isinstance(patch, Delayed):
|
|
156
157
|
_delayed_numpy_set_pipe(
|
|
157
158
|
array=array, patch=patch, slices=slices, transformations=transformations
|
|
158
159
|
)
|
|
@@ -193,12 +194,15 @@ def _mask_pipe_common(
|
|
|
193
194
|
**slice_kwargs,
|
|
194
195
|
)
|
|
195
196
|
|
|
196
|
-
if isinstance(array_patch, np.ndarray):
|
|
197
|
+
if isinstance(array_patch, np.ndarray) and isinstance(label_patch, np.ndarray):
|
|
197
198
|
label_patch = np.broadcast_to(label_patch, array_patch.shape)
|
|
198
|
-
elif isinstance(array_patch,
|
|
199
|
-
label_patch =
|
|
199
|
+
elif isinstance(array_patch, DaskArray) and isinstance(label_patch, DaskArray):
|
|
200
|
+
label_patch = da.broadcast_to(label_patch, array_patch.shape)
|
|
200
201
|
else:
|
|
201
|
-
raise NgioValueError(
|
|
202
|
+
raise NgioValueError(
|
|
203
|
+
"Incompatible types for array and label: "
|
|
204
|
+
f"{type(array_patch)} and {type(label_patch)}"
|
|
205
|
+
)
|
|
202
206
|
|
|
203
207
|
mask = label_patch == label
|
|
204
208
|
return array_patch, mask
|
|
@@ -225,7 +229,14 @@ def get_masked_pipe(
|
|
|
225
229
|
mode=mode,
|
|
226
230
|
**slice_kwargs,
|
|
227
231
|
)
|
|
228
|
-
array_patch
|
|
232
|
+
if isinstance(array_patch, np.ndarray):
|
|
233
|
+
array_patch[~mask] = 0
|
|
234
|
+
elif isinstance(array_patch, DaskArray):
|
|
235
|
+
array_patch = da.where(mask, array_patch, 0)
|
|
236
|
+
else:
|
|
237
|
+
raise NgioValueError(
|
|
238
|
+
"Mode not yet supported for masked array. Expected a numpy or dask array."
|
|
239
|
+
)
|
|
229
240
|
return array_patch
|
|
230
241
|
|
|
231
242
|
|
|
@@ -240,7 +251,7 @@ def set_masked_pipe(
|
|
|
240
251
|
axes_order: Collection[str] | None = None,
|
|
241
252
|
**slice_kwargs: slice | int | Iterable[int],
|
|
242
253
|
):
|
|
243
|
-
if isinstance(patch,
|
|
254
|
+
if isinstance(patch, DaskArray):
|
|
244
255
|
mode = "dask"
|
|
245
256
|
elif isinstance(patch, np.ndarray):
|
|
246
257
|
mode = "numpy"
|
|
@@ -259,7 +270,19 @@ def set_masked_pipe(
|
|
|
259
270
|
mode=mode,
|
|
260
271
|
**slice_kwargs,
|
|
261
272
|
)
|
|
262
|
-
|
|
273
|
+
if isinstance(patch, np.ndarray):
|
|
274
|
+
assert isinstance(array_patch, np.ndarray)
|
|
275
|
+
_patch = np.where(mask, patch, array_patch)
|
|
276
|
+
elif isinstance(patch, DaskArray):
|
|
277
|
+
_patch = da.where(mask, patch, array_patch)
|
|
278
|
+
else:
|
|
279
|
+
raise NgioValueError(
|
|
280
|
+
"Mode not yet supported for masked array. Expected a numpy or dask array."
|
|
281
|
+
)
|
|
263
282
|
set_pipe(
|
|
264
|
-
array,
|
|
283
|
+
array,
|
|
284
|
+
_patch,
|
|
285
|
+
dimensions=dimensions_array,
|
|
286
|
+
axes_order=axes_order,
|
|
287
|
+
**slice_kwargs,
|
|
265
288
|
)
|
ngio/common/_table_ops.py
CHANGED
ngio/hcs/__init__.py
CHANGED
ngio/hcs/{plate.py → _plate.py}
RENAMED
|
@@ -39,7 +39,6 @@ from ngio.tables import (
|
|
|
39
39
|
)
|
|
40
40
|
from ngio.utils import (
|
|
41
41
|
AccessModeLiteral,
|
|
42
|
-
NgioValidationError,
|
|
43
42
|
NgioValueError,
|
|
44
43
|
StoreOrGroup,
|
|
45
44
|
ZarrGroupHandler,
|
|
@@ -805,22 +804,38 @@ class OmeZarrPlate:
|
|
|
805
804
|
parallel_safe=parallel_safe,
|
|
806
805
|
)
|
|
807
806
|
|
|
808
|
-
|
|
809
|
-
def tables_container(self) -> TablesContainer:
|
|
807
|
+
def _get_tables_container(self) -> TablesContainer | None:
|
|
810
808
|
"""Return the tables container."""
|
|
811
809
|
if self._tables_container is None:
|
|
812
|
-
|
|
813
|
-
if
|
|
814
|
-
|
|
810
|
+
_tables_container = _default_table_container(self._group_handler)
|
|
811
|
+
if _tables_container is None:
|
|
812
|
+
return None
|
|
813
|
+
self._tables_container = _tables_container
|
|
815
814
|
return self._tables_container
|
|
816
815
|
|
|
817
|
-
|
|
816
|
+
@property
|
|
817
|
+
def tables_container(self) -> TablesContainer:
|
|
818
|
+
"""Return the tables container."""
|
|
819
|
+
_table_container = self._get_tables_container()
|
|
820
|
+
if _table_container is None:
|
|
821
|
+
raise NgioValueError(
|
|
822
|
+
"No tables container found. Please add a tables container to the plate."
|
|
823
|
+
)
|
|
824
|
+
return _table_container
|
|
825
|
+
|
|
826
|
+
def list_tables(self, filter_types: TypedTable | str | None = None) -> list[str]:
|
|
818
827
|
"""List all tables in the image."""
|
|
819
828
|
return self.tables_container.list(filter_types=filter_types)
|
|
820
829
|
|
|
821
830
|
def list_roi_tables(self) -> list[str]:
|
|
822
831
|
"""List all ROI tables in the image."""
|
|
823
|
-
|
|
832
|
+
masking_roi = self.tables_container.list(
|
|
833
|
+
filter_types="masking_roi_table",
|
|
834
|
+
)
|
|
835
|
+
roi = self.tables_container.list(
|
|
836
|
+
filter_types="roi_table",
|
|
837
|
+
)
|
|
838
|
+
return masking_roi + roi
|
|
824
839
|
|
|
825
840
|
def get_roi_table(self, name: str) -> RoiTable:
|
|
826
841
|
"""Get a ROI table from the image.
|
ngio/images/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""OME-Zarr object models."""
|
|
2
2
|
|
|
3
|
-
from ngio.images.
|
|
4
|
-
from ngio.images.
|
|
5
|
-
from ngio.images.
|
|
3
|
+
from ngio.images._image import Image, ImagesContainer
|
|
4
|
+
from ngio.images._label import Label, LabelsContainer
|
|
5
|
+
from ngio.images._ome_zarr_container import (
|
|
6
6
|
OmeZarrContainer,
|
|
7
7
|
create_empty_ome_zarr,
|
|
8
8
|
create_ome_zarr_from_array,
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
from collections.abc import Collection
|
|
4
4
|
from typing import Literal
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import dask.array as da
|
|
7
7
|
|
|
8
8
|
from ngio.common import Dimensions
|
|
9
|
-
from ngio.images.
|
|
10
|
-
from ngio.images.
|
|
9
|
+
from ngio.images._abstract_image import AbstractImage, consolidate_image
|
|
10
|
+
from ngio.images._create import create_empty_image_container
|
|
11
11
|
from ngio.ome_zarr_meta import (
|
|
12
12
|
ImageMetaHandler,
|
|
13
13
|
NgioImageMeta,
|
|
@@ -152,31 +152,33 @@ class ImagesContainer:
|
|
|
152
152
|
|
|
153
153
|
def set_channel_meta(
|
|
154
154
|
self,
|
|
155
|
-
labels: Collection[str] | int | None = None,
|
|
156
|
-
wavelength_id: Collection[str] | None = None,
|
|
157
|
-
start: Collection[float] | None = None,
|
|
158
|
-
end: Collection[float] | None = None,
|
|
155
|
+
labels: Collection[str | None] | int | None = None,
|
|
156
|
+
wavelength_id: Collection[str | None] | None = None,
|
|
157
|
+
start: Collection[float | None] | None = None,
|
|
158
|
+
end: Collection[float | None] | None = None,
|
|
159
159
|
percentiles: tuple[float, float] | None = None,
|
|
160
|
-
colors: Collection[str] | None = None,
|
|
161
|
-
active: Collection[bool] | None = None,
|
|
160
|
+
colors: Collection[str | None] | None = None,
|
|
161
|
+
active: Collection[bool | None] | None = None,
|
|
162
162
|
**omero_kwargs: dict,
|
|
163
163
|
) -> None:
|
|
164
164
|
"""Create a ChannelsMeta object with the default unit.
|
|
165
165
|
|
|
166
166
|
Args:
|
|
167
|
-
labels(Collection[str] | int): The list of channels names
|
|
168
|
-
If an integer is provided, the channels will
|
|
169
|
-
|
|
167
|
+
labels(Collection[str | None] | int): The list of channels names
|
|
168
|
+
in the image. If an integer is provided, the channels will
|
|
169
|
+
be named "channel_i".
|
|
170
|
+
wavelength_id(Collection[str | None]): The wavelength ID of the channel.
|
|
170
171
|
If None, the wavelength ID will be the same as the channel name.
|
|
171
|
-
start(Collection[float
|
|
172
|
+
start(Collection[float | None]): The start value for each channel.
|
|
172
173
|
If None, the start value will be computed from the image.
|
|
173
|
-
end(Collection[float
|
|
174
|
+
end(Collection[float | None]): The end value for each channel.
|
|
174
175
|
If None, the end value will be computed from the image.
|
|
175
|
-
percentiles(tuple[float, float] | None): The start and end
|
|
176
|
-
for each channel. If None, the percentiles will
|
|
177
|
-
|
|
176
|
+
percentiles(tuple[float, float] | None): The start and end
|
|
177
|
+
percentiles for each channel. If None, the percentiles will
|
|
178
|
+
not be computed.
|
|
179
|
+
colors(Collection[str | None]): The list of colors for the
|
|
178
180
|
channels. If None, the colors will be random.
|
|
179
|
-
active (Collection[bool
|
|
181
|
+
active (Collection[bool | None]): Whether the channel should
|
|
180
182
|
be shown by default.
|
|
181
183
|
omero_kwargs(dict): Extra fields to store in the omero attributes.
|
|
182
184
|
"""
|
|
@@ -376,9 +378,12 @@ def compute_image_percentile(
|
|
|
376
378
|
starts, ends = [], []
|
|
377
379
|
for c in range(image.num_channels):
|
|
378
380
|
if image.num_channels == 1:
|
|
379
|
-
data = image.get_array(mode="dask")
|
|
381
|
+
data = image.get_array(mode="dask")
|
|
380
382
|
else:
|
|
381
|
-
data = image.get_array(c=c, mode="dask")
|
|
383
|
+
data = image.get_array(c=c, mode="dask")
|
|
384
|
+
|
|
385
|
+
assert isinstance(data, da.Array), "Data must be a Dask array."
|
|
386
|
+
data = da.ravel(data)
|
|
382
387
|
# remove all the zeros
|
|
383
388
|
mask = data > 1e-16
|
|
384
389
|
data = data[mask]
|
|
@@ -391,7 +396,7 @@ def compute_image_percentile(
|
|
|
391
396
|
# compute the percentiles
|
|
392
397
|
_s_perc, _e_perc = da.percentile(
|
|
393
398
|
data, [start_percentile, end_percentile], method="nearest"
|
|
394
|
-
).compute()
|
|
399
|
+
).compute() # type: ignore
|
|
395
400
|
|
|
396
401
|
starts.append(float(_s_perc))
|
|
397
402
|
ends.append(float(_e_perc))
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
from collections.abc import Collection
|
|
4
4
|
from typing import Literal
|
|
5
5
|
|
|
6
|
+
import dask.array as da
|
|
7
|
+
|
|
6
8
|
from ngio.common import compute_masking_roi
|
|
7
|
-
from ngio.images.
|
|
8
|
-
from ngio.images.
|
|
9
|
-
from ngio.images.
|
|
9
|
+
from ngio.images._abstract_image import AbstractImage, consolidate_image
|
|
10
|
+
from ngio.images._create import create_empty_label_container
|
|
11
|
+
from ngio.images._image import Image
|
|
10
12
|
from ngio.ome_zarr_meta import (
|
|
11
13
|
LabelMetaHandler,
|
|
12
14
|
NgioLabelMeta,
|
|
@@ -309,6 +311,6 @@ def build_masking_roi_table(label: Label) -> MaskingRoiTable:
|
|
|
309
311
|
raise NgioValueError("Time series labels are not supported.")
|
|
310
312
|
|
|
311
313
|
array = label.get_array(axes_order=["z", "y", "x"], mode="dask")
|
|
312
|
-
|
|
314
|
+
assert isinstance(array, da.Array), "Array must be a Dask array."
|
|
313
315
|
rois = compute_masking_roi(array, label.pixel_size)
|
|
314
316
|
return MaskingRoiTable(rois, reference_label=label.meta.name)
|
|
@@ -4,8 +4,8 @@ from collections.abc import Collection, Iterable
|
|
|
4
4
|
from typing import Literal
|
|
5
5
|
|
|
6
6
|
from ngio.common import ArrayLike, get_masked_pipe, roi_to_slice_kwargs, set_masked_pipe
|
|
7
|
-
from ngio.images.
|
|
8
|
-
from ngio.images.
|
|
7
|
+
from ngio.images._image import Image
|
|
8
|
+
from ngio.images._label import Label
|
|
9
9
|
from ngio.ome_zarr_meta import ImageMetaHandler, LabelMetaHandler
|
|
10
10
|
from ngio.tables import MaskingRoiTable
|
|
11
11
|
from ngio.utils import (
|
|
@@ -5,10 +5,10 @@ from collections.abc import Collection
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
-
from ngio.images.
|
|
9
|
-
from ngio.images.
|
|
10
|
-
from ngio.images.
|
|
11
|
-
from ngio.images.
|
|
8
|
+
from ngio.images._create import create_empty_image_container
|
|
9
|
+
from ngio.images._image import Image, ImagesContainer
|
|
10
|
+
from ngio.images._label import Label, LabelsContainer
|
|
11
|
+
from ngio.images._masked_image import MaskedImage, MaskedLabel
|
|
12
12
|
from ngio.ome_zarr_meta import (
|
|
13
13
|
NgioImageMeta,
|
|
14
14
|
PixelSize,
|
|
@@ -68,7 +68,7 @@ class OmeZarrContainer:
|
|
|
68
68
|
group_handler: ZarrGroupHandler,
|
|
69
69
|
table_container: TablesContainer | None = None,
|
|
70
70
|
label_container: LabelsContainer | None = None,
|
|
71
|
-
|
|
71
|
+
validate_paths: bool = False,
|
|
72
72
|
) -> None:
|
|
73
73
|
"""Initialize the OmeZarrContainer."""
|
|
74
74
|
self._group_handler = group_handler
|
|
@@ -77,6 +77,10 @@ class OmeZarrContainer:
|
|
|
77
77
|
self._labels_container = label_container
|
|
78
78
|
self._tables_container = table_container
|
|
79
79
|
|
|
80
|
+
if validate_paths:
|
|
81
|
+
for level_path in self._images_container.levels_paths:
|
|
82
|
+
self.get_image(path=level_path)
|
|
83
|
+
|
|
80
84
|
def __repr__(self) -> str:
|
|
81
85
|
"""Return a string representation of the image."""
|
|
82
86
|
num_labels = len(self.list_labels())
|
|
@@ -99,24 +103,40 @@ class OmeZarrContainer:
|
|
|
99
103
|
"""Return the image container."""
|
|
100
104
|
return self._images_container
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
def labels_container(self) -> LabelsContainer:
|
|
106
|
+
def _get_labels_container(self) -> LabelsContainer | None:
|
|
104
107
|
"""Return the labels container."""
|
|
105
108
|
if self._labels_container is None:
|
|
106
|
-
|
|
107
|
-
if
|
|
108
|
-
|
|
109
|
+
_labels_container = _default_label_container(self._group_handler)
|
|
110
|
+
if _labels_container is None:
|
|
111
|
+
return None
|
|
112
|
+
self._labels_container = _labels_container
|
|
109
113
|
return self._labels_container
|
|
110
114
|
|
|
111
115
|
@property
|
|
112
|
-
def
|
|
116
|
+
def labels_container(self) -> LabelsContainer:
|
|
117
|
+
"""Return the labels container."""
|
|
118
|
+
_labels_container = self._get_labels_container()
|
|
119
|
+
if _labels_container is None:
|
|
120
|
+
raise NgioValidationError("No labels found in the image.")
|
|
121
|
+
return _labels_container
|
|
122
|
+
|
|
123
|
+
def _get_tables_container(self) -> TablesContainer | None:
|
|
113
124
|
"""Return the tables container."""
|
|
114
125
|
if self._tables_container is None:
|
|
115
|
-
|
|
116
|
-
if
|
|
117
|
-
|
|
126
|
+
_tables_container = _default_table_container(self._group_handler)
|
|
127
|
+
if _tables_container is None:
|
|
128
|
+
return None
|
|
129
|
+
self._tables_container = _tables_container
|
|
118
130
|
return self._tables_container
|
|
119
131
|
|
|
132
|
+
@property
|
|
133
|
+
def tables_container(self) -> TablesContainer:
|
|
134
|
+
"""Return the tables container."""
|
|
135
|
+
_tables_container = self._get_tables_container()
|
|
136
|
+
if _tables_container is None:
|
|
137
|
+
raise NgioValidationError("No tables found in the image.")
|
|
138
|
+
return _tables_container
|
|
139
|
+
|
|
120
140
|
@property
|
|
121
141
|
def image_meta(self) -> NgioImageMeta:
|
|
122
142
|
"""Return the image metadata."""
|
|
@@ -332,7 +352,7 @@ class OmeZarrContainer:
|
|
|
332
352
|
|
|
333
353
|
new_ome_zarr = OmeZarrContainer(
|
|
334
354
|
group_handler=handler,
|
|
335
|
-
|
|
355
|
+
validate_paths=False,
|
|
336
356
|
)
|
|
337
357
|
|
|
338
358
|
if copy_labels:
|
|
@@ -346,13 +366,25 @@ class OmeZarrContainer:
|
|
|
346
366
|
)
|
|
347
367
|
return new_ome_zarr
|
|
348
368
|
|
|
349
|
-
def list_tables(self, filter_types: str | None = None) -> list[str]:
|
|
369
|
+
def list_tables(self, filter_types: TypedTable | str | None = None) -> list[str]:
|
|
350
370
|
"""List all tables in the image."""
|
|
351
|
-
|
|
371
|
+
table_container = self._get_tables_container()
|
|
372
|
+
if table_container is None:
|
|
373
|
+
return []
|
|
374
|
+
|
|
375
|
+
return table_container.list(
|
|
376
|
+
filter_types=filter_types,
|
|
377
|
+
)
|
|
352
378
|
|
|
353
379
|
def list_roi_tables(self) -> list[str]:
|
|
354
380
|
"""List all ROI tables in the image."""
|
|
355
|
-
|
|
381
|
+
masking_roi = self.tables_container.list(
|
|
382
|
+
filter_types="masking_roi_table",
|
|
383
|
+
)
|
|
384
|
+
roi = self.tables_container.list(
|
|
385
|
+
filter_types="roi_table",
|
|
386
|
+
)
|
|
387
|
+
return masking_roi + roi
|
|
356
388
|
|
|
357
389
|
def get_roi_table(self, name: str) -> RoiTable:
|
|
358
390
|
"""Get a ROI table from the image.
|
|
@@ -479,7 +511,10 @@ class OmeZarrContainer:
|
|
|
479
511
|
|
|
480
512
|
def list_labels(self) -> list[str]:
|
|
481
513
|
"""List all labels in the image."""
|
|
482
|
-
|
|
514
|
+
label_container = self._get_labels_container()
|
|
515
|
+
if label_container is None:
|
|
516
|
+
return []
|
|
517
|
+
return label_container.list()
|
|
483
518
|
|
|
484
519
|
def get_label(
|
|
485
520
|
self,
|
|
@@ -597,7 +632,7 @@ def open_ome_zarr_container(
|
|
|
597
632
|
handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
|
|
598
633
|
return OmeZarrContainer(
|
|
599
634
|
group_handler=handler,
|
|
600
|
-
|
|
635
|
+
validate_paths=validate_arrays,
|
|
601
636
|
)
|
|
602
637
|
|
|
603
638
|
|
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from collections.abc import Collection
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from logging import Logger
|
|
6
5
|
from typing import Literal, TypeVar
|
|
7
6
|
|
|
8
7
|
import numpy as np
|
|
9
8
|
from pydantic import BaseModel, ConfigDict, Field
|
|
10
9
|
|
|
11
|
-
from ngio.utils import NgioValidationError, NgioValueError
|
|
12
|
-
|
|
13
|
-
logger = Logger(__name__)
|
|
10
|
+
from ngio.utils import NgioValidationError, NgioValueError, ngio_logger
|
|
14
11
|
|
|
15
12
|
T = TypeVar("T")
|
|
16
13
|
|
|
@@ -102,20 +99,20 @@ class Axis(BaseModel):
|
|
|
102
99
|
def implicit_type_cast(self, cast_type: AxisType) -> "Axis":
|
|
103
100
|
unit = self.unit
|
|
104
101
|
if self.axis_type != cast_type:
|
|
105
|
-
|
|
102
|
+
ngio_logger.warning(
|
|
106
103
|
f"Axis {self.on_disk_name} has type {self.axis_type}. "
|
|
107
104
|
f"Casting to {cast_type}."
|
|
108
105
|
)
|
|
109
106
|
|
|
110
107
|
if cast_type == AxisType.time and unit is None:
|
|
111
|
-
|
|
108
|
+
ngio_logger.warning(
|
|
112
109
|
f"Time axis {self.on_disk_name} has unit {self.unit}. "
|
|
113
110
|
f"Casting to {DefaultSpaceUnit}."
|
|
114
111
|
)
|
|
115
112
|
unit = DefaultTimeUnit
|
|
116
113
|
|
|
117
114
|
if cast_type == AxisType.space and unit is None:
|
|
118
|
-
|
|
115
|
+
ngio_logger.warning(
|
|
119
116
|
f"Space axis {self.on_disk_name} has unit {unit}. "
|
|
120
117
|
f"Casting to {DefaultSpaceUnit}."
|
|
121
118
|
)
|
|
@@ -329,33 +329,35 @@ class ChannelsMeta(BaseModel):
|
|
|
329
329
|
@classmethod
|
|
330
330
|
def default_init(
|
|
331
331
|
cls,
|
|
332
|
-
labels: Collection[str] | int,
|
|
333
|
-
wavelength_id: Collection[str] | None = None,
|
|
334
|
-
colors: Collection[str | NgioColors] | None = None,
|
|
335
|
-
start: Collection[int | float] | int | float | None = None,
|
|
336
|
-
end: Collection[int | float] | int | float | None = None,
|
|
337
|
-
active: Collection[bool] | None = None,
|
|
332
|
+
labels: Collection[str | None] | int,
|
|
333
|
+
wavelength_id: Collection[str | None] | None = None,
|
|
334
|
+
colors: Collection[str | NgioColors | None] | None = None,
|
|
335
|
+
start: Collection[int | float | None] | int | float | None = None,
|
|
336
|
+
end: Collection[int | float | None] | int | float | None = None,
|
|
337
|
+
active: Collection[bool | None] | None = None,
|
|
338
338
|
data_type: Any = np.uint16,
|
|
339
339
|
**omero_kwargs: dict,
|
|
340
340
|
) -> "ChannelsMeta":
|
|
341
341
|
"""Create a ChannelsMeta object with the default unit.
|
|
342
342
|
|
|
343
343
|
Args:
|
|
344
|
-
labels(Collection[str] | int): The list of channels names
|
|
345
|
-
If an integer is provided, the channels will be
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
344
|
+
labels(Collection[str | None] | int): The list of channels names
|
|
345
|
+
in the image. If an integer is provided, the channels will be
|
|
346
|
+
named "channel_i".
|
|
347
|
+
wavelength_id(Collection[str | None] | None): The wavelength ID of the
|
|
348
|
+
channel. If None, the wavelength ID will be the same as the
|
|
349
|
+
channel name.
|
|
350
|
+
colors(Collection[str | NgioColors | None] | None): The list of
|
|
351
|
+
colors for the channels. If None, the colors will be random.
|
|
352
|
+
start(Collection[int | float | None] | int | float | None): The start
|
|
353
|
+
value of the channel. If None, the start value will be the
|
|
354
|
+
minimum value of the data type.
|
|
355
|
+
end(Collection[int | float | None] | int | float | None): The end
|
|
356
|
+
value of the channel. If None, the end value will be the
|
|
357
|
+
maximum value of the data type.
|
|
356
358
|
data_type(Any): The data type of the channel. Will be used to set the
|
|
357
359
|
min and max values of the channel.
|
|
358
|
-
active (Collection[bool] | None):
|
|
360
|
+
active (Collection[bool | None] | None): Whether the channel should
|
|
359
361
|
be shown by default.
|
|
360
362
|
omero_kwargs(dict): Extra fields to store in the omero attributes.
|
|
361
363
|
"""
|
|
@@ -366,25 +368,35 @@ class ChannelsMeta(BaseModel):
|
|
|
366
368
|
labels = _check_unique(labels)
|
|
367
369
|
|
|
368
370
|
_wavelength_id: Collection[str | None] = [None] * len(labels)
|
|
369
|
-
if
|
|
371
|
+
if wavelength_id is None:
|
|
372
|
+
_wavelength_id: Collection[str | None] = [None] * len(labels)
|
|
373
|
+
else:
|
|
370
374
|
_wavelength_id = _check_elements(wavelength_id, str)
|
|
371
375
|
_wavelength_id = _check_unique(wavelength_id)
|
|
372
376
|
|
|
373
|
-
|
|
374
|
-
|
|
377
|
+
if colors is None:
|
|
378
|
+
_colors = [NgioColors.semi_random_pick(label) for label in labels]
|
|
379
|
+
else:
|
|
375
380
|
_colors = _check_elements(colors, str | NgioColors)
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
|
|
382
|
+
if start is None:
|
|
383
|
+
_start = [None] * len(labels)
|
|
384
|
+
elif isinstance(start, int | float):
|
|
385
|
+
_start = [start] * len(labels)
|
|
386
|
+
else:
|
|
379
387
|
_start = _check_elements(start, (int, float))
|
|
380
388
|
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
if end is None:
|
|
390
|
+
_end = [None] * len(labels)
|
|
391
|
+
elif isinstance(end, int | float):
|
|
392
|
+
_end = [end] * len(labels)
|
|
393
|
+
else:
|
|
383
394
|
_end = _check_elements(end, (int, float))
|
|
384
395
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
396
|
+
if active is None:
|
|
397
|
+
_active = [True] * len(labels)
|
|
398
|
+
else:
|
|
399
|
+
_active = _check_elements(active, (bool,))
|
|
388
400
|
|
|
389
401
|
all_lengths = [
|
|
390
402
|
len(labels),
|
ngio/tables/__init__.py
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"""Ngio Tables implementations."""
|
|
2
2
|
|
|
3
|
-
from ngio.tables.
|
|
4
|
-
ImplementedTableBackends,
|
|
5
|
-
TableBackend,
|
|
6
|
-
TableBackendProtocol,
|
|
7
|
-
)
|
|
8
|
-
from ngio.tables.tables_container import (
|
|
3
|
+
from ngio.tables._tables_container import (
|
|
9
4
|
ConditionTable,
|
|
10
5
|
FeatureTable,
|
|
11
6
|
GenericRoiTable,
|
|
@@ -19,6 +14,11 @@ from ngio.tables.tables_container import (
|
|
|
19
14
|
open_table_as,
|
|
20
15
|
open_tables_container,
|
|
21
16
|
)
|
|
17
|
+
from ngio.tables.backends import (
|
|
18
|
+
ImplementedTableBackends,
|
|
19
|
+
TableBackend,
|
|
20
|
+
TableBackendProtocol,
|
|
21
|
+
)
|
|
22
22
|
from ngio.tables.v1._generic_table import GenericTable
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
@@ -126,13 +126,18 @@ class Table(Protocol):
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
TypedTable = Literal[
|
|
129
|
+
"generic_table",
|
|
129
130
|
"roi_table",
|
|
130
131
|
"masking_roi_table",
|
|
131
132
|
"feature_table",
|
|
132
|
-
"generic_roi_table",
|
|
133
133
|
"condition_table",
|
|
134
134
|
]
|
|
135
135
|
|
|
136
|
+
TypedRoiTable = Literal[
|
|
137
|
+
"roi_table",
|
|
138
|
+
"masking_roi_table",
|
|
139
|
+
]
|
|
140
|
+
|
|
136
141
|
TableType = TypeVar("TableType", bound=Table)
|
|
137
142
|
|
|
138
143
|
|
|
@@ -192,19 +197,34 @@ class ImplementedTables:
|
|
|
192
197
|
table = table_cls.from_handler(handler=handler, backend=backend)
|
|
193
198
|
return table
|
|
194
199
|
|
|
195
|
-
def
|
|
200
|
+
def _add_implementation(
|
|
201
|
+
self, handler: type[Table], name: str, overwrite: bool = False
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Register a new table handler."""
|
|
204
|
+
if name in self._implemented_tables and not overwrite:
|
|
205
|
+
raise NgioValueError(
|
|
206
|
+
f"Table handler for {name} already implemented. "
|
|
207
|
+
"Use overwrite=True to replace it."
|
|
208
|
+
)
|
|
209
|
+
self._implemented_tables[name] = handler
|
|
210
|
+
|
|
211
|
+
def add_implementation(
|
|
212
|
+
self,
|
|
213
|
+
handler: type[Table],
|
|
214
|
+
overwrite: bool = False,
|
|
215
|
+
aliases: list[str] | None = None,
|
|
216
|
+
) -> None:
|
|
196
217
|
"""Register a new table handler."""
|
|
197
218
|
meta = TableMeta(
|
|
198
219
|
type=handler.table_type(),
|
|
199
220
|
table_version=handler.version(),
|
|
200
221
|
)
|
|
201
222
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
self._implemented_tables[meta.unique_name()] = handler
|
|
223
|
+
self._add_implementation(handler, meta.unique_name(), overwrite)
|
|
224
|
+
|
|
225
|
+
if aliases is not None:
|
|
226
|
+
for alias in aliases:
|
|
227
|
+
self._add_implementation(handler, alias, overwrite)
|
|
208
228
|
|
|
209
229
|
|
|
210
230
|
class TablesContainer:
|
|
@@ -240,14 +260,7 @@ class TablesContainer:
|
|
|
240
260
|
handler = self._group_handler.derive_handler(path=name)
|
|
241
261
|
return handler
|
|
242
262
|
|
|
243
|
-
def
|
|
244
|
-
"""List all ROI tables in the group."""
|
|
245
|
-
_tables = []
|
|
246
|
-
for _type in ["roi_table", "masking_roi_table"]:
|
|
247
|
-
_tables.extend(self.list(_type))
|
|
248
|
-
return _tables
|
|
249
|
-
|
|
250
|
-
def list(self, filter_types: str | None = None) -> list[str]:
|
|
263
|
+
def list(self, filter_types: TypedTable | str | None = None) -> list[str]:
|
|
251
264
|
"""List all labels in the group."""
|
|
252
265
|
tables = self._get_tables_list()
|
|
253
266
|
if filter_types is None:
|
|
@@ -180,24 +180,46 @@ class ImplementedTableBackends:
|
|
|
180
180
|
)
|
|
181
181
|
return backend
|
|
182
182
|
|
|
183
|
-
def
|
|
183
|
+
def _add_backend(
|
|
184
184
|
self,
|
|
185
|
-
|
|
185
|
+
table_backend: type[TableBackendProtocol],
|
|
186
|
+
name: str,
|
|
186
187
|
overwrite: bool = False,
|
|
187
|
-
):
|
|
188
|
+
) -> None:
|
|
188
189
|
"""Register a new handler."""
|
|
189
|
-
|
|
190
|
-
if backend_name in self._implemented_backends and not overwrite:
|
|
190
|
+
if name in self._implemented_backends and not overwrite:
|
|
191
191
|
raise NgioValueError(
|
|
192
|
-
f"Table backend {
|
|
192
|
+
f"Table backend {name} already implemented. "
|
|
193
193
|
"Use the `overwrite=True` parameter to overwrite it."
|
|
194
194
|
)
|
|
195
|
-
self._implemented_backends[
|
|
196
|
-
|
|
195
|
+
self._implemented_backends[name] = table_backend
|
|
197
196
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
def add_backend(
|
|
198
|
+
self,
|
|
199
|
+
table_backend: type[TableBackendProtocol],
|
|
200
|
+
overwrite: bool = False,
|
|
201
|
+
aliases: list[str] | None = None,
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Register a new handler."""
|
|
204
|
+
self._add_backend(
|
|
205
|
+
table_backend=table_backend,
|
|
206
|
+
name=table_backend.backend_name(),
|
|
207
|
+
overwrite=overwrite,
|
|
208
|
+
)
|
|
209
|
+
if aliases is not None:
|
|
210
|
+
for alias in aliases:
|
|
211
|
+
self._add_backend(
|
|
212
|
+
table_backend=table_backend, name=alias, overwrite=overwrite
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
ImplementedTableBackends().add_backend(AnnDataBackend, aliases=["anndata_v1"])
|
|
217
|
+
ImplementedTableBackends().add_backend(
|
|
218
|
+
JsonTableBackend, aliases=["experimental_json_v1"]
|
|
219
|
+
)
|
|
220
|
+
ImplementedTableBackends().add_backend(CsvTableBackend, aliases=["experimental_csv_v1"])
|
|
221
|
+
ImplementedTableBackends().add_backend(
|
|
222
|
+
ParquetTableBackend, aliases=["experimental_parquet_v1"]
|
|
223
|
+
)
|
|
202
224
|
|
|
203
225
|
TableBackend = Literal["anndata", "json", "csv", "parquet"] | str | TableBackendProtocol
|
ngio/tables/v1/_feature_table.py
CHANGED
|
@@ -8,7 +8,7 @@ from typing import Literal
|
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, Field
|
|
10
10
|
|
|
11
|
-
from ngio.tables.
|
|
11
|
+
from ngio.tables._abstract_table import AbstractBaseTable
|
|
12
12
|
from ngio.tables.backends import BackendMeta, TableBackend, TabularData
|
|
13
13
|
from ngio.utils import NgioValueError
|
|
14
14
|
from ngio.utils._zarr_utils import ZarrGroupHandler
|
|
@@ -26,7 +26,7 @@ class FeatureTableMeta(BackendMeta):
|
|
|
26
26
|
table_version: Literal["1"] = "1"
|
|
27
27
|
type: Literal["feature_table"] = "feature_table"
|
|
28
28
|
region: RegionMeta | None = None
|
|
29
|
-
instance_key: str = "label"
|
|
29
|
+
instance_key: str = "label" # Legacy field, kept for compatibility
|
|
30
30
|
# Backend metadata
|
|
31
31
|
index_key: str | None = "label"
|
|
32
32
|
index_type: Literal["int", "str"] | None = "int"
|
ngio/tables/v1/_generic_table.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Implementation of a generic table class."""
|
|
2
2
|
|
|
3
|
-
from ngio.tables.
|
|
3
|
+
from ngio.tables._abstract_table import AbstractBaseTable
|
|
4
4
|
from ngio.tables.backends import BackendMeta, TableBackend
|
|
5
5
|
from ngio.utils import ZarrGroupHandler
|
|
6
6
|
|
ngio/tables/v1/_roi_table.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ngio
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Next Generation file format IO
|
|
5
5
|
Project-URL: homepage, https://github.com/fractal-analytics-platform/ngio
|
|
6
6
|
Project-URL: repository, https://github.com/fractal-analytics-platform/ngio
|
|
@@ -63,7 +63,7 @@ Requires-Dist: pytest-cov; extra == 'test'
|
|
|
63
63
|
Requires-Dist: scikit-image; extra == 'test'
|
|
64
64
|
Description-Content-Type: text/markdown
|
|
65
65
|
|
|
66
|
-
#
|
|
66
|
+
# Ngio - Next Generation file format IO
|
|
67
67
|
|
|
68
68
|
[](https://github.com/lorenzocerrone/ngio/raw/main/LICENSE)
|
|
69
69
|
[](https://pypi.org/project/ngio)
|
|
@@ -71,36 +71,69 @@ Description-Content-Type: text/markdown
|
|
|
71
71
|
[](https://github.com/fractal-analytics-platform/ngio/actions/workflows/ci.yml)
|
|
72
72
|
[](https://codecov.io/gh/fractal-analytics-platform/ngio)
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
74
|
+
ngio is a Python library designed to simplify bioimage analysis workflows, offering an intuitive interface for working with OME-Zarr files.
|
|
75
|
+
|
|
76
|
+
## What is Ngio?
|
|
77
|
+
|
|
78
|
+
Ngio is built for the [OME-Zarr](https://ngff.openmicroscopy.org/) file format, a modern, cloud-optimized format for biological imaging data. OME-Zarr stores large, multi-dimensional microscopy images and metadata in an efficient and scalable way.
|
|
79
|
+
|
|
80
|
+
Ngio's mission is to streamline working with OME-Zarr files by providing a simple, object-based API for opening, exploring, and manipulating OME-Zarr images and high-content screening (HCS) plates. It also offers comprehensive support for labels, tables and regions of interest (ROIs), making it easy to extract and analyze specific regions in your data.
|
|
81
|
+
|
|
82
|
+
## Key Features
|
|
83
|
+
|
|
84
|
+
### 📊 Simple Object-Based API
|
|
85
|
+
|
|
86
|
+
- Easily open, explore, and manipulate OME-Zarr images and HCS plates
|
|
87
|
+
- Create and derive new images and labels with minimal boilerplate code
|
|
88
|
+
|
|
89
|
+
### 🔍 Rich Tables and Regions of Interest (ROI) Support
|
|
90
|
+
|
|
91
|
+
- Extract and analyze specific regions of interest
|
|
92
|
+
- Tight integration with [Tabular Data](https://fractal-analytics-platform.github.io/ngio/stable/table_specs/overview/)
|
|
93
|
+
|
|
94
|
+
### 🔄 Scalable Data Processing (Coming Soon)
|
|
95
|
+
|
|
96
|
+
- Powerful iterators for processing data at scale
|
|
97
|
+
- Efficient memory management for large datasets
|
|
98
|
+
|
|
99
|
+
## Installation
|
|
100
|
+
|
|
101
|
+
You can install ngio via pip:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install ngio
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To get started check out the [Quickstart Guide](https://fractal-analytics-platform.github.io/ngio/stable/getting_started/0_quickstart/).
|
|
108
|
+
|
|
109
|
+
## Supported OME-Zarr versions
|
|
110
|
+
|
|
111
|
+
Currently, ngio only supports OME-Zarr v0.4. Support for version 0.5 and higher is planned for future releases.
|
|
112
|
+
|
|
113
|
+
## Development Status
|
|
114
|
+
|
|
115
|
+
!!! warning
|
|
116
|
+
Ngio is under active development and is not yet stable. The API is subject to change, and bugs and breaking changes are expected.
|
|
117
|
+
We follow [Semantic Versioning](https://semver.org/). Which means for 0.x releases potentially breaking changes can be introduced in minor releases.
|
|
118
|
+
|
|
119
|
+
### Available Features
|
|
120
|
+
|
|
121
|
+
- ✅ OME-Zarr metadata handling and validation
|
|
122
|
+
- ✅ Image and label access across pyramid levels
|
|
123
|
+
- ✅ ROI and table support
|
|
124
|
+
- ✅ Streaming from remote sources
|
|
125
|
+
- ✅ Documentation and examples
|
|
126
|
+
|
|
127
|
+
### Upcoming Features
|
|
128
|
+
|
|
129
|
+
- Advanced image processing iterators
|
|
130
|
+
- Parallel processing capabilities
|
|
131
|
+
- Support for OME-Zarr v0.5 and Zarr v3
|
|
132
|
+
|
|
133
|
+
## Contributors
|
|
134
|
+
|
|
135
|
+
Ngio is developed at the [BioVisionCenter](https://www.biovisioncenter.uzh.ch/en.html), University of Zurich, by [@lorenzocerrone](https://github.com/lorenzocerrone) and [@jluethi](https://github.com/jluethi).
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
Ngio is released under the BSD-3-Clause License. See [LICENSE](https://github.com/fractal-analytics-platform/ngio/blob/main/LICENSE) for details.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
ngio/__init__.py,sha256=zce-RIfRhkiFyfKwO6s7A1-9ZCD3n6RuVuW3GeRj_VA,1309
|
|
2
2
|
ngio/common/__init__.py,sha256=BttXCAV2ks6_oXVDfapk1Lvlxp4J0ffX7-FXR3LL1BM,1834
|
|
3
|
-
ngio/common/_array_pipe.py,sha256=
|
|
3
|
+
ngio/common/_array_pipe.py,sha256=mvBsOtNSow9S6bSPMJ0288gCqQFYffGnaTTLyuB2XHw,8641
|
|
4
4
|
ngio/common/_axes_transforms.py,sha256=kWU0M5erNmgWBXdu5LNv-tLW3jqkT00MMYX7cz-kyHs,2035
|
|
5
5
|
ngio/common/_common_types.py,sha256=OkAYNSNjZkixL1MI-HPBVuXamheFBr862uJ4PvTxmhk,129
|
|
6
6
|
ngio/common/_dimensions.py,sha256=UV2XulWaROb3Y2f4fv27ZkTIu-MoS53U26aDkrv-_lk,3900
|
|
@@ -8,22 +8,22 @@ ngio/common/_masking_roi.py,sha256=-o6meGP17iTXEbkO9aGh1VX2drkc2laIcRJvCy_pRRM,4
|
|
|
8
8
|
ngio/common/_pyramid.py,sha256=SJzPauuduuqcm9B7nFCJhMTzIg6Knjsnp4CY4lN61Is,7411
|
|
9
9
|
ngio/common/_roi.py,sha256=dq1iVT8-G_zWuxcYWJeHfviBSbPgsyKUcDL3Vg6jx6I,5122
|
|
10
10
|
ngio/common/_slicer.py,sha256=AKpwXRncOmF9nhjKYma0C_41WqAgSv860beKGx-aw-0,3075
|
|
11
|
-
ngio/common/_table_ops.py,sha256=
|
|
11
|
+
ngio/common/_table_ops.py,sha256=ToNR7F053l1dubVv-gvBD-523T10HnE2UzqeU2BuNy8,15494
|
|
12
12
|
ngio/common/_zoom.py,sha256=KsURa5VuixmpbAAY5-6obmuQV8vfiHKZqBxZDXvchpM,5473
|
|
13
|
-
ngio/hcs/__init__.py,sha256=
|
|
14
|
-
ngio/hcs/
|
|
15
|
-
ngio/images/__init__.py,sha256=
|
|
16
|
-
ngio/images/
|
|
17
|
-
ngio/images/
|
|
18
|
-
ngio/images/
|
|
19
|
-
ngio/images/
|
|
20
|
-
ngio/images/
|
|
21
|
-
ngio/images/
|
|
13
|
+
ngio/hcs/__init__.py,sha256=G8j9vD-liLeB_UeGtKYIgshWvJnUA6ks9GwjvWBLdHs,357
|
|
14
|
+
ngio/hcs/_plate.py,sha256=Z8M3slG5t16MYvPwvAzj-e9O4yAswWZp7_EDBlAxiHc,44225
|
|
15
|
+
ngio/images/__init__.py,sha256=PlYvbHOMvZLDgn_PeGrytOEU3e_-AO8GWpRjEqoX_Gw,536
|
|
16
|
+
ngio/images/_abstract_image.py,sha256=8PNQPZjiDz-pcTFXSJAVw7nUr4yL_iRwqDEUTKkAnp0,10266
|
|
17
|
+
ngio/images/_create.py,sha256=XYn30m_2OSZeHHASYHc3eK9u_gZIYy9wo6mGdRGaq5c,9473
|
|
18
|
+
ngio/images/_image.py,sha256=B9MTk3Cei-FBR4jbhSujTSNcKM_ySape28yXR_JlY0A,17980
|
|
19
|
+
ngio/images/_label.py,sha256=Q1vPrXDcjJ7Gdd0wDz6NCVYQLNe1Ae6YDI4iK0oq31s,10557
|
|
20
|
+
ngio/images/_masked_image.py,sha256=sd6aQetNVwtDkwUvKX-lssQyHzt5m-CYZL8ZFkwDSn8,8533
|
|
21
|
+
ngio/images/_ome_zarr_container.py,sha256=hRx_8CPtFpxQUcprTT4_k5L5pjMUJ6t4wJaYr1tyljw,31181
|
|
22
22
|
ngio/ome_zarr_meta/__init__.py,sha256=oZ8PEsWM7U0KwzpsnvVfX9k4UfuTz5sZ8B6B9eY5hyY,1193
|
|
23
23
|
ngio/ome_zarr_meta/_meta_handlers.py,sha256=ctknNDT8jxwyvxQf9on5gW31H1tRRsnneO38GT2UXoE,25880
|
|
24
24
|
ngio/ome_zarr_meta/ngio_specs/__init__.py,sha256=05NQukZG0nNvjzf8AKWGu7PhjhQcImGSAOK3D3Bg-Js,1786
|
|
25
|
-
ngio/ome_zarr_meta/ngio_specs/_axes.py,sha256=
|
|
26
|
-
ngio/ome_zarr_meta/ngio_specs/_channels.py,sha256=
|
|
25
|
+
ngio/ome_zarr_meta/ngio_specs/_axes.py,sha256=tHtx6NfBgDcCgDk9CosjIjw1KZJ2qi0i_eoLgrdiEWw,16681
|
|
26
|
+
ngio/ome_zarr_meta/ngio_specs/_channels.py,sha256=Jwys1yYC8q6_gIaJ52KcKcTP7hzqVjBE-VlVQvJurCs,15394
|
|
27
27
|
ngio/ome_zarr_meta/ngio_specs/_dataset.py,sha256=hY8ogPPxvCgVg6k02t3zUr24lasYrvnxBd1iPEigdG4,5892
|
|
28
28
|
ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py,sha256=uh345KQmEQtslIyMmLK9sB-NbjPYxi0Y9FuYIFhd3Rc,17465
|
|
29
29
|
ngio/ome_zarr_meta/ngio_specs/_ngio_image.py,sha256=8E38Mgw-l0Ff1nkmCJIzo64G_paAVhM8xktUS_V5egY,17960
|
|
@@ -31,9 +31,9 @@ ngio/ome_zarr_meta/ngio_specs/_pixel_size.py,sha256=5TT8250XdCKUnk3OwZeyXIMNFKOg
|
|
|
31
31
|
ngio/ome_zarr_meta/v04/__init__.py,sha256=dJRzzxyYc81kf-0Hip_bqvbdManaM8XTdQX2meWyCSs,583
|
|
32
32
|
ngio/ome_zarr_meta/v04/_custom_models.py,sha256=5GxiDERvLuvq4QvApcA6EiKLS6hLFX1R0R_9rSaa85A,530
|
|
33
33
|
ngio/ome_zarr_meta/v04/_v04_spec_utils.py,sha256=05tEr2eEP_XVIfBMOAWLT7lzJV4KS5eYrpK8l94tn3w,15876
|
|
34
|
-
ngio/tables/__init__.py,sha256=
|
|
35
|
-
ngio/tables/
|
|
36
|
-
ngio/tables/
|
|
34
|
+
ngio/tables/__init__.py,sha256=rJGcyQUlHQb_ZbeMM9ICD8_mhTE2X9H5nKp0J-z9tMM,825
|
|
35
|
+
ngio/tables/_abstract_table.py,sha256=naAeDsPTVjDUOnAxj_57UWXApg_nyWCsWdZ0f7bm740,8169
|
|
36
|
+
ngio/tables/_tables_container.py,sha256=a2NNExiP1HqFVJ6HPLn0nEkYHLdf2LONVz5OxoH7OkY,12118
|
|
37
37
|
ngio/tables/backends/__init__.py,sha256=tx97ZkK5kF_sAGU2kf_ZI9JxbOXn8GVjE0rUUq9h-QM,1525
|
|
38
38
|
ngio/tables/backends/_abstract_backend.py,sha256=jtai3AqcLNZyxYyK0rernbMtcwxWhWAahCS0fPlMNls,7454
|
|
39
39
|
ngio/tables/backends/_anndata.py,sha256=8RX2NO15CLxys3p6gw54Nd-_0WATU4WBlNndX06wvzI,2587
|
|
@@ -42,20 +42,20 @@ ngio/tables/backends/_csv.py,sha256=Ev61D-AUKo4LIhXRmWPJgYbHI7eQdxiajQR574DevEM,
|
|
|
42
42
|
ngio/tables/backends/_json.py,sha256=1ZsEuXDJm1rOZV_KjFm8CB0qhv7L1W7L2EGWPf4q_p0,3137
|
|
43
43
|
ngio/tables/backends/_non_zarr_backends.py,sha256=SvPPhT6n5TrKUOyV1mNcdHQK49huZ5lwR8EVe9MdydM,7254
|
|
44
44
|
ngio/tables/backends/_parquet.py,sha256=ic-p86h8lce8q9luBJGRzy6vxlWyJvA0-2l5cUD6OqY,1398
|
|
45
|
-
ngio/tables/backends/_table_backends.py,sha256=
|
|
45
|
+
ngio/tables/backends/_table_backends.py,sha256=E4b1p_ZqRmEf3JK_Wp05r2J-q-PZQU_B1qFluBrjU9Q,7232
|
|
46
46
|
ngio/tables/backends/_utils.py,sha256=tJBSWN6OhO_1ybxl4vT2l3ImB028Fb6h0vaitPRcr8A,19668
|
|
47
47
|
ngio/tables/v1/__init__.py,sha256=Wr1_9RZFpaN8FYMTnxT9Yjkw4AS7y9FMWailmB_uj5g,617
|
|
48
|
-
ngio/tables/v1/_condition_table.py,sha256=
|
|
49
|
-
ngio/tables/v1/_feature_table.py,sha256=
|
|
50
|
-
ngio/tables/v1/_generic_table.py,sha256=
|
|
51
|
-
ngio/tables/v1/_roi_table.py,sha256=
|
|
48
|
+
ngio/tables/v1/_condition_table.py,sha256=IMU3sds8wrKGqU-C20HbpFCVGZX1L_V7Sh8f7CHB_x0,1781
|
|
49
|
+
ngio/tables/v1/_feature_table.py,sha256=n9uMHwoBh-_dlOhUXCFbmAjXFVXncNCR3SjE2qzXI68,3821
|
|
50
|
+
ngio/tables/v1/_generic_table.py,sha256=1ktJHeuv7U1g5Z8PFUuTkCjOzcYMQd8xegKHKUedJB8,1240
|
|
51
|
+
ngio/tables/v1/_roi_table.py,sha256=Q1dBQqPvxqBOqatGzYTAbxmgEnEkBawpSbCL38YBnE0,13456
|
|
52
52
|
ngio/utils/__init__.py,sha256=r3nuLWgp6-cQlS4ODjYSBrfgdTLkCOVke9jKbn1NpkA,1129
|
|
53
53
|
ngio/utils/_datasets.py,sha256=EdYJHIifpRou4f43dIuiKsdxe4K6FaeS4f1_e_EYrcI,1727
|
|
54
54
|
ngio/utils/_errors.py,sha256=pKQ12LUjQLYE1nUawemA5h7HsgznjaSvV1n2PQU33N0,759
|
|
55
|
-
ngio/utils/_fractal_fsspec_store.py,sha256=
|
|
55
|
+
ngio/utils/_fractal_fsspec_store.py,sha256=RdcCFOgHexRKX9zZvJV5RI-5OPc7VOPS6q_IeRxm24I,1548
|
|
56
56
|
ngio/utils/_logger.py,sha256=HIuqD_2ShfFGDswBddcouStbKfL0Vz_ah8cAIFGhbS8,888
|
|
57
57
|
ngio/utils/_zarr_utils.py,sha256=qOI-HL2HsfFLCj_yxsTR-aq4oHpSqS9KR13aEIvhGDY,13593
|
|
58
|
-
ngio-0.3.
|
|
59
|
-
ngio-0.3.
|
|
60
|
-
ngio-0.3.
|
|
61
|
-
ngio-0.3.
|
|
58
|
+
ngio-0.3.1.dist-info/METADATA,sha256=6KnWDNEe6TWTYu4kKvT9uJR6D6uikMu0W6VOgXoNXcw,5868
|
|
59
|
+
ngio-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
60
|
+
ngio-0.3.1.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
|
|
61
|
+
ngio-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|