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.
- ngio/__init__.py +2 -2
- ngio/common/__init__.py +11 -6
- ngio/common/_masking_roi.py +12 -41
- ngio/common/_pyramid.py +218 -78
- ngio/common/_roi.py +257 -329
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +10 -11
- ngio/hcs/_plate.py +114 -123
- ngio/images/_abstract_image.py +417 -35
- ngio/images/_create_synt_container.py +36 -43
- ngio/images/_create_utils.py +423 -0
- ngio/images/_image.py +155 -177
- ngio/images/_label.py +144 -119
- ngio/images/_ome_zarr_container.py +361 -196
- 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 +5 -4
- ngio/io_pipes/_ops_slices_utils.py +8 -5
- ngio/ome_zarr_meta/__init__.py +15 -18
- ngio/ome_zarr_meta/_meta_handlers.py +334 -713
- ngio/ome_zarr_meta/ngio_specs/_axes.py +1 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +54 -61
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +14 -68
- ngio/ome_zarr_meta/v04/__init__.py +1 -1
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +16 -61
- ngio/ome_zarr_meta/v05/__init__.py +1 -1
- ngio/ome_zarr_meta/v05/{_v05_spec_utils.py → _v05_spec.py} +18 -61
- ngio/tables/_tables_container.py +25 -20
- ngio/tables/backends/_anndata.py +57 -8
- ngio/tables/backends/_anndata_utils.py +1 -6
- ngio/tables/backends/_csv.py +3 -19
- ngio/tables/backends/_json.py +10 -13
- ngio/tables/backends/_parquet.py +3 -31
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/v1/_roi_table.py +44 -27
- ngio/utils/__init__.py +6 -12
- ngio/utils/_cache.py +48 -0
- ngio/utils/_zarr_utils.py +285 -245
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/METADATA +8 -4
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/RECORD +45 -45
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/WHEEL +1 -1
- ngio/images/_create.py +0 -283
- ngio/tables/backends/_non_zarr_backends.py +0 -196
- ngio/utils/_logger.py +0 -50
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/licenses/LICENSE +0 -0
ngio/images/_abstract_image.py
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
"""Generic class to handle Image-like data in a OME-NGFF file."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def meta_handler(self) -> ImageMetaHandler | LabelMetaHandler:
|
|
82
100
|
"""Return the metadata."""
|
|
83
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
roi_px
|
|
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.
|
|
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
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
74
|
-
Defaults to
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
101
|
+
ngff_version=ngff_version,
|
|
109
102
|
)
|
|
110
103
|
|
|
111
104
|
image = ome_zarr.get_image()
|