ngio 0.5.0__py3-none-any.whl → 0.5.0a2__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.
Files changed (53) hide show
  1. ngio/__init__.py +2 -5
  2. ngio/common/__init__.py +6 -11
  3. ngio/common/_masking_roi.py +54 -34
  4. ngio/common/_pyramid.py +85 -309
  5. ngio/common/_roi.py +330 -258
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +11 -10
  8. ngio/hcs/_plate.py +60 -132
  9. ngio/images/_abstract_image.py +35 -539
  10. ngio/images/_create.py +287 -0
  11. ngio/images/_create_synt_container.py +42 -39
  12. ngio/images/_image.py +250 -516
  13. ngio/images/_label.py +172 -249
  14. ngio/images/_masked_image.py +2 -2
  15. ngio/images/_ome_zarr_container.py +241 -644
  16. ngio/io_pipes/_io_pipes.py +9 -9
  17. ngio/io_pipes/_io_pipes_masked.py +7 -7
  18. ngio/io_pipes/_io_pipes_roi.py +6 -6
  19. ngio/io_pipes/_io_pipes_types.py +3 -3
  20. ngio/io_pipes/_match_shape.py +8 -6
  21. ngio/io_pipes/_ops_slices_utils.py +5 -8
  22. ngio/ome_zarr_meta/__init__.py +18 -29
  23. ngio/ome_zarr_meta/_meta_handlers.py +708 -392
  24. ngio/ome_zarr_meta/ngio_specs/__init__.py +0 -4
  25. ngio/ome_zarr_meta/ngio_specs/_axes.py +51 -152
  26. ngio/ome_zarr_meta/ngio_specs/_dataset.py +22 -13
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +91 -129
  28. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +68 -57
  29. ngio/ome_zarr_meta/v04/__init__.py +1 -5
  30. ngio/ome_zarr_meta/v04/{_v04_spec.py → _v04_spec_utils.py} +85 -54
  31. ngio/ome_zarr_meta/v05/__init__.py +1 -5
  32. ngio/ome_zarr_meta/v05/{_v05_spec.py → _v05_spec_utils.py} +87 -64
  33. ngio/resources/__init__.py +1 -1
  34. ngio/resources/resource_model.py +1 -1
  35. ngio/tables/_tables_container.py +11 -62
  36. ngio/tables/backends/_anndata.py +8 -58
  37. ngio/tables/backends/_anndata_utils.py +6 -1
  38. ngio/tables/backends/_csv.py +19 -3
  39. ngio/tables/backends/_json.py +13 -10
  40. ngio/tables/backends/_non_zarr_backends.py +196 -0
  41. ngio/tables/backends/_parquet.py +31 -3
  42. ngio/tables/v1/_roi_table.py +24 -41
  43. ngio/utils/__init__.py +12 -6
  44. ngio/utils/_datasets.py +0 -6
  45. ngio/utils/_logger.py +50 -0
  46. ngio/utils/_zarr_utils.py +58 -167
  47. {ngio-0.5.0.dist-info → ngio-0.5.0a2.dist-info}/METADATA +4 -11
  48. ngio-0.5.0a2.dist-info/RECORD +89 -0
  49. {ngio-0.5.0.dist-info → ngio-0.5.0a2.dist-info}/WHEEL +1 -1
  50. ngio/images/_create_utils.py +0 -406
  51. ngio/tables/backends/_py_arrow_backends.py +0 -222
  52. ngio-0.5.0.dist-info/RECORD +0 -88
  53. {ngio-0.5.0.dist-info → ngio-0.5.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,32 +1,19 @@
1
1
  """Generic class to handle Image-like data in a OME-NGFF file."""
2
2
 
3
- import warnings
4
- from abc import ABC, abstractmethod
5
- from collections.abc import Mapping, Sequence
6
- from typing import Any, Literal
3
+ from collections.abc import Sequence
4
+ from typing import Generic, Literal, TypeVar
7
5
 
8
6
  import dask.array as da
9
7
  import numpy as np
10
8
  import zarr
11
- from zarr.core.array import CompressorLike
12
9
 
13
10
  from ngio.common import (
14
11
  Dimensions,
15
12
  InterpolationOrder,
16
13
  Roi,
14
+ RoiPixels,
17
15
  consolidate_pyramid,
18
16
  )
19
- from ngio.common._pyramid import (
20
- ChunksLike,
21
- ShardsLike,
22
- compute_scales_from_shapes,
23
- compute_shapes_from_scaling_factors,
24
- )
25
- from ngio.images._create_utils import (
26
- _image_or_label_meta,
27
- compute_base_scale,
28
- init_image_like_from_shapes,
29
- )
30
17
  from ngio.io_pipes import (
31
18
  DaskGetter,
32
19
  DaskRoiGetter,
@@ -44,32 +31,15 @@ from ngio.ome_zarr_meta import (
44
31
  Dataset,
45
32
  ImageMetaHandler,
46
33
  LabelMetaHandler,
47
- NgioImageMeta,
48
34
  PixelSize,
49
35
  )
50
- from ngio.ome_zarr_meta.ngio_specs import (
51
- Channel,
52
- NgffVersions,
53
- NgioLabelMeta,
54
- )
55
- from ngio.ome_zarr_meta.ngio_specs._axes import (
56
- AxesSetup,
57
- DefaultSpaceUnit,
58
- DefaultTimeUnit,
59
- SpaceUnits,
60
- TimeUnits,
61
- )
62
36
  from ngio.tables import RoiTable
63
- from ngio.utils import (
64
- NgioFileExistsError,
65
- NgioValueError,
66
- StoreOrGroup,
67
- ZarrGroupHandler,
68
- )
69
- from ngio.utils._zarr_utils import find_dimension_separator
37
+ from ngio.utils import NgioFileExistsError, ZarrGroupHandler
38
+
39
+ _image_handler = TypeVar("_image_handler", ImageMetaHandler, LabelMetaHandler)
70
40
 
71
41
 
72
- class AbstractImage(ABC):
42
+ class AbstractImage(Generic[_image_handler]):
73
43
  """A class to handle a single image (or level) in an OME-Zarr image.
74
44
 
75
45
  This class is meant to be subclassed by specific image types.
@@ -79,7 +49,7 @@ class AbstractImage(ABC):
79
49
  self,
80
50
  group_handler: ZarrGroupHandler,
81
51
  path: str,
82
- meta_handler: ImageMetaHandler | LabelMetaHandler,
52
+ meta_handler: _image_handler,
83
53
  ) -> None:
84
54
  """Initialize the Image at a single level.
85
55
 
@@ -108,21 +78,14 @@ class AbstractImage(ABC):
108
78
  return self._path
109
79
 
110
80
  @property
111
- @abstractmethod
112
- def meta_handler(self) -> ImageMetaHandler | LabelMetaHandler:
81
+ def meta_handler(self) -> _image_handler:
113
82
  """Return the metadata."""
114
- pass
115
-
116
- @property
117
- @abstractmethod
118
- def meta(self) -> NgioImageMeta | NgioLabelMeta:
119
- """Return the metadata."""
120
- pass
83
+ return self._meta_handler
121
84
 
122
85
  @property
123
86
  def dataset(self) -> Dataset:
124
87
  """Return the dataset of the image."""
125
- return self.meta_handler.get_meta().get_dataset(path=self.path)
88
+ return self.meta_handler.meta.get_dataset(path=self.path)
126
89
 
127
90
  @property
128
91
  def dimensions(self) -> Dimensions:
@@ -143,11 +106,6 @@ class AbstractImage(ABC):
143
106
  """Return the axes handler of the image."""
144
107
  return self.dataset.axes_handler
145
108
 
146
- @property
147
- def axes_setup(self) -> AxesSetup:
148
- """Return the axes setup of the image."""
149
- return self.axes_handler.axes_setup
150
-
151
109
  @property
152
110
  def axes(self) -> tuple[str, ...]:
153
111
  """Return the axes of the image."""
@@ -217,47 +175,6 @@ class AbstractImage(ABC):
217
175
  """Return True if the image has the given axis."""
218
176
  return self.axes_handler.has_axis(axis)
219
177
 
220
- def set_axes_unit(
221
- self,
222
- space_unit: SpaceUnits = DefaultSpaceUnit,
223
- time_unit: TimeUnits = DefaultTimeUnit,
224
- ) -> None:
225
- """Set the axes unit of the image.
226
-
227
- Args:
228
- space_unit (SpaceUnits): The space unit of the image.
229
- time_unit (TimeUnits): The time unit of the image.
230
- """
231
- meta = self._meta_handler.get_meta()
232
- meta = meta.to_units(space_unit=space_unit, time_unit=time_unit)
233
- self._meta_handler.update_meta(meta) # type: ignore
234
-
235
- def set_axes_names(self, axes_names: Sequence[str]) -> None:
236
- """Set the axes names of the label.
237
-
238
- Args:
239
- axes_names (Sequence[str]): The axes names to set.
240
- """
241
- meta = self._meta_handler.get_meta()
242
- meta = meta.rename_axes(axes_names=axes_names)
243
- self._meta_handler._axes_setup = meta.axes_handler.axes_setup
244
- self._meta_handler.update_meta(meta) # type: ignore
245
-
246
- def set_name(
247
- self,
248
- name: str,
249
- ) -> None:
250
- """Set the name of the image in the metadata.
251
-
252
- This does not change the group name or any paths.
253
-
254
- Args:
255
- name (str): The name of the image.
256
- """
257
- meta = self._meta_handler.get_meta()
258
- meta = meta.rename_image(name=name)
259
- self._meta_handler.update_meta(meta) # type: ignore
260
-
261
178
  def _get_as_numpy(
262
179
  self,
263
180
  axes_order: Sequence[str] | None = None,
@@ -285,7 +202,7 @@ class AbstractImage(ABC):
285
202
 
286
203
  def _get_roi_as_numpy(
287
204
  self,
288
- roi: Roi,
205
+ roi: Roi | RoiPixels,
289
206
  axes_order: Sequence[str] | None = None,
290
207
  transforms: Sequence[TransformProtocol] | None = None,
291
208
  **slicing_kwargs: SlicingInputType,
@@ -335,7 +252,7 @@ class AbstractImage(ABC):
335
252
 
336
253
  def _get_roi_as_dask(
337
254
  self,
338
- roi: Roi,
255
+ roi: Roi | RoiPixels,
339
256
  axes_order: Sequence[str] | None = None,
340
257
  transforms: Sequence[TransformProtocol] | None = None,
341
258
  **slicing_kwargs: SlicingInputType,
@@ -392,7 +309,7 @@ class AbstractImage(ABC):
392
309
 
393
310
  def _get_roi(
394
311
  self,
395
- roi: Roi,
312
+ roi: Roi | RoiPixels,
396
313
  axes_order: Sequence[str] | None = None,
397
314
  transforms: Sequence[TransformProtocol] | None = None,
398
315
  mode: Literal["numpy", "dask"] = "numpy",
@@ -468,7 +385,7 @@ class AbstractImage(ABC):
468
385
 
469
386
  def _set_roi(
470
387
  self,
471
- roi: Roi,
388
+ roi: Roi | RoiPixels,
472
389
  patch: np.ndarray | da.Array,
473
390
  axes_order: Sequence[str] | None = None,
474
391
  transforms: Sequence[TransformProtocol] | None = None,
@@ -527,14 +444,25 @@ class AbstractImage(ABC):
527
444
 
528
445
  def roi(self, name: str | None = "image") -> Roi:
529
446
  """Return the ROI covering the entire image."""
530
- slices = {}
531
- for ax_name in ["t", "z", "y", "x"]:
532
- axis_size = self.dimensions.get(ax_name, default=None)
533
- if axis_size is None:
534
- continue
535
- slices[ax_name] = slice(0, axis_size)
536
- roi_px = Roi.from_values(name=name, slices=slices, space="pixel")
537
- return roi_px.to_world(pixel_size=self.pixel_size)
447
+ dim_x = self.dimensions.get("x")
448
+ dim_y = self.dimensions.get("y")
449
+ assert dim_x is not None and dim_y is not None
450
+ dim_z = self.dimensions.get("z")
451
+ z = None if dim_z is None else 0
452
+ dim_t = self.dimensions.get("t")
453
+ t = None if dim_t is None else 0
454
+ roi_px = RoiPixels(
455
+ name=name,
456
+ x=0,
457
+ y=0,
458
+ z=z,
459
+ t=t,
460
+ x_length=dim_x,
461
+ y_length=dim_y,
462
+ z_length=dim_z,
463
+ t_length=dim_t,
464
+ )
465
+ return roi_px.to_roi(pixel_size=self.pixel_size)
538
466
 
539
467
  def build_image_roi_table(self, name: str | None = "image") -> RoiTable:
540
468
  """Build the ROI table containing the ROI covering the entire image."""
@@ -648,7 +576,7 @@ def consolidate_image(
648
576
  mode: Literal["dask", "numpy", "coarsen"] = "dask",
649
577
  ) -> None:
650
578
  """Consolidate the image on disk."""
651
- target_paths = image.meta_handler.get_meta().paths
579
+ target_paths = image._meta_handler.meta.paths
652
580
  targets = [
653
581
  image._group_handler.get_array(path)
654
582
  for path in target_paths
@@ -657,435 +585,3 @@ def consolidate_image(
657
585
  consolidate_pyramid(
658
586
  source=image.zarr_array, targets=targets, order=order, mode=mode
659
587
  )
660
-
661
-
662
- def _shapes_from_ref_image(
663
- ref_image: AbstractImage,
664
- ) -> tuple[list[tuple[int, ...]], list[tuple[float, ...]]]:
665
- """Rebuild base shape based on a new shape."""
666
- meta = ref_image.meta
667
- paths = meta.paths
668
- index_path = paths.index(ref_image.path)
669
- sub_paths = paths[index_path:]
670
- group_handler = ref_image._group_handler
671
- shapes, scales = [], []
672
- for path in sub_paths:
673
- zarr_array = group_handler.get_array(path)
674
- shapes.append(zarr_array.shape)
675
- scales.append(meta.get_dataset(path=path).scale)
676
- if len(shapes) == len(paths):
677
- return shapes, scales
678
- missing_levels = len(paths) - len(shapes)
679
- extended_shapes = compute_shapes_from_scaling_factors(
680
- base_shape=shapes[-1],
681
- scaling_factors=ref_image.meta.scaling_factor(),
682
- num_levels=missing_levels + 1,
683
- )
684
- shapes.extend(extended_shapes[1:])
685
- extended_scales = compute_scales_from_shapes(
686
- shapes=extended_shapes,
687
- base_scale=scales[-1],
688
- )
689
- scales.extend(extended_scales[1:])
690
- return shapes, scales
691
-
692
-
693
- def _shapes_from_new_shape(
694
- ref_image: AbstractImage,
695
- shape: Sequence[int],
696
- ) -> tuple[list[tuple[int, ...]], list[tuple[float, ...]]]:
697
- """Rebuild pyramid shapes based on a new base shape."""
698
- if len(shape) != len(ref_image.shape):
699
- raise NgioValueError(
700
- "The shape of the new image does not match the reference image."
701
- f"Got shape {shape} for reference shape {ref_image.shape}."
702
- )
703
- base_shape = tuple(shape)
704
- scaling_factors = ref_image.meta.scaling_factor()
705
- num_levels = len(ref_image.meta.paths)
706
- shapes = compute_shapes_from_scaling_factors(
707
- base_shape=base_shape,
708
- scaling_factors=scaling_factors,
709
- num_levels=num_levels,
710
- )
711
- scales = compute_scales_from_shapes(
712
- shapes=shapes,
713
- base_scale=ref_image.dataset.scale,
714
- )
715
- return shapes, scales
716
-
717
-
718
- def _compute_pyramid_shapes(
719
- ref_image: AbstractImage,
720
- shape: Sequence[int] | None,
721
- ) -> tuple[list[tuple[int, ...]], list[tuple[float, ...]]]:
722
- """Rebuild pyramid shapes based on a new base shape."""
723
- if shape is None:
724
- return _shapes_from_ref_image(ref_image=ref_image)
725
- return _shapes_from_new_shape(ref_image=ref_image, shape=shape)
726
-
727
-
728
- def _check_len_compatibility(
729
- ref_shape: tuple[int, ...],
730
- chunks: ChunksLike,
731
- shards: ShardsLike | None,
732
- translation: Sequence[float] | None = None,
733
- ) -> None:
734
- """Check if the chunks and shards are compatible with the reference shape.
735
-
736
- Args:
737
- ref_shape: The reference shape.
738
- chunks: The chunks to check.
739
- shards: The shards to check.
740
- translation: The translation to check.
741
- """
742
- if chunks != "auto":
743
- if len(chunks) != len(ref_shape):
744
- raise NgioValueError(
745
- "The length of the chunks must be the same as the number of dimensions."
746
- )
747
- if shards is not None and shards != "auto":
748
- if len(shards) != len(ref_shape):
749
- raise NgioValueError(
750
- "The length of the shards must be the same as the number of dimensions."
751
- )
752
- if translation is not None:
753
- if len(translation) != len(ref_shape):
754
- raise NgioValueError(
755
- "The length of the translation must be the same as the number of "
756
- "dimensions."
757
- )
758
-
759
-
760
- def _apply_channel_policy(
761
- ref_image: AbstractImage,
762
- channels_policy: Literal["squeeze", "same", "singleton"] | int,
763
- shapes: list[tuple[int, ...]],
764
- axes: tuple[str, ...],
765
- chunks: ChunksLike,
766
- shards: ShardsLike | None,
767
- translation: Sequence[float],
768
- scales: list[tuple[float, ...]] | tuple[float, ...],
769
- ) -> tuple[
770
- list[tuple[int, ...]],
771
- tuple[str, ...],
772
- ChunksLike,
773
- ShardsLike | None,
774
- tuple[float, ...],
775
- list[tuple[float, ...]] | tuple[float, ...],
776
- ]:
777
- """Apply the channel policy to the shapes and axes.
778
-
779
- Args:
780
- ref_image: The reference image.
781
- channels_policy: The channels policy to apply.
782
- shapes: The shapes of the pyramid levels.
783
- axes: The axes of the image.
784
- chunks: The chunks of the image.
785
- shards: The shards of the image.
786
- translation: The translation of the image.
787
- scales: The scales of the image.
788
-
789
- Returns:
790
- The new shapes and axes after applying the channel policy.
791
- """
792
- translation = tuple(translation)
793
- if channels_policy == "same":
794
- return shapes, axes, chunks, shards, translation, scales
795
-
796
- if channels_policy == "singleton":
797
- # Treat 'singleton' as setting channel size to 1
798
- channels_policy = 1
799
-
800
- channel_index = ref_image.axes_handler.get_index("c")
801
- if channel_index is None:
802
- if channels_policy == "squeeze":
803
- return shapes, axes, chunks, shards, translation, scales
804
- raise NgioValueError(
805
- f"Cannot apply channel policy {channels_policy=} to an image "
806
- "without channels axis."
807
- )
808
- if channels_policy == "squeeze":
809
- new_shapes = []
810
- for shape in shapes:
811
- new_shape = shape[:channel_index] + shape[channel_index + 1 :]
812
- new_shapes.append(new_shape)
813
-
814
- if isinstance(scales, tuple):
815
- new_scales = scales[:channel_index] + scales[channel_index + 1 :]
816
- else:
817
- new_scales = []
818
- for scale in scales:
819
- new_scale = scale[:channel_index] + scale[channel_index + 1 :]
820
- new_scales.append(new_scale)
821
-
822
- new_axes = axes[:channel_index] + axes[channel_index + 1 :]
823
- if chunks == "auto":
824
- new_chunks: ChunksLike = "auto"
825
- else:
826
- new_chunks = chunks[:channel_index] + chunks[channel_index + 1 :]
827
- if shards == "auto" or shards is None:
828
- new_shards: ShardsLike | None = shards
829
- else:
830
- new_shards = shards[:channel_index] + shards[channel_index + 1 :]
831
-
832
- translation = translation[:channel_index] + translation[channel_index + 1 :]
833
- return new_shapes, new_axes, new_chunks, new_shards, translation, new_scales
834
- elif isinstance(channels_policy, int):
835
- new_shapes = []
836
- for shape in shapes:
837
- new_shape = (
838
- *shape[:channel_index],
839
- channels_policy,
840
- *shape[channel_index + 1 :],
841
- )
842
- new_shapes.append(new_shape)
843
- return new_shapes, axes, chunks, shards, translation, scales
844
- else:
845
- raise NgioValueError(
846
- f"Invalid channels policy: {channels_policy}. "
847
- "Must be 'squeeze', 'same', or an integer."
848
- )
849
-
850
-
851
- def _check_channels_meta_compatibility(
852
- meta_type: type[_image_or_label_meta],
853
- ref_image: AbstractImage,
854
- channels_meta: Sequence[str | Channel] | None,
855
- ) -> Sequence[str | Channel] | None:
856
- """Check if the channels metadata is compatible with the reference image.
857
-
858
- Args:
859
- meta_type: The metadata type.
860
- ref_image: The reference image.
861
- channels_meta: The channels metadata to check.
862
-
863
- Returns:
864
- The channels metadata if compatible, None otherwise.
865
- """
866
- if issubclass(meta_type, NgioLabelMeta):
867
- if channels_meta is not None:
868
- raise NgioValueError("Cannot set channels_meta for a label image.")
869
- return None
870
- if channels_meta is not None:
871
- return channels_meta
872
- assert isinstance(ref_image.meta, NgioImageMeta)
873
- ref_meta = ref_image.meta
874
- index_c = ref_meta.axes_handler.get_index("c")
875
- if index_c is None:
876
- return None
877
-
878
- # If the channels number does not match, return None
879
- # Else return the channels metadata from the reference image
880
- ref_shape = ref_image.shape
881
- ref_num_channels = ref_shape[index_c] if index_c is not None else 1
882
- channels_ = ref_meta.channels_meta.channels if ref_meta.channels_meta else []
883
- # Reset to None if number of channels do not match
884
- channels_meta_ = channels_ if ref_num_channels == len(channels_) else None
885
- return channels_meta_
886
-
887
-
888
- def adapt_scales(
889
- scales: list[tuple[float, ...]],
890
- pixelsize: float | tuple[float, float] | None,
891
- z_spacing: float | None,
892
- time_spacing: float | None,
893
- ref_image: AbstractImage,
894
- ) -> list[tuple[float, ...]] | tuple[float, ...]:
895
- if pixelsize is None and z_spacing is None and time_spacing is None:
896
- return scales
897
- pixel_size = ref_image.pixel_size
898
- if pixelsize is None:
899
- pixelsize = (pixel_size.y, pixel_size.x)
900
- if z_spacing is None:
901
- z_spacing = pixel_size.z
902
- else:
903
- z_spacing = z_spacing
904
- if time_spacing is None:
905
- time_spacing = pixel_size.t
906
- else:
907
- time_spacing = time_spacing
908
- base_scale = compute_base_scale(
909
- pixelsize=pixelsize,
910
- z_spacing=z_spacing,
911
- time_spacing=time_spacing,
912
- axes_handler=ref_image.axes_handler,
913
- )
914
- return base_scale
915
-
916
-
917
- def abstract_derive(
918
- *,
919
- ref_image: AbstractImage,
920
- meta_type: type[_image_or_label_meta],
921
- store: StoreOrGroup,
922
- overwrite: bool = False,
923
- # Metadata parameters
924
- shape: Sequence[int] | None = None,
925
- pixelsize: float | tuple[float, float] | None = None,
926
- z_spacing: float | None = None,
927
- time_spacing: float | None = None,
928
- name: str | None = None,
929
- translation: Sequence[float] | None = None,
930
- channels_policy: Literal["squeeze", "same", "singleton"] | int = "same",
931
- channels_meta: Sequence[str | Channel] | None = None,
932
- ngff_version: NgffVersions | None = None,
933
- # Zarr Array parameters
934
- chunks: ChunksLike | None = None,
935
- shards: ShardsLike | None = None,
936
- dtype: str | None = None,
937
- dimension_separator: Literal[".", "/"] | None = None,
938
- compressors: CompressorLike | None = None,
939
- extra_array_kwargs: Mapping[str, Any] | None = None,
940
- # Deprecated arguments
941
- labels: Sequence[str] | None = None,
942
- pixel_size: PixelSize | None = None,
943
- ) -> tuple[ZarrGroupHandler, AxesSetup]:
944
- """Create an empty OME-Zarr image from an existing image.
945
-
946
- If a kwarg is not provided, the value from the reference image will be used.
947
-
948
- Args:
949
- ref_image (AbstractImage): The reference image to derive from.
950
- meta_type (type[_image_or_label_meta]): The metadata type to use.
951
- store (StoreOrGroup): The Zarr store or group to create the image in.
952
- overwrite (bool): Whether to overwrite an existing image.
953
- shape (Sequence[int] | None): The shape of the new image.
954
- pixelsize (float | tuple[float, float] | None): The pixel size of the new image.
955
- z_spacing (float | None): The z spacing of the new image.
956
- time_spacing (float | None): The time spacing of the new image.
957
- name (str | None): The name of the new image.
958
- translation (Sequence[float] | None): The translation for each axis
959
- at the highest resolution level. Defaults to None.
960
- channels_policy (Literal["squeeze", "same", "singleton"] | int):
961
- Possible policies:
962
- - If "squeeze", the channels axis will be removed (no matter its size).
963
- - If "same", the channels axis will be kept as is (if it exists).
964
- - If "singleton", the channels axis will be set to size 1.
965
- - If an integer is provided, the channels axis will be changed to have that
966
- size.
967
- channels_meta (Sequence[str | Channel] | None): The channels metadata
968
- of the new image.
969
- ngff_version (NgffVersions | None): The NGFF version to use.
970
- chunks (ChunksLike | None): The chunk shape of the new image.
971
- shards (ShardsLike | None): The shard shape of the new image.
972
- dtype (str | None): The data type of the new image.
973
- dimension_separator (Literal[".", "/"] | None): The separator to use for
974
- dimensions.
975
- compressors (CompressorLike | None): The compressors to use.
976
- extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
977
- the zarr array creation.
978
- labels (Sequence[str] | None): The labels of the new image.
979
- This argument is DEPRECATED please use channels_meta instead.
980
- pixel_size (PixelSize | None): The pixel size of the new image.
981
- This argument is DEPRECATED please use pixelsize, z_spacing,
982
- and time_spacing instead.
983
-
984
- Returns:
985
- ImagesContainer: The new derived image.
986
-
987
- """
988
- # TODO: remove in ngio 0.6
989
- if labels is not None:
990
- warnings.warn(
991
- "The 'labels' argument is deprecated and will be removed in "
992
- "ngio=0.6. Please use 'channels_meta' instead.",
993
- DeprecationWarning,
994
- stacklevel=2,
995
- )
996
- channels_meta = list(labels)
997
- if pixel_size is not None:
998
- warnings.warn(
999
- "The 'pixel_size' argument is deprecated and will be removed in "
1000
- "ngio=0.6. Please use 'pixelsize', 'z_spacing', and 'time_spacing'"
1001
- "instead.",
1002
- DeprecationWarning,
1003
- stacklevel=2,
1004
- )
1005
- pixelsize = (pixel_size.y, pixel_size.x)
1006
- # End of deprecated arguments handling
1007
- ref_meta = ref_image.meta
1008
-
1009
- shapes, scales = _compute_pyramid_shapes(
1010
- shape=shape,
1011
- ref_image=ref_image,
1012
- )
1013
- ref_shape = next(iter(shapes))
1014
-
1015
- scales = adapt_scales(
1016
- scales=scales,
1017
- pixelsize=pixelsize,
1018
- z_spacing=z_spacing,
1019
- time_spacing=time_spacing,
1020
- ref_image=ref_image,
1021
- )
1022
-
1023
- if name is None:
1024
- name = ref_meta.name
1025
-
1026
- if dtype is None:
1027
- dtype = ref_image.dtype
1028
-
1029
- if dimension_separator is None:
1030
- dimension_separator = find_dimension_separator(ref_image.zarr_array)
1031
-
1032
- if compressors is None:
1033
- compressors = ref_image.zarr_array.compressors # type: ignore
1034
-
1035
- if translation is None:
1036
- translation = ref_image.dataset.translation
1037
-
1038
- if chunks is None:
1039
- chunks = ref_image.zarr_array.chunks
1040
- if shards is None:
1041
- shards = ref_image.zarr_array.shards
1042
-
1043
- _check_len_compatibility(
1044
- ref_shape=ref_shape,
1045
- chunks=chunks,
1046
- shards=shards,
1047
- translation=translation,
1048
- )
1049
-
1050
- if ngff_version is None:
1051
- ngff_version = ref_meta.version
1052
-
1053
- shapes, axes, chunks, shards, translation, scales = _apply_channel_policy(
1054
- ref_image=ref_image,
1055
- channels_policy=channels_policy,
1056
- shapes=shapes,
1057
- axes=ref_image.axes,
1058
- chunks=chunks,
1059
- shards=shards,
1060
- translation=translation,
1061
- scales=scales,
1062
- )
1063
- channels_meta_ = _check_channels_meta_compatibility(
1064
- meta_type=meta_type,
1065
- ref_image=ref_image,
1066
- channels_meta=channels_meta,
1067
- )
1068
-
1069
- handler, axes_setup = init_image_like_from_shapes(
1070
- store=store,
1071
- meta_type=meta_type,
1072
- shapes=shapes,
1073
- base_scale=scales,
1074
- levels=ref_meta.paths,
1075
- translation=translation,
1076
- time_unit=ref_image.time_unit,
1077
- space_unit=ref_image.space_unit,
1078
- axes_names=axes,
1079
- name=name,
1080
- axes_setup=ref_image.axes_setup,
1081
- channels_meta=channels_meta_,
1082
- chunks=chunks,
1083
- shards=shards,
1084
- dtype=dtype,
1085
- dimension_separator=dimension_separator,
1086
- compressors=compressors,
1087
- overwrite=overwrite,
1088
- ngff_version=ngff_version,
1089
- extra_array_kwargs=extra_array_kwargs,
1090
- )
1091
- return handler, axes_setup