ngio 0.2.0a2__py3-none-any.whl → 0.5.0b4__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 (106) hide show
  1. ngio/__init__.py +40 -12
  2. ngio/common/__init__.py +16 -32
  3. ngio/common/_dimensions.py +270 -48
  4. ngio/common/_masking_roi.py +153 -0
  5. ngio/common/_pyramid.py +267 -73
  6. ngio/common/_roi.py +290 -66
  7. ngio/common/_synt_images_utils.py +101 -0
  8. ngio/common/_zoom.py +54 -22
  9. ngio/experimental/__init__.py +5 -0
  10. ngio/experimental/iterators/__init__.py +15 -0
  11. ngio/experimental/iterators/_abstract_iterator.py +390 -0
  12. ngio/experimental/iterators/_feature.py +189 -0
  13. ngio/experimental/iterators/_image_processing.py +130 -0
  14. ngio/experimental/iterators/_mappers.py +48 -0
  15. ngio/experimental/iterators/_rois_utils.py +126 -0
  16. ngio/experimental/iterators/_segmentation.py +235 -0
  17. ngio/hcs/__init__.py +17 -58
  18. ngio/hcs/_plate.py +1354 -0
  19. ngio/images/__init__.py +30 -9
  20. ngio/images/_abstract_image.py +968 -0
  21. ngio/images/_create_synt_container.py +132 -0
  22. ngio/images/_create_utils.py +423 -0
  23. ngio/images/_image.py +926 -0
  24. ngio/images/_label.py +417 -0
  25. ngio/images/_masked_image.py +531 -0
  26. ngio/images/_ome_zarr_container.py +1235 -0
  27. ngio/images/_table_ops.py +471 -0
  28. ngio/io_pipes/__init__.py +75 -0
  29. ngio/io_pipes/_io_pipes.py +361 -0
  30. ngio/io_pipes/_io_pipes_masked.py +488 -0
  31. ngio/io_pipes/_io_pipes_roi.py +146 -0
  32. ngio/io_pipes/_io_pipes_types.py +56 -0
  33. ngio/io_pipes/_match_shape.py +377 -0
  34. ngio/io_pipes/_ops_axes.py +344 -0
  35. ngio/io_pipes/_ops_slices.py +411 -0
  36. ngio/io_pipes/_ops_slices_utils.py +199 -0
  37. ngio/io_pipes/_ops_transforms.py +104 -0
  38. ngio/io_pipes/_zoom_transform.py +180 -0
  39. ngio/ome_zarr_meta/__init__.py +39 -15
  40. ngio/ome_zarr_meta/_meta_handlers.py +490 -96
  41. ngio/ome_zarr_meta/ngio_specs/__init__.py +24 -10
  42. ngio/ome_zarr_meta/ngio_specs/_axes.py +268 -234
  43. ngio/ome_zarr_meta/ngio_specs/_channels.py +125 -41
  44. ngio/ome_zarr_meta/ngio_specs/_dataset.py +42 -87
  45. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +536 -2
  46. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +202 -198
  47. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +72 -34
  48. ngio/ome_zarr_meta/v04/__init__.py +21 -5
  49. ngio/ome_zarr_meta/v04/_custom_models.py +18 -0
  50. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +151 -90
  51. ngio/ome_zarr_meta/v05/__init__.py +27 -0
  52. ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
  53. ngio/ome_zarr_meta/v05/_v05_spec.py +511 -0
  54. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  55. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  56. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  57. ngio/resources/__init__.py +55 -0
  58. ngio/resources/resource_model.py +36 -0
  59. ngio/tables/__init__.py +20 -4
  60. ngio/tables/_abstract_table.py +270 -0
  61. ngio/tables/_tables_container.py +449 -0
  62. ngio/tables/backends/__init__.py +50 -1
  63. ngio/tables/backends/_abstract_backend.py +200 -31
  64. ngio/tables/backends/_anndata.py +139 -0
  65. ngio/tables/backends/_anndata_utils.py +10 -114
  66. ngio/tables/backends/_csv.py +19 -0
  67. ngio/tables/backends/_json.py +92 -0
  68. ngio/tables/backends/_parquet.py +19 -0
  69. ngio/tables/backends/_py_arrow_backends.py +222 -0
  70. ngio/tables/backends/_table_backends.py +162 -38
  71. ngio/tables/backends/_utils.py +608 -0
  72. ngio/tables/v1/__init__.py +19 -4
  73. ngio/tables/v1/_condition_table.py +71 -0
  74. ngio/tables/v1/_feature_table.py +79 -115
  75. ngio/tables/v1/_generic_table.py +21 -90
  76. ngio/tables/v1/_roi_table.py +486 -137
  77. ngio/transforms/__init__.py +5 -0
  78. ngio/transforms/_zoom.py +19 -0
  79. ngio/utils/__init__.py +16 -14
  80. ngio/utils/_cache.py +48 -0
  81. ngio/utils/_datasets.py +121 -13
  82. ngio/utils/_fractal_fsspec_store.py +42 -0
  83. ngio/utils/_zarr_utils.py +374 -218
  84. ngio-0.5.0b4.dist-info/METADATA +147 -0
  85. ngio-0.5.0b4.dist-info/RECORD +88 -0
  86. {ngio-0.2.0a2.dist-info → ngio-0.5.0b4.dist-info}/WHEEL +1 -1
  87. ngio/common/_array_pipe.py +0 -160
  88. ngio/common/_axes_transforms.py +0 -63
  89. ngio/common/_common_types.py +0 -5
  90. ngio/common/_slicer.py +0 -97
  91. ngio/images/abstract_image.py +0 -240
  92. ngio/images/create.py +0 -251
  93. ngio/images/image.py +0 -389
  94. ngio/images/label.py +0 -236
  95. ngio/images/omezarr_container.py +0 -535
  96. ngio/ome_zarr_meta/_generic_handlers.py +0 -320
  97. ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
  98. ngio/tables/_validators.py +0 -192
  99. ngio/tables/backends/_anndata_v1.py +0 -75
  100. ngio/tables/backends/_json_v1.py +0 -56
  101. ngio/tables/tables_container.py +0 -300
  102. ngio/tables/v1/_masking_roi_table.py +0 -175
  103. ngio/utils/_logger.py +0 -29
  104. ngio-0.2.0a2.dist-info/METADATA +0 -95
  105. ngio-0.2.0a2.dist-info/RECORD +0 -53
  106. {ngio-0.2.0a2.dist-info → ngio-0.5.0b4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,104 @@
1
+ from collections.abc import Sequence
2
+ from typing import Protocol
3
+
4
+ import dask.array as da
5
+ import numpy as np
6
+
7
+ from ngio.io_pipes._ops_axes import AxesOps
8
+ from ngio.io_pipes._ops_slices import SlicingOps
9
+
10
+
11
+ class TransformProtocol(Protocol):
12
+ """Protocol for a generic transform."""
13
+
14
+ def get_as_numpy_transform(
15
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
16
+ ) -> np.ndarray:
17
+ """A transformation to be applied after loading a numpy array."""
18
+ ...
19
+
20
+ def get_as_dask_transform(
21
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
22
+ ) -> da.Array:
23
+ """A transformation to be applied after loading a dask array."""
24
+ ...
25
+
26
+ def set_as_numpy_transform(
27
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
28
+ ) -> np.ndarray:
29
+ """A transformation to be applied before writing a numpy array."""
30
+ ...
31
+
32
+ def set_as_dask_transform(
33
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
34
+ ) -> da.Array:
35
+ """A transformation to be applied before writing a dask array."""
36
+ ...
37
+
38
+
39
+ def get_as_numpy_transform(
40
+ array: np.ndarray,
41
+ slicing_ops: SlicingOps,
42
+ axes_ops: AxesOps,
43
+ transforms: Sequence[TransformProtocol] | None = None,
44
+ ) -> np.ndarray:
45
+ """Apply a numpy transform to an array."""
46
+ if transforms is None:
47
+ return array
48
+
49
+ for transform in transforms:
50
+ array = transform.get_as_numpy_transform(
51
+ array, slicing_ops=slicing_ops, axes_ops=axes_ops
52
+ )
53
+ return array
54
+
55
+
56
+ def get_as_dask_transform(
57
+ array: da.Array,
58
+ slicing_ops: SlicingOps,
59
+ axes_ops: AxesOps,
60
+ transforms: Sequence[TransformProtocol] | None = None,
61
+ ) -> da.Array:
62
+ """Apply a dask transform to an array."""
63
+ if transforms is None:
64
+ return array
65
+
66
+ for transform in transforms:
67
+ array = transform.get_as_dask_transform(
68
+ array, slicing_ops=slicing_ops, axes_ops=axes_ops
69
+ )
70
+ return array
71
+
72
+
73
+ def set_as_numpy_transform(
74
+ array: np.ndarray,
75
+ slicing_ops: SlicingOps,
76
+ axes_ops: AxesOps,
77
+ transforms: Sequence[TransformProtocol] | None = None,
78
+ ) -> np.ndarray:
79
+ """Apply inverse numpy transforms to an array."""
80
+ if transforms is None:
81
+ return array
82
+
83
+ for transform in transforms:
84
+ array = transform.set_as_numpy_transform(
85
+ array, slicing_ops=slicing_ops, axes_ops=axes_ops
86
+ )
87
+ return array
88
+
89
+
90
+ def set_as_dask_transform(
91
+ array: da.Array,
92
+ slicing_ops: SlicingOps,
93
+ axes_ops: AxesOps,
94
+ transforms: Sequence[TransformProtocol] | None = None,
95
+ ) -> da.Array:
96
+ """Apply inverse dask transforms to an array."""
97
+ if transforms is None:
98
+ return array
99
+
100
+ for transform in transforms:
101
+ array = transform.set_as_dask_transform(
102
+ array, slicing_ops=slicing_ops, axes_ops=axes_ops
103
+ )
104
+ return array
@@ -0,0 +1,180 @@
1
+ import math
2
+ from collections.abc import Sequence
3
+
4
+ import dask.array as da
5
+ import numpy as np
6
+
7
+ from ngio.common._dimensions import Dimensions
8
+ from ngio.common._zoom import (
9
+ InterpolationOrder,
10
+ dask_zoom,
11
+ numpy_zoom,
12
+ )
13
+ from ngio.io_pipes._ops_axes import AxesOps
14
+ from ngio.io_pipes._ops_slices import SlicingOps
15
+
16
+
17
+ class BaseZoomTransform:
18
+ def __init__(
19
+ self,
20
+ input_dimensions: Dimensions,
21
+ target_dimensions: Dimensions,
22
+ order: InterpolationOrder = "nearest",
23
+ ) -> None:
24
+ self._input_dimensions = input_dimensions
25
+ self._target_dimensions = target_dimensions
26
+ self._input_pixel_size = input_dimensions.pixel_size
27
+ self._target_pixel_size = target_dimensions.pixel_size
28
+ self._order: InterpolationOrder = order
29
+
30
+ def _normalize_shape(
31
+ self, slice_: slice | int | tuple, scale: float, max_dim: int
32
+ ) -> int:
33
+ if isinstance(slice_, slice):
34
+ _start = slice_.start or 0
35
+ _start_int = math.floor(_start * scale)
36
+ if slice_.stop is not None:
37
+ _stop = slice_.stop * scale
38
+ _stop = min(_stop, max_dim)
39
+ else:
40
+ _stop = max_dim
41
+ _stop_int = math.ceil(_stop)
42
+ target_shape = _stop_int - _start_int
43
+
44
+ elif isinstance(slice_, int):
45
+ target_shape = 1
46
+ elif isinstance(slice_, tuple):
47
+ target_shape = len(slice_) * scale
48
+ else:
49
+ raise ValueError(f"Unsupported slice type: {type(slice_)}")
50
+ return math.ceil(target_shape)
51
+
52
+ def _compute_zoom_shape(
53
+ self,
54
+ array_shape: Sequence[int],
55
+ axes_ops: AxesOps,
56
+ slicing_ops: SlicingOps,
57
+ ) -> tuple[int, ...]:
58
+ assert len(array_shape) == len(axes_ops.output_axes)
59
+
60
+ target_shape = []
61
+ for shape, ax_name in zip(array_shape, axes_ops.output_axes, strict=True):
62
+ ax_type = self._input_dimensions.axes_handler.get_axis(ax_name)
63
+ if ax_type is None:
64
+ # Unknown axis can only be a virtual axis
65
+ # So we set it to 1
66
+ target_shape.append(1)
67
+ continue
68
+ elif ax_type.axis_type == "channel":
69
+ # Do not scale channel axis
70
+ target_shape.append(shape)
71
+ continue
72
+ t_dim = self._target_dimensions.get(ax_name, default=1)
73
+ in_pix = self._input_pixel_size.get(ax_name, default=1.0)
74
+ t_pix = self._target_pixel_size.get(ax_name, default=1.0)
75
+ slice_ = slicing_ops.get(ax_name, normalize=False)
76
+ scale = in_pix / t_pix
77
+ _target_shape = self._normalize_shape(
78
+ slice_=slice_, scale=scale, max_dim=t_dim
79
+ )
80
+ target_shape.append(_target_shape)
81
+ return tuple(target_shape)
82
+
83
+ def _compute_inverse_zoom_shape(
84
+ self,
85
+ array_shape: Sequence[int],
86
+ axes_ops: AxesOps,
87
+ slicing_ops: SlicingOps,
88
+ ) -> tuple[int, ...]:
89
+ assert len(array_shape) == len(axes_ops.output_axes)
90
+
91
+ target_shape = []
92
+ for shape, ax_name in zip(array_shape, axes_ops.output_axes, strict=True):
93
+ ax_type = self._input_dimensions.axes_handler.get_axis(ax_name)
94
+ if ax_type is not None and ax_type.axis_type == "channel":
95
+ # Do not scale channel axis
96
+ target_shape.append(shape)
97
+ continue
98
+ in_dim = self._input_dimensions.get(ax_name, default=1)
99
+ slice_ = slicing_ops.get(ax_name=ax_name, normalize=True)
100
+ target_shape.append(
101
+ self._normalize_shape(slice_=slice_, scale=1, max_dim=in_dim)
102
+ )
103
+
104
+ # Since we are basing the rescaling on the slice, we need to ensure
105
+ # that the input image we got is roughly the right size.
106
+ # This is a safeguard against user errors.
107
+ expected_shape = self._compute_zoom_shape(
108
+ array_shape=target_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
109
+ )
110
+ if any(
111
+ abs(es - s) > 1 for es, s in zip(expected_shape, array_shape, strict=True)
112
+ ):
113
+ raise ValueError(
114
+ f"Input array shape {array_shape} is not compatible with the expected "
115
+ f"shape {expected_shape} based on the zoom transform.\n"
116
+ )
117
+ return tuple(target_shape)
118
+
119
+ def _numpy_zoom(
120
+ self, array: np.ndarray, target_shape: tuple[int, ...]
121
+ ) -> np.ndarray:
122
+ if array.shape == target_shape:
123
+ return array
124
+ return numpy_zoom(
125
+ source_array=array, target_shape=target_shape, order=self._order
126
+ )
127
+
128
+ def _dask_zoom(
129
+ self,
130
+ array: da.Array,
131
+ array_shape: tuple[int, ...],
132
+ target_shape: tuple[int, ...],
133
+ ) -> da.Array:
134
+ if array_shape == target_shape:
135
+ return array
136
+ return dask_zoom(
137
+ source_array=array, target_shape=target_shape, order=self._order
138
+ )
139
+
140
+ def get_as_numpy_transform(
141
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
142
+ ) -> np.ndarray:
143
+ """Apply the scaling transformation to a numpy array."""
144
+ target_shape = self._compute_zoom_shape(
145
+ array_shape=array.shape, axes_ops=axes_ops, slicing_ops=slicing_ops
146
+ )
147
+ return self._numpy_zoom(array=array, target_shape=target_shape)
148
+
149
+ def get_as_dask_transform(
150
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
151
+ ) -> da.Array:
152
+ """Apply the scaling transformation to a dask array."""
153
+ array_shape = tuple(int(s) for s in array.shape)
154
+ target_shape = self._compute_zoom_shape(
155
+ array_shape=array_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
156
+ )
157
+ return self._dask_zoom(
158
+ array=array, array_shape=array_shape, target_shape=target_shape
159
+ )
160
+
161
+ def set_as_numpy_transform(
162
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
163
+ ) -> np.ndarray:
164
+ """Apply the inverse scaling transformation to a numpy array."""
165
+ target_shape = self._compute_inverse_zoom_shape(
166
+ array_shape=array.shape, axes_ops=axes_ops, slicing_ops=slicing_ops
167
+ )
168
+ return self._numpy_zoom(array=array, target_shape=target_shape)
169
+
170
+ def set_as_dask_transform(
171
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
172
+ ) -> da.Array:
173
+ """Apply the inverse scaling transformation to a dask array."""
174
+ array_shape = tuple(int(s) for s in array.shape)
175
+ target_shape = self._compute_inverse_zoom_shape(
176
+ array_shape=array_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
177
+ )
178
+ return self._dask_zoom(
179
+ array=array, array_shape=array_shape, target_shape=target_shape
180
+ )
@@ -1,35 +1,59 @@
1
1
  """Utilities for reading and writing OME-Zarr metadata."""
2
2
 
3
- from ngio.ome_zarr_meta._generic_handlers import (
4
- BaseImageMetaHandler,
5
- BaseLabelMetaHandler,
3
+ from ngio.ome_zarr_meta._meta_handlers import (
6
4
  ImageMetaHandler,
7
5
  LabelMetaHandler,
8
- )
9
- from ngio.ome_zarr_meta._meta_handlers import (
10
- ImplementedImageMetaHandlers,
11
- ImplementedLabelMetaHandlers,
12
- open_image_meta_handler,
6
+ LabelsGroupMetaHandler,
7
+ PlateMetaHandler,
8
+ WellMetaHandler,
9
+ update_ngio_image_meta,
10
+ update_ngio_label_meta,
11
+ update_ngio_labels_group_meta,
12
+ update_ngio_meta,
13
+ update_ngio_plate_meta,
14
+ update_ngio_well_meta,
13
15
  )
14
16
  from ngio.ome_zarr_meta.ngio_specs import (
15
- AxesMapper,
17
+ AxesHandler,
16
18
  Dataset,
19
+ DefaultNgffVersion,
20
+ ImageInWellPath,
21
+ NgffVersions,
17
22
  NgioImageMeta,
18
23
  NgioLabelMeta,
24
+ NgioLabelsGroupMeta,
25
+ NgioPlateMeta,
26
+ NgioWellMeta,
19
27
  PixelSize,
28
+ build_canonical_axes_handler,
29
+ path_in_well_validation,
20
30
  )
21
31
 
22
32
  __all__ = [
23
- "AxesMapper",
24
- "BaseImageMetaHandler",
25
- "BaseLabelMetaHandler",
33
+ "AxesHandler",
26
34
  "Dataset",
35
+ "DefaultNgffVersion",
36
+ "ImageInWellPath",
27
37
  "ImageMetaHandler",
28
- "ImplementedImageMetaHandlers",
29
- "ImplementedLabelMetaHandlers",
30
38
  "LabelMetaHandler",
39
+ "LabelsGroupMetaHandler",
40
+ "NgffVersions",
41
+ "NgffVersions",
31
42
  "NgioImageMeta",
32
43
  "NgioLabelMeta",
44
+ "NgioLabelsGroupMeta",
45
+ "NgioPlateMeta",
46
+ "NgioWellMeta",
33
47
  "PixelSize",
34
- "open_image_meta_handler",
48
+ "PlateMetaHandler",
49
+ "PlateMetaHandler",
50
+ "WellMetaHandler",
51
+ "build_canonical_axes_handler",
52
+ "path_in_well_validation",
53
+ "update_ngio_image_meta",
54
+ "update_ngio_label_meta",
55
+ "update_ngio_labels_group_meta",
56
+ "update_ngio_meta",
57
+ "update_ngio_plate_meta",
58
+ "update_ngio_well_meta",
35
59
  ]