ngio 0.5.0__py3-none-any.whl → 0.5.0a1__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/__init__.py +2 -5
- ngio/common/__init__.py +6 -11
- ngio/common/_masking_roi.py +54 -34
- ngio/common/_pyramid.py +87 -321
- ngio/common/_roi.py +330 -258
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +11 -10
- ngio/hcs/_plate.py +136 -192
- ngio/images/_abstract_image.py +35 -539
- ngio/images/_create.py +283 -0
- ngio/images/_create_synt_container.py +43 -40
- ngio/images/_image.py +251 -517
- ngio/images/_label.py +172 -249
- ngio/images/_masked_image.py +2 -2
- ngio/images/_ome_zarr_container.py +241 -644
- ngio/io_pipes/_io_pipes.py +9 -9
- ngio/io_pipes/_io_pipes_masked.py +7 -7
- ngio/io_pipes/_io_pipes_roi.py +6 -6
- ngio/io_pipes/_io_pipes_types.py +3 -3
- ngio/io_pipes/_match_shape.py +8 -6
- ngio/io_pipes/_ops_slices_utils.py +5 -8
- ngio/ome_zarr_meta/__init__.py +18 -29
- ngio/ome_zarr_meta/_meta_handlers.py +708 -392
- ngio/ome_zarr_meta/ngio_specs/__init__.py +0 -4
- ngio/ome_zarr_meta/ngio_specs/_axes.py +51 -152
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +22 -13
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +91 -129
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +68 -57
- ngio/ome_zarr_meta/v04/__init__.py +1 -5
- ngio/ome_zarr_meta/v04/{_v04_spec.py → _v04_spec_utils.py} +85 -54
- ngio/ome_zarr_meta/v05/__init__.py +1 -5
- ngio/ome_zarr_meta/v05/{_v05_spec.py → _v05_spec_utils.py} +87 -64
- ngio/resources/__init__.py +1 -1
- ngio/resources/resource_model.py +1 -1
- ngio/tables/_tables_container.py +27 -85
- ngio/tables/backends/_anndata.py +8 -58
- ngio/tables/backends/_anndata_utils.py +6 -1
- ngio/tables/backends/_csv.py +19 -3
- ngio/tables/backends/_json.py +13 -10
- ngio/tables/backends/_non_zarr_backends.py +196 -0
- ngio/tables/backends/_parquet.py +31 -3
- ngio/tables/v1/_roi_table.py +27 -44
- ngio/utils/__init__.py +12 -8
- ngio/utils/_datasets.py +0 -6
- ngio/utils/_logger.py +50 -0
- ngio/utils/_zarr_utils.py +250 -292
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/METADATA +6 -13
- ngio-0.5.0a1.dist-info/RECORD +88 -0
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/WHEEL +1 -1
- ngio/images/_create_utils.py +0 -406
- ngio/tables/backends/_py_arrow_backends.py +0 -222
- ngio/utils/_cache.py +0 -48
- ngio-0.5.0.dist-info/RECORD +0 -88
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/licenses/LICENSE +0 -0
ngio/images/_image.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""Generic class to handle Image-like data in a OME-NGFF file."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from
|
|
5
|
-
from typing import Any, Literal
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import Literal
|
|
6
5
|
|
|
7
6
|
import dask.array as da
|
|
8
7
|
import numpy as np
|
|
@@ -13,9 +12,10 @@ from ngio.common import (
|
|
|
13
12
|
Dimensions,
|
|
14
13
|
InterpolationOrder,
|
|
15
14
|
Roi,
|
|
15
|
+
RoiPixels,
|
|
16
16
|
)
|
|
17
|
-
from ngio.
|
|
18
|
-
from ngio.images.
|
|
17
|
+
from ngio.images._abstract_image import AbstractImage
|
|
18
|
+
from ngio.images._create import create_empty_image_container
|
|
19
19
|
from ngio.io_pipes import (
|
|
20
20
|
SlicingInputType,
|
|
21
21
|
TransformProtocol,
|
|
@@ -24,22 +24,24 @@ from ngio.ome_zarr_meta import (
|
|
|
24
24
|
ImageMetaHandler,
|
|
25
25
|
NgioImageMeta,
|
|
26
26
|
PixelSize,
|
|
27
|
+
find_image_meta_handler,
|
|
27
28
|
)
|
|
28
29
|
from ngio.ome_zarr_meta.ngio_specs import (
|
|
29
30
|
Channel,
|
|
30
31
|
ChannelsMeta,
|
|
32
|
+
ChannelVisualisation,
|
|
31
33
|
DefaultSpaceUnit,
|
|
32
34
|
DefaultTimeUnit,
|
|
33
35
|
NgffVersions,
|
|
34
36
|
SpaceUnits,
|
|
35
37
|
TimeUnits,
|
|
36
38
|
)
|
|
37
|
-
from ngio.ome_zarr_meta.ngio_specs._axes import AxesSetup
|
|
38
39
|
from ngio.utils import (
|
|
39
|
-
|
|
40
|
+
NgioValidationError,
|
|
40
41
|
StoreOrGroup,
|
|
41
42
|
ZarrGroupHandler,
|
|
42
43
|
)
|
|
44
|
+
from ngio.utils._zarr_utils import find_dimension_separator
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
class ChannelSelectionModel(BaseModel):
|
|
@@ -47,7 +49,7 @@ class ChannelSelectionModel(BaseModel):
|
|
|
47
49
|
|
|
48
50
|
This model is used to select a channel by label, wavelength ID, or index.
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
Args:
|
|
51
53
|
identifier (str): Unique identifier for the channel.
|
|
52
54
|
This can be a channel label, wavelength ID, or index.
|
|
53
55
|
mode (Literal["label", "wavelength_id", "index"]): Specifies how to
|
|
@@ -88,7 +90,7 @@ def _check_channel_meta(meta: NgioImageMeta, dimension: Dimensions) -> ChannelsM
|
|
|
88
90
|
return ChannelsMeta.default_init(labels=c_dim)
|
|
89
91
|
|
|
90
92
|
if len(meta.channels_meta.channels) != c_dim:
|
|
91
|
-
raise
|
|
93
|
+
raise NgioValidationError(
|
|
92
94
|
"The number of channels does not match the image. "
|
|
93
95
|
f"Expected {len(meta.channels_meta.channels)} channels, got {c_dim}."
|
|
94
96
|
)
|
|
@@ -96,7 +98,7 @@ def _check_channel_meta(meta: NgioImageMeta, dimension: Dimensions) -> ChannelsM
|
|
|
96
98
|
return meta.channels_meta
|
|
97
99
|
|
|
98
100
|
|
|
99
|
-
class Image(AbstractImage):
|
|
101
|
+
class Image(AbstractImage[ImageMetaHandler]):
|
|
100
102
|
"""A class to handle a single image (or level) in an OME-Zarr image.
|
|
101
103
|
|
|
102
104
|
This class is meant to be subclassed by specific image types.
|
|
@@ -106,7 +108,7 @@ class Image(AbstractImage):
|
|
|
106
108
|
self,
|
|
107
109
|
group_handler: ZarrGroupHandler,
|
|
108
110
|
path: str,
|
|
109
|
-
meta_handler: ImageMetaHandler,
|
|
111
|
+
meta_handler: ImageMetaHandler | None,
|
|
110
112
|
) -> None:
|
|
111
113
|
"""Initialize the Image at a single level.
|
|
112
114
|
|
|
@@ -116,22 +118,16 @@ class Image(AbstractImage):
|
|
|
116
118
|
meta_handler: The image metadata handler.
|
|
117
119
|
|
|
118
120
|
"""
|
|
121
|
+
if meta_handler is None:
|
|
122
|
+
meta_handler = find_image_meta_handler(group_handler)
|
|
119
123
|
super().__init__(
|
|
120
124
|
group_handler=group_handler, path=path, meta_handler=meta_handler
|
|
121
125
|
)
|
|
122
126
|
|
|
123
|
-
@property
|
|
124
|
-
def meta_handler(self) -> ImageMetaHandler:
|
|
125
|
-
"""Return the metadata handler."""
|
|
126
|
-
assert isinstance(self._meta_handler, ImageMetaHandler)
|
|
127
|
-
return self._meta_handler
|
|
128
|
-
|
|
129
127
|
@property
|
|
130
128
|
def meta(self) -> NgioImageMeta:
|
|
131
129
|
"""Return the metadata."""
|
|
132
|
-
|
|
133
|
-
assert isinstance(meta, NgioImageMeta)
|
|
134
|
-
return meta
|
|
130
|
+
return self._meta_handler.meta
|
|
135
131
|
|
|
136
132
|
@property
|
|
137
133
|
def channels_meta(self) -> ChannelsMeta:
|
|
@@ -191,7 +187,7 @@ class Image(AbstractImage):
|
|
|
191
187
|
|
|
192
188
|
def get_roi_as_numpy(
|
|
193
189
|
self,
|
|
194
|
-
roi: Roi,
|
|
190
|
+
roi: Roi | RoiPixels,
|
|
195
191
|
channel_selection: ChannelSlicingInputType = None,
|
|
196
192
|
axes_order: Sequence[str] | None = None,
|
|
197
193
|
transforms: Sequence[TransformProtocol] | None = None,
|
|
@@ -245,7 +241,7 @@ class Image(AbstractImage):
|
|
|
245
241
|
|
|
246
242
|
def get_roi_as_dask(
|
|
247
243
|
self,
|
|
248
|
-
roi: Roi,
|
|
244
|
+
roi: Roi | RoiPixels,
|
|
249
245
|
channel_selection: ChannelSlicingInputType = None,
|
|
250
246
|
axes_order: Sequence[str] | None = None,
|
|
251
247
|
transforms: Sequence[TransformProtocol] | None = None,
|
|
@@ -302,7 +298,7 @@ class Image(AbstractImage):
|
|
|
302
298
|
|
|
303
299
|
def get_roi(
|
|
304
300
|
self,
|
|
305
|
-
roi: Roi,
|
|
301
|
+
roi: Roi | RoiPixels,
|
|
306
302
|
channel_selection: ChannelSlicingInputType = None,
|
|
307
303
|
axes_order: Sequence[str] | None = None,
|
|
308
304
|
transforms: Sequence[TransformProtocol] | None = None,
|
|
@@ -362,7 +358,7 @@ class Image(AbstractImage):
|
|
|
362
358
|
|
|
363
359
|
def set_roi(
|
|
364
360
|
self,
|
|
365
|
-
roi: Roi,
|
|
361
|
+
roi: Roi | RoiPixels,
|
|
366
362
|
patch: np.ndarray | da.Array,
|
|
367
363
|
channel_selection: ChannelSlicingInputType = None,
|
|
368
364
|
axes_order: Sequence[str] | None = None,
|
|
@@ -400,135 +396,67 @@ class Image(AbstractImage):
|
|
|
400
396
|
|
|
401
397
|
|
|
402
398
|
class ImagesContainer:
|
|
403
|
-
"""A class to handle the /
|
|
399
|
+
"""A class to handle the /labels group in an OME-NGFF file."""
|
|
404
400
|
|
|
405
|
-
def __init__(
|
|
406
|
-
|
|
407
|
-
group_handler: ZarrGroupHandler,
|
|
408
|
-
axes_setup: AxesSetup | None,
|
|
409
|
-
version: NgffVersions | None = None,
|
|
410
|
-
validate_paths: bool = True,
|
|
411
|
-
) -> None:
|
|
412
|
-
"""Initialize the ImagesContainer."""
|
|
401
|
+
def __init__(self, group_handler: ZarrGroupHandler) -> None:
|
|
402
|
+
"""Initialize the LabelGroupHandler."""
|
|
413
403
|
self._group_handler = group_handler
|
|
414
|
-
self._meta_handler =
|
|
415
|
-
group_handler=group_handler, axes_setup=axes_setup, version=version
|
|
416
|
-
)
|
|
417
|
-
if validate_paths:
|
|
418
|
-
for level_path in self._meta_handler.get_meta().paths:
|
|
419
|
-
self.get(path=level_path)
|
|
404
|
+
self._meta_handler = find_image_meta_handler(group_handler)
|
|
420
405
|
|
|
421
406
|
@property
|
|
422
407
|
def meta(self) -> NgioImageMeta:
|
|
423
408
|
"""Return the metadata."""
|
|
424
|
-
return self._meta_handler.
|
|
425
|
-
|
|
426
|
-
@property
|
|
427
|
-
def channels_meta(self) -> ChannelsMeta:
|
|
428
|
-
"""Return the channels metadata."""
|
|
429
|
-
return self.get().channels_meta
|
|
430
|
-
|
|
431
|
-
@property
|
|
432
|
-
def axes_setup(self) -> AxesSetup:
|
|
433
|
-
"""Return the axes setup."""
|
|
434
|
-
return self.meta.axes_handler.axes_setup
|
|
435
|
-
|
|
436
|
-
@property
|
|
437
|
-
def level_paths(self) -> list[str]:
|
|
438
|
-
"""Return the paths of the levels in the image."""
|
|
439
|
-
return self.meta.paths
|
|
440
|
-
|
|
441
|
-
@property
|
|
442
|
-
def levels_paths(self) -> list[str]:
|
|
443
|
-
"""Deprecated: use 'level_paths' instead."""
|
|
444
|
-
warnings.warn(
|
|
445
|
-
"'levels_paths' is deprecated and will be removed in ngio=0.6. "
|
|
446
|
-
"Please use 'level_paths' instead.",
|
|
447
|
-
DeprecationWarning,
|
|
448
|
-
stacklevel=2,
|
|
449
|
-
)
|
|
450
|
-
return self.level_paths
|
|
409
|
+
return self._meta_handler.meta
|
|
451
410
|
|
|
452
411
|
@property
|
|
453
412
|
def levels(self) -> int:
|
|
454
413
|
"""Return the number of levels in the image."""
|
|
455
|
-
return self.meta.levels
|
|
456
|
-
|
|
457
|
-
@property
|
|
458
|
-
def is_3d(self) -> bool:
|
|
459
|
-
"""Return True if the image is 3D."""
|
|
460
|
-
return self.get().is_3d
|
|
461
|
-
|
|
462
|
-
@property
|
|
463
|
-
def is_2d(self) -> bool:
|
|
464
|
-
"""Return True if the image is 2D."""
|
|
465
|
-
return self.get().is_2d
|
|
414
|
+
return self._meta_handler.meta.levels
|
|
466
415
|
|
|
467
416
|
@property
|
|
468
|
-
def
|
|
469
|
-
"""Return
|
|
470
|
-
return self.
|
|
471
|
-
|
|
472
|
-
@property
|
|
473
|
-
def is_2d_time_series(self) -> bool:
|
|
474
|
-
"""Return True if the image is a 2D time series."""
|
|
475
|
-
return self.get().is_2d_time_series
|
|
476
|
-
|
|
477
|
-
@property
|
|
478
|
-
def is_3d_time_series(self) -> bool:
|
|
479
|
-
"""Return True if the image is a 3D time series."""
|
|
480
|
-
return self.get().is_3d_time_series
|
|
481
|
-
|
|
482
|
-
@property
|
|
483
|
-
def is_multi_channels(self) -> bool:
|
|
484
|
-
"""Return True if the image is multichannel."""
|
|
485
|
-
return self.get().is_multi_channels
|
|
486
|
-
|
|
487
|
-
@property
|
|
488
|
-
def space_unit(self) -> str | None:
|
|
489
|
-
"""Return the space unit of the image."""
|
|
490
|
-
return self.meta.space_unit
|
|
417
|
+
def levels_paths(self) -> list[str]:
|
|
418
|
+
"""Return the paths of the levels in the image."""
|
|
419
|
+
return self._meta_handler.meta.paths
|
|
491
420
|
|
|
492
421
|
@property
|
|
493
|
-
def
|
|
494
|
-
"""Return the
|
|
495
|
-
|
|
422
|
+
def num_channels(self) -> int:
|
|
423
|
+
"""Return the number of channels."""
|
|
424
|
+
image = self.get()
|
|
425
|
+
return image.num_channels
|
|
496
426
|
|
|
497
427
|
@property
|
|
498
428
|
def channel_labels(self) -> list[str]:
|
|
499
429
|
"""Return the channels of the image."""
|
|
500
|
-
|
|
430
|
+
image = self.get()
|
|
431
|
+
return image.channel_labels
|
|
501
432
|
|
|
502
433
|
@property
|
|
503
434
|
def wavelength_ids(self) -> list[str | None]:
|
|
504
|
-
"""Return the
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
@property
|
|
508
|
-
def num_channels(self) -> int:
|
|
509
|
-
"""Return the number of channels."""
|
|
510
|
-
return self.get().num_channels
|
|
435
|
+
"""Return the wavelength of the image."""
|
|
436
|
+
image = self.get()
|
|
437
|
+
return image.wavelength_ids
|
|
511
438
|
|
|
512
439
|
def get_channel_idx(
|
|
513
440
|
self, channel_label: str | None = None, wavelength_id: str | None = None
|
|
514
441
|
) -> int:
|
|
515
|
-
"""Get the index of a channel by
|
|
516
|
-
|
|
442
|
+
"""Get the index of a channel by label or wavelength ID.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
channel_label (str | None): The label of the channel.
|
|
446
|
+
If None a wavelength ID must be provided.
|
|
447
|
+
wavelength_id (str | None): The wavelength ID of the channel.
|
|
448
|
+
If None a channel label must be provided.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
int: The index of the channel.
|
|
452
|
+
|
|
453
|
+
"""
|
|
454
|
+
image = self.get()
|
|
455
|
+
return image.get_channel_idx(
|
|
517
456
|
channel_label=channel_label, wavelength_id=wavelength_id
|
|
518
457
|
)
|
|
519
458
|
|
|
520
|
-
def
|
|
521
|
-
self,
|
|
522
|
-
channels_meta: ChannelsMeta | None = None,
|
|
523
|
-
) -> None:
|
|
524
|
-
"""Set the channels metadata."""
|
|
525
|
-
if channels_meta is None:
|
|
526
|
-
channels_meta = ChannelsMeta.default_init(labels=self.num_channels)
|
|
527
|
-
meta = self.meta
|
|
528
|
-
meta.set_channels_meta(channels_meta)
|
|
529
|
-
self._meta_handler.update_meta(meta)
|
|
530
|
-
|
|
531
|
-
def _set_channel_meta_legacy(
|
|
459
|
+
def set_channel_meta(
|
|
532
460
|
self,
|
|
533
461
|
labels: Sequence[str | None] | int | None = None,
|
|
534
462
|
wavelength_id: Sequence[str | None] | None = None,
|
|
@@ -564,22 +492,32 @@ class ImagesContainer:
|
|
|
564
492
|
ref_image = self.get(path=low_res_dataset.path)
|
|
565
493
|
|
|
566
494
|
if start is not None and end is None:
|
|
567
|
-
raise
|
|
495
|
+
raise NgioValidationError(
|
|
496
|
+
"If start is provided, end must be provided as well."
|
|
497
|
+
)
|
|
568
498
|
if end is not None and start is None:
|
|
569
|
-
raise
|
|
499
|
+
raise NgioValidationError(
|
|
500
|
+
"If end is provided, start must be provided as well."
|
|
501
|
+
)
|
|
570
502
|
|
|
571
503
|
if start is not None and percentiles is not None:
|
|
572
|
-
raise
|
|
504
|
+
raise NgioValidationError(
|
|
573
505
|
"If start and end are provided, percentiles must be None."
|
|
574
506
|
)
|
|
575
507
|
|
|
508
|
+
if percentiles is not None:
|
|
509
|
+
start, end = compute_image_percentile(
|
|
510
|
+
ref_image,
|
|
511
|
+
start_percentile=percentiles[0],
|
|
512
|
+
end_percentile=percentiles[1],
|
|
513
|
+
)
|
|
576
514
|
elif start is not None and end is not None:
|
|
577
515
|
if len(start) != len(end):
|
|
578
|
-
raise
|
|
516
|
+
raise NgioValidationError(
|
|
579
517
|
"The start and end lists must have the same length."
|
|
580
518
|
)
|
|
581
519
|
if len(start) != self.num_channels:
|
|
582
|
-
raise
|
|
520
|
+
raise NgioValidationError(
|
|
583
521
|
"The start and end lists must have the same length as "
|
|
584
522
|
"the number of channels."
|
|
585
523
|
)
|
|
@@ -603,212 +541,44 @@ class ImagesContainer:
|
|
|
603
541
|
data_type=ref_image.dtype,
|
|
604
542
|
**omero_kwargs,
|
|
605
543
|
)
|
|
606
|
-
self._set_channel_meta(channel_meta)
|
|
607
|
-
if percentiles is not None:
|
|
608
|
-
self.set_channel_windows_with_percentiles(percentiles=percentiles)
|
|
609
|
-
|
|
610
|
-
def set_channel_meta(
|
|
611
|
-
self,
|
|
612
|
-
channel_meta: ChannelsMeta | None = None,
|
|
613
|
-
labels: Sequence[str | None] | int | None = None,
|
|
614
|
-
wavelength_id: Sequence[str | None] | None = None,
|
|
615
|
-
start: Sequence[float | None] | None = None,
|
|
616
|
-
end: Sequence[float | None] | None = None,
|
|
617
|
-
percentiles: tuple[float, float] | None = None,
|
|
618
|
-
colors: Sequence[str | None] | None = None,
|
|
619
|
-
active: Sequence[bool | None] | None = None,
|
|
620
|
-
**omero_kwargs: dict,
|
|
621
|
-
) -> None:
|
|
622
|
-
"""Create a ChannelsMeta object with the default unit.
|
|
623
|
-
|
|
624
|
-
Args:
|
|
625
|
-
channel_meta (ChannelsMeta | None): The channels metadata to set.
|
|
626
|
-
If none, it will fall back to the deprecated parameters.
|
|
627
|
-
labels(Sequence[str | None] | int): Deprecated. The list of channels names
|
|
628
|
-
in the image. If an integer is provided, the channels will
|
|
629
|
-
be named "channel_i".
|
|
630
|
-
wavelength_id(Sequence[str | None]): Deprecated. The wavelength ID of the
|
|
631
|
-
channel. If None, the wavelength ID will be the same as
|
|
632
|
-
the channel name.
|
|
633
|
-
start(Sequence[float | None]): Deprecated. The start value for each channel.
|
|
634
|
-
If None, the start value will be computed from the image.
|
|
635
|
-
end(Sequence[float | None]): Deprecated. The end value for each channel.
|
|
636
|
-
If None, the end value will be computed from the image.
|
|
637
|
-
percentiles(tuple[float, float] | None): Deprecated. The start and end
|
|
638
|
-
percentiles for each channel. If None, the percentiles will
|
|
639
|
-
not be computed.
|
|
640
|
-
colors(Sequence[str | None]): Deprecated. The list of colors for the
|
|
641
|
-
channels. If None, the colors will be random.
|
|
642
|
-
active (Sequence[bool | None]): Deprecated. Whether the channel should
|
|
643
|
-
be shown by default.
|
|
644
|
-
omero_kwargs(dict): Deprecated. Extra fields to store in the omero
|
|
645
|
-
attributes.
|
|
646
|
-
"""
|
|
647
|
-
_is_legacy = any(
|
|
648
|
-
param is not None
|
|
649
|
-
for param in [
|
|
650
|
-
labels,
|
|
651
|
-
wavelength_id,
|
|
652
|
-
start,
|
|
653
|
-
end,
|
|
654
|
-
percentiles,
|
|
655
|
-
colors,
|
|
656
|
-
active,
|
|
657
|
-
]
|
|
658
|
-
)
|
|
659
|
-
if _is_legacy:
|
|
660
|
-
warnings.warn(
|
|
661
|
-
"The following parameters are deprecated and will be removed in "
|
|
662
|
-
"ngio=0.6: labels, wavelength_id, start, end, percentiles, "
|
|
663
|
-
"colors, active, omero_kwargs. Please use the "
|
|
664
|
-
"'channel_meta' parameter instead.",
|
|
665
|
-
DeprecationWarning,
|
|
666
|
-
stacklevel=2,
|
|
667
|
-
)
|
|
668
|
-
self._set_channel_meta_legacy(
|
|
669
|
-
labels=labels,
|
|
670
|
-
wavelength_id=wavelength_id,
|
|
671
|
-
start=start,
|
|
672
|
-
end=end,
|
|
673
|
-
percentiles=percentiles,
|
|
674
|
-
colors=colors,
|
|
675
|
-
active=active,
|
|
676
|
-
**omero_kwargs,
|
|
677
|
-
)
|
|
678
|
-
return None
|
|
679
|
-
self._set_channel_meta(channel_meta)
|
|
680
544
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
) -> None:
|
|
685
|
-
"""Update the labels of the channels.
|
|
686
|
-
|
|
687
|
-
Args:
|
|
688
|
-
labels (Sequence[str]): The new labels for the channels.
|
|
689
|
-
"""
|
|
690
|
-
channels_meta = self.channels_meta
|
|
691
|
-
if len(labels) != len(channels_meta.channels):
|
|
692
|
-
raise NgioValueError(
|
|
693
|
-
"The number of labels must match the number of channels."
|
|
694
|
-
)
|
|
695
|
-
new_channels = []
|
|
696
|
-
for label, ch in zip(labels, channels_meta.channels, strict=True):
|
|
697
|
-
channel = ch.model_copy(update={"label": label})
|
|
698
|
-
new_channels.append(channel)
|
|
699
|
-
new_meta = channels_meta.model_copy(update={"channels": new_channels})
|
|
700
|
-
self._set_channel_meta(new_meta)
|
|
701
|
-
|
|
702
|
-
def set_channel_colors(
|
|
703
|
-
self,
|
|
704
|
-
colors: Sequence[str],
|
|
705
|
-
) -> None:
|
|
706
|
-
"""Update the colors of the channels.
|
|
707
|
-
|
|
708
|
-
Args:
|
|
709
|
-
colors (Sequence[str]): The new colors for the channels.
|
|
710
|
-
"""
|
|
711
|
-
channel_meta = self.channels_meta
|
|
712
|
-
if len(colors) != len(channel_meta.channels):
|
|
713
|
-
raise NgioValueError(
|
|
714
|
-
"The number of colors must match the number of channels."
|
|
715
|
-
)
|
|
716
|
-
new_channels = []
|
|
717
|
-
for color, ch in zip(colors, channel_meta.channels, strict=True):
|
|
718
|
-
ch_visualisation = ch.channel_visualisation.model_copy(
|
|
719
|
-
update={"color": color}
|
|
720
|
-
)
|
|
721
|
-
channel = ch.model_copy(update={"channel_visualisation": ch_visualisation})
|
|
722
|
-
new_channels.append(channel)
|
|
723
|
-
new_meta = channel_meta.model_copy(update={"channels": new_channels})
|
|
724
|
-
self._set_channel_meta(new_meta)
|
|
545
|
+
meta = self.meta
|
|
546
|
+
meta.set_channels_meta(channel_meta)
|
|
547
|
+
self._meta_handler.write_meta(meta)
|
|
725
548
|
|
|
726
549
|
def set_channel_percentiles(
|
|
727
550
|
self,
|
|
728
551
|
start_percentile: float = 0.1,
|
|
729
552
|
end_percentile: float = 99.9,
|
|
730
553
|
) -> None:
|
|
731
|
-
"""
|
|
554
|
+
"""Update the percentiles of the channels."""
|
|
555
|
+
if self.meta._channels_meta is None:
|
|
556
|
+
raise NgioValidationError("The channels meta is not initialized.")
|
|
732
557
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
warnings.warn(
|
|
738
|
-
"The 'set_channel_percentiles' method is deprecated and will be removed in "
|
|
739
|
-
"ngio=0.6. Please use 'set_channel_windows_with_percentiles' instead.",
|
|
740
|
-
DeprecationWarning,
|
|
741
|
-
stacklevel=2,
|
|
742
|
-
)
|
|
743
|
-
self.set_channel_windows_with_percentiles(
|
|
744
|
-
percentiles=(start_percentile, end_percentile)
|
|
558
|
+
low_res_dataset = self.meta.get_lowest_resolution_dataset()
|
|
559
|
+
ref_image = self.get(path=low_res_dataset.path)
|
|
560
|
+
starts, ends = compute_image_percentile(
|
|
561
|
+
ref_image, start_percentile=start_percentile, end_percentile=end_percentile
|
|
745
562
|
)
|
|
746
563
|
|
|
747
|
-
def set_channel_windows(
|
|
748
|
-
self,
|
|
749
|
-
starts_ends: Sequence[tuple[float, float]],
|
|
750
|
-
min_max: Sequence[tuple[float, float]] | None = None,
|
|
751
|
-
) -> None:
|
|
752
|
-
"""Update the channel windows.
|
|
753
|
-
|
|
754
|
-
These values are used by viewers to set the display
|
|
755
|
-
range of each channel.
|
|
756
|
-
|
|
757
|
-
Args:
|
|
758
|
-
starts_ends (Sequence[tuple[float, float]]): The start and end values
|
|
759
|
-
for each channel.
|
|
760
|
-
min_max (Sequence[tuple[float, float]] | None): The min and max values
|
|
761
|
-
for each channel. If None, the min and max values will not be updated.
|
|
762
|
-
"""
|
|
763
|
-
current_channels = self.channels_meta.channels
|
|
764
|
-
if len(starts_ends) != len(current_channels):
|
|
765
|
-
raise NgioValueError(
|
|
766
|
-
"The number of start-end pairs must match the number of channels."
|
|
767
|
-
)
|
|
768
|
-
if min_max is not None and len(min_max) != len(current_channels):
|
|
769
|
-
raise NgioValueError(
|
|
770
|
-
"The number of min-max pairs must match the number of channels."
|
|
771
|
-
)
|
|
772
|
-
if min_max is None:
|
|
773
|
-
min_max_ = [None] * len(current_channels)
|
|
774
|
-
else:
|
|
775
|
-
min_max_ = list(min_max)
|
|
776
564
|
channels = []
|
|
777
|
-
for
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
565
|
+
for c, channel in enumerate(self.meta._channels_meta.channels):
|
|
566
|
+
new_v = ChannelVisualisation(
|
|
567
|
+
start=starts[c],
|
|
568
|
+
end=ends[c],
|
|
569
|
+
**channel.channel_visualisation.model_dump(exclude={"start", "end"}),
|
|
570
|
+
)
|
|
571
|
+
new_c = Channel(
|
|
572
|
+
channel_visualisation=new_v,
|
|
573
|
+
**channel.model_dump(exclude={"channel_visualisation"}),
|
|
786
574
|
)
|
|
787
|
-
channels.append(
|
|
575
|
+
channels.append(new_c)
|
|
576
|
+
|
|
788
577
|
new_meta = ChannelsMeta(channels=channels)
|
|
578
|
+
|
|
789
579
|
meta = self.meta
|
|
790
580
|
meta.set_channels_meta(new_meta)
|
|
791
|
-
self._meta_handler.
|
|
792
|
-
|
|
793
|
-
def set_channel_windows_with_percentiles(
|
|
794
|
-
self,
|
|
795
|
-
percentiles: tuple[float, float] | list[tuple[float, float]] = (0.1, 99.9),
|
|
796
|
-
) -> None:
|
|
797
|
-
"""Update the channel windows using percentiles.
|
|
798
|
-
|
|
799
|
-
Args:
|
|
800
|
-
percentiles (tuple[float, float] | list[tuple[float, float]]):
|
|
801
|
-
The start and end percentiles for each channel.
|
|
802
|
-
If a single tuple is provided,
|
|
803
|
-
the same percentiles will be used for all channels.
|
|
804
|
-
"""
|
|
805
|
-
if self.meta._channels_meta is None:
|
|
806
|
-
raise NgioValueError("The channels meta is not initialized.")
|
|
807
|
-
|
|
808
|
-
low_res_dataset = self.meta.get_lowest_resolution_dataset()
|
|
809
|
-
ref_image = self.get(path=low_res_dataset.path)
|
|
810
|
-
starts_ends = compute_image_percentile(ref_image, percentiles=percentiles)
|
|
811
|
-
self.set_channel_windows(starts_ends=starts_ends)
|
|
581
|
+
self._meta_handler.write_meta(meta)
|
|
812
582
|
|
|
813
583
|
def set_axes_unit(
|
|
814
584
|
self,
|
|
@@ -821,127 +591,64 @@ class ImagesContainer:
|
|
|
821
591
|
space_unit (SpaceUnits): The space unit of the image.
|
|
822
592
|
time_unit (TimeUnits): The time unit of the image.
|
|
823
593
|
"""
|
|
824
|
-
self.
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
self,
|
|
828
|
-
axes_names: Sequence[str],
|
|
829
|
-
) -> None:
|
|
830
|
-
"""Set the axes names of the image.
|
|
831
|
-
|
|
832
|
-
Args:
|
|
833
|
-
axes_names (Sequence[str]): The axes names of the image.
|
|
834
|
-
"""
|
|
835
|
-
image = self.get()
|
|
836
|
-
image.set_axes_names(axes_names=axes_names)
|
|
837
|
-
self._meta_handler._axes_setup = image.meta.axes_handler.axes_setup
|
|
838
|
-
|
|
839
|
-
def set_name(
|
|
840
|
-
self,
|
|
841
|
-
name: str,
|
|
842
|
-
) -> None:
|
|
843
|
-
"""Set the name of the image in the metadata.
|
|
844
|
-
|
|
845
|
-
This does not change the group name or any paths.
|
|
846
|
-
|
|
847
|
-
Args:
|
|
848
|
-
name (str): The name of the image.
|
|
849
|
-
"""
|
|
850
|
-
self.get().set_name(name=name)
|
|
594
|
+
meta = self.meta
|
|
595
|
+
meta = meta.to_units(space_unit=space_unit, time_unit=time_unit)
|
|
596
|
+
self._meta_handler.write_meta(meta)
|
|
851
597
|
|
|
852
598
|
def derive(
|
|
853
599
|
self,
|
|
854
600
|
store: StoreOrGroup,
|
|
855
601
|
ref_path: str | None = None,
|
|
856
|
-
# Metadata parameters
|
|
857
602
|
shape: Sequence[int] | None = None,
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
603
|
+
labels: Sequence[str] | None = None,
|
|
604
|
+
pixel_size: PixelSize | None = None,
|
|
605
|
+
axes_names: Sequence[str] | None = None,
|
|
861
606
|
name: str | None = None,
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
607
|
+
chunks: Sequence[int] | None = None,
|
|
608
|
+
dtype: str | None = None,
|
|
609
|
+
dimension_separator: Literal[".", "/"] | None = None,
|
|
610
|
+
compressors: CompressorLike | None = None,
|
|
865
611
|
ngff_version: NgffVersions | None = None,
|
|
866
|
-
# Zarr Array parameters
|
|
867
|
-
chunks: ChunksLike | None = None,
|
|
868
|
-
shards: ShardsLike | None = None,
|
|
869
|
-
dtype: str = "uint16",
|
|
870
|
-
dimension_separator: Literal[".", "/"] = "/",
|
|
871
|
-
compressors: CompressorLike = "auto",
|
|
872
|
-
extra_array_kwargs: Mapping[str, Any] | None = None,
|
|
873
612
|
overwrite: bool = False,
|
|
874
|
-
# Deprecated arguments
|
|
875
|
-
labels: Sequence[str] | None = None,
|
|
876
|
-
pixel_size: PixelSize | None = None,
|
|
877
613
|
) -> "ImagesContainer":
|
|
878
614
|
"""Create an empty OME-Zarr image from an existing image.
|
|
879
615
|
|
|
880
|
-
If a kwarg is not provided, the value from the reference image will be used.
|
|
881
|
-
|
|
882
616
|
Args:
|
|
883
617
|
store (StoreOrGroup): The Zarr store or group to create the image in.
|
|
884
|
-
ref_path (str | None): The path to the reference image in
|
|
885
|
-
container.
|
|
618
|
+
ref_path (str | None): The path to the reference image in
|
|
619
|
+
the image container.
|
|
886
620
|
shape (Sequence[int] | None): The shape of the new image.
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
time_spacing (float | None): The time spacing of the new image.
|
|
621
|
+
labels (Sequence[str] | None): The labels of the new image.
|
|
622
|
+
pixel_size (PixelSize | None): The pixel size of the new image.
|
|
623
|
+
axes_names (Sequence[str] | None): The axes names of the new image.
|
|
891
624
|
name (str | None): The name of the new image.
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
Possible policies:
|
|
898
|
-
- If "squeeze", the channels axis will be removed (no matter its size).
|
|
899
|
-
- If "same", the channels axis will be kept as is (if it exists).
|
|
900
|
-
- If "singleton", the channels axis will be set to size 1.
|
|
901
|
-
- If an integer is provided, the channels axis will be changed to have
|
|
902
|
-
that size.
|
|
903
|
-
ngff_version (NgffVersions | None): The NGFF version to use.
|
|
904
|
-
chunks (ChunksLike | None): The chunk shape of the new image.
|
|
905
|
-
shards (ShardsLike | None): The shard shape of the new image.
|
|
625
|
+
chunks (Sequence[int] | Literal["auto"]): The chunk shape of the new image.
|
|
626
|
+
dimension_separator (DIMENSION_SEPARATOR | None): The separator to use for
|
|
627
|
+
dimensions. If None it will use the same as the reference image.
|
|
628
|
+
compressors: The compressor to use. If None it will use
|
|
629
|
+
the same as the reference image.
|
|
906
630
|
dtype (str | None): The data type of the new image.
|
|
907
|
-
|
|
908
|
-
dimensions.
|
|
909
|
-
compressors (CompressorLike | None): The compressors to use.
|
|
910
|
-
extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
|
|
911
|
-
the zarr array creation.
|
|
631
|
+
ngff_version (NgffVersions): The NGFF version to use.
|
|
912
632
|
overwrite (bool): Whether to overwrite an existing image.
|
|
913
|
-
labels (Sequence[str] | None): The labels of the new image.
|
|
914
|
-
This argument is deprecated please use channels_meta instead.
|
|
915
|
-
pixel_size (PixelSize | None): The pixel size of the new image.
|
|
916
|
-
This argument is deprecated please use pixelsize, z_spacing,
|
|
917
|
-
and time_spacing instead.
|
|
918
633
|
|
|
919
634
|
Returns:
|
|
920
|
-
ImagesContainer: The new
|
|
921
|
-
|
|
635
|
+
ImagesContainer: The new image
|
|
922
636
|
"""
|
|
923
637
|
return derive_image_container(
|
|
924
638
|
image_container=self,
|
|
925
639
|
store=store,
|
|
926
640
|
ref_path=ref_path,
|
|
927
641
|
shape=shape,
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
642
|
+
labels=labels,
|
|
643
|
+
pixel_size=pixel_size,
|
|
644
|
+
axes_names=axes_names,
|
|
931
645
|
name=name,
|
|
932
|
-
translation=translation,
|
|
933
|
-
channels_meta=channels_meta,
|
|
934
|
-
channels_policy=channels_policy,
|
|
935
|
-
ngff_version=ngff_version,
|
|
936
646
|
chunks=chunks,
|
|
937
|
-
shards=shards,
|
|
938
647
|
dtype=dtype,
|
|
939
648
|
dimension_separator=dimension_separator,
|
|
940
649
|
compressors=compressors,
|
|
941
|
-
|
|
650
|
+
ngff_version=ngff_version,
|
|
942
651
|
overwrite=overwrite,
|
|
943
|
-
labels=labels,
|
|
944
|
-
pixel_size=pixel_size,
|
|
945
652
|
)
|
|
946
653
|
|
|
947
654
|
def get(
|
|
@@ -960,7 +667,7 @@ class ImagesContainer:
|
|
|
960
667
|
closest pixel size level will be returned.
|
|
961
668
|
|
|
962
669
|
"""
|
|
963
|
-
dataset = self._meta_handler.
|
|
670
|
+
dataset = self._meta_handler.meta.get_dataset(
|
|
964
671
|
path=path, pixel_size=pixel_size, strict=strict
|
|
965
672
|
)
|
|
966
673
|
return Image(
|
|
@@ -972,53 +679,34 @@ class ImagesContainer:
|
|
|
972
679
|
|
|
973
680
|
def compute_image_percentile(
|
|
974
681
|
image: Image,
|
|
975
|
-
|
|
976
|
-
|
|
682
|
+
start_percentile: float = 0.1,
|
|
683
|
+
end_percentile: float = 99.9,
|
|
684
|
+
) -> tuple[list[float], list[float]]:
|
|
977
685
|
"""Compute the start and end percentiles for each channel of an image.
|
|
978
686
|
|
|
979
687
|
Args:
|
|
980
688
|
image: The image to compute the percentiles for.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
for all channels.
|
|
689
|
+
start_percentile: The start percentile to compute.
|
|
690
|
+
end_percentile: The end percentile to compute.
|
|
984
691
|
|
|
985
692
|
Returns:
|
|
986
693
|
A tuple containing the start and end percentiles for each channel.
|
|
987
694
|
"""
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
"(start_percentile, end_percentile) or "
|
|
995
|
-
"a list of such tuples with length equal to the number of channels."
|
|
996
|
-
)
|
|
997
|
-
if not isinstance(percentiles[0], float) or not isinstance(
|
|
998
|
-
percentiles[1], float
|
|
999
|
-
):
|
|
1000
|
-
raise NgioValueError(
|
|
1001
|
-
"Percentiles must be a tuple of two floats: "
|
|
1002
|
-
"(start_percentile, end_percentile) or "
|
|
1003
|
-
"a list of such tuples with length equal to the number of channels."
|
|
1004
|
-
)
|
|
1005
|
-
percentiles = [percentiles] * num_channels
|
|
695
|
+
starts, ends = [], []
|
|
696
|
+
for c in range(image.num_channels):
|
|
697
|
+
if image.num_channels == 1:
|
|
698
|
+
data = image.get_as_dask()
|
|
699
|
+
else:
|
|
700
|
+
data = image.get_as_dask(c=c)
|
|
1006
701
|
|
|
1007
|
-
if len(percentiles) != num_channels:
|
|
1008
|
-
raise NgioValueError(
|
|
1009
|
-
"If a list of percentiles is provided, its length must be equal "
|
|
1010
|
-
"to the number of channels."
|
|
1011
|
-
)
|
|
1012
|
-
starts_and_ends = []
|
|
1013
|
-
for c_idx, (start_percentile, end_percentile) in enumerate(percentiles):
|
|
1014
|
-
data = image.get_as_dask(c=c_idx)
|
|
1015
702
|
data = da.ravel(data)
|
|
1016
703
|
# remove all the zeros
|
|
1017
704
|
mask = data > 1e-16
|
|
1018
705
|
data = data[mask]
|
|
1019
706
|
_data = data.compute()
|
|
1020
707
|
if _data.size == 0:
|
|
1021
|
-
|
|
708
|
+
starts.append(0.0)
|
|
709
|
+
ends.append(0.0)
|
|
1022
710
|
continue
|
|
1023
711
|
|
|
1024
712
|
# compute the percentiles
|
|
@@ -1026,107 +714,153 @@ def compute_image_percentile(
|
|
|
1026
714
|
data, [start_percentile, end_percentile], method="nearest"
|
|
1027
715
|
).compute() # type: ignore (return type is a tuple of floats)
|
|
1028
716
|
|
|
1029
|
-
|
|
1030
|
-
|
|
717
|
+
starts.append(float(_s_perc))
|
|
718
|
+
ends.append(float(_e_perc))
|
|
719
|
+
return starts, ends
|
|
1031
720
|
|
|
1032
721
|
|
|
1033
722
|
def derive_image_container(
|
|
1034
|
-
*,
|
|
1035
723
|
image_container: ImagesContainer,
|
|
1036
724
|
store: StoreOrGroup,
|
|
1037
725
|
ref_path: str | None = None,
|
|
1038
|
-
# Metadata parameters
|
|
1039
726
|
shape: Sequence[int] | None = None,
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
727
|
+
labels: Sequence[str] | None = None,
|
|
728
|
+
pixel_size: PixelSize | None = None,
|
|
729
|
+
axes_names: Sequence[str] | None = None,
|
|
1043
730
|
name: str | None = None,
|
|
1044
|
-
|
|
1045
|
-
channels_policy: Literal["same", "squeeze", "singleton"] | int = "same",
|
|
1046
|
-
channels_meta: Sequence[str | Channel] | None = None,
|
|
1047
|
-
ngff_version: NgffVersions | None = None,
|
|
1048
|
-
# Zarr Array parameters
|
|
1049
|
-
chunks: ChunksLike | None = None,
|
|
1050
|
-
shards: ShardsLike | None = None,
|
|
731
|
+
chunks: Sequence[int] | None = None,
|
|
1051
732
|
dtype: str | None = None,
|
|
1052
733
|
dimension_separator: Literal[".", "/"] | None = None,
|
|
1053
734
|
compressors: CompressorLike | None = None,
|
|
1054
|
-
|
|
735
|
+
ngff_version: NgffVersions | None = None,
|
|
1055
736
|
overwrite: bool = False,
|
|
1056
|
-
# Deprecated arguments
|
|
1057
|
-
labels: Sequence[str] | None = None,
|
|
1058
|
-
pixel_size: PixelSize | None = None,
|
|
1059
737
|
) -> ImagesContainer:
|
|
1060
|
-
"""
|
|
1061
|
-
|
|
1062
|
-
If a kwarg is not provided, the value from the reference image will be used.
|
|
738
|
+
"""Create an empty OME-Zarr image from an existing image.
|
|
1063
739
|
|
|
1064
740
|
Args:
|
|
1065
|
-
image_container (ImagesContainer): The image container to derive the new image
|
|
1066
|
-
from.
|
|
741
|
+
image_container (ImagesContainer): The image container to derive the new image.
|
|
1067
742
|
store (StoreOrGroup): The Zarr store or group to create the image in.
|
|
1068
743
|
ref_path (str | None): The path to the reference image in the image container.
|
|
1069
744
|
shape (Sequence[int] | None): The shape of the new image.
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
745
|
+
labels (Sequence[str] | None): The labels of the new image.
|
|
746
|
+
pixel_size (PixelSize | None): The pixel size of the new image.
|
|
747
|
+
axes_names (Sequence[str] | None): The axes names of the new image.
|
|
1073
748
|
name (str | None): The name of the new image.
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
- If "singleton", the channels axis will be set to size 1.
|
|
1081
|
-
- If an integer is provided, the channels axis will be changed to have
|
|
1082
|
-
that size.
|
|
1083
|
-
channels_meta (Sequence[str | Channel] | None): The channels metadata
|
|
1084
|
-
of the new image.
|
|
1085
|
-
ngff_version (NgffVersions | None): The NGFF version to use.
|
|
1086
|
-
chunks (ChunksLike | None): The chunk shape of the new image.
|
|
1087
|
-
shards (ShardsLike | None): The shard shape of the new image.
|
|
749
|
+
chunks (Sequence[int] | None): The chunk shape of the new image.
|
|
750
|
+
dimension_separator (DIMENSION_SEPARATOR | None): The separator to use for
|
|
751
|
+
dimensions. If None it will use the same as the reference image.
|
|
752
|
+
compressors (CompressorLike | None): The compressors to use. If None it will use
|
|
753
|
+
the same as the reference image.
|
|
754
|
+
ngff_version (NgffVersions): The NGFF version to use.
|
|
1088
755
|
dtype (str | None): The data type of the new image.
|
|
1089
|
-
|
|
1090
|
-
dimensions.
|
|
1091
|
-
compressors (CompressorLike | None): The compressors to use.
|
|
1092
|
-
extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
|
|
1093
|
-
the zarr array creation.
|
|
1094
|
-
overwrite (bool): Whether to overwrite an existing image. Defaults to False.
|
|
1095
|
-
labels (Sequence[str] | None): Deprecated. This argument is deprecated,
|
|
1096
|
-
please use channels_meta instead.
|
|
1097
|
-
pixel_size (PixelSize | None): Deprecated. The pixel size of the new image.
|
|
1098
|
-
This argument is deprecated, please use pixelsize, z_spacing,
|
|
1099
|
-
and time_spacing instead.
|
|
756
|
+
overwrite (bool): Whether to overwrite an existing image.
|
|
1100
757
|
|
|
1101
758
|
Returns:
|
|
1102
|
-
ImagesContainer: The new
|
|
759
|
+
ImagesContainer: The new image
|
|
1103
760
|
|
|
1104
761
|
"""
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
762
|
+
if ref_path is None:
|
|
763
|
+
ref_image = image_container.get()
|
|
764
|
+
else:
|
|
765
|
+
ref_image = image_container.get(path=ref_path)
|
|
766
|
+
|
|
767
|
+
ref_meta = ref_image.meta
|
|
768
|
+
|
|
769
|
+
if shape is None:
|
|
770
|
+
shape = ref_image.shape
|
|
771
|
+
|
|
772
|
+
if pixel_size is None:
|
|
773
|
+
pixel_size = ref_image.pixel_size
|
|
774
|
+
|
|
775
|
+
if axes_names is None:
|
|
776
|
+
axes_names = ref_meta.axes_handler.axes_names
|
|
777
|
+
|
|
778
|
+
if len(axes_names) != len(shape):
|
|
779
|
+
raise NgioValidationError(
|
|
780
|
+
"The axes names of the new image does not match the reference image."
|
|
781
|
+
f"Got {axes_names} for shape {shape}."
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
if chunks is None:
|
|
785
|
+
chunks = ref_image.chunks
|
|
786
|
+
|
|
787
|
+
if len(chunks) != len(shape):
|
|
788
|
+
raise NgioValidationError(
|
|
789
|
+
"The chunks of the new image does not match the reference image."
|
|
790
|
+
f"Got {chunks} for shape {shape}."
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
if name is None:
|
|
794
|
+
name = ref_meta.name
|
|
795
|
+
|
|
796
|
+
if dtype is None:
|
|
797
|
+
dtype = ref_image.dtype
|
|
798
|
+
|
|
799
|
+
if dimension_separator is None:
|
|
800
|
+
dimension_separator = find_dimension_separator(ref_image.zarr_array)
|
|
801
|
+
|
|
802
|
+
if compressors is None:
|
|
803
|
+
compressors = ref_image.zarr_array.compressors # type: ignore
|
|
804
|
+
|
|
805
|
+
if ngff_version is None:
|
|
806
|
+
ngff_version = ref_meta.version
|
|
807
|
+
|
|
808
|
+
handler = create_empty_image_container(
|
|
1109
809
|
store=store,
|
|
1110
810
|
shape=shape,
|
|
1111
|
-
pixelsize=
|
|
1112
|
-
z_spacing=
|
|
1113
|
-
time_spacing=
|
|
811
|
+
pixelsize=pixel_size.x,
|
|
812
|
+
z_spacing=pixel_size.z,
|
|
813
|
+
time_spacing=pixel_size.t,
|
|
814
|
+
levels=ref_meta.paths,
|
|
815
|
+
yx_scaling_factor=ref_meta.yx_scaling(),
|
|
816
|
+
z_scaling_factor=ref_meta.z_scaling(),
|
|
817
|
+
time_unit=pixel_size.time_unit,
|
|
818
|
+
space_unit=pixel_size.space_unit,
|
|
819
|
+
axes_names=axes_names,
|
|
1114
820
|
name=name,
|
|
1115
|
-
translation=translation,
|
|
1116
|
-
channels_meta=channels_meta,
|
|
1117
|
-
channels_policy=channels_policy,
|
|
1118
|
-
ngff_version=ngff_version,
|
|
1119
821
|
chunks=chunks,
|
|
1120
|
-
shards=shards,
|
|
1121
822
|
dtype=dtype,
|
|
1122
823
|
dimension_separator=dimension_separator,
|
|
1123
824
|
compressors=compressors,
|
|
1124
|
-
extra_array_kwargs=extra_array_kwargs,
|
|
1125
825
|
overwrite=overwrite,
|
|
1126
|
-
|
|
1127
|
-
pixel_size=pixel_size,
|
|
826
|
+
version=ngff_version,
|
|
1128
827
|
)
|
|
1129
|
-
|
|
828
|
+
image_container = ImagesContainer(handler)
|
|
829
|
+
|
|
830
|
+
if ref_image.num_channels == image_container.num_channels:
|
|
831
|
+
_labels = ref_image.channel_labels
|
|
832
|
+
wavelength_id = ref_image.wavelength_ids
|
|
833
|
+
|
|
834
|
+
channel_meta = ref_image.channels_meta
|
|
835
|
+
colors = [c.channel_visualisation.color for c in channel_meta.channels]
|
|
836
|
+
active = [c.channel_visualisation.active for c in channel_meta.channels]
|
|
837
|
+
start = [c.channel_visualisation.start for c in channel_meta.channels]
|
|
838
|
+
end = [c.channel_visualisation.end for c in channel_meta.channels]
|
|
839
|
+
else:
|
|
840
|
+
_labels = None
|
|
841
|
+
wavelength_id = None
|
|
842
|
+
colors = None
|
|
843
|
+
active = None
|
|
844
|
+
start = None
|
|
845
|
+
end = None
|
|
846
|
+
|
|
847
|
+
if labels is not None:
|
|
848
|
+
if len(labels) != image_container.num_channels:
|
|
849
|
+
raise NgioValidationError(
|
|
850
|
+
"The number of labels does not match the number of channels."
|
|
851
|
+
)
|
|
852
|
+
_labels = labels
|
|
853
|
+
|
|
854
|
+
image_container.set_channel_meta(
|
|
855
|
+
labels=_labels,
|
|
856
|
+
wavelength_id=wavelength_id,
|
|
857
|
+
percentiles=None,
|
|
858
|
+
colors=colors,
|
|
859
|
+
active=active,
|
|
860
|
+
start=start,
|
|
861
|
+
end=end,
|
|
862
|
+
)
|
|
863
|
+
return image_container
|
|
1130
864
|
|
|
1131
865
|
|
|
1132
866
|
def _parse_str_or_model(
|
|
@@ -1135,9 +869,9 @@ def _parse_str_or_model(
|
|
|
1135
869
|
"""Parse a string or ChannelSelectionModel to an integer channel index."""
|
|
1136
870
|
if isinstance(channel_selection, int):
|
|
1137
871
|
if channel_selection < 0:
|
|
1138
|
-
raise
|
|
872
|
+
raise NgioValidationError("Channel index must be a non-negative integer.")
|
|
1139
873
|
if channel_selection >= image.num_channels:
|
|
1140
|
-
raise
|
|
874
|
+
raise NgioValidationError(
|
|
1141
875
|
"Channel index must be less than the number "
|
|
1142
876
|
f"of channels ({image.num_channels})."
|
|
1143
877
|
)
|
|
@@ -1151,11 +885,11 @@ def _parse_str_or_model(
|
|
|
1151
885
|
)
|
|
1152
886
|
elif channel_selection.mode == "wavelength_id":
|
|
1153
887
|
return image.get_channel_idx(
|
|
1154
|
-
|
|
888
|
+
channel_label=str(channel_selection.identifier)
|
|
1155
889
|
)
|
|
1156
890
|
elif channel_selection.mode == "index":
|
|
1157
891
|
return int(channel_selection.identifier)
|
|
1158
|
-
raise
|
|
892
|
+
raise NgioValidationError(
|
|
1159
893
|
"Invalid channel selection type. "
|
|
1160
894
|
f"{channel_selection} is of type {type(channel_selection)} ",
|
|
1161
895
|
"supported types are str, ChannelSelectionModel, and int.",
|
|
@@ -1174,7 +908,7 @@ def _parse_channel_selection(
|
|
|
1174
908
|
elif isinstance(channel_selection, Sequence):
|
|
1175
909
|
_sequence = [_parse_str_or_model(image, cs) for cs in channel_selection]
|
|
1176
910
|
return {"c": _sequence}
|
|
1177
|
-
raise
|
|
911
|
+
raise NgioValidationError(
|
|
1178
912
|
f"Invalid channel selection type {type(channel_selection)}. "
|
|
1179
913
|
"Supported types are int, str, ChannelSelectionModel, and Sequence."
|
|
1180
914
|
)
|
|
@@ -1188,7 +922,7 @@ def add_channel_selection_to_slicing_dict(
|
|
|
1188
922
|
"""Add channel selection information to the slicing dictionary."""
|
|
1189
923
|
channel_info = _parse_channel_selection(image, channel_selection)
|
|
1190
924
|
if "c" in slicing_dict and channel_info:
|
|
1191
|
-
raise
|
|
925
|
+
raise NgioValidationError(
|
|
1192
926
|
"Both channel_selection and 'c' in slicing_kwargs are provided. "
|
|
1193
927
|
"Which channel selection should be used is ambiguous. "
|
|
1194
928
|
"Please provide only one."
|