Skip to content

ngio.common API documentation

ngio.common

Common classes and functions that are used across the package.

ArrayLike module-attribute

ArrayLike: TypeAlias = ndarray | Array | Delayed

Dimensions

Dimensions(shape: tuple[int, ...], axes_mapper: AxesMapper)

Dimension metadata.

Create a Dimension object from a Zarr array.

Parameters:

  • shape (tuple[int, ...]) –

    The shape of the Zarr array.

  • axes_mapper (AxesMapper) –

    The axes mapper object.

Source code in ngio/common/_dimensions.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def __init__(
    self,
    shape: tuple[int, ...],
    axes_mapper: AxesMapper,
) -> None:
    """Create a Dimension object from a Zarr array.

    Args:
        shape: The shape of the Zarr array.
        axes_mapper: The axes mapper object.
    """
    self._shape = shape
    self._axes_mapper = axes_mapper

    if len(self._shape) != len(self._axes_mapper.on_disk_axes):
        raise NgioValidationError(
            "The number of dimensions must match the number of axes. "
            f"Expected Axis {self._axes_mapper.on_disk_axes_names} but got shape "
            f"{self._shape}."
        )
axes_mapper property
axes_mapper: AxesMapper

Return the axes mapper object.

on_disk_shape property
on_disk_shape: tuple[int, ...]

Return the shape as a tuple.

is_time_series property
is_time_series: bool

Return whether the data is a time series.

is_2d property
is_2d: bool

Return whether the data is 2D.

is_2d_time_series property
is_2d_time_series: bool

Return whether the data is a 2D time series.

is_3d property
is_3d: bool

Return whether the data is 3D.

is_3d_time_series property
is_3d_time_series: bool

Return whether the data is a 3D time series.

is_multi_channels property
is_multi_channels: bool

Return whether the data has multiple channels.

get
get(axis_name: str, default: int | None = None) -> int

Return the dimension of the given axis name.

Parameters:

  • axis_name (str) –

    The name of the axis (either canonical or non-canonical).

  • default (int | None, default: None ) –

    The default value to return if the axis does not exist. If None, an error is raised.

Source code in ngio/common/_dimensions.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def get(self, axis_name: str, default: int | None = None) -> int:
    """Return the dimension of the given axis name.

    Args:
        axis_name: The name of the axis (either canonical or non-canonical).
        default: The default value to return if the axis does not exist. If None,
            an error is raised.
    """
    index = self._axes_mapper.get_index(axis_name)
    if index is None:
        if default is not None:
            return default
        raise NgioValueError(f"Axis {axis_name} does not exist.")

    return self._shape[index]
has_axis
has_axis(axis_name: str) -> bool

Return whether the axis exists.

Source code in ngio/common/_dimensions.py
59
60
61
62
63
64
def has_axis(self, axis_name: str) -> bool:
    """Return whether the axis exists."""
    index = self._axes_mapper.get_axis(axis_name)
    if index is None:
        return False
    return True

AbstractTransform

Abstract base class for a generic transform.

transform_numpy
transform_numpy(array: ndarray) -> ndarray

Transform a numpy array.

Source code in ngio/common/_io_transforms.py
12
13
14
def transform_numpy(self, array: np.ndarray) -> np.ndarray:
    """Transform a numpy array."""
    raise NotImplementedError("Subclasses should implement this method.")
transform_dask
transform_dask(array: Array) -> Array

Transform a dask array.

Source code in ngio/common/_io_transforms.py
16
17
18
def transform_dask(self, array: da.Array) -> da.Array:
    """Transform a dask array."""
    raise NotImplementedError("Subclasses should implement this method.")
transform_delayed
transform_delayed(array: Delayed) -> Delayed

Transform a delayed dask array.

Source code in ngio/common/_io_transforms.py
20
21
22
def transform_delayed(self, array: Delayed) -> Delayed:
    """Transform a delayed dask array."""
    return delayed(self.transform_numpy)(array)

TransformProtocol

Bases: Protocol

Protocol numpy, dask, or delayed array transforms.

transform_numpy
transform_numpy(array: ndarray) -> ndarray

Transform a numpy array.

Source code in ngio/common/_io_transforms.py
28
29
30
def transform_numpy(self, array: np.ndarray) -> np.ndarray:
    """Transform a numpy array."""
    ...
transform_dask
transform_dask(array: Array) -> Array

Transform a dask array.

Source code in ngio/common/_io_transforms.py
32
33
34
def transform_dask(self, array: da.Array) -> da.Array:
    """Transform a dask array."""
    ...
transform_delayed
transform_delayed(array: Delayed) -> Delayed

Transform a delayed dask array.

Source code in ngio/common/_io_transforms.py
36
37
38
def transform_delayed(self, array: Delayed) -> Delayed:
    """Transform a delayed dask array."""
    ...

Roi

Bases: BaseModel

Region of interest (ROI) metadata.

name instance-attribute
name: str
x_length instance-attribute
x_length: float
y_length instance-attribute
y_length: float
z_length class-attribute instance-attribute
z_length: float = 1.0
t_length class-attribute instance-attribute
t_length: float = 1.0
x class-attribute instance-attribute
x: float = 0.0
y class-attribute instance-attribute
y: float = 0.0
z class-attribute instance-attribute
z: float = 0.0
t class-attribute instance-attribute
t: float = 0.0
unit class-attribute instance-attribute
unit: SpaceUnits | str | None = Field(
    DefaultSpaceUnit, repr=False
)
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
to_pixel_roi
to_pixel_roi(
    pixel_size: PixelSize, dimensions: Dimensions
) -> RoiPixels

Convert to raster coordinates.

Source code in ngio/common/_roi.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def to_pixel_roi(
    self, pixel_size: PixelSize, dimensions: Dimensions
) -> "RoiPixels":
    """Convert to raster coordinates."""
    dim_x = dimensions.get("x")
    dim_y = dimensions.get("y")
    # Will default to 1 if z does not exist
    dim_z = dimensions.get("z", default=1)
    dim_t = dimensions.get("t", default=1)
    extra_dict = self.model_extra if self.model_extra else {}

    return RoiPixels(
        name=self.name,
        x=_to_raster(self.x, pixel_size.x, dim_x),
        y=_to_raster(self.y, pixel_size.y, dim_y),
        z=_to_raster(self.z, pixel_size.z, dim_z),
        t=_to_raster(self.t, pixel_size.t, dim_t),
        x_length=_to_raster(self.x_length, pixel_size.x, dim_x),
        y_length=_to_raster(self.y_length, pixel_size.y, dim_y),
        z_length=_to_raster(self.z_length, pixel_size.z, dim_z),
        t_length=_to_raster(self.t_length, pixel_size.t, dim_t),
        **extra_dict,
    )
zoom
zoom(zoom_factor: float = 1) -> Roi

Zoom the ROI by a factor.

Parameters:

  • zoom_factor (float, default: 1 ) –

    The zoom factor. If the zoom factor is less than 1 the ROI will be zoomed in. If the zoom factor is greater than 1 the ROI will be zoomed out. If the zoom factor is 1 the ROI will not be changed.

Source code in ngio/common/_roi.py
69
70
71
72
73
74
75
76
77
78
def zoom(self, zoom_factor: float = 1) -> "Roi":
    """Zoom the ROI by a factor.

    Args:
        zoom_factor: The zoom factor. If the zoom factor
            is less than 1 the ROI will be zoomed in.
            If the zoom factor is greater than 1 the ROI will be zoomed out.
            If the zoom factor is 1 the ROI will not be changed.
    """
    return zoom_roi(self, zoom_factor)

RoiPixels

Bases: BaseModel

Region of interest (ROI) metadata.

name instance-attribute
name: str
x_length instance-attribute
x_length: int
y_length instance-attribute
y_length: int
z_length class-attribute instance-attribute
z_length: int = 1
t_length class-attribute instance-attribute
t_length: int = 1
x class-attribute instance-attribute
x: int = 0
y class-attribute instance-attribute
y: int = 0
z class-attribute instance-attribute
z: int = 0
t class-attribute instance-attribute
t: int = 0
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='allow')
to_roi
to_roi(pixel_size: PixelSize) -> Roi

Convert to world coordinates.

Source code in ngio/common/_roi.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def to_roi(self, pixel_size: PixelSize) -> Roi:
    """Convert to world coordinates."""
    extra_dict = self.model_extra if self.model_extra else {}
    return Roi(
        name=self.name,
        x=_to_world(self.x, pixel_size.x),
        y=_to_world(self.y, pixel_size.y),
        z=_to_world(self.z, pixel_size.z),
        t=_to_world(self.t, pixel_size.t),
        x_length=_to_world(self.x_length, pixel_size.x),
        y_length=_to_world(self.y_length, pixel_size.y),
        z_length=_to_world(self.z_length, pixel_size.z),
        t_length=_to_world(self.t_length, pixel_size.t),
        unit=pixel_size.space_unit,
        **extra_dict,
    )
to_slices
to_slices() -> dict[str, slice]

Return the slices for the ROI.

Source code in ngio/common/_roi.py
112
113
114
115
116
117
118
119
def to_slices(self) -> dict[str, slice]:
    """Return the slices for the ROI."""
    return {
        "x": slice(self.x, self.x + self.x_length),
        "y": slice(self.y, self.y + self.y_length),
        "z": slice(self.z, self.z + self.z_length),
        "t": slice(self.t, self.t + self.t_length),
    }

get_as_dask

get_as_dask(
    array: Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> Array
Source code in ngio/common/_array_io_pipe.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def get_as_dask(
    array: zarr.Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> DaskArray:
    slices, axes_ops = _setup_from_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    return _dask_get_pipe(
        array=array, slices=slices, axes_ops=axes_ops, transforms=transforms
    )

get_as_delayed

get_as_delayed(
    array: Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> Delayed
Source code in ngio/common/_array_io_pipe.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def get_as_delayed(
    array: zarr.Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> Delayed:
    slices, axes_ops = _setup_from_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    return _delayed_numpy_get_pipe(
        array=array, slices=slices, axes_ops=axes_ops, transforms=transforms
    )

get_as_numpy

get_as_numpy(
    array: Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> ndarray
Source code in ngio/common/_array_io_pipe.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def get_as_numpy(
    array: zarr.Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> np.ndarray:
    slices, axes_ops = _setup_from_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    return _numpy_get_pipe(
        array=array, slices=slices, axes_ops=axes_ops, transforms=transforms
    )

get_masked_as_dask

get_masked_as_dask(
    array: Array,
    label_array: Array,
    label: int,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> Array
Source code in ngio/common/_array_io_pipe.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def get_masked_as_dask(
    array: zarr.Array,
    label_array: zarr.Array,
    label: int,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> DaskArray:
    array_patch, mask = _mask_pipe_common_dask(
        array=array,
        label_array=label_array,
        label=label,
        dimensions_array=dimensions_array,
        dimensions_label=dimensions_label,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )
    array_patch = da.where(mask, array_patch, 0)
    return array_patch

get_masked_as_numpy

get_masked_as_numpy(
    array: Array,
    label_array: Array,
    label: int,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> ndarray
Source code in ngio/common/_array_io_pipe.py
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
def get_masked_as_numpy(
    array: zarr.Array,
    label_array: zarr.Array,
    label: int,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> np.ndarray:
    array_patch, mask = _mask_pipe_common_numpy(
        array=array,
        label_array=label_array,
        label=label,
        dimensions_array=dimensions_array,
        dimensions_label=dimensions_label,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )
    array_patch[~mask] = 0
    return array_patch

set_dask

set_dask(
    array: Array,
    patch: Array,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
)
Source code in ngio/common/_array_io_pipe.py
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
def set_dask(
    array: zarr.Array,
    patch: DaskArray,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
):
    slices, axes_ops = _setup_to_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    _dask_set_pipe(
        array=array,
        patch=patch,
        slices=slices,
        axes_ops=axes_ops,
        transforms=transforms,
    )

set_dask_masked

set_dask_masked(
    array: Array,
    label_array: Array,
    label: int,
    patch: Array,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
)
Source code in ngio/common/_array_io_pipe.py
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
def set_dask_masked(
    array: zarr.Array,
    label_array: zarr.Array,
    label: int,
    patch: DaskArray,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
):
    array_patch, mask = _mask_pipe_common_dask(
        array=array,
        label_array=label_array,
        label=label,
        dimensions_array=dimensions_array,
        dimensions_label=dimensions_label,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )
    _patch = da.where(mask, patch, array_patch)

    set_dask(
        array,
        _patch,
        dimensions=dimensions_array,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )

set_delayed

set_delayed(
    array: Array,
    patch: ndarray | Delayed,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
)
Source code in ngio/common/_array_io_pipe.py
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def set_delayed(
    array: zarr.Array,
    patch: np.ndarray | Delayed,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
):
    slices, axes_ops = _setup_to_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    _delayed_numpy_set_pipe(
        array=array,
        patch=patch,
        slices=slices,
        axes_ops=axes_ops,
        transforms=transforms,
    )

set_numpy

set_numpy(
    array: Array,
    patch: ndarray,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
)
Source code in ngio/common/_array_io_pipe.py
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
def set_numpy(
    array: zarr.Array,
    patch: np.ndarray,
    *,
    dimensions: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
):
    slices, axes_ops = _setup_to_disk_pipe(
        dimensions=dimensions, axes_order=axes_order, **slice_kwargs
    )
    _numpy_set_pipe(
        array=array,
        patch=patch,
        slices=slices,
        axes_ops=axes_ops,
        transforms=transforms,
    )

set_numpy_masked

set_numpy_masked(
    array: Array,
    label_array: Array,
    label: int,
    patch: ndarray,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
)
Source code in ngio/common/_array_io_pipe.py
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
def set_numpy_masked(
    array: zarr.Array,
    label_array: zarr.Array,
    label: int,
    patch: np.ndarray,
    *,
    dimensions_array: Dimensions,
    dimensions_label: Dimensions,
    axes_order: Collection[str] | None = None,
    transforms: Collection[TransformProtocol] | None = None,
    **slice_kwargs: slice | int | Iterable[int],
):
    array_patch, mask = _mask_pipe_common_numpy(
        array=array,
        label_array=label_array,
        label=label,
        dimensions_array=dimensions_array,
        dimensions_label=dimensions_label,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )
    _patch = np.where(mask, patch, array_patch)

    set_numpy(
        array,
        _patch,
        dimensions=dimensions_array,
        axes_order=axes_order,
        transforms=transforms,
        **slice_kwargs,
    )

compute_masking_roi

compute_masking_roi(
    segmentation: ndarray | Array, pixel_size: PixelSize
) -> list[Roi]

Compute a ROIs for each label in a segmentation.

This function expects a 2D or 3D segmentation array. And this function expects the axes order to be 'zyx' or 'yx'. Other axes orders are not supported.

Source code in ngio/common/_masking_roi.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def compute_masking_roi(
    segmentation: np.ndarray | da.Array, pixel_size: PixelSize
) -> list[Roi]:
    """Compute a ROIs for each label in a segmentation.

    This function expects a 2D or 3D segmentation array.
    And this function expects the axes order to be 'zyx' or 'yx'.
    Other axes orders are not supported.

    """
    if segmentation.ndim not in [2, 3]:
        raise NgioValueError("Only 2D and 3D segmentations are supported.")

    if isinstance(segmentation, da.Array):
        slices = lazy_compute_slices(segmentation)
    else:
        slices = compute_slices(segmentation)

    rois = []
    for label, slice_ in slices.items():
        if len(slice_) == 2:
            min_z, min_y, min_x = 0, slice_[0].start, slice_[1].start
            max_z, max_y, max_x = 1, slice_[0].stop, slice_[1].stop
        elif len(slice_) == 3:
            min_z, min_y, min_x = slice_[0].start, slice_[1].start, slice_[2].start
            max_z, max_y, max_x = slice_[0].stop, slice_[1].stop, slice_[2].stop
        else:
            raise ValueError("Invalid slice length.")
        roi = RoiPixels(
            name=str(label),
            x_length=max_x - min_x,
            y_length=max_y - min_y,
            z_length=max_z - min_z,
            x=min_x,
            y=min_y,
            z=min_z,
        )

        roi = roi.to_roi(pixel_size)
        rois.append(roi)
    return rois

consolidate_pyramid

consolidate_pyramid(
    source: Array,
    targets: list[Array],
    order: Literal[0, 1, 2] = 1,
    mode: Literal["dask", "numpy", "coarsen"] = "dask",
) -> None

Consolidate the Zarr array.

Source code in ngio/common/_pyramid.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def consolidate_pyramid(
    source: zarr.Array,
    targets: list[zarr.Array],
    order: Literal[0, 1, 2] = 1,
    mode: Literal["dask", "numpy", "coarsen"] = "dask",
) -> None:
    """Consolidate the Zarr array."""
    processed = [source]
    to_be_processed = targets

    while to_be_processed:
        source_id, target_id = _find_closest_arrays(processed, to_be_processed)

        source_image = processed[source_id]
        target_image = to_be_processed.pop(target_id)

        on_disk_zoom(
            source=source_image,
            target=target_image,
            mode=mode,
            order=order,
        )
        processed.append(target_image)

init_empty_pyramid

init_empty_pyramid(
    store: StoreOrGroup,
    paths: list[str],
    ref_shape: Collection[int],
    scaling_factors: Collection[float],
    chunks: Collection[int] | None = None,
    dtype: str = "uint16",
    mode: AccessModeLiteral = "a",
) -> None
Source code in ngio/common/_pyramid.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def init_empty_pyramid(
    store: StoreOrGroup,
    paths: list[str],
    ref_shape: Collection[int],
    scaling_factors: Collection[float],
    chunks: Collection[int] | None = None,
    dtype: str = "uint16",
    mode: AccessModeLiteral = "a",
) -> None:
    # Return the an Image object
    if chunks is not None and len(chunks) != len(ref_shape):
        raise NgioValueError(
            "The shape and chunks must have the same number of dimensions."
        )

    if chunks is not None:
        chunks = [min(c, s) for c, s in zip(chunks, ref_shape, strict=True)]

    if len(ref_shape) != len(scaling_factors):
        raise NgioValueError(
            "The shape and scaling factor must have the same number of dimensions."
        )

    root_group = open_group_wrapper(store, mode=mode)

    for path in paths:
        if any(s < 1 for s in ref_shape):
            raise NgioValueError(
                "Level shape must be at least 1 on all dimensions. "
                f"Calculated shape: {ref_shape} at level {path}."
            )
        new_arr = root_group.zeros(
            name=path,
            shape=ref_shape,
            dtype=dtype,
            chunks=chunks,
            dimension_separator="/",
            overwrite=True,
        )

        # Todo redo this with when a proper build of pyramid is implemented
        _shape = []
        for s, sc in zip(ref_shape, scaling_factors, strict=True):
            if math.floor(s / sc) % 2 == 0:
                _shape.append(math.floor(s / sc))
            else:
                _shape.append(math.ceil(s / sc))
        ref_shape = _shape

        if chunks is None:
            chunks = new_arr.chunks
            if chunks is None:
                raise NgioValueError("Something went wrong with the chunks")
        chunks = [min(c, s) for c, s in zip(chunks, ref_shape, strict=True)]
    return None

on_disk_zoom

on_disk_zoom(
    source: Array,
    target: Array,
    order: Literal[0, 1, 2] = 1,
    mode: Literal["dask", "numpy", "coarsen"] = "dask",
) -> None

Apply a zoom operation from a source zarr array to a target zarr array.

Parameters:

  • source (Array) –

    The source array to zoom.

  • target (Array) –

    The target array to save the zoomed result to.

  • order (Literal[0, 1, 2], default: 1 ) –

    The order of interpolation. Defaults to 1.

  • mode (Literal['dask', 'numpy', 'coarsen'], default: 'dask' ) –

    The mode to use. Defaults to "dask".

Source code in ngio/common/_pyramid.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def on_disk_zoom(
    source: zarr.Array,
    target: zarr.Array,
    order: Literal[0, 1, 2] = 1,
    mode: Literal["dask", "numpy", "coarsen"] = "dask",
) -> None:
    """Apply a zoom operation from a source zarr array to a target zarr array.

    Args:
        source (zarr.Array): The source array to zoom.
        target (zarr.Array): The target array to save the zoomed result to.
        order (Literal[0, 1, 2]): The order of interpolation. Defaults to 1.
        mode (Literal["dask", "numpy", "coarsen"]): The mode to use. Defaults to "dask".
    """
    if not isinstance(source, zarr.Array):
        raise NgioValueError("source must be a zarr array")

    if not isinstance(target, zarr.Array):
        raise NgioValueError("target must be a zarr array")

    if source.dtype != target.dtype:
        raise NgioValueError("source and target must have the same dtype")

    match mode:
        case "numpy":
            return _on_disk_numpy_zoom(source, target, order)
        case "dask":
            return _on_disk_dask_zoom(source, target, order)
        case "coarsen":
            return _on_disk_coarsen(
                source,
                target,
            )
        case _:
            raise NgioValueError("mode must be either 'dask', 'numpy' or 'coarsen'")

roi_to_slice_kwargs

roi_to_slice_kwargs(
    roi: Roi | RoiPixels,
    dimensions: Dimensions,
    pixel_size: PixelSize | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> dict[str, slice | int | Iterable[int]]

Convert a WorldCooROI to slice_kwargs.

Source code in ngio/common/_roi.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def roi_to_slice_kwargs(
    roi: Roi | RoiPixels,
    dimensions: Dimensions,
    pixel_size: PixelSize | None = None,
    **slice_kwargs: slice | int | Iterable[int],
) -> dict[str, slice | int | Iterable[int]]:
    """Convert a WorldCooROI to slice_kwargs."""
    if isinstance(roi, Roi):
        if pixel_size is None:
            raise NgioValueError(
                "pixel_size must be provided when converting a Roi to slice_kwargs."
            )
        pixel_roi = roi.to_pixel_roi(
            pixel_size=pixel_size, dimensions=dimensions
        ).to_slices()
    elif isinstance(roi, RoiPixels):
        pixel_roi = roi.to_slices()
    else:
        raise TypeError(f"Unsupported ROI type: {type(roi)}")

    for ax in ["x", "y", "z", "t"]:
        if not dimensions.has_axis(axis_name=ax):
            pixel_roi.pop(ax, None)

    for key in slice_kwargs.keys():
        if key in pixel_roi:
            raise NgioValueError(
                f"Key {key} is already in the slice_kwargs. "
                "Ambiguous which one to use: "
                f"{key}={slice_kwargs[key]} or roi_{key}={pixel_roi[key]}"
            )
    return {**pixel_roi, **slice_kwargs}

concatenate_image_tables

concatenate_image_tables(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> Table

Concatenate tables from different images into a single table.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • extras (Collection[dict[str, str]]) –

    A collection of extras dictionaries for each image. this will be added as columns to the table, and will be concatenated with the table index to create a new index.

  • name (str) –

    The name of the table to concatenate.

  • index_key (str | None, default: None ) –

    The key to use for the index of the concatenated table.

  • strict (bool, default: True ) –

    If True, raise an error if the table is not found in the image.

  • mode (Literal['eager', 'lazy'], default: 'eager' ) –

    The mode to use for concatenation. Can be 'eager' or 'lazy'. if 'eager', the table will be loaded into memory. if 'lazy', the table will be loaded as a lazy frame.

Source code in ngio/common/_table_ops.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
def concatenate_image_tables(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> Table:
    """Concatenate tables from different images into a single table.

    Args:
        images: A collection of images.
        extras: A collection of extras dictionaries for each image.
            this will be added as columns to the table, and will be
            concatenated with the table index to create a new index.
        name: The name of the table to concatenate.
        index_key: The key to use for the index of the concatenated table.
        strict: If True, raise an error if the table is not found in the image.
        mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
            if 'eager', the table will be loaded into memory.
            if 'lazy', the table will be loaded as a lazy frame.
    """
    return _concatenate_image_tables(
        images=images,
        extras=extras,
        name=name,
        table_cls=None,
        index_key=index_key,
        strict=strict,
        mode=mode,
    )

concatenate_image_tables_as

concatenate_image_tables_as(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    table_cls: type[TableType],
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> TableType

Concatenate tables from different images into a single table.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • extras (Collection[dict[str, str]]) –

    A collection of extras dictionaries for each image. this will be added as columns to the table, and will be concatenated with the table index to create a new index.

  • name (str) –

    The name of the table to concatenate.

  • table_cls (type[TableType]) –

    The output will be casted to this class, if the new table_cls is compatible with the table_cls of the input tables.

  • index_key (str | None, default: None ) –

    The key to use for the index of the concatenated table.

  • strict (bool, default: True ) –

    If True, raise an error if the table is not found in the image.

  • mode (Literal['eager', 'lazy'], default: 'eager' ) –

    The mode to use for concatenation. Can be 'eager' or 'lazy'. if 'eager', the table will be loaded into memory. if 'lazy', the table will be loaded as a lazy frame.

Source code in ngio/common/_table_ops.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def concatenate_image_tables_as(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    table_cls: type[TableType],
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> TableType:
    """Concatenate tables from different images into a single table.

    Args:
        images: A collection of images.
        extras: A collection of extras dictionaries for each image.
            this will be added as columns to the table, and will be
            concatenated with the table index to create a new index.
        name: The name of the table to concatenate.
        table_cls: The output will be casted to this class, if the new table_cls is
            compatible with the table_cls of the input tables.
        index_key: The key to use for the index of the concatenated table.
        strict: If True, raise an error if the table is not found in the image.
        mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
            if 'eager', the table will be loaded into memory.
            if 'lazy', the table will be loaded as a lazy frame.
    """
    table = _concatenate_image_tables(
        images=images,
        extras=extras,
        name=name,
        table_cls=table_cls,
        index_key=index_key,
        strict=strict,
        mode=mode,
    )
    if not isinstance(table, table_cls):
        raise ValueError(f"Table is not of type {table_cls}. Got {type(table)}")
    return table

concatenate_image_tables_as_async async

concatenate_image_tables_as_async(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    table_cls: type[TableType],
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> TableType

Concatenate tables from different images into a single table.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • extras (Collection[dict[str, str]]) –

    A collection of extras dictionaries for each image. this will be added as columns to the table, and will be concatenated with the table index to create a new index.

  • name (str) –

    The name of the table to concatenate.

  • table_cls (type[TableType]) –

    The output will be casted to this class, if the new table_cls is compatible with the table_cls of the input tables.

  • index_key (str | None, default: None ) –

    The key to use for the index of the concatenated table.

  • strict (bool, default: True ) –

    If True, raise an error if the table is not found in the image.

  • mode (Literal['eager', 'lazy'], default: 'eager' ) –

    The mode to use for concatenation. Can be 'eager' or 'lazy'. if 'eager', the table will be loaded into memory. if 'lazy', the table will be loaded as a lazy frame.

Source code in ngio/common/_table_ops.py
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
async def concatenate_image_tables_as_async(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    table_cls: type[TableType],
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> TableType:
    """Concatenate tables from different images into a single table.

    Args:
        images: A collection of images.
        extras: A collection of extras dictionaries for each image.
            this will be added as columns to the table, and will be
            concatenated with the table index to create a new index.
        name: The name of the table to concatenate.
        table_cls: The output will be casted to this class, if the new table_cls is
            compatible with the table_cls of the input tables.
        index_key: The key to use for the index of the concatenated table.
        strict: If True, raise an error if the table is not found in the image.
        mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
            if 'eager', the table will be loaded into memory.
            if 'lazy', the table will be loaded as a lazy frame.
    """
    table = await _concatenate_image_tables_async(
        images=images,
        extras=extras,
        name=name,
        table_cls=table_cls,
        index_key=index_key,
        strict=strict,
        mode=mode,
    )
    if not isinstance(table, table_cls):
        raise ValueError(f"Table is not of type {table_cls}. Got {type(table)}")
    return table

concatenate_image_tables_async async

concatenate_image_tables_async(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> Table

Concatenate tables from different images into a single table.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • extras (Collection[dict[str, str]]) –

    A collection of extras dictionaries for each image. this will be added as columns to the table, and will be concatenated with the table index to create a new index.

  • name (str) –

    The name of the table to concatenate.

  • index_key (str | None, default: None ) –

    The key to use for the index of the concatenated table.

  • strict (bool, default: True ) –

    If True, raise an error if the table is not found in the image.

  • mode (Literal['eager', 'lazy'], default: 'eager' ) –

    The mode to use for concatenation. Can be 'eager' or 'lazy'. if 'eager', the table will be loaded into memory. if 'lazy', the table will be loaded as a lazy frame.

Source code in ngio/common/_table_ops.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
async def concatenate_image_tables_async(
    images: Collection[OmeZarrContainer],
    extras: Collection[dict[str, str]],
    name: str,
    index_key: str | None = None,
    strict: bool = True,
    mode: Literal["eager", "lazy"] = "eager",
) -> Table:
    """Concatenate tables from different images into a single table.

    Args:
        images: A collection of images.
        extras: A collection of extras dictionaries for each image.
            this will be added as columns to the table, and will be
            concatenated with the table index to create a new index.
        name: The name of the table to concatenate.
        index_key: The key to use for the index of the concatenated table.
        strict: If True, raise an error if the table is not found in the image.
        mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
            if 'eager', the table will be loaded into memory.
            if 'lazy', the table will be loaded as a lazy frame.
    """
    return await _concatenate_image_tables_async(
        images=images,
        extras=extras,
        name=name,
        table_cls=None,
        index_key=index_key,
        strict=strict,
        mode=mode,
    )

conctatenate_tables

conctatenate_tables(
    tables: Collection[TableWithExtras],
    mode: Literal["eager", "lazy"] = "eager",
    index_key: str | None = None,
    table_cls: type[TableType] | None = None,
) -> Table

Concatenate tables from different plates into a single table.

Source code in ngio/common/_table_ops.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def conctatenate_tables(
    tables: Collection[TableWithExtras],
    mode: Literal["eager", "lazy"] = "eager",
    index_key: str | None = None,
    table_cls: type[TableType] | None = None,
) -> Table:
    """Concatenate tables from different plates into a single table."""
    if len(tables) == 0:
        raise ValueError("No tables to concatenate.")

    table0 = next(iter(tables)).table

    if mode == "lazy":
        concatenated_table = _pl_concat(tables=tables, index_key=index_key)
    elif mode == "eager":
        concatenated_table = _pd_concat(tables=tables, index_key=index_key)
    else:
        raise ValueError(f"Unknown mode: {mode}. Use 'eager' or 'lazy'.")

    meta = table0.meta
    meta.index_key = index_key
    meta.index_type = "str"

    if table_cls is not None:
        return table_cls.from_table_data(
            table_data=concatenated_table,
            meta=meta,
        )
    return table0.from_table_data(
        table_data=concatenated_table,
        meta=meta,
    )

list_image_tables

list_image_tables(
    images: Collection[OmeZarrContainer],
    filter_types: str | None = None,
    mode: Literal["common", "all"] = "common",
) -> list[str]

List all table names in the images.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • filter_types (str | None, default: None ) –

    The type of tables to filter. If None, return all tables. Defaults to None.

  • mode (Literal['common', 'all'], default: 'common' ) –

    Whether to return only common tables between all images or all tables. Defaults to "common".

Source code in ngio/common/_table_ops.py
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
def list_image_tables(
    images: Collection[OmeZarrContainer],
    filter_types: str | None = None,
    mode: Literal["common", "all"] = "common",
) -> list[str]:
    """List all table names in the images.

    Args:
        images: A collection of images.
        filter_types (str | None): The type of tables to filter. If None,
            return all tables. Defaults to None.
        mode (Literal["common", "all"]): Whether to return only common tables
            between all images or all tables. Defaults to "common".
    """
    tables_names = []
    for image in images:
        tables = image.list_tables(filter_types=filter_types)
        tables_names.append(tables)

    return _tables_names_coalesce(
        tables_names=tables_names,
        mode=mode,
    )

list_image_tables_async async

list_image_tables_async(
    images: Collection[OmeZarrContainer],
    filter_types: str | None = None,
    mode: Literal["common", "all"] = "common",
) -> list[str]

List all image tables in the image asynchronously.

Parameters:

  • images (Collection[OmeZarrContainer]) –

    A collection of images.

  • filter_types (str | None, default: None ) –

    The type of tables to filter. If None, return all tables. Defaults to None.

  • mode (Literal['common', 'all'], default: 'common' ) –

    Whether to return only common tables between all images or all tables. Defaults to "common".

Source code in ngio/common/_table_ops.py
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
async def list_image_tables_async(
    images: Collection[OmeZarrContainer],
    filter_types: str | None = None,
    mode: Literal["common", "all"] = "common",
) -> list[str]:
    """List all image tables in the image asynchronously.

    Args:
        images: A collection of images.
        filter_types (str | None): The type of tables to filter. If None,
            return all tables. Defaults to None.
        mode (Literal["common", "all"]): Whether to return only common tables
            between all images or all tables. Defaults to "common".
    """
    images_ids = []

    # key table name, value list of paths
    def process_image(
        image: OmeZarrContainer, filter_types: str | None = None
    ) -> list[str]:
        tables = image.list_tables(filter_types=filter_types)
        return tables

    tasks = []
    for i, image in enumerate(images):
        images_ids.append(i)
        task = asyncio.to_thread(process_image, image, filter_types=filter_types)
        tasks.append(task)

    tables_names = await asyncio.gather(*tasks)
    return _tables_names_coalesce(
        tables_names=tables_names,
        mode=mode,
    )

dask_zoom

dask_zoom(
    source_array: Array,
    scale: tuple[int, ...] | None = None,
    target_shape: tuple[int, ...] | None = None,
    order: Literal[0, 1, 2] = 1,
) -> Array

Dask implementation of zooming an array.

Only one of scale or target_shape must be provided.

Parameters:

  • source_array (Array) –

    The source array to zoom.

  • scale (tuple[int, ...] | None, default: None ) –

    The scale factor to zoom by.

  • target_shape ((tuple[int, ...], None), default: None ) –

    The target shape to zoom to.

  • order (Literal[0, 1, 2], default: 1 ) –

    The order of interpolation. Defaults to 1.

Returns:

  • Array

    da.Array: The zoomed array.

Source code in ngio/common/_zoom.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def dask_zoom(
    source_array: da.Array,
    scale: tuple[int, ...] | None = None,
    target_shape: tuple[int, ...] | None = None,
    order: Literal[0, 1, 2] = 1,
) -> da.Array:
    """Dask implementation of zooming an array.

    Only one of scale or target_shape must be provided.

    Args:
        source_array (da.Array): The source array to zoom.
        scale (tuple[int, ...] | None): The scale factor to zoom by.
        target_shape (tuple[int, ...], None): The target shape to zoom to.
        order (Literal[0, 1, 2]): The order of interpolation. Defaults to 1.

    Returns:
        da.Array: The zoomed array.
    """
    # This function follow the implementation from:
    # https://github.com/ome/ome-zarr-py/blob/master/ome_zarr/dask_utils.py
    # The module was contributed by Andreas Eisenbarth @aeisenbarth
    # See https://github.com/toloudis/ome-zarr-py/pull/

    _scale, _target_shape = _zoom_inputs_check(
        source_array=source_array, scale=scale, target_shape=target_shape
    )

    # Rechunk to better match the scaling operation
    source_chunks = np.array(source_array.chunksize)  # type: ignore (da.Array.chunksize is a tuple of ints)
    better_source_chunks = np.maximum(1, np.round(source_chunks * _scale) / _scale)
    better_source_chunks = better_source_chunks.astype(int)
    source_array = source_array.rechunk(better_source_chunks)  # type: ignore (better_source_chunks is a valid input for rechunk)

    # Calculate the block output shape
    block_output_shape = tuple(np.ceil(better_source_chunks * _scale).astype(int))

    zoom_wrapper = partial(
        fast_zoom, zoom=_scale, order=order, mode="grid-constant", grid_mode=True
    )

    out_array = da.map_blocks(
        zoom_wrapper, source_array, chunks=block_output_shape, dtype=source_array.dtype
    )

    # Slice and rechunk to target
    slices = tuple(slice(0, ts, 1) for ts in _target_shape)
    out_array = out_array[slices]
    return out_array

numpy_zoom

numpy_zoom(
    source_array: ndarray,
    scale: tuple[int, ...] | None = None,
    target_shape: tuple[int, ...] | None = None,
    order: Literal[0, 1, 2] = 1,
) -> ndarray

Numpy implementation of zooming an array.

Only one of scale or target_shape must be provided.

Parameters:

  • source_array (ndarray) –

    The source array to zoom.

  • scale (tuple[int, ...] | None, default: None ) –

    The scale factor to zoom by.

  • target_shape ((tuple[int, ...], None), default: None ) –

    The target shape to zoom to.

  • order (Literal[0, 1, 2], default: 1 ) –

    The order of interpolation. Defaults to 1.

Returns:

  • ndarray

    np.ndarray: The zoomed array

Source code in ngio/common/_zoom.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def numpy_zoom(
    source_array: np.ndarray,
    scale: tuple[int, ...] | None = None,
    target_shape: tuple[int, ...] | None = None,
    order: Literal[0, 1, 2] = 1,
) -> np.ndarray:
    """Numpy implementation of zooming an array.

    Only one of scale or target_shape must be provided.

    Args:
        source_array (np.ndarray): The source array to zoom.
        scale (tuple[int, ...] | None): The scale factor to zoom by.
        target_shape (tuple[int, ...], None): The target shape to zoom to.
        order (Literal[0, 1, 2]): The order of interpolation. Defaults to 1.

    Returns:
        np.ndarray: The zoomed array
    """
    _scale, _ = _zoom_inputs_check(
        source_array=source_array, scale=scale, target_shape=target_shape
    )

    out_array = fast_zoom(
        source_array, zoom=_scale, order=order, mode="grid-constant", grid_mode=True
    )
    assert isinstance(out_array, np.ndarray)
    return out_array