ngio 0.3.4__py3-none-any.whl → 0.4.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 (73) hide show
  1. ngio/__init__.py +7 -2
  2. ngio/common/__init__.py +5 -52
  3. ngio/common/_dimensions.py +270 -55
  4. ngio/common/_masking_roi.py +38 -10
  5. ngio/common/_pyramid.py +51 -30
  6. ngio/common/_roi.py +269 -82
  7. ngio/common/_synt_images_utils.py +101 -0
  8. ngio/common/_zoom.py +49 -19
  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 +127 -0
  16. ngio/experimental/iterators/_segmentation.py +235 -0
  17. ngio/hcs/_plate.py +41 -36
  18. ngio/images/__init__.py +22 -1
  19. ngio/images/_abstract_image.py +403 -176
  20. ngio/images/_create.py +31 -15
  21. ngio/images/_create_synt_container.py +138 -0
  22. ngio/images/_image.py +452 -63
  23. ngio/images/_label.py +56 -30
  24. ngio/images/_masked_image.py +387 -129
  25. ngio/images/_ome_zarr_container.py +237 -67
  26. ngio/{common → images}/_table_ops.py +41 -41
  27. ngio/io_pipes/__init__.py +75 -0
  28. ngio/io_pipes/_io_pipes.py +361 -0
  29. ngio/io_pipes/_io_pipes_masked.py +488 -0
  30. ngio/io_pipes/_io_pipes_roi.py +152 -0
  31. ngio/io_pipes/_io_pipes_types.py +56 -0
  32. ngio/io_pipes/_match_shape.py +376 -0
  33. ngio/io_pipes/_ops_axes.py +344 -0
  34. ngio/io_pipes/_ops_slices.py +446 -0
  35. ngio/io_pipes/_ops_slices_utils.py +196 -0
  36. ngio/io_pipes/_ops_transforms.py +104 -0
  37. ngio/io_pipes/_zoom_transform.py +175 -0
  38. ngio/ome_zarr_meta/__init__.py +4 -2
  39. ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -10
  40. ngio/ome_zarr_meta/ngio_specs/_axes.py +186 -175
  41. ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
  42. ngio/ome_zarr_meta/ngio_specs/_dataset.py +48 -122
  43. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +6 -15
  44. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +38 -87
  45. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +17 -1
  46. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +34 -31
  47. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  48. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  49. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  50. ngio/resources/__init__.py +55 -0
  51. ngio/resources/resource_model.py +36 -0
  52. ngio/tables/backends/_abstract_backend.py +5 -6
  53. ngio/tables/backends/_anndata.py +1 -2
  54. ngio/tables/backends/_anndata_utils.py +3 -3
  55. ngio/tables/backends/_non_zarr_backends.py +1 -1
  56. ngio/tables/backends/_table_backends.py +0 -1
  57. ngio/tables/backends/_utils.py +3 -3
  58. ngio/tables/v1/_roi_table.py +165 -70
  59. ngio/transforms/__init__.py +5 -0
  60. ngio/transforms/_zoom.py +19 -0
  61. ngio/utils/__init__.py +2 -3
  62. ngio/utils/_datasets.py +5 -0
  63. ngio/utils/_logger.py +19 -0
  64. ngio/utils/_zarr_utils.py +6 -6
  65. {ngio-0.3.4.dist-info → ngio-0.4.0.dist-info}/METADATA +24 -22
  66. ngio-0.4.0.dist-info/RECORD +85 -0
  67. ngio/common/_array_pipe.py +0 -288
  68. ngio/common/_axes_transforms.py +0 -64
  69. ngio/common/_common_types.py +0 -5
  70. ngio/common/_slicer.py +0 -96
  71. ngio-0.3.4.dist-info/RECORD +0 -61
  72. {ngio-0.3.4.dist-info → ngio-0.4.0.dist-info}/WHEEL +0 -0
  73. {ngio-0.3.4.dist-info → ngio-0.4.0.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,175 @@
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 not None and ax_type.axis_type == "channel":
64
+ # Do not scale channel axis
65
+ target_shape.append(shape)
66
+ continue
67
+ t_dim = self._target_dimensions.get(ax_name, default=1)
68
+ in_pix = self._input_pixel_size.get(ax_name, default=1.0)
69
+ t_pix = self._target_pixel_size.get(ax_name, default=1.0)
70
+ slice_ = slicing_ops.get(ax_name, normalize=False)
71
+ scale = in_pix / t_pix
72
+ _target_shape = self._normalize_shape(
73
+ slice_=slice_, scale=scale, max_dim=t_dim
74
+ )
75
+ target_shape.append(_target_shape)
76
+ return tuple(target_shape)
77
+
78
+ def _compute_inverse_zoom_shape(
79
+ self,
80
+ array_shape: Sequence[int],
81
+ axes_ops: AxesOps,
82
+ slicing_ops: SlicingOps,
83
+ ) -> tuple[int, ...]:
84
+ assert len(array_shape) == len(axes_ops.output_axes)
85
+
86
+ target_shape = []
87
+ for shape, ax_name in zip(array_shape, axes_ops.output_axes, strict=True):
88
+ ax_type = self._input_dimensions.axes_handler.get_axis(ax_name)
89
+ if ax_type is not None and ax_type.axis_type == "channel":
90
+ # Do not scale channel axis
91
+ target_shape.append(shape)
92
+ continue
93
+ in_dim = self._input_dimensions.get(ax_name, default=1)
94
+ slice_ = slicing_ops.get(ax_name=ax_name, normalize=True)
95
+ target_shape.append(
96
+ self._normalize_shape(slice_=slice_, scale=1, max_dim=in_dim)
97
+ )
98
+
99
+ # Since we are basing the rescaling on the slice, we need to ensure
100
+ # that the input image we got is roughly the right size.
101
+ # This is a safeguard against user errors.
102
+ expected_shape = self._compute_zoom_shape(
103
+ array_shape=target_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
104
+ )
105
+ if any(
106
+ abs(es - s) > 1 for es, s in zip(expected_shape, array_shape, strict=True)
107
+ ):
108
+ raise ValueError(
109
+ f"Input array shape {array_shape} is not compatible with the expected "
110
+ f"shape {expected_shape} based on the zoom transform.\n"
111
+ )
112
+ return tuple(target_shape)
113
+
114
+ def _numpy_zoom(
115
+ self, array: np.ndarray, target_shape: tuple[int, ...]
116
+ ) -> np.ndarray:
117
+ if array.shape == target_shape:
118
+ return array
119
+ return numpy_zoom(
120
+ source_array=array, target_shape=target_shape, order=self._order
121
+ )
122
+
123
+ def _dask_zoom(
124
+ self,
125
+ array: da.Array,
126
+ array_shape: tuple[int, ...],
127
+ target_shape: tuple[int, ...],
128
+ ) -> da.Array:
129
+ if array_shape == target_shape:
130
+ return array
131
+ return dask_zoom(
132
+ source_array=array, target_shape=target_shape, order=self._order
133
+ )
134
+
135
+ def get_as_numpy_transform(
136
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
137
+ ) -> np.ndarray:
138
+ """Apply the scaling transformation to a numpy array."""
139
+ target_shape = self._compute_zoom_shape(
140
+ array_shape=array.shape, axes_ops=axes_ops, slicing_ops=slicing_ops
141
+ )
142
+ return self._numpy_zoom(array=array, target_shape=target_shape)
143
+
144
+ def get_as_dask_transform(
145
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
146
+ ) -> da.Array:
147
+ """Apply the scaling transformation to a dask array."""
148
+ array_shape = tuple(int(s) for s in array.shape)
149
+ target_shape = self._compute_zoom_shape(
150
+ array_shape=array_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
151
+ )
152
+ return self._dask_zoom(
153
+ array=array, array_shape=array_shape, target_shape=target_shape
154
+ )
155
+
156
+ def set_as_numpy_transform(
157
+ self, array: np.ndarray, slicing_ops: SlicingOps, axes_ops: AxesOps
158
+ ) -> np.ndarray:
159
+ """Apply the inverse scaling transformation to a numpy array."""
160
+ target_shape = self._compute_inverse_zoom_shape(
161
+ array_shape=array.shape, axes_ops=axes_ops, slicing_ops=slicing_ops
162
+ )
163
+ return self._numpy_zoom(array=array, target_shape=target_shape)
164
+
165
+ def set_as_dask_transform(
166
+ self, array: da.Array, slicing_ops: SlicingOps, axes_ops: AxesOps
167
+ ) -> da.Array:
168
+ """Apply the inverse scaling transformation to a dask array."""
169
+ array_shape = tuple(int(s) for s in array.shape)
170
+ target_shape = self._compute_inverse_zoom_shape(
171
+ array_shape=array_shape, axes_ops=axes_ops, slicing_ops=slicing_ops
172
+ )
173
+ return self._dask_zoom(
174
+ array=array, array_shape=array_shape, target_shape=target_shape
175
+ )
@@ -13,7 +13,7 @@ from ngio.ome_zarr_meta._meta_handlers import (
13
13
  get_well_meta_handler,
14
14
  )
15
15
  from ngio.ome_zarr_meta.ngio_specs import (
16
- AxesMapper,
16
+ AxesHandler,
17
17
  Dataset,
18
18
  ImageInWellPath,
19
19
  NgffVersions,
@@ -22,11 +22,12 @@ from ngio.ome_zarr_meta.ngio_specs import (
22
22
  NgioPlateMeta,
23
23
  NgioWellMeta,
24
24
  PixelSize,
25
+ build_canonical_axes_handler,
25
26
  path_in_well_validation,
26
27
  )
27
28
 
28
29
  __all__ = [
29
- "AxesMapper",
30
+ "AxesHandler",
30
31
  "Dataset",
31
32
  "ImageInWellPath",
32
33
  "ImageMetaHandler",
@@ -40,6 +41,7 @@ __all__ = [
40
41
  "NgioPlateMeta",
41
42
  "NgioWellMeta",
42
43
  "PixelSize",
44
+ "build_canonical_axes_handler",
43
45
  "find_image_meta_handler",
44
46
  "find_label_meta_handler",
45
47
  "find_plate_meta_handler",
@@ -7,18 +7,15 @@ This models can be tr
7
7
  """
8
8
 
9
9
  from ngio.ome_zarr_meta.ngio_specs._axes import (
10
- AxesExpand,
11
- AxesMapper,
10
+ AxesHandler,
12
11
  AxesSetup,
13
- AxesSqueeze,
14
- AxesTransformation,
15
- AxesTranspose,
16
12
  Axis,
17
13
  AxisType,
18
14
  DefaultSpaceUnit,
19
15
  DefaultTimeUnit,
20
16
  SpaceUnits,
21
17
  TimeUnits,
18
+ build_canonical_axes_handler,
22
19
  canonical_axes_order,
23
20
  canonical_label_axes_order,
24
21
  )
@@ -47,12 +44,8 @@ from ngio.ome_zarr_meta.ngio_specs._ngio_image import (
47
44
  from ngio.ome_zarr_meta.ngio_specs._pixel_size import PixelSize
48
45
 
49
46
  __all__ = [
50
- "AxesExpand",
51
- "AxesMapper",
47
+ "AxesHandler",
52
48
  "AxesSetup",
53
- "AxesSqueeze",
54
- "AxesTransformation",
55
- "AxesTranspose",
56
49
  "Axis",
57
50
  "AxisType",
58
51
  "Channel",
@@ -74,6 +67,7 @@ __all__ = [
74
67
  "PixelSize",
75
68
  "SpaceUnits",
76
69
  "TimeUnits",
70
+ "build_canonical_axes_handler",
77
71
  "canonical_axes_order",
78
72
  "canonical_label_axes_order",
79
73
  "default_channel_name",