ngio 0.3.0a0__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} +27 -12
- 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} +59 -24
- ngio/ome_zarr_meta/_meta_handlers.py +16 -8
- ngio/ome_zarr_meta/ngio_specs/_axes.py +4 -7
- ngio/ome_zarr_meta/ngio_specs/_channels.py +41 -29
- ngio/tables/__init__.py +7 -2
- ngio/tables/{abstract_table.py → _abstract_table.py} +5 -4
- ngio/tables/{tables_container.py → _tables_container.py} +42 -29
- ngio/tables/backends/__init__.py +6 -4
- ngio/tables/backends/_abstract_backend.py +1 -1
- ngio/tables/backends/{_anndata_v1.py → _anndata.py} +1 -1
- ngio/tables/backends/{_csv_v1.py → _csv.py} +2 -2
- ngio/tables/backends/{_json_v1.py → _json.py} +1 -1
- ngio/tables/backends/{_parquet_v1.py → _parquet.py} +2 -2
- ngio/tables/backends/_table_backends.py +41 -17
- ngio/tables/v1/__init__.py +12 -3
- ngio/tables/v1/_condition_table.py +8 -4
- ngio/tables/v1/_feature_table.py +11 -13
- ngio/tables/v1/_generic_table.py +14 -3
- ngio/tables/v1/_roi_table.py +11 -7
- ngio/utils/_fractal_fsspec_store.py +1 -1
- {ngio-0.3.0a0.dist-info → ngio-0.3.1.dist-info}/METADATA +69 -35
- ngio-0.3.1.dist-info/RECORD +61 -0
- ngio-0.3.0a0.dist-info/RECORD +0 -61
- /ngio/images/{abstract_image.py → _abstract_image.py} +0 -0
- /ngio/images/{create.py → _create.py} +0 -0
- /ngio/tables/backends/{_non_zarr_backends_v1.py → _non_zarr_backends.py} +0 -0
- {ngio-0.3.0a0.dist-info → ngio-0.3.1.dist-info}/WHEEL +0 -0
- {ngio-0.3.0a0.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
|
@@ -32,14 +32,13 @@ from ngio.tables import (
|
|
|
32
32
|
MaskingRoiTable,
|
|
33
33
|
RoiTable,
|
|
34
34
|
Table,
|
|
35
|
-
|
|
35
|
+
TableBackend,
|
|
36
36
|
TablesContainer,
|
|
37
37
|
TableType,
|
|
38
38
|
TypedTable,
|
|
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.
|
|
@@ -909,14 +924,14 @@ class OmeZarrPlate:
|
|
|
909
924
|
self,
|
|
910
925
|
name: str,
|
|
911
926
|
table_cls: type[TableType],
|
|
912
|
-
backend:
|
|
927
|
+
backend: TableBackend | None = None,
|
|
913
928
|
) -> TableType:
|
|
914
929
|
"""Get a table from the image as a specific type.
|
|
915
930
|
|
|
916
931
|
Args:
|
|
917
932
|
name (str): The name of the table.
|
|
918
933
|
table_cls (type[TableType]): The type of the table.
|
|
919
|
-
backend (
|
|
934
|
+
backend (TableBackend | None): The backend to use. If None,
|
|
920
935
|
the default backend is used.
|
|
921
936
|
"""
|
|
922
937
|
return self.tables_container.get_as(
|
|
@@ -929,7 +944,7 @@ class OmeZarrPlate:
|
|
|
929
944
|
self,
|
|
930
945
|
name: str,
|
|
931
946
|
table: Table,
|
|
932
|
-
backend:
|
|
947
|
+
backend: TableBackend = "anndata",
|
|
933
948
|
overwrite: bool = False,
|
|
934
949
|
) -> None:
|
|
935
950
|
"""Add a table to 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,
|
|
@@ -28,7 +28,7 @@ from ngio.tables import (
|
|
|
28
28
|
MaskingRoiTable,
|
|
29
29
|
RoiTable,
|
|
30
30
|
Table,
|
|
31
|
-
|
|
31
|
+
TableBackend,
|
|
32
32
|
TablesContainer,
|
|
33
33
|
TableType,
|
|
34
34
|
TypedTable,
|
|
@@ -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.
|
|
@@ -441,14 +473,14 @@ class OmeZarrContainer:
|
|
|
441
473
|
self,
|
|
442
474
|
name: str,
|
|
443
475
|
table_cls: type[TableType],
|
|
444
|
-
backend:
|
|
476
|
+
backend: TableBackend | None = None,
|
|
445
477
|
) -> TableType:
|
|
446
478
|
"""Get a table from the image as a specific type.
|
|
447
479
|
|
|
448
480
|
Args:
|
|
449
481
|
name (str): The name of the table.
|
|
450
482
|
table_cls (type[TableType]): The type of the table.
|
|
451
|
-
backend (
|
|
483
|
+
backend (TableBackend | None): The backend to use. If None,
|
|
452
484
|
the default backend is used.
|
|
453
485
|
"""
|
|
454
486
|
return self.tables_container.get_as(
|
|
@@ -469,7 +501,7 @@ class OmeZarrContainer:
|
|
|
469
501
|
self,
|
|
470
502
|
name: str,
|
|
471
503
|
table: Table,
|
|
472
|
-
backend:
|
|
504
|
+
backend: TableBackend = "anndata",
|
|
473
505
|
overwrite: bool = False,
|
|
474
506
|
) -> None:
|
|
475
507
|
"""Add a table to 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
|
|
|
@@ -187,10 +187,6 @@ class GenericMetaHandler(
|
|
|
187
187
|
|
|
188
188
|
raise NgioValueError(f"Could not load metadata: {meta_or_error}")
|
|
189
189
|
|
|
190
|
-
def safe_load_meta(self) -> _image_meta | ConverterError:
|
|
191
|
-
"""Load the metadata from the store."""
|
|
192
|
-
return self._load_meta(return_error=True)
|
|
193
|
-
|
|
194
190
|
def _write_meta(self, meta) -> None:
|
|
195
191
|
"""Write the metadata to the store."""
|
|
196
192
|
_meta = self._meta_exporter(metadata=meta)
|
|
@@ -217,6 +213,10 @@ class ImageMetaHandler(
|
|
|
217
213
|
return meta
|
|
218
214
|
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
219
215
|
|
|
216
|
+
def safe_load_meta(self) -> NgioImageMeta | ConverterError:
|
|
217
|
+
"""Load the metadata from the store."""
|
|
218
|
+
return self._load_meta(return_error=True)
|
|
219
|
+
|
|
220
220
|
|
|
221
221
|
class LabelMetaHandler(
|
|
222
222
|
GenericMetaHandler[NgioLabelMeta, LabelMetaImporter, LabelMetaExporter]
|
|
@@ -230,6 +230,10 @@ class LabelMetaHandler(
|
|
|
230
230
|
return meta
|
|
231
231
|
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
232
232
|
|
|
233
|
+
def safe_load_meta(self) -> NgioLabelMeta | ConverterError:
|
|
234
|
+
"""Load the metadata from the store."""
|
|
235
|
+
return self._load_meta(return_error=True)
|
|
236
|
+
|
|
233
237
|
|
|
234
238
|
###########################################################################
|
|
235
239
|
#
|
|
@@ -267,10 +271,6 @@ class GenericHCSMetaHandler(Generic[_hcs_meta, _hcs_meta_importer, _hcs_meta_exp
|
|
|
267
271
|
|
|
268
272
|
raise NgioValueError(f"Could not load metadata: {meta_or_error}")
|
|
269
273
|
|
|
270
|
-
def safe_load_meta(self) -> _hcs_meta | ConverterError:
|
|
271
|
-
"""Load the metadata from the store."""
|
|
272
|
-
return self._load_meta(return_error=True)
|
|
273
|
-
|
|
274
274
|
def _write_meta(self, meta) -> None:
|
|
275
275
|
_meta = self._meta_exporter(metadata=meta)
|
|
276
276
|
self._group_handler.write_attrs(_meta)
|
|
@@ -295,6 +295,10 @@ class WellMetaHandler(
|
|
|
295
295
|
return meta
|
|
296
296
|
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
297
297
|
|
|
298
|
+
def safe_load_meta(self) -> NgioWellMeta | ConverterError:
|
|
299
|
+
"""Load the metadata from the store."""
|
|
300
|
+
return self._load_meta(return_error=True)
|
|
301
|
+
|
|
298
302
|
|
|
299
303
|
class PlateMetaHandler(
|
|
300
304
|
GenericHCSMetaHandler[NgioPlateMeta, PlateMetaImporter, PlateMetaExporter]
|
|
@@ -308,6 +312,10 @@ class PlateMetaHandler(
|
|
|
308
312
|
return meta
|
|
309
313
|
raise NgioValueError(f"Could not load metadata: {meta}")
|
|
310
314
|
|
|
315
|
+
def safe_load_meta(self) -> NgioPlateMeta | ConverterError:
|
|
316
|
+
"""Load the metadata from the store."""
|
|
317
|
+
return self._load_meta(return_error=True)
|
|
318
|
+
|
|
311
319
|
|
|
312
320
|
###########################################################################
|
|
313
321
|
#
|