ngio 0.2.9__py3-none-any.whl → 0.3.0__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/__init__.py +16 -0
- ngio/common/_array_pipe.py +50 -27
- ngio/common/_table_ops.py +471 -0
- ngio/hcs/__init__.py +1 -1
- ngio/hcs/{plate.py → _plate.py} +451 -78
- 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} +152 -86
- ngio/ome_zarr_meta/_meta_handlers.py +16 -8
- ngio/ome_zarr_meta/ngio_specs/_channels.py +41 -29
- ngio/tables/__init__.py +14 -2
- ngio/tables/_abstract_table.py +269 -0
- ngio/tables/{tables_container.py → _tables_container.py} +186 -100
- ngio/tables/backends/__init__.py +20 -0
- ngio/tables/backends/_abstract_backend.py +58 -80
- ngio/tables/backends/{_anndata_v1.py → _anndata.py} +5 -1
- ngio/tables/backends/_csv.py +35 -0
- ngio/tables/backends/{_json_v1.py → _json.py} +4 -1
- ngio/tables/backends/{_csv_v1.py → _non_zarr_backends.py} +61 -27
- ngio/tables/backends/_parquet.py +47 -0
- ngio/tables/backends/_table_backends.py +39 -18
- ngio/tables/backends/_utils.py +147 -1
- ngio/tables/v1/__init__.py +19 -3
- ngio/tables/v1/_condition_table.py +71 -0
- ngio/tables/v1/_feature_table.py +63 -129
- ngio/tables/v1/_generic_table.py +21 -159
- ngio/tables/v1/_roi_table.py +285 -201
- ngio/utils/_fractal_fsspec_store.py +29 -0
- {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/METADATA +4 -3
- ngio-0.3.0.dist-info/RECORD +61 -0
- ngio/tables/_validators.py +0 -108
- ngio-0.2.9.dist-info/RECORD +0 -57
- /ngio/images/{abstract_image.py → _abstract_image.py} +0 -0
- /ngio/images/{create.py → _create.py} +0 -0
- {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/WHEEL +0 -0
- {ngio-0.2.9.dist-info → ngio-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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 (
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""Abstract class for handling OME-NGFF images."""
|
|
2
2
|
|
|
3
|
+
import warnings
|
|
3
4
|
from collections.abc import Collection
|
|
4
|
-
from typing import Literal, overload
|
|
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,
|
|
@@ -22,12 +22,15 @@ from ngio.ome_zarr_meta.ngio_specs import (
|
|
|
22
22
|
TimeUnits,
|
|
23
23
|
)
|
|
24
24
|
from ngio.tables import (
|
|
25
|
+
ConditionTable,
|
|
25
26
|
FeatureTable,
|
|
26
27
|
GenericRoiTable,
|
|
27
28
|
MaskingRoiTable,
|
|
28
29
|
RoiTable,
|
|
29
30
|
Table,
|
|
31
|
+
TableBackend,
|
|
30
32
|
TablesContainer,
|
|
33
|
+
TableType,
|
|
31
34
|
TypedTable,
|
|
32
35
|
)
|
|
33
36
|
from ngio.utils import (
|
|
@@ -65,7 +68,7 @@ class OmeZarrContainer:
|
|
|
65
68
|
group_handler: ZarrGroupHandler,
|
|
66
69
|
table_container: TablesContainer | None = None,
|
|
67
70
|
label_container: LabelsContainer | None = None,
|
|
68
|
-
|
|
71
|
+
validate_paths: bool = False,
|
|
69
72
|
) -> None:
|
|
70
73
|
"""Initialize the OmeZarrContainer."""
|
|
71
74
|
self._group_handler = group_handler
|
|
@@ -74,6 +77,10 @@ class OmeZarrContainer:
|
|
|
74
77
|
self._labels_container = label_container
|
|
75
78
|
self._tables_container = table_container
|
|
76
79
|
|
|
80
|
+
if validate_paths:
|
|
81
|
+
for level_path in self._images_container.levels_paths:
|
|
82
|
+
self.get_image(path=level_path)
|
|
83
|
+
|
|
77
84
|
def __repr__(self) -> str:
|
|
78
85
|
"""Return a string representation of the image."""
|
|
79
86
|
num_labels = len(self.list_labels())
|
|
@@ -96,24 +103,40 @@ class OmeZarrContainer:
|
|
|
96
103
|
"""Return the image container."""
|
|
97
104
|
return self._images_container
|
|
98
105
|
|
|
99
|
-
|
|
100
|
-
def labels_container(self) -> LabelsContainer:
|
|
106
|
+
def _get_labels_container(self) -> LabelsContainer | None:
|
|
101
107
|
"""Return the labels container."""
|
|
102
108
|
if self._labels_container is None:
|
|
103
|
-
|
|
104
|
-
if
|
|
105
|
-
|
|
109
|
+
_labels_container = _default_label_container(self._group_handler)
|
|
110
|
+
if _labels_container is None:
|
|
111
|
+
return None
|
|
112
|
+
self._labels_container = _labels_container
|
|
106
113
|
return self._labels_container
|
|
107
114
|
|
|
108
115
|
@property
|
|
109
|
-
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:
|
|
110
124
|
"""Return the tables container."""
|
|
111
125
|
if self._tables_container is None:
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
126
|
+
_tables_container = _default_table_container(self._group_handler)
|
|
127
|
+
if _tables_container is None:
|
|
128
|
+
return None
|
|
129
|
+
self._tables_container = _tables_container
|
|
115
130
|
return self._tables_container
|
|
116
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
|
+
|
|
117
140
|
@property
|
|
118
141
|
def image_meta(self) -> NgioImageMeta:
|
|
119
142
|
"""Return the image metadata."""
|
|
@@ -264,9 +287,7 @@ class OmeZarrContainer:
|
|
|
264
287
|
if masking_table_name is None:
|
|
265
288
|
masking_table = masking_label.build_masking_roi_table()
|
|
266
289
|
else:
|
|
267
|
-
masking_table = self.
|
|
268
|
-
masking_table_name, check_type="masking_roi_table"
|
|
269
|
-
)
|
|
290
|
+
masking_table = self.get_masking_roi_table(name=masking_table_name)
|
|
270
291
|
|
|
271
292
|
return MaskedImage(
|
|
272
293
|
group_handler=image._group_handler,
|
|
@@ -331,7 +352,7 @@ class OmeZarrContainer:
|
|
|
331
352
|
|
|
332
353
|
new_ome_zarr = OmeZarrContainer(
|
|
333
354
|
group_handler=handler,
|
|
334
|
-
|
|
355
|
+
validate_paths=False,
|
|
335
356
|
)
|
|
336
357
|
|
|
337
358
|
if copy_labels:
|
|
@@ -345,84 +366,128 @@ class OmeZarrContainer:
|
|
|
345
366
|
)
|
|
346
367
|
return new_ome_zarr
|
|
347
368
|
|
|
348
|
-
def list_tables(self) -> list[str]:
|
|
369
|
+
def list_tables(self, filter_types: TypedTable | str | None = None) -> list[str]:
|
|
349
370
|
"""List all tables in the image."""
|
|
350
|
-
|
|
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
|
+
)
|
|
351
378
|
|
|
352
379
|
def list_roi_tables(self) -> list[str]:
|
|
353
380
|
"""List all ROI tables in the image."""
|
|
354
|
-
|
|
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
|
|
355
388
|
|
|
356
|
-
|
|
357
|
-
|
|
389
|
+
def get_roi_table(self, name: str) -> RoiTable:
|
|
390
|
+
"""Get a ROI table from the image.
|
|
358
391
|
|
|
359
|
-
|
|
360
|
-
|
|
392
|
+
Args:
|
|
393
|
+
name (str): The name of the table.
|
|
394
|
+
"""
|
|
395
|
+
table = self.tables_container.get(name=name, strict=True)
|
|
396
|
+
if not isinstance(table, RoiTable):
|
|
397
|
+
raise NgioValueError(f"Table {name} is not a ROI table. Got {type(table)}")
|
|
398
|
+
return table
|
|
361
399
|
|
|
362
|
-
|
|
363
|
-
|
|
400
|
+
def get_masking_roi_table(self, name: str) -> MaskingRoiTable:
|
|
401
|
+
"""Get a masking ROI table from the image.
|
|
364
402
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
403
|
+
Args:
|
|
404
|
+
name (str): The name of the table.
|
|
405
|
+
"""
|
|
406
|
+
table = self.tables_container.get(name=name, strict=True)
|
|
407
|
+
if not isinstance(table, MaskingRoiTable):
|
|
408
|
+
raise NgioValueError(
|
|
409
|
+
f"Table {name} is not a masking ROI table. Got {type(table)}"
|
|
410
|
+
)
|
|
411
|
+
return table
|
|
369
412
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
self, name: str, check_type: Literal["feature_table"]
|
|
373
|
-
) -> FeatureTable: ...
|
|
413
|
+
def get_feature_table(self, name: str) -> FeatureTable:
|
|
414
|
+
"""Get a feature table from the image.
|
|
374
415
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
416
|
+
Args:
|
|
417
|
+
name (str): The name of the table.
|
|
418
|
+
"""
|
|
419
|
+
table = self.tables_container.get(name=name, strict=True)
|
|
420
|
+
if not isinstance(table, FeatureTable):
|
|
421
|
+
raise NgioValueError(
|
|
422
|
+
f"Table {name} is not a feature table. Got {type(table)}"
|
|
423
|
+
)
|
|
424
|
+
return table
|
|
425
|
+
|
|
426
|
+
def get_generic_roi_table(self, name: str) -> GenericRoiTable:
|
|
427
|
+
"""Get a generic ROI table from the image.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
name (str): The name of the table.
|
|
431
|
+
"""
|
|
432
|
+
table = self.tables_container.get(name=name, strict=True)
|
|
433
|
+
if not isinstance(table, GenericRoiTable):
|
|
434
|
+
raise NgioValueError(
|
|
435
|
+
f"Table {name} is not a generic ROI table. Got {type(table)}"
|
|
436
|
+
)
|
|
437
|
+
return table
|
|
438
|
+
|
|
439
|
+
def get_condition_table(self, name: str) -> ConditionTable:
|
|
440
|
+
"""Get a condition table from the image.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
name (str): The name of the table.
|
|
444
|
+
"""
|
|
445
|
+
table = self.tables_container.get(name=name, strict=True)
|
|
446
|
+
if not isinstance(table, ConditionTable):
|
|
447
|
+
raise NgioValueError(
|
|
448
|
+
f"Table {name} is not a condition table. Got {type(table)}"
|
|
449
|
+
)
|
|
450
|
+
return table
|
|
379
451
|
|
|
380
452
|
def get_table(self, name: str, check_type: TypedTable | None = None) -> Table:
|
|
381
453
|
"""Get a table from the image.
|
|
382
454
|
|
|
383
455
|
Args:
|
|
384
456
|
name (str): The name of the table.
|
|
385
|
-
check_type (TypedTable | None):
|
|
386
|
-
|
|
387
|
-
|
|
457
|
+
check_type (TypedTable | None): Deprecated. Please use
|
|
458
|
+
'get_table_as' instead, or one of the type specific
|
|
459
|
+
get_*table() methods.
|
|
460
|
+
|
|
461
|
+
"""
|
|
462
|
+
if check_type is not None:
|
|
463
|
+
warnings.warn(
|
|
464
|
+
"The 'check_type' argument is deprecated, and will be removed in "
|
|
465
|
+
"ngio=0.3. Use 'get_table_as' instead or one of the "
|
|
466
|
+
"type specific get_*table() methods.",
|
|
467
|
+
DeprecationWarning,
|
|
468
|
+
stacklevel=2,
|
|
469
|
+
)
|
|
470
|
+
return self.tables_container.get(name=name, strict=False)
|
|
471
|
+
|
|
472
|
+
def get_table_as(
|
|
473
|
+
self,
|
|
474
|
+
name: str,
|
|
475
|
+
table_cls: type[TableType],
|
|
476
|
+
backend: TableBackend | None = None,
|
|
477
|
+
) -> TableType:
|
|
478
|
+
"""Get a table from the image as a specific type.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
name (str): The name of the table.
|
|
482
|
+
table_cls (type[TableType]): The type of the table.
|
|
483
|
+
backend (TableBackend | None): The backend to use. If None,
|
|
484
|
+
the default backend is used.
|
|
388
485
|
"""
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
match check_type:
|
|
395
|
-
case "roi_table":
|
|
396
|
-
if not isinstance(table, RoiTable):
|
|
397
|
-
raise NgioValueError(
|
|
398
|
-
f"Table '{name}' is not a ROI table. Found type: {table.type()}"
|
|
399
|
-
)
|
|
400
|
-
return table
|
|
401
|
-
case "masking_roi_table":
|
|
402
|
-
if not isinstance(table, MaskingRoiTable):
|
|
403
|
-
raise NgioValueError(
|
|
404
|
-
f"Table '{name}' is not a masking ROI table. "
|
|
405
|
-
f"Found type: {table.type()}"
|
|
406
|
-
)
|
|
407
|
-
return table
|
|
408
|
-
|
|
409
|
-
case "generic_roi_table":
|
|
410
|
-
if not isinstance(table, GenericRoiTable):
|
|
411
|
-
raise NgioValueError(
|
|
412
|
-
f"Table '{name}' is not a generic ROI table. "
|
|
413
|
-
f"Found type: {table.type()}"
|
|
414
|
-
)
|
|
415
|
-
return table
|
|
416
|
-
|
|
417
|
-
case "feature_table":
|
|
418
|
-
if not isinstance(table, FeatureTable):
|
|
419
|
-
raise NgioValueError(
|
|
420
|
-
f"Table '{name}' is not a feature table. "
|
|
421
|
-
f"Found type: {table.type()}"
|
|
422
|
-
)
|
|
423
|
-
return table
|
|
424
|
-
case _:
|
|
425
|
-
raise NgioValueError(f"Unknown check_type: {check_type}")
|
|
486
|
+
return self.tables_container.get_as(
|
|
487
|
+
name=name,
|
|
488
|
+
table_cls=table_cls,
|
|
489
|
+
backend=backend,
|
|
490
|
+
)
|
|
426
491
|
|
|
427
492
|
def build_image_roi_table(self, name: str = "image") -> RoiTable:
|
|
428
493
|
"""Compute the ROI table for an image."""
|
|
@@ -436,7 +501,7 @@ class OmeZarrContainer:
|
|
|
436
501
|
self,
|
|
437
502
|
name: str,
|
|
438
503
|
table: Table,
|
|
439
|
-
backend:
|
|
504
|
+
backend: TableBackend = "anndata",
|
|
440
505
|
overwrite: bool = False,
|
|
441
506
|
) -> None:
|
|
442
507
|
"""Add a table to the image."""
|
|
@@ -446,7 +511,10 @@ class OmeZarrContainer:
|
|
|
446
511
|
|
|
447
512
|
def list_labels(self) -> list[str]:
|
|
448
513
|
"""List all labels in the image."""
|
|
449
|
-
|
|
514
|
+
label_container = self._get_labels_container()
|
|
515
|
+
if label_container is None:
|
|
516
|
+
return []
|
|
517
|
+
return label_container.list()
|
|
450
518
|
|
|
451
519
|
def get_label(
|
|
452
520
|
self,
|
|
@@ -499,9 +567,7 @@ class OmeZarrContainer:
|
|
|
499
567
|
if masking_table_name is None:
|
|
500
568
|
masking_table = masking_label.build_masking_roi_table()
|
|
501
569
|
else:
|
|
502
|
-
masking_table = self.
|
|
503
|
-
masking_table_name, check_type="masking_roi_table"
|
|
504
|
-
)
|
|
570
|
+
masking_table = self.get_masking_roi_table(name=masking_table_name)
|
|
505
571
|
|
|
506
572
|
return MaskedLabel(
|
|
507
573
|
group_handler=label._group_handler,
|
|
@@ -566,7 +632,7 @@ def open_ome_zarr_container(
|
|
|
566
632
|
handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
|
|
567
633
|
return OmeZarrContainer(
|
|
568
634
|
group_handler=handler,
|
|
569
|
-
|
|
635
|
+
validate_paths=validate_arrays,
|
|
570
636
|
)
|
|
571
637
|
|
|
572
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
|
#
|
|
@@ -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),
|