ngio 0.3.4__py3-none-any.whl → 0.4.0a1__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 (61) hide show
  1. ngio/__init__.py +6 -0
  2. ngio/common/__init__.py +50 -48
  3. ngio/common/_array_io_pipes.py +549 -0
  4. ngio/common/_array_io_utils.py +508 -0
  5. ngio/common/_dimensions.py +63 -27
  6. ngio/common/_masking_roi.py +38 -10
  7. ngio/common/_pyramid.py +9 -7
  8. ngio/common/_roi.py +571 -72
  9. ngio/common/_synt_images_utils.py +101 -0
  10. ngio/common/_zoom.py +17 -12
  11. ngio/common/transforms/__init__.py +5 -0
  12. ngio/common/transforms/_label.py +12 -0
  13. ngio/common/transforms/_zoom.py +109 -0
  14. ngio/experimental/__init__.py +5 -0
  15. ngio/experimental/iterators/__init__.py +17 -0
  16. ngio/experimental/iterators/_abstract_iterator.py +170 -0
  17. ngio/experimental/iterators/_feature.py +151 -0
  18. ngio/experimental/iterators/_image_processing.py +169 -0
  19. ngio/experimental/iterators/_rois_utils.py +127 -0
  20. ngio/experimental/iterators/_segmentation.py +278 -0
  21. ngio/hcs/_plate.py +41 -36
  22. ngio/images/__init__.py +22 -1
  23. ngio/images/_abstract_image.py +247 -117
  24. ngio/images/_create.py +15 -15
  25. ngio/images/_create_synt_container.py +128 -0
  26. ngio/images/_image.py +425 -62
  27. ngio/images/_label.py +33 -30
  28. ngio/images/_masked_image.py +396 -122
  29. ngio/images/_ome_zarr_container.py +203 -66
  30. ngio/{common → images}/_table_ops.py +41 -41
  31. ngio/ome_zarr_meta/ngio_specs/__init__.py +2 -8
  32. ngio/ome_zarr_meta/ngio_specs/_axes.py +151 -128
  33. ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
  34. ngio/ome_zarr_meta/ngio_specs/_dataset.py +7 -7
  35. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +6 -15
  36. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +11 -68
  37. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +1 -1
  38. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  39. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  40. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  41. ngio/resources/__init__.py +54 -0
  42. ngio/resources/resource_model.py +35 -0
  43. ngio/tables/backends/_abstract_backend.py +5 -6
  44. ngio/tables/backends/_anndata.py +1 -2
  45. ngio/tables/backends/_anndata_utils.py +3 -3
  46. ngio/tables/backends/_non_zarr_backends.py +1 -1
  47. ngio/tables/backends/_table_backends.py +0 -1
  48. ngio/tables/backends/_utils.py +3 -3
  49. ngio/tables/v1/_roi_table.py +156 -69
  50. ngio/utils/__init__.py +2 -3
  51. ngio/utils/_logger.py +19 -0
  52. ngio/utils/_zarr_utils.py +1 -5
  53. {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/METADATA +12 -10
  54. ngio-0.4.0a1.dist-info/RECORD +76 -0
  55. ngio/common/_array_pipe.py +0 -288
  56. ngio/common/_axes_transforms.py +0 -64
  57. ngio/common/_common_types.py +0 -5
  58. ngio/common/_slicer.py +0 -96
  59. ngio-0.3.4.dist-info/RECORD +0 -61
  60. {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/WHEEL +0 -0
  61. {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/licenses/LICENSE +0 -0
ngio/common/_roi.py CHANGED
@@ -4,21 +4,38 @@ These are the interfaces bwteen the ROI tables / masking ROI tables and
4
4
  the ImageLikeHandler.
5
5
  """
6
6
 
7
- from collections.abc import Iterable
7
+ from collections.abc import Callable, Sequence
8
+ from typing import Generic, TypeVar
9
+ from warnings import warn
8
10
 
11
+ import dask.array as da
9
12
  import numpy as np
10
- from pydantic import BaseModel, ConfigDict, Field
11
-
13
+ import zarr
14
+ from pydantic import BaseModel, ConfigDict
15
+
16
+ from ngio.common._array_io_pipes import (
17
+ build_dask_getter,
18
+ build_dask_setter,
19
+ build_masked_dask_getter,
20
+ build_masked_dask_setter,
21
+ build_masked_numpy_getter,
22
+ build_masked_numpy_setter,
23
+ build_numpy_getter,
24
+ build_numpy_setter,
25
+ )
26
+ from ngio.common._array_io_utils import SlicingInputType, TransformProtocol
12
27
  from ngio.common._dimensions import Dimensions
13
28
  from ngio.ome_zarr_meta.ngio_specs import DefaultSpaceUnit, PixelSize, SpaceUnits
14
29
  from ngio.utils import NgioValueError
15
30
 
16
31
 
17
- def _to_raster(value: float, pixel_size: float, max_shape: int) -> int:
32
+ def _to_raster(value: float, pixel_size: float, max_shape: int | None) -> int:
18
33
  """Convert to raster coordinates."""
19
34
  round_value = int(np.round(value / pixel_size))
20
35
  # Ensure the value is within the image shape boundaries
21
- return max(0, min(round_value, max_shape))
36
+ if max_shape is not None:
37
+ return max(0, min(round_value, max_shape))
38
+ return round_value
22
39
 
23
40
 
24
41
  def _to_world(value: int, pixel_size: float) -> float:
@@ -26,40 +43,197 @@ def _to_world(value: int, pixel_size: float) -> float:
26
43
  return value * pixel_size
27
44
 
28
45
 
29
- class Roi(BaseModel):
30
- """Region of interest (ROI) metadata."""
46
+ T = TypeVar("T", int, float)
47
+
48
+
49
+ class GenericRoi(BaseModel, Generic[T]):
50
+ """A generic Region of Interest (ROI) model."""
31
51
 
32
52
  name: str
33
- x_length: float
34
- y_length: float
35
- z_length: float = 1.0
36
- x: float = 0.0
37
- y: float = 0.0
38
- z: float = 0.0
39
- unit: SpaceUnits | str | None = Field(DefaultSpaceUnit, repr=False)
53
+ x: T
54
+ y: T
55
+ z: T | None = None
56
+ t: T | None = None
57
+ x_length: T
58
+ y_length: T
59
+ z_length: T | None = None
60
+ t_length: T | None = None
61
+ label: int | None = None
62
+ unit: SpaceUnits | str | None = None
40
63
 
41
64
  model_config = ConfigDict(extra="allow")
42
65
 
43
- def to_pixel_roi(
44
- self, pixel_size: PixelSize, dimensions: Dimensions
66
+ def intersection(self, other: "GenericRoi[T]") -> "GenericRoi[T] | None":
67
+ """Calculate the intersection of this ROI with another ROI."""
68
+ return roi_intersection(self, other)
69
+
70
+
71
+ def _1d_intersection(
72
+ a: T | None, a_length: T | None, b: T | None, b_length: T | None
73
+ ) -> tuple[T | None, T | None]:
74
+ """Calculate the intersection of two 1D intervals."""
75
+ if a is None:
76
+ if b is not None and b_length is not None:
77
+ return b, b_length
78
+ return None, None
79
+ if b is None:
80
+ if a is not None and a_length is not None:
81
+ return a, a_length
82
+ return None, None
83
+
84
+ assert (
85
+ a is not None
86
+ and a_length is not None
87
+ and b is not None
88
+ and b_length is not None
89
+ )
90
+ start = max(a, b)
91
+ end = min(a + a_length, b + b_length)
92
+ length = end - start
93
+
94
+ if length <= 0:
95
+ return None, None
96
+
97
+ return start, length
98
+
99
+
100
+ def roi_intersection(
101
+ ref_roi: GenericRoi[T], other_roi: GenericRoi[T]
102
+ ) -> GenericRoi[T] | None:
103
+ """Calculate the intersection of two ROIs."""
104
+ if (
105
+ ref_roi.unit is not None
106
+ and other_roi.unit is not None
107
+ and ref_roi.unit != other_roi.unit
108
+ ):
109
+ raise NgioValueError(
110
+ "Cannot calculate intersection of ROIs with different units."
111
+ )
112
+
113
+ x, x_length = _1d_intersection(
114
+ ref_roi.x, ref_roi.x_length, other_roi.x, other_roi.x_length
115
+ )
116
+ assert x is not None and x_length is not None
117
+
118
+ y, y_length = _1d_intersection(
119
+ ref_roi.y, ref_roi.y_length, other_roi.y, other_roi.y_length
120
+ )
121
+ assert y is not None and y_length is not None
122
+
123
+ z, z_length = _1d_intersection(
124
+ ref_roi.z, ref_roi.z_length, other_roi.z, other_roi.z_length
125
+ )
126
+ t, t_length = _1d_intersection(
127
+ ref_roi.t, ref_roi.t_length, other_roi.t, other_roi.t_length
128
+ )
129
+
130
+ if (
131
+ x_length <= 0
132
+ or y_length <= 0
133
+ or (z_length is not None and z_length <= 0)
134
+ or (t_length is not None and t_length <= 0)
135
+ ):
136
+ # No intersection
137
+ return None
138
+
139
+ # Find label
140
+ if ref_roi.label is not None and other_roi.label is not None:
141
+ if ref_roi.label != other_roi.label:
142
+ raise NgioValueError(
143
+ "Cannot calculate intersection of ROIs with different labels."
144
+ )
145
+ label = ref_roi.label or other_roi.label
146
+
147
+ cls_ref = ref_roi.__class__
148
+ return cls_ref(
149
+ name=f"[{ref_roi.name}_x_{other_roi.name}]",
150
+ x=x,
151
+ y=y,
152
+ z=z,
153
+ t=t,
154
+ x_length=x_length,
155
+ y_length=y_length,
156
+ z_length=z_length,
157
+ t_length=t_length,
158
+ unit=ref_roi.unit,
159
+ label=label,
160
+ )
161
+
162
+
163
+ class Roi(GenericRoi[float]):
164
+ x: float = 0.0
165
+ y: float = 0.0
166
+ unit: SpaceUnits | str | None = DefaultSpaceUnit
167
+
168
+ def to_roi_pixels(
169
+ self, pixel_size: PixelSize, dimensions: Dimensions | None = None
45
170
  ) -> "RoiPixels":
46
171
  """Convert to raster coordinates."""
47
- dim_x = dimensions.get("x")
48
- dim_y = dimensions.get("y")
49
- # Will default to 1 if z does not exist
50
- dim_z = dimensions.get("z", strict=False)
172
+ if dimensions is None:
173
+ # No check for dimensions
174
+ dim_x, dim_y, dim_z, dim_t = None, None, None, None
175
+ else:
176
+ dim_x, dim_y, dim_z, dim_t = (
177
+ dimensions.get("x"),
178
+ dimensions.get("y"),
179
+ dimensions.get("z"),
180
+ dimensions.get("t"),
181
+ )
182
+
183
+ x = _to_raster(self.x, pixel_size.x, dim_x)
184
+ x_length = _to_raster(self.x_length, pixel_size.x, dim_x)
185
+ y = _to_raster(self.y, pixel_size.y, dim_y)
186
+ y_length = _to_raster(self.y_length, pixel_size.y, dim_y)
187
+
188
+ if self.z is None:
189
+ z = None
190
+ else:
191
+ z = _to_raster(self.z, pixel_size.z, dim_z)
192
+
193
+ if self.z_length is None:
194
+ z_length = None
195
+ else:
196
+ z_length = _to_raster(self.z_length, pixel_size.z, dim_z)
51
197
 
198
+ if self.t is None:
199
+ t = None
200
+ else:
201
+ t = _to_raster(self.t, pixel_size.t, dim_t)
202
+
203
+ if self.t_length is None:
204
+ t_length = None
205
+ else:
206
+ t_length = _to_raster(self.t_length, pixel_size.t, dim_t)
207
+
208
+ extra_dict = self.model_extra if self.model_extra else {}
52
209
  return RoiPixels(
53
210
  name=self.name,
54
- x=_to_raster(self.x, pixel_size.x, dim_x),
55
- y=_to_raster(self.y, pixel_size.y, dim_y),
56
- z=_to_raster(self.z, pixel_size.z, dim_z),
57
- x_length=_to_raster(self.x_length, pixel_size.x, dim_x),
58
- y_length=_to_raster(self.y_length, pixel_size.y, dim_y),
59
- z_length=_to_raster(self.z_length, pixel_size.z, dim_z),
60
- **self.model_extra,
211
+ x=x,
212
+ y=y,
213
+ z=z,
214
+ t=t,
215
+ x_length=x_length,
216
+ y_length=y_length,
217
+ z_length=z_length,
218
+ t_length=t_length,
219
+ label=self.label,
220
+ unit=self.unit,
221
+ **extra_dict,
222
+ )
223
+
224
+ def to_pixel_roi(
225
+ self, pixel_size: PixelSize, dimensions: Dimensions | None = None
226
+ ) -> "RoiPixels":
227
+ """Convert to raster coordinates."""
228
+ warn(
229
+ "to_pixel_roi is deprecated and will be removed in a future release. "
230
+ "Use to_roi_pixels instead.",
231
+ DeprecationWarning,
232
+ stacklevel=2,
61
233
  )
62
234
 
235
+ return self.to_roi_pixels(pixel_size=pixel_size, dimensions=dimensions)
236
+
63
237
  def zoom(self, zoom_factor: float = 1) -> "Roi":
64
238
  """Zoom the ROI by a factor.
65
239
 
@@ -72,38 +246,72 @@ class Roi(BaseModel):
72
246
  return zoom_roi(self, zoom_factor)
73
247
 
74
248
 
75
- class RoiPixels(BaseModel):
76
- """Region of interest (ROI) metadata."""
249
+ class RoiPixels(GenericRoi[int]):
250
+ """Region of interest (ROI) in pixel coordinates."""
77
251
 
78
- name: str
79
- x: int
80
- y: int
81
- z: int
82
- x_length: int
83
- y_length: int
84
- z_length: int
85
- model_config = ConfigDict(extra="allow")
252
+ x: int = 0
253
+ y: int = 0
254
+ unit: SpaceUnits | str | None = None
86
255
 
87
- def to_roi(self, pixel_size: PixelSize) -> Roi:
88
- """Convert to world coordinates."""
256
+ def to_roi(self, pixel_size: PixelSize) -> "Roi":
257
+ """Convert to raster coordinates."""
258
+ x = _to_world(self.x, pixel_size.x)
259
+ x_length = _to_world(self.x_length, pixel_size.x)
260
+ y = _to_world(self.y, pixel_size.y)
261
+ y_length = _to_world(self.y_length, pixel_size.y)
262
+
263
+ if self.z is None:
264
+ z = None
265
+ else:
266
+ z = _to_world(self.z, pixel_size.z)
267
+
268
+ if self.z_length is None:
269
+ z_length = None
270
+ else:
271
+ z_length = _to_world(self.z_length, pixel_size.z)
272
+
273
+ if self.t is None:
274
+ t = None
275
+ else:
276
+ t = _to_world(self.t, pixel_size.t)
277
+
278
+ if self.t_length is None:
279
+ t_length = None
280
+ else:
281
+ t_length = _to_world(self.t_length, pixel_size.t)
282
+
283
+ extra_dict = self.model_extra if self.model_extra else {}
89
284
  return Roi(
90
285
  name=self.name,
91
- x=_to_world(self.x, pixel_size.x),
92
- y=_to_world(self.y, pixel_size.y),
93
- z=_to_world(self.z, pixel_size.z),
94
- x_length=_to_world(self.x_length, pixel_size.x),
95
- y_length=_to_world(self.y_length, pixel_size.y),
96
- z_length=_to_world(self.z_length, pixel_size.z),
97
- unit=pixel_size.space_unit,
98
- **self.model_extra,
286
+ x=x,
287
+ y=y,
288
+ z=z,
289
+ t=t,
290
+ x_length=x_length,
291
+ y_length=y_length,
292
+ z_length=z_length,
293
+ t_length=t_length,
294
+ label=self.label,
295
+ unit=self.unit,
296
+ **extra_dict,
99
297
  )
100
298
 
101
- def to_slices(self) -> dict[str, slice]:
102
- """Return the slices for the ROI."""
299
+ def to_slicing_dict(self) -> dict[str, SlicingInputType]:
300
+ x_slice = slice(self.x, self.x + self.x_length)
301
+ y_slice = slice(self.y, self.y + self.y_length)
302
+ if self.z is not None and self.z_length is not None:
303
+ z_slice = slice(self.z, self.z + self.z_length)
304
+ else:
305
+ z_slice = slice(None)
306
+ if self.t is not None and self.t_length is not None:
307
+ t_slice = slice(self.t, self.t + self.t_length)
308
+ else:
309
+ t_slice = slice(None)
103
310
  return {
104
- "x": slice(self.x, self.x + self.x_length),
105
- "y": slice(self.y, self.y + self.y_length),
106
- "z": slice(self.z, self.z + self.z_length),
311
+ "x": x_slice,
312
+ "y": y_slice,
313
+ "z": z_slice,
314
+ "t": t_slice,
107
315
  }
108
316
 
109
317
 
@@ -134,34 +342,325 @@ def zoom_roi(roi: Roi, zoom_factor: float = 1) -> Roi:
134
342
  x=new_x,
135
343
  y=new_y,
136
344
  z=roi.z,
345
+ t=roi.t,
137
346
  x_length=roi.x_length + diff_x,
138
347
  y_length=roi.y_length + diff_y,
139
348
  z_length=roi.z_length,
349
+ t_length=roi.t_length,
350
+ label=roi.label,
140
351
  unit=roi.unit,
141
352
  )
142
-
143
353
  return new_roi
144
354
 
145
355
 
146
- def roi_to_slice_kwargs(
147
- roi: Roi,
148
- pixel_size: PixelSize,
356
+ def roi_to_slicing_dict(
357
+ roi: Roi | RoiPixels,
149
358
  dimensions: Dimensions,
150
- **slice_kwargs: slice | int | Iterable[int],
151
- ) -> dict[str, slice | int | Iterable[int]]:
152
- """Convert a WorldCooROI to slice_kwargs."""
153
- raster_roi = roi.to_pixel_roi(
154
- pixel_size=pixel_size, dimensions=dimensions
155
- ).to_slices()
156
-
157
- if not dimensions.has_axis(axis_name="z"):
158
- raster_roi.pop("z")
159
-
160
- for key in slice_kwargs.keys():
161
- if key in raster_roi:
359
+ pixel_size: PixelSize | None = None,
360
+ slicing_dict: dict[str, SlicingInputType] | None = None,
361
+ ) -> dict[str, SlicingInputType]:
362
+ """Convert a ROI to a slicing dictionary."""
363
+ if isinstance(roi, Roi):
364
+ if pixel_size is None:
162
365
  raise NgioValueError(
163
- f"Key {key} is already in the slice_kwargs. "
164
- "Ambiguous which one to use: "
165
- f"{key}={slice_kwargs[key]} or roi_{key}={raster_roi[key]}"
366
+ "pixel_size must be provided when converting a Roi to slice_kwargs."
166
367
  )
167
- return {**raster_roi, **slice_kwargs}
368
+ roi = roi.to_roi_pixels(pixel_size=pixel_size, dimensions=dimensions)
369
+
370
+ roi_slicing_dict = roi.to_slicing_dict()
371
+ if slicing_dict is None:
372
+ return roi_slicing_dict
373
+
374
+ # Additional slice kwargs can be provided
375
+ # and will override the ones from the ROI
376
+ roi_slicing_dict.update(slicing_dict)
377
+ return roi_slicing_dict
378
+
379
+
380
+ def build_roi_numpy_getter(
381
+ zarr_array: zarr.Array,
382
+ dimensions: Dimensions,
383
+ roi: Roi | RoiPixels,
384
+ pixel_size: PixelSize | None = None,
385
+ axes_order: Sequence[str] | None = None,
386
+ transforms: Sequence[TransformProtocol] | None = None,
387
+ slicing_dict: dict[str, SlicingInputType] | None = None,
388
+ remove_channel_selection: bool = False,
389
+ ) -> Callable[[], np.ndarray]:
390
+ """Prepare slice kwargs for setting an array."""
391
+ input_slice_kwargs = roi_to_slicing_dict(
392
+ roi=roi,
393
+ dimensions=dimensions,
394
+ pixel_size=pixel_size,
395
+ slicing_dict=slicing_dict,
396
+ )
397
+ return build_numpy_getter(
398
+ zarr_array=zarr_array,
399
+ dimensions=dimensions,
400
+ axes_order=axes_order,
401
+ transforms=transforms,
402
+ slicing_dict=input_slice_kwargs,
403
+ remove_channel_selection=remove_channel_selection,
404
+ )
405
+
406
+
407
+ def build_roi_numpy_setter(
408
+ zarr_array: zarr.Array,
409
+ dimensions: Dimensions,
410
+ roi: Roi | RoiPixels,
411
+ pixel_size: PixelSize | None = None,
412
+ axes_order: Sequence[str] | None = None,
413
+ transforms: Sequence[TransformProtocol] | None = None,
414
+ slicing_dict: dict[str, SlicingInputType] | None = None,
415
+ ) -> Callable[[np.ndarray], None]:
416
+ """Prepare slice kwargs for setting an array."""
417
+ input_slice_kwargs = roi_to_slicing_dict(
418
+ roi=roi,
419
+ dimensions=dimensions,
420
+ pixel_size=pixel_size,
421
+ slicing_dict=slicing_dict,
422
+ )
423
+ return build_numpy_setter(
424
+ zarr_array=zarr_array,
425
+ dimensions=dimensions,
426
+ axes_order=axes_order,
427
+ transforms=transforms,
428
+ slicing_dict=input_slice_kwargs,
429
+ )
430
+
431
+
432
+ def build_roi_dask_getter(
433
+ zarr_array: zarr.Array,
434
+ dimensions: Dimensions,
435
+ roi: Roi | RoiPixels,
436
+ pixel_size: PixelSize | None = None,
437
+ axes_order: Sequence[str] | None = None,
438
+ transforms: Sequence[TransformProtocol] | None = None,
439
+ slicing_dict: dict[str, SlicingInputType] | None = None,
440
+ remove_channel_selection: bool = False,
441
+ ) -> Callable[[], da.Array]:
442
+ """Prepare slice kwargs for getting an array."""
443
+ input_slice_kwargs = roi_to_slicing_dict(
444
+ roi=roi,
445
+ dimensions=dimensions,
446
+ pixel_size=pixel_size,
447
+ slicing_dict=slicing_dict,
448
+ )
449
+ return build_dask_getter(
450
+ zarr_array=zarr_array,
451
+ dimensions=dimensions,
452
+ axes_order=axes_order,
453
+ transforms=transforms,
454
+ slicing_dict=input_slice_kwargs,
455
+ remove_channel_selection=remove_channel_selection,
456
+ )
457
+
458
+
459
+ def build_roi_dask_setter(
460
+ zarr_array: zarr.Array,
461
+ dimensions: Dimensions,
462
+ roi: Roi | RoiPixels,
463
+ pixel_size: PixelSize | None = None,
464
+ axes_order: Sequence[str] | None = None,
465
+ transforms: Sequence[TransformProtocol] | None = None,
466
+ slicing_dict: dict[str, SlicingInputType] | None = None,
467
+ ) -> Callable[[da.Array], None]:
468
+ """Prepare slice kwargs for setting an array."""
469
+ input_slice_kwargs = roi_to_slicing_dict(
470
+ roi=roi,
471
+ dimensions=dimensions,
472
+ pixel_size=pixel_size,
473
+ slicing_dict=slicing_dict,
474
+ )
475
+ return build_dask_setter(
476
+ zarr_array=zarr_array,
477
+ dimensions=dimensions,
478
+ axes_order=axes_order,
479
+ transforms=transforms,
480
+ slicing_dict=input_slice_kwargs,
481
+ )
482
+
483
+
484
+ ################################################################
485
+ #
486
+ # Masked ROIs array pipes
487
+ #
488
+ ################################################################
489
+
490
+
491
+ def build_roi_masked_numpy_getter(
492
+ *,
493
+ roi: Roi | RoiPixels,
494
+ zarr_array: zarr.Array,
495
+ dimensions: Dimensions,
496
+ pixel_size: PixelSize | None = None,
497
+ label_zarr_array: zarr.Array,
498
+ label_dimensions: Dimensions,
499
+ label_pixel_size: PixelSize | None = None,
500
+ axes_order: Sequence[str] | None = None,
501
+ transforms: Sequence[TransformProtocol] | None = None,
502
+ label_transforms: Sequence[TransformProtocol] | None = None,
503
+ slicing_dict: dict[str, SlicingInputType] | None = None,
504
+ label_slicing_dict: dict[str, SlicingInputType] | None = None,
505
+ fill_value: int | float = 0,
506
+ allow_scaling: bool = True,
507
+ ) -> Callable[[], np.ndarray]:
508
+ """Prepare slice kwargs for getting a masked array."""
509
+ input_slice_kwargs = roi_to_slicing_dict(
510
+ roi=roi,
511
+ dimensions=dimensions,
512
+ pixel_size=pixel_size,
513
+ slicing_dict=slicing_dict,
514
+ )
515
+ label_slice_kwargs = roi_to_slicing_dict(
516
+ roi=roi,
517
+ dimensions=label_dimensions,
518
+ pixel_size=label_pixel_size,
519
+ slicing_dict=label_slicing_dict,
520
+ )
521
+ return build_masked_numpy_getter(
522
+ zarr_array=zarr_array,
523
+ dimensions=dimensions,
524
+ label_zarr_array=label_zarr_array,
525
+ label_dimensions=label_dimensions,
526
+ label_id=roi.label,
527
+ axes_order=axes_order,
528
+ transforms=transforms,
529
+ label_transforms=label_transforms,
530
+ slicing_dict=input_slice_kwargs,
531
+ label_slicing_dict=label_slice_kwargs,
532
+ fill_value=fill_value,
533
+ allow_scaling=allow_scaling,
534
+ )
535
+
536
+
537
+ def build_roi_masked_numpy_setter(
538
+ *,
539
+ roi: Roi | RoiPixels,
540
+ zarr_array: zarr.Array,
541
+ dimensions: Dimensions,
542
+ pixel_size: PixelSize | None = None,
543
+ label_zarr_array: zarr.Array,
544
+ label_dimensions: Dimensions,
545
+ label_pixel_size: PixelSize | None = None,
546
+ axes_order: Sequence[str] | None = None,
547
+ transforms: Sequence[TransformProtocol] | None = None,
548
+ label_transforms: Sequence[TransformProtocol] | None = None,
549
+ slicing_dict: dict[str, SlicingInputType] | None = None,
550
+ label_slicing_dict: dict[str, SlicingInputType] | None = None,
551
+ allow_scaling: bool = True,
552
+ ) -> Callable[[np.ndarray], None]:
553
+ """Prepare slice kwargs for setting a masked array."""
554
+ input_slice_kwargs = roi_to_slicing_dict(
555
+ roi=roi,
556
+ dimensions=dimensions,
557
+ pixel_size=pixel_size,
558
+ slicing_dict=slicing_dict,
559
+ )
560
+ label_slice_kwargs = roi_to_slicing_dict(
561
+ roi=roi,
562
+ dimensions=label_dimensions,
563
+ pixel_size=label_pixel_size,
564
+ slicing_dict=label_slicing_dict,
565
+ )
566
+ return build_masked_numpy_setter(
567
+ zarr_array=zarr_array,
568
+ dimensions=dimensions,
569
+ label_zarr_array=label_zarr_array,
570
+ label_dimensions=label_dimensions,
571
+ label_id=roi.label,
572
+ axes_order=axes_order,
573
+ transforms=transforms,
574
+ label_transforms=label_transforms,
575
+ slicing_dict=input_slice_kwargs,
576
+ label_slicing_dict=label_slice_kwargs,
577
+ allow_scaling=allow_scaling,
578
+ )
579
+
580
+
581
+ def build_roi_masked_dask_getter(
582
+ *,
583
+ roi: Roi | RoiPixels,
584
+ zarr_array: zarr.Array,
585
+ dimensions: Dimensions,
586
+ pixel_size: PixelSize | None = None,
587
+ label_zarr_array: zarr.Array,
588
+ label_dimensions: Dimensions,
589
+ label_pixel_size: PixelSize | None = None,
590
+ axes_order: Sequence[str] | None = None,
591
+ transforms: Sequence[TransformProtocol] | None = None,
592
+ label_transforms: Sequence[TransformProtocol] | None = None,
593
+ slicing_dict: dict[str, SlicingInputType] | None = None,
594
+ label_slicing_dict: dict[str, SlicingInputType] | None = None,
595
+ allow_scaling: bool = True,
596
+ ) -> Callable[[], da.Array]:
597
+ """Prepare slice kwargs for getting a masked array."""
598
+ input_slice_kwargs = roi_to_slicing_dict(
599
+ roi=roi,
600
+ dimensions=dimensions,
601
+ pixel_size=pixel_size,
602
+ slicing_dict=slicing_dict,
603
+ )
604
+ label_slice_kwargs = roi_to_slicing_dict(
605
+ roi=roi,
606
+ dimensions=label_dimensions,
607
+ pixel_size=label_pixel_size,
608
+ slicing_dict=label_slicing_dict,
609
+ )
610
+ return build_masked_dask_getter(
611
+ zarr_array=zarr_array,
612
+ dimensions=dimensions,
613
+ label_zarr_array=label_zarr_array,
614
+ label_dimensions=label_dimensions,
615
+ label_id=roi.label,
616
+ axes_order=axes_order,
617
+ transforms=transforms,
618
+ label_transforms=label_transforms,
619
+ slicing_dict=input_slice_kwargs,
620
+ label_slicing_dict=label_slice_kwargs,
621
+ allow_scaling=allow_scaling,
622
+ )
623
+
624
+
625
+ def build_roi_masked_dask_setter(
626
+ *,
627
+ roi: Roi | RoiPixels,
628
+ zarr_array: zarr.Array,
629
+ dimensions: Dimensions,
630
+ pixel_size: PixelSize | None = None,
631
+ label_zarr_array: zarr.Array,
632
+ label_dimensions: Dimensions,
633
+ label_pixel_size: PixelSize | None = None,
634
+ axes_order: Sequence[str] | None = None,
635
+ transforms: Sequence[TransformProtocol] | None = None,
636
+ label_transforms: Sequence[TransformProtocol] | None = None,
637
+ slicing_dict: dict[str, SlicingInputType] | None = None,
638
+ label_slicing_dict: dict[str, SlicingInputType] | None = None,
639
+ allow_scaling: bool = True,
640
+ ) -> Callable[[da.Array], None]:
641
+ """Prepare slice kwargs for setting a masked array."""
642
+ input_slice_kwargs = roi_to_slicing_dict(
643
+ roi=roi,
644
+ dimensions=dimensions,
645
+ pixel_size=pixel_size,
646
+ slicing_dict=slicing_dict,
647
+ )
648
+ label_slice_kwargs = roi_to_slicing_dict(
649
+ roi=roi,
650
+ dimensions=label_dimensions,
651
+ pixel_size=label_pixel_size,
652
+ slicing_dict=label_slicing_dict,
653
+ )
654
+ return build_masked_dask_setter(
655
+ zarr_array=zarr_array,
656
+ dimensions=dimensions,
657
+ label_zarr_array=label_zarr_array,
658
+ label_dimensions=label_dimensions,
659
+ label_id=roi.label,
660
+ axes_order=axes_order,
661
+ transforms=transforms,
662
+ label_transforms=label_transforms,
663
+ slicing_dict=input_slice_kwargs,
664
+ label_slicing_dict=label_slice_kwargs,
665
+ allow_scaling=allow_scaling,
666
+ )