ngio 0.5.0a1__py3-none-any.whl → 0.5.0a3__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 (48) hide show
  1. ngio/__init__.py +2 -2
  2. ngio/common/__init__.py +11 -6
  3. ngio/common/_masking_roi.py +12 -41
  4. ngio/common/_pyramid.py +218 -78
  5. ngio/common/_roi.py +257 -329
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +10 -11
  8. ngio/hcs/_plate.py +114 -123
  9. ngio/images/_abstract_image.py +417 -35
  10. ngio/images/_create_synt_container.py +36 -43
  11. ngio/images/_create_utils.py +423 -0
  12. ngio/images/_image.py +155 -177
  13. ngio/images/_label.py +144 -119
  14. ngio/images/_ome_zarr_container.py +361 -196
  15. ngio/io_pipes/_io_pipes.py +9 -9
  16. ngio/io_pipes/_io_pipes_masked.py +7 -7
  17. ngio/io_pipes/_io_pipes_roi.py +6 -6
  18. ngio/io_pipes/_io_pipes_types.py +3 -3
  19. ngio/io_pipes/_match_shape.py +5 -4
  20. ngio/io_pipes/_ops_slices_utils.py +8 -5
  21. ngio/ome_zarr_meta/__init__.py +15 -18
  22. ngio/ome_zarr_meta/_meta_handlers.py +334 -713
  23. ngio/ome_zarr_meta/ngio_specs/_axes.py +1 -0
  24. ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
  25. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +54 -61
  26. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +14 -68
  27. ngio/ome_zarr_meta/v04/__init__.py +1 -1
  28. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +16 -61
  29. ngio/ome_zarr_meta/v05/__init__.py +1 -1
  30. ngio/ome_zarr_meta/v05/{_v05_spec_utils.py → _v05_spec.py} +18 -61
  31. ngio/tables/_tables_container.py +25 -20
  32. ngio/tables/backends/_anndata.py +57 -8
  33. ngio/tables/backends/_anndata_utils.py +1 -6
  34. ngio/tables/backends/_csv.py +3 -19
  35. ngio/tables/backends/_json.py +10 -13
  36. ngio/tables/backends/_parquet.py +3 -31
  37. ngio/tables/backends/_py_arrow_backends.py +222 -0
  38. ngio/tables/v1/_roi_table.py +44 -27
  39. ngio/utils/__init__.py +6 -12
  40. ngio/utils/_cache.py +48 -0
  41. ngio/utils/_zarr_utils.py +285 -245
  42. {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/METADATA +8 -4
  43. {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/RECORD +45 -45
  44. {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/WHEEL +1 -1
  45. ngio/images/_create.py +0 -283
  46. ngio/tables/backends/_non_zarr_backends.py +0 -196
  47. ngio/utils/_logger.py +0 -50
  48. {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,26 @@
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 ChunksLike, ShardsLike, shapes_from_scaling_factors
20
+ from ngio.images._create_utils import (
21
+ _image_or_label_meta,
22
+ init_image_like_from_shapes,
23
+ )
17
24
  from ngio.io_pipes import (
18
25
  DaskGetter,
19
26
  DaskRoiGetter,
@@ -31,15 +38,25 @@ from ngio.ome_zarr_meta import (
31
38
  Dataset,
32
39
  ImageMetaHandler,
33
40
  LabelMetaHandler,
41
+ NgioImageMeta,
34
42
  PixelSize,
35
43
  )
44
+ from ngio.ome_zarr_meta.ngio_specs import (
45
+ Channel,
46
+ NgffVersions,
47
+ NgioLabelMeta,
48
+ )
36
49
  from ngio.tables import RoiTable
37
- from ngio.utils import NgioFileExistsError, ZarrGroupHandler
38
-
39
- _image_handler = TypeVar("_image_handler", ImageMetaHandler, LabelMetaHandler)
50
+ from ngio.utils import (
51
+ NgioFileExistsError,
52
+ NgioValueError,
53
+ StoreOrGroup,
54
+ ZarrGroupHandler,
55
+ )
56
+ from ngio.utils._zarr_utils import find_dimension_separator
40
57
 
41
58
 
42
- class AbstractImage(Generic[_image_handler]):
59
+ class AbstractImage(ABC):
43
60
  """A class to handle a single image (or level) in an OME-Zarr image.
44
61
 
45
62
  This class is meant to be subclassed by specific image types.
@@ -49,7 +66,7 @@ class AbstractImage(Generic[_image_handler]):
49
66
  self,
50
67
  group_handler: ZarrGroupHandler,
51
68
  path: str,
52
- meta_handler: _image_handler,
69
+ meta_handler: ImageMetaHandler | LabelMetaHandler,
53
70
  ) -> None:
54
71
  """Initialize the Image at a single level.
55
72
 
@@ -78,14 +95,21 @@ class AbstractImage(Generic[_image_handler]):
78
95
  return self._path
79
96
 
80
97
  @property
81
- def meta_handler(self) -> _image_handler:
98
+ @abstractmethod
99
+ def meta_handler(self) -> ImageMetaHandler | LabelMetaHandler:
82
100
  """Return the metadata."""
83
- return self._meta_handler
101
+ pass
102
+
103
+ @property
104
+ @abstractmethod
105
+ def meta(self) -> NgioImageMeta | NgioLabelMeta:
106
+ """Return the metadata."""
107
+ pass
84
108
 
85
109
  @property
86
110
  def dataset(self) -> Dataset:
87
111
  """Return the dataset of the image."""
88
- return self.meta_handler.meta.get_dataset(path=self.path)
112
+ return self.meta_handler.get_meta().get_dataset(path=self.path)
89
113
 
90
114
  @property
91
115
  def dimensions(self) -> Dimensions:
@@ -202,7 +226,7 @@ class AbstractImage(Generic[_image_handler]):
202
226
 
203
227
  def _get_roi_as_numpy(
204
228
  self,
205
- roi: Roi | RoiPixels,
229
+ roi: Roi,
206
230
  axes_order: Sequence[str] | None = None,
207
231
  transforms: Sequence[TransformProtocol] | None = None,
208
232
  **slicing_kwargs: SlicingInputType,
@@ -252,7 +276,7 @@ class AbstractImage(Generic[_image_handler]):
252
276
 
253
277
  def _get_roi_as_dask(
254
278
  self,
255
- roi: Roi | RoiPixels,
279
+ roi: Roi,
256
280
  axes_order: Sequence[str] | None = None,
257
281
  transforms: Sequence[TransformProtocol] | None = None,
258
282
  **slicing_kwargs: SlicingInputType,
@@ -309,7 +333,7 @@ class AbstractImage(Generic[_image_handler]):
309
333
 
310
334
  def _get_roi(
311
335
  self,
312
- roi: Roi | RoiPixels,
336
+ roi: Roi,
313
337
  axes_order: Sequence[str] | None = None,
314
338
  transforms: Sequence[TransformProtocol] | None = None,
315
339
  mode: Literal["numpy", "dask"] = "numpy",
@@ -385,7 +409,7 @@ class AbstractImage(Generic[_image_handler]):
385
409
 
386
410
  def _set_roi(
387
411
  self,
388
- roi: Roi | RoiPixels,
412
+ roi: Roi,
389
413
  patch: np.ndarray | da.Array,
390
414
  axes_order: Sequence[str] | None = None,
391
415
  transforms: Sequence[TransformProtocol] | None = None,
@@ -444,25 +468,14 @@ class AbstractImage(Generic[_image_handler]):
444
468
 
445
469
  def roi(self, name: str | None = "image") -> Roi:
446
470
  """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)
471
+ slices = {}
472
+ for ax_name in ["t", "z", "y", "x"]:
473
+ axis_size = self.dimensions.get(ax_name, default=None)
474
+ if axis_size is None:
475
+ continue
476
+ slices[ax_name] = slice(0, axis_size)
477
+ roi_px = Roi.from_values(name=name, slices=slices, space="pixel")
478
+ return roi_px.to_world(pixel_size=self.pixel_size)
466
479
 
467
480
  def build_image_roi_table(self, name: str | None = "image") -> RoiTable:
468
481
  """Build the ROI table containing the ROI covering the entire image."""
@@ -576,7 +589,7 @@ def consolidate_image(
576
589
  mode: Literal["dask", "numpy", "coarsen"] = "dask",
577
590
  ) -> None:
578
591
  """Consolidate the image on disk."""
579
- target_paths = image._meta_handler.meta.paths
592
+ target_paths = image.meta_handler.get_meta().paths
580
593
  targets = [
581
594
  image._group_handler.get_array(path)
582
595
  for path in target_paths
@@ -585,3 +598,372 @@ def consolidate_image(
585
598
  consolidate_pyramid(
586
599
  source=image.zarr_array, targets=targets, order=order, mode=mode
587
600
  )
601
+
602
+
603
+ def _shapes_from_ref_image(
604
+ ref_image: AbstractImage,
605
+ ) -> list[tuple[int, ...]]:
606
+ """Rebuild base shape based on a new shape."""
607
+ paths = ref_image.meta.paths
608
+ index_path = paths.index(ref_image.path)
609
+ sub_paths = paths[index_path:]
610
+ group_handler = ref_image._group_handler
611
+ shapes = []
612
+ for path in sub_paths:
613
+ zarr_array = group_handler.get_array(path)
614
+ shapes.append(zarr_array.shape)
615
+ if len(shapes) == len(paths):
616
+ return shapes
617
+ missing_levels = len(paths) - len(shapes)
618
+ print(ref_image.meta.scaling_factor())
619
+ extended_shapes = shapes_from_scaling_factors(
620
+ base_shape=shapes[-1],
621
+ scaling_factors=ref_image.meta.scaling_factor(),
622
+ num_levels=missing_levels + 1,
623
+ )
624
+ shapes.extend(extended_shapes[1:])
625
+ return shapes
626
+
627
+
628
+ def _shapes_from_new_shape(
629
+ ref_image: AbstractImage,
630
+ shape: Sequence[int],
631
+ ) -> list[tuple[int, ...]]:
632
+ """Rebuild pyramid shapes based on a new base shape."""
633
+ if len(shape) != len(ref_image.shape):
634
+ raise NgioValueError(
635
+ "The shape of the new image does not match the reference image."
636
+ f"Got shape {shape} for reference shape {ref_image.shape}."
637
+ )
638
+ base_shape = tuple(shape)
639
+ scaling_factors = ref_image.meta.scaling_factor()
640
+ num_levels = len(ref_image.meta.paths)
641
+ return shapes_from_scaling_factors(
642
+ base_shape=base_shape,
643
+ scaling_factors=scaling_factors,
644
+ num_levels=num_levels,
645
+ )
646
+
647
+
648
+ def _compute_pyramid_shapes(
649
+ ref_image: AbstractImage,
650
+ shape: Sequence[int] | None,
651
+ ) -> list[tuple[int, ...]]:
652
+ """Rebuild pyramid shapes based on a new base shape."""
653
+ if shape is None:
654
+ return _shapes_from_ref_image(ref_image=ref_image)
655
+ return _shapes_from_new_shape(ref_image=ref_image, shape=shape)
656
+
657
+
658
+ def _check_chunks_and_shards_compatibility(
659
+ ref_shape: tuple[int, ...],
660
+ chunks: ChunksLike,
661
+ shards: ShardsLike | None,
662
+ ) -> None:
663
+ """Check if the chunks and shards are compatible with the reference shape.
664
+
665
+ Args:
666
+ ref_shape: The reference shape.
667
+ chunks: The chunks to check.
668
+ shards: The shards to check.
669
+ """
670
+ if chunks != "auto":
671
+ if len(chunks) != len(ref_shape):
672
+ raise NgioValueError(
673
+ "The length of the chunks must be the same as the number of dimensions."
674
+ )
675
+ if shards is not None and shards != "auto":
676
+ if len(shards) != len(ref_shape):
677
+ raise NgioValueError(
678
+ "The length of the shards must be the same as the number of dimensions."
679
+ )
680
+
681
+
682
+ def _apply_channel_policy(
683
+ ref_image: AbstractImage,
684
+ channels_policy: Literal["squeeze", "same"] | int,
685
+ shapes: list[tuple[int, ...]],
686
+ axes: tuple[str, ...],
687
+ chunks: ChunksLike,
688
+ shards: ShardsLike | None,
689
+ ) -> tuple[list[tuple[int, ...]], tuple[str, ...], ChunksLike, ShardsLike | None]:
690
+ """Apply the channel policy to the shapes and axes.
691
+
692
+ Args:
693
+ ref_image: The reference image.
694
+ channels_policy: The channels policy to apply.
695
+ shapes: The shapes of the pyramid levels.
696
+ axes: The axes of the image.
697
+ chunks: The chunks of the image.
698
+ shards: The shards of the image.
699
+
700
+ Returns:
701
+ The new shapes and axes after applying the channel policy.
702
+ """
703
+ if channels_policy == "same":
704
+ return shapes, axes, chunks, shards
705
+
706
+ channel_index = ref_image.axes_handler.get_index("c")
707
+ if channel_index is None:
708
+ if channels_policy == "squeeze":
709
+ return shapes, axes, chunks, shards
710
+ raise NgioValueError(
711
+ f"Cannot apply channel policy {channels_policy=} to an image "
712
+ "without channels axis."
713
+ )
714
+ if channels_policy == "squeeze":
715
+ new_shapes = []
716
+ for shape in shapes:
717
+ new_shape = shape[:channel_index] + shape[channel_index + 1 :]
718
+ new_shapes.append(new_shape)
719
+ new_axes = axes[:channel_index] + axes[channel_index + 1 :]
720
+ if chunks == "auto":
721
+ new_chunks: ChunksLike = "auto"
722
+ else:
723
+ new_chunks = chunks[:channel_index] + chunks[channel_index + 1 :]
724
+ if shards == "auto" or shards is None:
725
+ new_shards: ShardsLike | None = shards
726
+ else:
727
+ new_shards = shards[:channel_index] + shards[channel_index + 1 :]
728
+ return new_shapes, new_axes, new_chunks, new_shards
729
+ elif isinstance(channels_policy, int):
730
+ new_shapes = []
731
+ for shape in shapes:
732
+ if shape[channel_index] != channels_policy:
733
+ raise NgioValueError(
734
+ f"Cannot apply channel policy {channels_policy=} to an image "
735
+ f"with {shape[channel_index]} channels."
736
+ )
737
+ new_shape = (
738
+ *shape[:channel_index],
739
+ channels_policy,
740
+ *shape[channel_index + 1 :],
741
+ )
742
+ new_shapes.append(new_shape)
743
+ return new_shapes, axes, chunks, shards
744
+ else:
745
+ raise NgioValueError(
746
+ f"Invalid channels policy: {channels_policy}. "
747
+ "Must be 'squeeze', 'same', or an integer."
748
+ )
749
+
750
+
751
+ def _check_channels_meta_compatibility(
752
+ meta_type: type[_image_or_label_meta],
753
+ ref_image: AbstractImage,
754
+ channels_meta: Sequence[str | Channel] | None,
755
+ ) -> Sequence[str | Channel] | None:
756
+ """Check if the channels metadata is compatible with the reference image.
757
+
758
+ Args:
759
+ meta_type: The metadata type.
760
+ ref_image: The reference image.
761
+ channels_meta: The channels metadata to check.
762
+
763
+ Returns:
764
+ The channels metadata if compatible, None otherwise.
765
+ """
766
+ if issubclass(meta_type, NgioLabelMeta):
767
+ if channels_meta is not None:
768
+ raise NgioValueError("Cannot set channels_meta for a label image.")
769
+ return None
770
+ if channels_meta is not None:
771
+ return channels_meta
772
+ assert isinstance(ref_image.meta, NgioImageMeta)
773
+ ref_meta = ref_image.meta
774
+ index_c = ref_meta.axes_handler.get_index("c")
775
+ if index_c is None:
776
+ return None
777
+
778
+ # If the channels number does not match, return None
779
+ # Else return the channels metadata from the reference image
780
+ ref_shape = ref_image.shape
781
+ ref_num_channels = ref_shape[index_c] if index_c is not None else 1
782
+ channels_ = ref_meta.channels_meta.channels if ref_meta.channels_meta else []
783
+ # Reset to None if number of channels do not match
784
+ channels_meta_ = channels_ if ref_num_channels == len(channels_) else None
785
+ return channels_meta_
786
+
787
+
788
+ def abstract_derive(
789
+ *,
790
+ ref_image: AbstractImage,
791
+ meta_type: type[_image_or_label_meta],
792
+ store: StoreOrGroup,
793
+ overwrite: bool = False,
794
+ # Metadata parameters
795
+ shape: Sequence[int] | None = None,
796
+ pixelsize: float | tuple[float, float] | None = None,
797
+ z_spacing: float | None = None,
798
+ time_spacing: float | None = None,
799
+ name: str | None = None,
800
+ channels_policy: Literal["squeeze", "same"] | int = "same",
801
+ channels_meta: Sequence[str | Channel] | None = None,
802
+ ngff_version: NgffVersions | None = None,
803
+ # Zarr Array parameters
804
+ chunks: ChunksLike = "auto",
805
+ shards: ShardsLike | None = None,
806
+ dtype: str | None = None,
807
+ dimension_separator: Literal[".", "/"] | None = None,
808
+ compressors: CompressorLike | None = None,
809
+ extra_array_kwargs: Mapping[str, Any] | None = None,
810
+ # Deprecated arguments
811
+ labels: Sequence[str] | None = None,
812
+ pixel_size: PixelSize | None = None,
813
+ ) -> ZarrGroupHandler:
814
+ """Create an empty OME-Zarr image from an existing image.
815
+
816
+ If a kwarg is not provided, the value from the reference image will be used.
817
+
818
+ Args:
819
+ ref_image (AbstractImage): The reference image to derive from.
820
+ meta_type (type[_image_or_label_meta]): The metadata type to use.
821
+ store (StoreOrGroup): The Zarr store or group to create the image in.
822
+ overwrite (bool): Whether to overwrite an existing image.
823
+ shape (Sequence[int] | None): The shape of the new image.
824
+ pixelsize (float | tuple[float, float] | None): The pixel size of the new image.
825
+ z_spacing (float | None): The z spacing of the new image.
826
+ time_spacing (float | None): The time spacing of the new image.
827
+ axes_names (Sequence[str] | None): The axes names of the new image.
828
+ name (str | None): The name of the new image.
829
+ channels_policy (Literal["squeeze", "same"] | int): Possible policies:
830
+ - If "squeeze", the channels axis will be removed (no matter its size).
831
+ - If "same", the channels axis will be kept as is (if it exists).
832
+ - If an integer is provided, the channels axis will be changed to have that
833
+ size.
834
+ channels_meta (Sequence[str | Channel] | None): The channels metadata
835
+ of the new image.
836
+ ngff_version (NgffVersions | None): The NGFF version to use.
837
+ chunks (Sequence[int] | None): The chunk shape of the new image.
838
+ shards (ShardsLike | None): The shard shape of the new image.
839
+ dtype (str | None): The data type of the new image.
840
+ dimension_separator (DIMENSION_SEPARATOR | None): The separator to use for
841
+ dimensions.
842
+ compressors (CompressorLike | None): The compressors to use.
843
+ extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
844
+ the zarr array creation.
845
+ labels (Sequence[str] | None): The labels of the new image.
846
+ This argument is DEPRECATED please use channels_meta instead.
847
+ pixel_size (PixelSize | None): The pixel size of the new image.
848
+ This argument is DEPRECATED please use pixelsize, z_spacing,
849
+ and time_spacing instead.
850
+
851
+ Returns:
852
+ ImagesContainer: The new derived image.
853
+
854
+ """
855
+ # TODO: remove in ngio 0.6
856
+ if labels is not None:
857
+ warnings.warn(
858
+ "The 'labels' argument is deprecated and will be removed in "
859
+ "a future release.",
860
+ DeprecationWarning,
861
+ stacklevel=2,
862
+ )
863
+ channels_meta = list(labels)
864
+ if pixel_size is not None:
865
+ warnings.warn(
866
+ "The 'pixel_size' argument is deprecated and will be removed in "
867
+ "a future release.",
868
+ DeprecationWarning,
869
+ stacklevel=2,
870
+ )
871
+ pixelsize_ = (pixel_size.y, pixel_size.x)
872
+ z_spacing_ = pixel_size.z
873
+ time_spacing_ = pixel_size.t
874
+ else:
875
+ if pixelsize is None:
876
+ pixelsize_ = (ref_image.pixel_size.y, ref_image.pixel_size.x)
877
+ else:
878
+ pixelsize_ = pixelsize
879
+
880
+ if z_spacing is None:
881
+ z_spacing_ = ref_image.pixel_size.z
882
+ else:
883
+ z_spacing_ = z_spacing
884
+
885
+ if time_spacing is None:
886
+ time_spacing_ = ref_image.pixel_size.t
887
+ else:
888
+ time_spacing_ = time_spacing
889
+ ref_meta = ref_image.meta
890
+
891
+ shapes = _compute_pyramid_shapes(
892
+ shape=shape,
893
+ ref_image=ref_image,
894
+ )
895
+ ref_shape = next(iter(shapes))
896
+
897
+ if pixelsize is None:
898
+ pixelsize = (ref_image.pixel_size.y, ref_image.pixel_size.x)
899
+
900
+ if z_spacing is None:
901
+ z_spacing = ref_image.pixel_size.z
902
+
903
+ if time_spacing is None:
904
+ time_spacing = ref_image.pixel_size.t
905
+
906
+ if name is None:
907
+ name = ref_meta.name
908
+
909
+ if dtype is None:
910
+ dtype = ref_image.dtype
911
+
912
+ if dimension_separator is None:
913
+ dimension_separator = find_dimension_separator(ref_image.zarr_array)
914
+
915
+ if compressors is None:
916
+ compressors = ref_image.zarr_array.compressors # type: ignore
917
+
918
+ if chunks is None:
919
+ chunks = ref_image.zarr_array.chunks
920
+
921
+ if shards is None:
922
+ shards = ref_image.zarr_array.shards
923
+
924
+ _check_chunks_and_shards_compatibility(
925
+ ref_shape=ref_shape,
926
+ chunks=chunks,
927
+ shards=shards,
928
+ )
929
+
930
+ if ngff_version is None:
931
+ ngff_version = ref_meta.version
932
+
933
+ shapes, axes, chunks, shards = _apply_channel_policy(
934
+ ref_image=ref_image,
935
+ channels_policy=channels_policy,
936
+ shapes=shapes,
937
+ axes=ref_image.axes,
938
+ chunks=chunks,
939
+ shards=shards,
940
+ )
941
+ channels_meta_ = _check_channels_meta_compatibility(
942
+ meta_type=meta_type,
943
+ ref_image=ref_image,
944
+ channels_meta=channels_meta,
945
+ )
946
+
947
+ handler = init_image_like_from_shapes(
948
+ store=store,
949
+ meta_type=meta_type,
950
+ shapes=shapes,
951
+ pixelsize=pixelsize_,
952
+ z_spacing=z_spacing_,
953
+ time_spacing=time_spacing_,
954
+ levels=ref_meta.paths,
955
+ time_unit=ref_image.time_unit,
956
+ space_unit=ref_image.space_unit,
957
+ axes_names=axes,
958
+ name=name,
959
+ channels_meta=channels_meta_,
960
+ chunks=chunks,
961
+ shards=shards,
962
+ dtype=dtype,
963
+ dimension_separator=dimension_separator,
964
+ compressors=compressors,
965
+ overwrite=overwrite,
966
+ ngff_version=ngff_version,
967
+ extra_array_kwargs=extra_array_kwargs,
968
+ )
969
+ return handler
@@ -1,15 +1,17 @@
1
1
  """Abstract class for handling OME-NGFF images."""
2
2
 
3
- from collections.abc import Sequence
4
- from typing import Literal
3
+ from collections.abc import Mapping, Sequence
4
+ from typing import Any, Literal
5
5
 
6
6
  import numpy as np
7
7
  import PIL.Image
8
8
  from zarr.core.array import CompressorLike
9
9
 
10
+ from ngio.common._pyramid import ChunksLike, ShardsLike
10
11
  from ngio.common._synt_images_utils import fit_to_shape
11
12
  from ngio.images._ome_zarr_container import OmeZarrContainer, create_ome_zarr_from_array
12
13
  from ngio.ome_zarr_meta.ngio_specs import (
14
+ Channel,
13
15
  DefaultNgffVersion,
14
16
  NgffVersions,
15
17
  )
@@ -28,52 +30,45 @@ def create_synthetic_ome_zarr(
28
30
  shape: Sequence[int],
29
31
  reference_sample: AVAILABLE_SAMPLES | SampleInfo = "Cardiomyocyte",
30
32
  levels: int | list[str] = 5,
31
- xy_scaling_factor: float = 2,
32
- z_scaling_factor: float = 1.0,
33
- axes_names: Sequence[str] | None = None,
34
- chunks: Sequence[int] | Literal["auto"] = "auto",
35
- channel_labels: list[str] | None = None,
36
- channel_wavelengths: list[str] | None = None,
37
- channel_colors: Sequence[str] | None = None,
38
- channel_active: Sequence[bool] | None = None,
39
33
  table_backend: TableBackend = DefaultTableBackend,
34
+ scaling_factors: Sequence[float] | Literal["auto"] = "auto",
35
+ axes_names: Sequence[str] | None = None,
36
+ channels_meta: Sequence[str | Channel] | None = None,
37
+ ngff_version: NgffVersions = DefaultNgffVersion,
38
+ chunks: ChunksLike = "auto",
39
+ shards: ShardsLike | None = None,
40
40
  dimension_separator: Literal[".", "/"] = "/",
41
41
  compressors: CompressorLike = "auto",
42
+ extra_array_kwargs: Mapping[str, Any] | None = None,
42
43
  overwrite: bool = False,
43
- version: NgffVersions = DefaultNgffVersion,
44
44
  ) -> OmeZarrContainer:
45
- """Create an empty OME-Zarr image with the given shape and metadata.
45
+ """Create a synthetic OME-Zarr image with the given shape and metadata.
46
46
 
47
47
  Args:
48
48
  store (StoreOrGroup): The Zarr store or group to create the image in.
49
49
  shape (Sequence[int]): The shape of the image.
50
50
  reference_sample (AVAILABLE_SAMPLES | SampleInfo): The reference sample to use.
51
- levels (int | list[str], optional): The number of levels in the pyramid or a
52
- list of level names. Defaults to 5.
53
- xy_scaling_factor (float, optional): The down-scaling factor in x and y
54
- dimensions. Defaults to 2.0.
55
- z_scaling_factor (float, optional): The down-scaling factor in z dimension.
56
- Defaults to 1.0.
57
- axes_names (Sequence[str] | None, optional): The names of the axes.
58
- If None the canonical names are used. Defaults to None.
59
- chunks (Sequence[int] | Literal["auto"]): The chunk shape. If None the shape
60
- is used. Defaults to "auto".
61
- channel_labels (list[str] | None, optional): The labels of the channels.
62
- Defaults to None.
63
- channel_wavelengths (list[str] | None, optional): The wavelengths of the
64
- channels. Defaults to None.
65
- channel_colors (Sequence[str] | None, optional): The colors of the channels.
51
+ Defaults to "Cardiomyocyte".
52
+ levels (int | list[str]): The number of levels in the pyramid or a list of
53
+ level names. Defaults to 5.
54
+ table_backend (TableBackend): Table backend to be used to store tables.
55
+ Defaults to DefaultTableBackend.
56
+ scaling_factors (Sequence[float] | Literal["auto"]): The down-scaling factors
57
+ for the pyramid levels. Defaults to "auto".
58
+ axes_names (Sequence[str] | None): The names of the axes. If None the
59
+ canonical names are used. Defaults to None.
60
+ channels_meta (Sequence[str | Channel] | None): The channels metadata.
66
61
  Defaults to None.
67
- channel_active (Sequence[bool] | None, optional): Whether the channels are
68
- active. Defaults to None.
69
- table_backend (TableBackend): Table backend to be used to store tables
70
- dimension_separator (DIMENSION_SEPARATOR): The separator to use for
62
+ ngff_version (NgffVersions): The version of the OME-Zarr specification.
63
+ Defaults to DefaultNgffVersion.
64
+ chunks (ChunksLike): The chunk shape. Defaults to "auto".
65
+ shards (ShardsLike | None): The shard shape. Defaults to None.
66
+ dimension_separator (Literal[".", "/"]): The separator to use for
71
67
  dimensions. Defaults to "/".
72
68
  compressors (CompressorLike): The compressors to use. Defaults to "auto".
73
- overwrite (bool, optional): Whether to overwrite an existing image.
74
- Defaults to True.
75
- version (NgffVersion, optional): The version of the OME-Zarr specification.
76
- Defaults to DefaultNgffVersion.
69
+ extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
70
+ the zarr array creation. Defaults to None.
71
+ overwrite (bool): Whether to overwrite an existing image. Defaults to False.
77
72
  """
78
73
  if isinstance(reference_sample, str):
79
74
  sample_info = get_sample_info(reference_sample)
@@ -87,25 +82,23 @@ def create_synthetic_ome_zarr(
87
82
  ome_zarr = create_ome_zarr_from_array(
88
83
  store=store,
89
84
  array=raw,
90
- xy_pixelsize=sample_info.xy_pixelsize,
85
+ pixelsize=sample_info.xy_pixelsize,
91
86
  z_spacing=sample_info.z_spacing,
92
87
  time_spacing=sample_info.time_spacing,
93
88
  levels=levels,
94
- xy_scaling_factor=xy_scaling_factor,
95
- z_scaling_factor=z_scaling_factor,
96
89
  space_unit=sample_info.space_unit,
97
90
  time_unit=sample_info.time_unit,
98
91
  axes_names=axes_names,
99
- channel_labels=channel_labels,
100
- channel_wavelengths=channel_wavelengths,
101
- channel_colors=channel_colors,
102
- channel_active=channel_active,
92
+ channels_meta=channels_meta,
93
+ scaling_factors=scaling_factors,
94
+ extra_array_kwargs=extra_array_kwargs,
103
95
  name=sample_info.name,
104
96
  chunks=chunks,
97
+ shards=shards,
105
98
  overwrite=overwrite,
106
99
  dimension_separator=dimension_separator,
107
100
  compressors=compressors,
108
- version=version,
101
+ ngff_version=ngff_version,
109
102
  )
110
103
 
111
104
  image = ome_zarr.get_image()