ngio 0.4.0a3__py3-none-any.whl → 0.4.0b1__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 (54) hide show
  1. ngio/__init__.py +1 -2
  2. ngio/common/__init__.py +2 -51
  3. ngio/common/_dimensions.py +253 -74
  4. ngio/common/_pyramid.py +42 -23
  5. ngio/common/_roi.py +49 -413
  6. ngio/common/_zoom.py +32 -7
  7. ngio/experimental/iterators/__init__.py +0 -2
  8. ngio/experimental/iterators/_abstract_iterator.py +246 -26
  9. ngio/experimental/iterators/_feature.py +90 -52
  10. ngio/experimental/iterators/_image_processing.py +24 -63
  11. ngio/experimental/iterators/_mappers.py +48 -0
  12. ngio/experimental/iterators/_rois_utils.py +4 -4
  13. ngio/experimental/iterators/_segmentation.py +38 -85
  14. ngio/images/_abstract_image.py +192 -95
  15. ngio/images/_create.py +16 -0
  16. ngio/images/_create_synt_container.py +10 -0
  17. ngio/images/_image.py +35 -9
  18. ngio/images/_label.py +26 -3
  19. ngio/images/_masked_image.py +45 -61
  20. ngio/images/_ome_zarr_container.py +33 -0
  21. ngio/io_pipes/__init__.py +75 -0
  22. ngio/io_pipes/_io_pipes.py +361 -0
  23. ngio/io_pipes/_io_pipes_masked.py +488 -0
  24. ngio/io_pipes/_io_pipes_roi.py +152 -0
  25. ngio/io_pipes/_io_pipes_types.py +56 -0
  26. ngio/io_pipes/_match_shape.py +376 -0
  27. ngio/io_pipes/_ops_axes.py +344 -0
  28. ngio/io_pipes/_ops_slices.py +446 -0
  29. ngio/io_pipes/_ops_slices_utils.py +196 -0
  30. ngio/io_pipes/_ops_transforms.py +104 -0
  31. ngio/io_pipes/_zoom_transform.py +175 -0
  32. ngio/ome_zarr_meta/__init__.py +4 -2
  33. ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -4
  34. ngio/ome_zarr_meta/ngio_specs/_axes.py +129 -141
  35. ngio/ome_zarr_meta/ngio_specs/_dataset.py +47 -121
  36. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +30 -22
  37. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +17 -1
  38. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +33 -30
  39. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  40. ngio/resources/__init__.py +1 -0
  41. ngio/resources/resource_model.py +1 -0
  42. ngio/{common/transforms → transforms}/__init__.py +1 -1
  43. ngio/transforms/_zoom.py +19 -0
  44. ngio/utils/_datasets.py +5 -0
  45. ngio/utils/_zarr_utils.py +5 -1
  46. {ngio-0.4.0a3.dist-info → ngio-0.4.0b1.dist-info}/METADATA +1 -1
  47. ngio-0.4.0b1.dist-info/RECORD +85 -0
  48. ngio/common/_array_io_pipes.py +0 -554
  49. ngio/common/_array_io_utils.py +0 -508
  50. ngio/common/transforms/_label.py +0 -12
  51. ngio/common/transforms/_zoom.py +0 -109
  52. ngio-0.4.0a3.dist-info/RECORD +0 -76
  53. {ngio-0.4.0a3.dist-info → ngio-0.4.0b1.dist-info}/WHEEL +0 -0
  54. {ngio-0.4.0a3.dist-info → ngio-0.4.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -1,508 +0,0 @@
1
- from collections.abc import Mapping, Sequence
2
- from typing import Protocol, TypeAlias, TypeVar
3
-
4
- import dask.array as da
5
- import numpy as np
6
- import zarr
7
- from dask.array import Array as DaskArray
8
-
9
- from ngio.common._dimensions import Dimensions
10
- from ngio.ome_zarr_meta.ngio_specs import Axis, SlicingOps
11
- from ngio.utils import NgioValueError
12
-
13
- SlicingInputType: TypeAlias = slice | Sequence[int] | int | None
14
- SlicingType: TypeAlias = slice | tuple[int, ...] | int
15
- ArrayLike: TypeAlias = np.ndarray | DaskArray
16
-
17
- ##############################################################
18
- #
19
- # Slicing Operations
20
- #
21
- ##############################################################
22
-
23
-
24
- def _validate_int(value: int, shape: int) -> int:
25
- """Validate an integer value for slicing."""
26
- if not isinstance(value, int):
27
- raise NgioValueError(f"Invalid value {value} of type {type(value)}")
28
- if value < 0 or value >= shape:
29
- raise NgioValueError(
30
- f"Invalid value {value}. Index out of bounds for axis of shape {shape}"
31
- )
32
- return value
33
-
34
-
35
- def _try_to_slice(input: Sequence[int]) -> slice | tuple[int, ...]:
36
- """Try to convert a list of integers into a slice if they are contiguous.
37
-
38
- - If the input is empty, return an empty tuple.
39
- - If the input is sorted, and contains contiguous integers,
40
- return a slice from the minimum to the maximum integer.
41
- - Otherwise, return the input as a tuple.
42
-
43
- This is useful for optimizing array slicing operations
44
- by allowing the use of slices when possible, which can be more efficient.
45
- """
46
- if not input:
47
- return ()
48
-
49
- # If the input is not sorted, return it as a tuple
50
- max_input = max(input)
51
- min_input = min(input)
52
- assert min_input >= 0, "Input must contain non-negative integers"
53
- assert max_input >= 0, "Input must contain non-negative integers"
54
-
55
- if sorted(input) == list(range(min_input, max_input + 1)):
56
- return slice(min_input, max_input + 1)
57
-
58
- return tuple(input)
59
-
60
-
61
- def _validate_iter_of_ints(value: Sequence, shape: int) -> slice | tuple[int, ...]:
62
- value = [_validate_int(v, shape=shape) for v in value]
63
- return _try_to_slice(value)
64
-
65
-
66
- def _validate_slice(value: slice, shape: int) -> slice:
67
- """Validate a slice object and return it with adjusted start and stop."""
68
- start = value.start if value.start is not None else 0
69
- start = max(start, 0)
70
- stop = value.stop if value.stop is not None else shape
71
- return slice(start, stop)
72
-
73
-
74
- def _remove_channel_slicing(
75
- slicing_dict: dict[str, SlicingInputType],
76
- dimensions: Dimensions,
77
- ) -> dict[str, SlicingInputType]:
78
- """This utility function removes the channel selection from the slice kwargs.
79
-
80
- if ignore_channel_selection is True, it will remove the channel selection
81
- regardless of the dimensions. If the ignore_channel_selection is False
82
- it will fail.
83
- """
84
- if dimensions.is_multi_channels:
85
- return slicing_dict
86
-
87
- if "c" in slicing_dict:
88
- slicing_dict.pop("c", None)
89
- return slicing_dict
90
-
91
-
92
- def _check_slicing_virtual_axes(slice_: SlicingInputType) -> bool:
93
- """Check if the slice_ is compatible with virtual axes.
94
-
95
- Virtual axes are axes that are not present in the actual data,
96
- such as time or channel axes in some datasets.
97
- So the only valid slices for virtual axes are:
98
- - None: means all data along the axis
99
- - 0: means the first element along the axis
100
- - slice([0, None], [1, None])
101
- """
102
- if slice_ is None or slice_ == 0:
103
- return True
104
- if isinstance(slice_, slice):
105
- if slice_.start is None and slice_.stop is None:
106
- return True
107
- if slice_.start == 0 and slice_.stop is None:
108
- return True
109
- if slice_.start is None and slice_.stop == 0:
110
- return True
111
- if slice_.start == 0 and slice_.stop == 1:
112
- return True
113
- if isinstance(slice_, Sequence):
114
- if len(slice_) == 1 and slice_[0] == 0:
115
- return True
116
- return False
117
-
118
-
119
- def _normalize_slicing_dict(
120
- dimensions: Dimensions,
121
- slicing_dict: Mapping[str, SlicingInputType],
122
- remove_channel_selection: bool = False,
123
- ) -> dict[str, SlicingInputType]:
124
- """Convert slice kwargs to the on-disk axes names."""
125
- normalized_slicing_dict: dict[str, SlicingInputType] = {}
126
- for axis_name, slice_ in slicing_dict.items():
127
- axis = dimensions.axes_mapper.get_axis(axis_name)
128
- if axis is None:
129
- # Virtual axes should be allowed to be selected
130
- # Common use case is still allowing channel_selection
131
- # When the zarr has not channel axis.
132
- if not _check_slicing_virtual_axes(slice_):
133
- raise NgioValueError(
134
- f"Invalid axis selection:{axis_name}={slice_}. "
135
- f"Not found on the on-disk axes {dimensions.axes}."
136
- )
137
- # Virtual axes can be safely ignored
138
- continue
139
- on_disk_name = axis.on_disk_name
140
- if on_disk_name in normalized_slicing_dict:
141
- raise NgioValueError(
142
- f"Duplicate axis {on_disk_name} in slice kwargs. "
143
- "Please provide unique axis names."
144
- )
145
- normalized_slicing_dict[axis.on_disk_name] = slice_
146
-
147
- if remove_channel_selection:
148
- normalized_slicing_dict = _remove_channel_slicing(
149
- slicing_dict=normalized_slicing_dict, dimensions=dimensions
150
- )
151
- return normalized_slicing_dict
152
-
153
-
154
- def _normalize_axes_order(
155
- dimensions: Dimensions,
156
- axes_order: Sequence[str],
157
- ) -> list[str]:
158
- """Convert axes order to the on-disk axes names.
159
-
160
- In this way there is not unambiguity in the axes order.
161
- """
162
- new_axes_order = []
163
- for axis_name in axes_order:
164
- axis = dimensions.axes_mapper.get_axis(axis_name)
165
- if axis is None:
166
- new_axes_order.append(axis_name)
167
- else:
168
- new_axes_order.append(axis.on_disk_name)
169
- return new_axes_order
170
-
171
-
172
- def _normalize_slice_input(
173
- axis: Axis,
174
- slicing_dict: dict[str, SlicingInputType],
175
- dimensions: Dimensions,
176
- requires_axes_ops: bool,
177
- axes_order: list[str],
178
- ) -> SlicingType:
179
- """Normalize a slice input to a tuple of slices.
180
-
181
- Make sure that the slice is valid for the given axis and dimensions.
182
- And transform it to either a slice or a tuple of integers.
183
- If the axis is not present in the slicing_dict, return a full slice.
184
- """
185
- axis_name = axis.on_disk_name
186
- if axis_name not in slicing_dict:
187
- # If no slice is provided for the axis, use a full slice
188
- return slice(None)
189
-
190
- value = slicing_dict[axis_name]
191
- if value is None:
192
- return slice(None)
193
-
194
- shape = dimensions.get(axis_name, default=None)
195
- if shape is None:
196
- raise NgioValueError(f"Unknown dimension {axis_name} for axis {axis}.")
197
-
198
- if isinstance(value, int):
199
- value = _validate_int(value, shape)
200
- if requires_axes_ops or axis_name in axes_order:
201
- # Axes ops require all dimensions to be preserved
202
- value = slice(value, value + 1)
203
- return value
204
- elif isinstance(value, Sequence):
205
- return _validate_iter_of_ints(value, shape)
206
- elif isinstance(value, slice):
207
- return _validate_slice(value, shape)
208
-
209
- raise NgioValueError(f"Invalid slice definition {value} of type {type(value)}")
210
-
211
-
212
- def _build_slicing_tuple(
213
- *,
214
- dimensions: Dimensions,
215
- slicing_dict: dict[str, SlicingInputType],
216
- axes_order: list[str] | None = None,
217
- requires_axes_ops: bool = False,
218
- remove_channel_selection: bool = False,
219
- ) -> tuple[SlicingType, ...] | None:
220
- """Assemble slices to be used to query the array."""
221
- if len(slicing_dict) == 0:
222
- # Skip unnecessary computation if no slicing is requested
223
- return None
224
- _axes_order = (
225
- _normalize_axes_order(dimensions=dimensions, axes_order=axes_order)
226
- if axes_order is not None
227
- else []
228
- )
229
- _slicing_dict = _normalize_slicing_dict(
230
- dimensions=dimensions,
231
- slicing_dict=slicing_dict,
232
- remove_channel_selection=remove_channel_selection,
233
- )
234
-
235
- slicing_tuple = tuple(
236
- _normalize_slice_input(
237
- axis=axis,
238
- slicing_dict=_slicing_dict,
239
- dimensions=dimensions,
240
- requires_axes_ops=requires_axes_ops,
241
- axes_order=_axes_order,
242
- )
243
- for axis in dimensions.axes_mapper.axes
244
- )
245
- return slicing_tuple
246
-
247
-
248
- def get_slice_as_numpy(
249
- zarr_array: zarr.Array, slice_tuple: tuple[SlicingType, ...] | None
250
- ) -> np.ndarray:
251
- if slice_tuple is None:
252
- return zarr_array[...]
253
-
254
- if all(not isinstance(s, tuple) for s in slice_tuple):
255
- return zarr_array[slice_tuple]
256
-
257
- # If there are tuple[int, ...] we need to handle them separately
258
- # this is a workaround for the fact that zarr does not support
259
- # non-contiguous slicing with tuples/lists.
260
- first_slice_tuple = []
261
- for s in slice_tuple:
262
- if isinstance(s, tuple):
263
- first_slice_tuple.append(slice(None))
264
- else:
265
- first_slice_tuple.append(s)
266
- second_slice_tuple = []
267
- for s in slice_tuple:
268
- if isinstance(s, tuple):
269
- second_slice_tuple.append(s)
270
- else:
271
- second_slice_tuple.append(slice(None))
272
-
273
- return zarr_array[tuple(first_slice_tuple)][tuple(second_slice_tuple)]
274
-
275
-
276
- def get_slice_as_dask(
277
- zarr_array: zarr.Array, slice_tuple: tuple[SlicingType, ...] | None
278
- ) -> da.Array:
279
- da_array = da.from_zarr(zarr_array)
280
- if slice_tuple is None:
281
- return da_array
282
-
283
- if any(isinstance(s, tuple) for s in slice_tuple):
284
- raise NotImplementedError(
285
- "Slicing with non-contiguous tuples/lists "
286
- "is not supported yet for Dask arrays. Use the "
287
- "numpy api to get the correct array slice."
288
- )
289
- return da_array[slice_tuple]
290
-
291
-
292
- def set_numpy_patch(
293
- zarr_array: zarr.Array,
294
- patch: np.ndarray,
295
- slice_tuple: tuple[SlicingType, ...] | None,
296
- ) -> None:
297
- if slice_tuple is None:
298
- zarr_array[...] = patch
299
- return
300
- zarr_array[slice_tuple] = patch
301
-
302
-
303
- def set_dask_patch(
304
- zarr_array: zarr.Array, patch: da.Array, slice_tuple: tuple[SlicingType, ...] | None
305
- ) -> None:
306
- da.to_zarr(arr=patch, url=zarr_array, region=slice_tuple)
307
-
308
-
309
- ##############################################################
310
- #
311
- # Array Axes Operations
312
- #
313
- ##############################################################
314
-
315
-
316
- def apply_numpy_axes_ops(
317
- array: np.ndarray,
318
- squeeze_axes: tuple[int, ...] | None = None,
319
- transpose_axes: tuple[int, ...] | None = None,
320
- expand_axes: tuple[int, ...] | None = None,
321
- ) -> np.ndarray:
322
- """Apply axes operations to a numpy array."""
323
- if squeeze_axes is not None:
324
- array = np.squeeze(array, axis=squeeze_axes)
325
- if transpose_axes is not None:
326
- array = np.transpose(array, axes=transpose_axes)
327
- if expand_axes is not None:
328
- array = np.expand_dims(array, axis=expand_axes)
329
- return array
330
-
331
-
332
- def apply_dask_axes_ops(
333
- array: da.Array,
334
- squeeze_axes: tuple[int, ...] | None = None,
335
- transpose_axes: tuple[int, ...] | None = None,
336
- expand_axes: tuple[int, ...] | None = None,
337
- ) -> da.Array:
338
- """Apply axes operations to a dask array."""
339
- if squeeze_axes is not None:
340
- array = da.squeeze(array, axis=squeeze_axes)
341
- if transpose_axes is not None:
342
- array = da.transpose(array, axes=transpose_axes)
343
- if expand_axes is not None:
344
- array = da.expand_dims(array, axis=expand_axes)
345
- return array
346
-
347
-
348
- T = TypeVar("T")
349
-
350
-
351
- def apply_sequence_axes_ops(
352
- input_: Sequence[T],
353
- default: T,
354
- squeeze_axes: tuple[int, ...] | None = None,
355
- transpose_axes: tuple[int, ...] | None = None,
356
- expand_axes: tuple[int, ...] | None = None,
357
- ) -> list[T]:
358
- input_list = list(input_)
359
- if squeeze_axes is not None:
360
- for offset, ax in enumerate(squeeze_axes):
361
- input_list.pop(ax - offset)
362
-
363
- if transpose_axes is not None:
364
- input_list = [input_list[i] for i in transpose_axes]
365
-
366
- if expand_axes is not None:
367
- for ax in expand_axes:
368
- input_list.insert(ax, default)
369
-
370
- return input_list
371
-
372
-
373
- #############################################################
374
- #
375
- # Transform Protocol
376
- #
377
- #############################################################
378
-
379
-
380
- class TransformProtocol(Protocol):
381
- """Protocol for a generic transform."""
382
-
383
- def apply_numpy_transform(
384
- self, array: np.ndarray, slicing_ops: SlicingOps
385
- ) -> np.ndarray:
386
- """A transformation to be applied after loading a numpy array."""
387
- ...
388
-
389
- def apply_dask_transform(
390
- self, array: da.Array, slicing_ops: SlicingOps
391
- ) -> da.Array:
392
- """A transformation to be applied after loading a dask array."""
393
- ...
394
-
395
- def apply_inverse_numpy_transform(
396
- self, array: np.ndarray, slicing_ops: SlicingOps
397
- ) -> np.ndarray:
398
- """A transformation to be applied before writing a numpy array."""
399
- ...
400
-
401
- def apply_inverse_dask_transform(
402
- self, array: da.Array, slicing_ops: SlicingOps
403
- ) -> da.Array:
404
- """A transformation to be applied before writing a dask array."""
405
- ...
406
-
407
-
408
- def apply_numpy_transforms(
409
- array: np.ndarray,
410
- slicing_ops: SlicingOps,
411
- transforms: Sequence[TransformProtocol] | None = None,
412
- ) -> np.ndarray:
413
- """Apply a numpy transform to an array."""
414
- if transforms is None:
415
- return array
416
-
417
- for transform in transforms:
418
- array = transform.apply_numpy_transform(array, slicing_ops=slicing_ops)
419
- return array
420
-
421
-
422
- def apply_dask_transforms(
423
- array: da.Array,
424
- slicing_ops: SlicingOps,
425
- transforms: Sequence[TransformProtocol] | None = None,
426
- ) -> da.Array:
427
- """Apply a dask transform to an array."""
428
- if transforms is None:
429
- return array
430
-
431
- for transform in transforms:
432
- array = transform.apply_dask_transform(array, slicing_ops=slicing_ops)
433
- return array
434
-
435
-
436
- def apply_inverse_numpy_transforms(
437
- array: np.ndarray,
438
- slicing_ops: SlicingOps,
439
- transforms: Sequence[TransformProtocol] | None = None,
440
- ) -> np.ndarray:
441
- """Apply inverse numpy transforms to an array."""
442
- if transforms is None:
443
- return array
444
-
445
- for transform in transforms:
446
- array = transform.apply_inverse_numpy_transform(array, slicing_ops=slicing_ops)
447
- return array
448
-
449
-
450
- def apply_inverse_dask_transforms(
451
- array: da.Array,
452
- slicing_ops: SlicingOps,
453
- transforms: Sequence[TransformProtocol] | None = None,
454
- ) -> da.Array:
455
- """Apply inverse dask transforms to an array."""
456
- if transforms is None:
457
- return array
458
-
459
- for transform in transforms:
460
- array = transform.apply_inverse_dask_transform(array, slicing_ops=slicing_ops)
461
- return array
462
-
463
-
464
- def setup_from_disk_pipe(
465
- *,
466
- dimensions: Dimensions,
467
- slicing_dict: dict[str, SlicingInputType],
468
- axes_order: Sequence[str] | None = None,
469
- remove_channel_selection: bool = False,
470
- ) -> SlicingOps:
471
- if axes_order is not None:
472
- axes_order = _normalize_axes_order(dimensions=dimensions, axes_order=axes_order)
473
- slicing_ops = dimensions.axes_mapper.to_order(axes_order)
474
- else:
475
- slicing_ops = SlicingOps()
476
-
477
- slicing_tuple = _build_slicing_tuple(
478
- dimensions=dimensions,
479
- slicing_dict=slicing_dict,
480
- axes_order=axes_order,
481
- requires_axes_ops=slicing_ops.requires_axes_ops,
482
- remove_channel_selection=remove_channel_selection,
483
- )
484
- slicing_ops.slice_tuple = slicing_tuple
485
- return slicing_ops
486
-
487
-
488
- def setup_to_disk_pipe(
489
- *,
490
- dimensions: Dimensions,
491
- slicing_dict: dict[str, SlicingInputType],
492
- axes_order: Sequence[str] | None = None,
493
- remove_channel_selection: bool = False,
494
- ) -> SlicingOps:
495
- if axes_order is not None:
496
- axes_order = _normalize_axes_order(dimensions=dimensions, axes_order=axes_order)
497
- slicing_ops = dimensions.axes_mapper.from_order(axes_order)
498
- else:
499
- slicing_ops = SlicingOps()
500
-
501
- slicing_tuple = _build_slicing_tuple(
502
- dimensions=dimensions,
503
- slicing_dict=slicing_dict,
504
- requires_axes_ops=slicing_ops.requires_axes_ops,
505
- remove_channel_selection=remove_channel_selection,
506
- )
507
- slicing_ops.slice_tuple = slicing_tuple
508
- return slicing_ops
@@ -1,12 +0,0 @@
1
- """Transforms and pre-post-processors for label images."""
2
-
3
- # def make_unique_label_np(x: np.ndarray, p: int, n: int) -> np.ndarray:
4
- # """Make a unique label for the patch."""
5
- # x = np.where(x > 0, (1 + p - n) + x * n, 0)
6
- # return x
7
- #
8
- #
9
- # def make_unique_label_da(x: da.Array, p: int, n: int) -> da.Array:
10
- # """Make a unique label for the patch."""
11
- # x = da.where(x > 0, (1 + p - n) + x * n, 0)
12
- # return x
@@ -1,109 +0,0 @@
1
- from collections.abc import Sequence
2
- from typing import Literal
3
-
4
- import dask.array as da
5
- import numpy as np
6
-
7
- from ngio.common._array_io_utils import apply_sequence_axes_ops
8
- from ngio.common._dimensions import Dimensions
9
- from ngio.common._zoom import dask_zoom, numpy_zoom
10
- from ngio.ome_zarr_meta.ngio_specs import SlicingOps
11
-
12
-
13
- class ZoomTransform:
14
- def __init__(self, scale: Sequence[float], order: Literal[0, 1, 2]):
15
- self._scale = tuple(scale)
16
- self._order: Literal[0, 1, 2] = order
17
-
18
- @property
19
- def scale(self) -> tuple[float, ...]:
20
- return self._scale
21
-
22
- @property
23
- def inv_scale(self) -> tuple[float, ...]:
24
- return tuple([1 / s for s in self._scale])
25
-
26
- @classmethod
27
- def from_dimensions(
28
- cls,
29
- original_dimension: Dimensions,
30
- target_dimension: Dimensions,
31
- order: Literal[0, 1, 2],
32
- ):
33
- scale = []
34
- for o_ax_name in original_dimension.axes_mapper.axes_names:
35
- t_ax = target_dimension.axes_mapper.get_axis(name=o_ax_name)
36
- if t_ax is None:
37
- _scale = 1
38
- else:
39
- t_shape = target_dimension.get(o_ax_name)
40
- o_shape = original_dimension.get(o_ax_name)
41
- assert t_shape is not None and o_shape is not None
42
- _scale = t_shape / o_shape
43
- scale.append(_scale)
44
-
45
- return cls(scale, order)
46
-
47
- def apply_numpy_transform(
48
- self, array: np.ndarray, slicing_ops: SlicingOps
49
- ) -> np.ndarray:
50
- """Apply the scaling transformation to a numpy array."""
51
- scale = tuple(
52
- apply_sequence_axes_ops(
53
- self.scale,
54
- default=1,
55
- squeeze_axes=slicing_ops.squeeze_axes,
56
- transpose_axes=slicing_ops.transpose_axes,
57
- expand_axes=slicing_ops.expand_axes,
58
- )
59
- )
60
- array = numpy_zoom(source_array=array, scale=scale, order=self._order)
61
- return array
62
-
63
- def apply_dask_transform(
64
- self, array: da.Array, slicing_ops: SlicingOps
65
- ) -> da.Array:
66
- """Apply the scaling transformation to a dask array."""
67
- scale = tuple(
68
- apply_sequence_axes_ops(
69
- self.scale,
70
- default=1,
71
- squeeze_axes=slicing_ops.squeeze_axes,
72
- transpose_axes=slicing_ops.transpose_axes,
73
- expand_axes=slicing_ops.expand_axes,
74
- )
75
- )
76
- array = dask_zoom(source_array=array, scale=scale, order=self._order)
77
- return array
78
-
79
- def apply_inverse_numpy_transform(
80
- self, array: np.ndarray, slicing_ops: SlicingOps
81
- ) -> np.ndarray:
82
- """Apply the inverse scaling transformation to a numpy array."""
83
- scale = tuple(
84
- apply_sequence_axes_ops(
85
- self.inv_scale,
86
- default=1,
87
- squeeze_axes=slicing_ops.squeeze_axes,
88
- transpose_axes=slicing_ops.transpose_axes,
89
- expand_axes=slicing_ops.expand_axes,
90
- )
91
- )
92
- array = numpy_zoom(source_array=array, scale=scale, order=self._order)
93
- return array
94
-
95
- def apply_inverse_dask_transform(
96
- self, array: da.Array, slicing_ops: SlicingOps
97
- ) -> da.Array:
98
- """Apply the inverse scaling transformation to a dask array."""
99
- scale = tuple(
100
- apply_sequence_axes_ops(
101
- self.inv_scale,
102
- default=1,
103
- squeeze_axes=slicing_ops.squeeze_axes,
104
- transpose_axes=slicing_ops.transpose_axes,
105
- expand_axes=slicing_ops.expand_axes,
106
- )
107
- )
108
- array = dask_zoom(source_array=array, scale=scale, order=self._order)
109
- return array