ngio 0.4.7__py3-none-any.whl → 0.5.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.
Files changed (57) hide show
  1. ngio/__init__.py +5 -2
  2. ngio/common/__init__.py +11 -6
  3. ngio/common/_masking_roi.py +34 -54
  4. ngio/common/_pyramid.py +322 -75
  5. ngio/common/_roi.py +258 -330
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +10 -11
  8. ngio/hcs/_plate.py +192 -136
  9. ngio/images/_abstract_image.py +539 -35
  10. ngio/images/_create_synt_container.py +45 -47
  11. ngio/images/_create_utils.py +406 -0
  12. ngio/images/_image.py +524 -248
  13. ngio/images/_label.py +257 -180
  14. ngio/images/_masked_image.py +2 -2
  15. ngio/images/_ome_zarr_container.py +658 -255
  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 +6 -8
  21. ngio/io_pipes/_ops_slices_utils.py +8 -5
  22. ngio/ome_zarr_meta/__init__.py +29 -18
  23. ngio/ome_zarr_meta/_meta_handlers.py +402 -689
  24. ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -0
  25. ngio/ome_zarr_meta/ngio_specs/_axes.py +152 -51
  26. ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +129 -91
  28. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +69 -69
  29. ngio/ome_zarr_meta/v04/__init__.py +5 -1
  30. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +55 -86
  31. ngio/ome_zarr_meta/v05/__init__.py +27 -0
  32. ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
  33. ngio/ome_zarr_meta/v05/_v05_spec.py +495 -0
  34. ngio/resources/__init__.py +1 -1
  35. ngio/resources/resource_model.py +1 -1
  36. ngio/tables/_tables_container.py +82 -24
  37. ngio/tables/backends/_abstract_backend.py +7 -0
  38. ngio/tables/backends/_anndata.py +60 -7
  39. ngio/tables/backends/_anndata_utils.py +2 -4
  40. ngio/tables/backends/_csv.py +3 -19
  41. ngio/tables/backends/_json.py +10 -13
  42. ngio/tables/backends/_parquet.py +3 -31
  43. ngio/tables/backends/_py_arrow_backends.py +222 -0
  44. ngio/tables/backends/_utils.py +1 -1
  45. ngio/tables/v1/_roi_table.py +41 -24
  46. ngio/utils/__init__.py +8 -12
  47. ngio/utils/_cache.py +48 -0
  48. ngio/utils/_datasets.py +6 -0
  49. ngio/utils/_zarr_utils.py +354 -236
  50. {ngio-0.4.7.dist-info → ngio-0.5.0.dist-info}/METADATA +13 -6
  51. ngio-0.5.0.dist-info/RECORD +88 -0
  52. ngio/images/_create.py +0 -276
  53. ngio/tables/backends/_non_zarr_backends.py +0 -196
  54. ngio/utils/_logger.py +0 -50
  55. ngio-0.4.7.dist-info/RECORD +0 -85
  56. {ngio-0.4.7.dist-info → ngio-0.5.0.dist-info}/WHEEL +0 -0
  57. {ngio-0.4.7.dist-info → ngio-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,32 @@
1
1
  """Generic class to handle Image-like data in a OME-NGFF file."""
2
2
 
3
- from collections.abc import Sequence
4
- from typing import Generic, Literal, TypeVar
3
+ import warnings
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Mapping, Sequence
6
+ from typing import Any, Literal
5
7
 
6
8
  import dask.array as da
7
9
  import numpy as np
8
10
  import zarr
11
+ from zarr.core.array import CompressorLike
9
12
 
10
13
  from ngio.common import (
11
14
  Dimensions,
12
15
  InterpolationOrder,
13
16
  Roi,
14
- RoiPixels,
15
17
  consolidate_pyramid,
16
18
  )
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
+ )
17
30
  from ngio.io_pipes import (
18
31
  DaskGetter,
19
32
  DaskRoiGetter,
@@ -31,15 +44,32 @@ from ngio.ome_zarr_meta import (
31
44
  Dataset,
32
45
  ImageMetaHandler,
33
46
  LabelMetaHandler,
47
+ NgioImageMeta,
34
48
  PixelSize,
35
49
  )
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
+ )
36
62
  from ngio.tables import RoiTable
37
- from ngio.utils import NgioFileExistsError, ZarrGroupHandler
38
-
39
- _image_handler = TypeVar("_image_handler", ImageMetaHandler, LabelMetaHandler)
63
+ from ngio.utils import (
64
+ NgioFileExistsError,
65
+ NgioValueError,
66
+ StoreOrGroup,
67
+ ZarrGroupHandler,
68
+ )
69
+ from ngio.utils._zarr_utils import find_dimension_separator
40
70
 
41
71
 
42
- class AbstractImage(Generic[_image_handler]):
72
+ class AbstractImage(ABC):
43
73
  """A class to handle a single image (or level) in an OME-Zarr image.
44
74
 
45
75
  This class is meant to be subclassed by specific image types.
@@ -49,7 +79,7 @@ class AbstractImage(Generic[_image_handler]):
49
79
  self,
50
80
  group_handler: ZarrGroupHandler,
51
81
  path: str,
52
- meta_handler: _image_handler,
82
+ meta_handler: ImageMetaHandler | LabelMetaHandler,
53
83
  ) -> None:
54
84
  """Initialize the Image at a single level.
55
85
 
@@ -78,14 +108,21 @@ class AbstractImage(Generic[_image_handler]):
78
108
  return self._path
79
109
 
80
110
  @property
81
- def meta_handler(self) -> _image_handler:
111
+ @abstractmethod
112
+ def meta_handler(self) -> ImageMetaHandler | LabelMetaHandler:
82
113
  """Return the metadata."""
83
- return self._meta_handler
114
+ pass
115
+
116
+ @property
117
+ @abstractmethod
118
+ def meta(self) -> NgioImageMeta | NgioLabelMeta:
119
+ """Return the metadata."""
120
+ pass
84
121
 
85
122
  @property
86
123
  def dataset(self) -> Dataset:
87
124
  """Return the dataset of the image."""
88
- return self.meta_handler.meta.get_dataset(path=self.path)
125
+ return self.meta_handler.get_meta().get_dataset(path=self.path)
89
126
 
90
127
  @property
91
128
  def dimensions(self) -> Dimensions:
@@ -106,6 +143,11 @@ class AbstractImage(Generic[_image_handler]):
106
143
  """Return the axes handler of the image."""
107
144
  return self.dataset.axes_handler
108
145
 
146
+ @property
147
+ def axes_setup(self) -> AxesSetup:
148
+ """Return the axes setup of the image."""
149
+ return self.axes_handler.axes_setup
150
+
109
151
  @property
110
152
  def axes(self) -> tuple[str, ...]:
111
153
  """Return the axes of the image."""
@@ -175,6 +217,47 @@ class AbstractImage(Generic[_image_handler]):
175
217
  """Return True if the image has the given axis."""
176
218
  return self.axes_handler.has_axis(axis)
177
219
 
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
+
178
261
  def _get_as_numpy(
179
262
  self,
180
263
  axes_order: Sequence[str] | None = None,
@@ -202,7 +285,7 @@ class AbstractImage(Generic[_image_handler]):
202
285
 
203
286
  def _get_roi_as_numpy(
204
287
  self,
205
- roi: Roi | RoiPixels,
288
+ roi: Roi,
206
289
  axes_order: Sequence[str] | None = None,
207
290
  transforms: Sequence[TransformProtocol] | None = None,
208
291
  **slicing_kwargs: SlicingInputType,
@@ -252,7 +335,7 @@ class AbstractImage(Generic[_image_handler]):
252
335
 
253
336
  def _get_roi_as_dask(
254
337
  self,
255
- roi: Roi | RoiPixels,
338
+ roi: Roi,
256
339
  axes_order: Sequence[str] | None = None,
257
340
  transforms: Sequence[TransformProtocol] | None = None,
258
341
  **slicing_kwargs: SlicingInputType,
@@ -309,7 +392,7 @@ class AbstractImage(Generic[_image_handler]):
309
392
 
310
393
  def _get_roi(
311
394
  self,
312
- roi: Roi | RoiPixels,
395
+ roi: Roi,
313
396
  axes_order: Sequence[str] | None = None,
314
397
  transforms: Sequence[TransformProtocol] | None = None,
315
398
  mode: Literal["numpy", "dask"] = "numpy",
@@ -385,7 +468,7 @@ class AbstractImage(Generic[_image_handler]):
385
468
 
386
469
  def _set_roi(
387
470
  self,
388
- roi: Roi | RoiPixels,
471
+ roi: Roi,
389
472
  patch: np.ndarray | da.Array,
390
473
  axes_order: Sequence[str] | None = None,
391
474
  transforms: Sequence[TransformProtocol] | None = None,
@@ -444,25 +527,14 @@ class AbstractImage(Generic[_image_handler]):
444
527
 
445
528
  def roi(self, name: str | None = "image") -> Roi:
446
529
  """Return the ROI covering the entire image."""
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)
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)
466
538
 
467
539
  def build_image_roi_table(self, name: str | None = "image") -> RoiTable:
468
540
  """Build the ROI table containing the ROI covering the entire image."""
@@ -576,7 +648,7 @@ def consolidate_image(
576
648
  mode: Literal["dask", "numpy", "coarsen"] = "dask",
577
649
  ) -> None:
578
650
  """Consolidate the image on disk."""
579
- target_paths = image._meta_handler.meta.paths
651
+ target_paths = image.meta_handler.get_meta().paths
580
652
  targets = [
581
653
  image._group_handler.get_array(path)
582
654
  for path in target_paths
@@ -585,3 +657,435 @@ def consolidate_image(
585
657
  consolidate_pyramid(
586
658
  source=image.zarr_array, targets=targets, order=order, mode=mode
587
659
  )
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