ngio 0.2.0a3__py3-none-any.whl → 0.2.0b2__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 (49) hide show
  1. ngio/__init__.py +4 -4
  2. ngio/common/__init__.py +12 -2
  3. ngio/common/_array_pipe.py +106 -0
  4. ngio/common/_axes_transforms.py +3 -2
  5. ngio/common/_dimensions.py +7 -0
  6. ngio/common/_masking_roi.py +158 -0
  7. ngio/common/_pyramid.py +16 -11
  8. ngio/common/_roi.py +74 -0
  9. ngio/common/_slicer.py +1 -2
  10. ngio/common/_zoom.py +5 -3
  11. ngio/hcs/__init__.py +2 -57
  12. ngio/hcs/plate.py +399 -0
  13. ngio/images/abstract_image.py +97 -28
  14. ngio/images/create.py +48 -29
  15. ngio/images/image.py +99 -46
  16. ngio/images/label.py +109 -92
  17. ngio/images/masked_image.py +259 -0
  18. ngio/images/omezarr_container.py +201 -64
  19. ngio/ome_zarr_meta/__init__.py +25 -13
  20. ngio/ome_zarr_meta/_meta_handlers.py +718 -69
  21. ngio/ome_zarr_meta/ngio_specs/__init__.py +8 -0
  22. ngio/ome_zarr_meta/ngio_specs/_channels.py +11 -0
  23. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +374 -2
  24. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +169 -119
  25. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +35 -3
  26. ngio/ome_zarr_meta/v04/__init__.py +17 -5
  27. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +85 -12
  28. ngio/tables/__init__.py +2 -0
  29. ngio/tables/_validators.py +2 -4
  30. ngio/tables/backends/_anndata_utils.py +5 -1
  31. ngio/tables/backends/_anndata_v1.py +2 -1
  32. ngio/tables/backends/_json_v1.py +1 -1
  33. ngio/tables/tables_container.py +12 -2
  34. ngio/tables/v1/__init__.py +1 -2
  35. ngio/tables/v1/_feature_table.py +7 -5
  36. ngio/tables/v1/_generic_table.py +65 -11
  37. ngio/tables/v1/_roi_table.py +145 -27
  38. ngio/utils/_datasets.py +4 -2
  39. ngio/utils/_fractal_fsspec_store.py +3 -2
  40. ngio/utils/_logger.py +3 -1
  41. ngio/utils/_zarr_utils.py +25 -2
  42. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/METADATA +6 -2
  43. ngio-0.2.0b2.dist-info/RECORD +54 -0
  44. ngio/ome_zarr_meta/_generic_handlers.py +0 -320
  45. ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
  46. ngio/tables/v1/_masking_roi_table.py +0 -175
  47. ngio-0.2.0a3.dist-info/RECORD +0 -54
  48. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/WHEEL +0 -0
  49. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/licenses/LICENSE +0 -0
ngio/images/label.py CHANGED
@@ -3,16 +3,17 @@
3
3
  from collections.abc import Collection
4
4
  from typing import Literal
5
5
 
6
+ from ngio.common import compute_masking_roi
6
7
  from ngio.images.abstract_image import AbstractImage, consolidate_image
7
8
  from ngio.images.create import _create_empty_label
8
9
  from ngio.images.image import Image
9
10
  from ngio.ome_zarr_meta import (
10
- ImplementedLabelMetaHandlers,
11
11
  LabelMetaHandler,
12
12
  NgioLabelMeta,
13
13
  PixelSize,
14
+ find_label_meta_handler,
14
15
  )
15
- from ngio.ome_zarr_meta.ngio_specs import SpaceUnits, TimeUnits
16
+ from ngio.tables import MaskingROITable
16
17
  from ngio.utils import (
17
18
  NgioValidationError,
18
19
  NgioValueError,
@@ -39,9 +40,7 @@ class Label(AbstractImage[LabelMetaHandler]):
39
40
 
40
41
  """
41
42
  if meta_handler is None:
42
- meta_handler = ImplementedLabelMetaHandlers().find_meta_handler(
43
- group_handler
44
- )
43
+ meta_handler = find_label_meta_handler(group_handler)
45
44
  super().__init__(
46
45
  group_handler=group_handler, path=path, meta_handler=meta_handler
47
46
  )
@@ -51,6 +50,10 @@ class Label(AbstractImage[LabelMetaHandler]):
51
50
  """Return the metadata."""
52
51
  return self._meta_handler.meta
53
52
 
53
+ def build_masking_roi_table(self) -> MaskingROITable:
54
+ """Compute the masking ROI table."""
55
+ return build_masking_roi_table(self)
56
+
54
57
  def consolidate(
55
58
  self,
56
59
  mode: Literal["dask", "numpy", "coarsen"] = "dask",
@@ -87,11 +90,6 @@ class LabelsContainer:
87
90
  attrs = self._group_handler.load_attrs()
88
91
  return attrs.get("labels", [])
89
92
 
90
- def _get(self, name: str, path: str) -> Label:
91
- """Get a label from the group."""
92
- group_handler = self._group_handler.derive_handler(name)
93
- return Label(group_handler, path, None)
94
-
95
93
  def get(
96
94
  self,
97
95
  name: str,
@@ -111,9 +109,7 @@ class LabelsContainer:
111
109
 
112
110
  """
113
111
  group_handler = self._group_handler.derive_handler(name)
114
- label_meta_handler = ImplementedLabelMetaHandlers().find_meta_handler(
115
- group_handler
116
- )
112
+ label_meta_handler = find_label_meta_handler(group_handler)
117
113
  path = label_meta_handler.meta.get_dataset(
118
114
  path=path, pixel_size=pixel_size, strict=strict
119
115
  ).path
@@ -124,56 +120,32 @@ class LabelsContainer:
124
120
  name: str,
125
121
  ref_image: Image,
126
122
  shape: Collection[int] | None = None,
123
+ pixel_size: PixelSize | None = None,
124
+ axes_names: Collection[str] | None = None,
127
125
  chunks: Collection[int] | None = None,
128
- dtype: str = "uint16",
129
- xy_scaling_factor=2.0,
130
- z_scaling_factor=1.0,
126
+ dtype: str | None = None,
131
127
  overwrite: bool = False,
132
- ) -> None:
133
- """Add a label to the group."""
134
- existing_labels = self.list()
135
- if name in existing_labels and not overwrite:
136
- raise NgioValueError(
137
- f"Table '{name}' already exists in the group. "
138
- "Use overwrite=True to replace it."
139
- )
128
+ ) -> "Label":
129
+ """Create an empty OME-Zarr label from a reference image.
140
130
 
141
- label_group = self._group_handler.get_group(name, create_mode=True)
131
+ And add the label to the /labels group.
142
132
 
143
- _derive_label(
144
- ref_image=ref_image,
145
- store=label_group,
146
- shape=shape,
147
- chunks=chunks,
148
- dtype=dtype,
149
- xy_scaling_factor=xy_scaling_factor,
150
- z_scaling_factor=z_scaling_factor,
151
- overwrite=overwrite,
152
- )
153
-
154
- if name not in existing_labels:
155
- existing_labels.append(name)
156
- self._group_handler.write_attrs({"labels": existing_labels})
133
+ Args:
134
+ store (StoreOrGroup): The Zarr store or group to create the image in.
135
+ ref_image (Image): The reference image.
136
+ name (str): The name of the new image.
137
+ shape (Collection[int] | None): The shape of the new image.
138
+ pixel_size (PixelSize | None): The pixel size of the new image.
139
+ axes_names (Collection[str] | None): The axes names of the new image.
140
+ For labels, the channel axis is not allowed.
141
+ chunks (Collection[int] | None): The chunk shape of the new image.
142
+ dtype (str | None): The data type of the new image.
143
+ overwrite (bool): Whether to overwrite an existing image.
144
+
145
+ Returns:
146
+ Label: The new label.
157
147
 
158
- def new(
159
- self,
160
- name: str,
161
- shape: Collection[int],
162
- xy_pixelsize: float,
163
- z_spacing: float = 1.0,
164
- time_spacing: float = 1.0,
165
- levels: "int | list[str]" = 5,
166
- xy_scaling_factor: float = 2.0,
167
- z_scaling_factor: float = 1.0,
168
- space_unit: SpaceUnits | str | None = None,
169
- time_unit: TimeUnits | str | None = None,
170
- axes_names: Collection[str] | None = None,
171
- chunks: Collection[int] | None = None,
172
- dtype: str = "uint16",
173
- overwrite: bool = False,
174
- version: str = "0.4",
175
- ) -> None:
176
- """Add a label to the group."""
148
+ """
177
149
  existing_labels = self.list()
178
150
  if name in existing_labels and not overwrite:
179
151
  raise NgioValueError(
@@ -183,82 +155,127 @@ class LabelsContainer:
183
155
 
184
156
  label_group = self._group_handler.get_group(name, create_mode=True)
185
157
 
186
- _create_empty_label(
158
+ _derive_label(
187
159
  store=label_group,
160
+ ref_image=ref_image,
161
+ name=name,
188
162
  shape=shape,
189
- xy_pixelsize=xy_pixelsize,
190
- z_spacing=z_spacing,
191
- time_spacing=time_spacing,
192
- levels=levels,
193
- xy_scaling_factor=xy_scaling_factor,
194
- z_scaling_factor=z_scaling_factor,
195
- space_unit=space_unit,
196
- time_unit=time_unit,
163
+ pixel_size=pixel_size,
197
164
  axes_names=axes_names,
198
165
  chunks=chunks,
199
166
  dtype=dtype,
200
167
  overwrite=overwrite,
201
- version=version,
202
168
  )
203
169
 
204
170
  if name not in existing_labels:
205
171
  existing_labels.append(name)
206
172
  self._group_handler.write_attrs({"labels": existing_labels})
207
173
 
174
+ return self.get(name)
175
+
208
176
 
209
177
  def _derive_label(
210
- ref_image: Image,
211
178
  store: StoreOrGroup,
179
+ ref_image: Image,
180
+ name: str,
212
181
  shape: Collection[int] | None = None,
182
+ pixel_size: PixelSize | None = None,
183
+ axes_names: Collection[str] | None = None,
213
184
  chunks: Collection[int] | None = None,
214
- dtype: str = "uint16",
215
- xy_scaling_factor=2.0,
216
- z_scaling_factor=1.0,
185
+ dtype: str | None = None,
217
186
  overwrite: bool = False,
218
187
  ) -> None:
219
- """Create an OME-Zarr image from a numpy array."""
188
+ """Create an empty OME-Zarr label from a reference image.
189
+
190
+ Args:
191
+ store (StoreOrGroup): The Zarr store or group to create the image in.
192
+ ref_image (Image): The reference image.
193
+ name (str): The name of the new image.
194
+ shape (Collection[int] | None): The shape of the new image.
195
+ pixel_size (PixelSize | None): The pixel size of the new image.
196
+ axes_names (Collection[str] | None): The axes names of the new image.
197
+ For labels, the channel axis is not allowed.
198
+ chunks (Collection[int] | None): The chunk shape of the new image.
199
+ dtype (str | None): The data type of the new image.
200
+ overwrite (bool): Whether to overwrite an existing image.
201
+
202
+ Returns:
203
+ None
204
+
205
+ """
220
206
  ref_meta = ref_image.meta
221
- # remove channls if present
222
- shape_ref = ref_image.shape
223
- chunks_ref = ref_image.chunks
224
- axes_names_ref = ref_image.dataset.axes_mapper.on_disk_axes_names
225
- c_axis = ref_image.dataset.axes_mapper.get_index("c")
226
- if c_axis is not None:
227
- shape_ref = shape_ref[:c_axis] + shape_ref[c_axis + 1 :]
228
- chunks_ref = chunks_ref[:c_axis] + chunks_ref[c_axis + 1 :]
229
- axes_names_ref = axes_names_ref[:c_axis] + axes_names_ref[c_axis + 1 :]
230
207
 
231
208
  if shape is None:
232
- shape = shape_ref
209
+ shape = ref_image.shape
233
210
 
234
- if chunks is None:
235
- chunks = chunks_ref
211
+ if pixel_size is None:
212
+ pixel_size = ref_image.pixel_size
213
+
214
+ if axes_names is None:
215
+ axes_names = ref_meta.axes_mapper.on_disk_axes_names
216
+ c_axis = ref_meta.axes_mapper.get_index("c")
217
+ else:
218
+ if "c" in axes_names:
219
+ raise NgioValidationError(
220
+ "Labels cannot have a channel axis. "
221
+ "Please remove the channel axis from the axes names."
222
+ )
223
+ c_axis = None
236
224
 
237
- if len(shape) != len(shape_ref):
225
+ if len(axes_names) != len(shape):
238
226
  raise NgioValidationError(
239
- "The shape of the new image does not match the reference image."
227
+ "The axes names of the new image does not match the reference image."
228
+ f"Got {axes_names} for shape {shape}."
240
229
  )
241
230
 
242
- if len(chunks) != len(chunks_ref):
231
+ if chunks is None:
232
+ chunks = ref_image.chunks
233
+
234
+ if len(chunks) != len(shape):
243
235
  raise NgioValidationError(
244
236
  "The chunks of the new image does not match the reference image."
237
+ f"Got {chunks} for shape {shape}."
245
238
  )
246
239
 
240
+ if dtype is None:
241
+ dtype = ref_image.dtype
242
+
243
+ if c_axis is not None:
244
+ # remove channel if present
245
+ shape = list(shape)
246
+ shape = shape[:c_axis] + shape[c_axis + 1 :]
247
+ chunks = list(chunks)
248
+ chunks = chunks[:c_axis] + chunks[c_axis + 1 :]
249
+ axes_names = list(axes_names)
250
+ axes_names = axes_names[:c_axis] + axes_names[c_axis + 1 :]
251
+
247
252
  _ = _create_empty_label(
248
253
  store=store,
249
254
  shape=shape,
250
- xy_pixelsize=ref_image.pixel_size.x,
255
+ pixelsize=ref_image.pixel_size.x,
251
256
  z_spacing=ref_image.pixel_size.z,
252
257
  time_spacing=ref_image.pixel_size.t,
253
258
  levels=ref_meta.levels,
254
- xy_scaling_factor=xy_scaling_factor,
255
- z_scaling_factor=z_scaling_factor,
259
+ yx_scaling_factor=ref_meta.yx_scaling(),
260
+ z_scaling_factor=ref_meta.z_scaling(),
256
261
  time_unit=ref_image.pixel_size.time_unit,
257
262
  space_unit=ref_image.pixel_size.space_unit,
258
- axes_names=axes_names_ref,
263
+ axes_names=axes_names,
259
264
  chunks=chunks,
260
265
  dtype=dtype,
261
266
  overwrite=overwrite,
262
267
  version=ref_meta.version,
268
+ name=name,
263
269
  )
264
270
  return None
271
+
272
+
273
+ def build_masking_roi_table(label: Label) -> MaskingROITable:
274
+ """Compute the masking ROI table for a label."""
275
+ if label.dimensions.is_time_series:
276
+ raise NgioValueError("Time series labels are not supported.")
277
+
278
+ array = label.get_array(axes_order=["z", "y", "x"], mode="dask")
279
+
280
+ rois = compute_masking_roi(array, label.pixel_size)
281
+ return MaskingROITable(rois, reference_label=label.meta.name)
@@ -0,0 +1,259 @@
1
+ """A module for handling label images in OME-NGFF files."""
2
+
3
+ from collections.abc import Collection, Iterable
4
+ from typing import Literal
5
+
6
+ from ngio.common import ArrayLike, get_masked_pipe, roi_to_slice_kwargs, set_masked_pipe
7
+ from ngio.images.image import Image
8
+ from ngio.images.label import Label
9
+ from ngio.ome_zarr_meta import ImageMetaHandler, LabelMetaHandler
10
+ from ngio.tables import MaskingROITable
11
+ from ngio.utils import (
12
+ ZarrGroupHandler,
13
+ )
14
+
15
+
16
+ class MaskedImage(Image):
17
+ """Placeholder class for a label."""
18
+
19
+ def __init__(
20
+ self,
21
+ group_handler: ZarrGroupHandler,
22
+ path: str,
23
+ meta_handler: ImageMetaHandler | None,
24
+ label: Label,
25
+ masking_roi_table: MaskingROITable,
26
+ ) -> None:
27
+ """Initialize the Image at a single level.
28
+
29
+ Args:
30
+ group_handler: The Zarr group handler.
31
+ path: The path to the image in the omezarr file.
32
+ meta_handler: The image metadata handler.
33
+ label: The label image.
34
+ masking_roi_table: The masking ROI table.
35
+
36
+ """
37
+ super().__init__(
38
+ group_handler=group_handler, path=path, meta_handler=meta_handler
39
+ )
40
+ self._label = label
41
+ self._masking_roi_table = masking_roi_table
42
+
43
+ def get_roi(
44
+ self,
45
+ label: int,
46
+ zoom_factor: float = 1.0,
47
+ axes_order: Collection[str] | None = None,
48
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
49
+ **slice_kwargs: slice | int | Iterable[int],
50
+ ) -> ArrayLike:
51
+ """Return the array for a given ROI."""
52
+ roi = self._masking_roi_table.get(label)
53
+ roi = roi.zoom(zoom_factor)
54
+ return super().get_roi(
55
+ roi=roi, axes_order=axes_order, mode=mode, **slice_kwargs
56
+ )
57
+
58
+ def set_roi(
59
+ self,
60
+ label: int,
61
+ patch: ArrayLike,
62
+ zoom_factor: float = 1.0,
63
+ axes_order: Collection[str] | None = None,
64
+ **slice_kwargs: slice | int | Iterable[int],
65
+ ) -> None:
66
+ """Set the array for a given ROI."""
67
+ roi = self._masking_roi_table.get(label)
68
+ roi = roi.zoom(zoom_factor)
69
+ return super().set_roi(
70
+ roi=roi, patch=patch, axes_order=axes_order, **slice_kwargs
71
+ )
72
+
73
+ def get_roi_masked(
74
+ self,
75
+ label: int,
76
+ axes_order: Collection[str] | None = None,
77
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
78
+ zoom_factor: float = 1.0,
79
+ **slice_kwargs: slice | int | Iterable[int],
80
+ ) -> ArrayLike:
81
+ """Return the masked array for a given label."""
82
+ return get_masked_roi_pipe(
83
+ image=self,
84
+ label=label,
85
+ axes_order=axes_order,
86
+ mode=mode,
87
+ zoom_factor=zoom_factor,
88
+ **slice_kwargs,
89
+ )
90
+
91
+ def set_roi_masked(
92
+ self,
93
+ label: int,
94
+ patch: ArrayLike,
95
+ axes_order: Collection[str] | None = None,
96
+ zoom_factor: float = 1.0,
97
+ **slice_kwargs: slice | int | Iterable[int],
98
+ ) -> None:
99
+ """Set the masked array for a given label."""
100
+ return set_masked_roi_pipe(
101
+ image=self,
102
+ label=label,
103
+ patch=patch,
104
+ axes_order=axes_order,
105
+ zoom_factor=zoom_factor,
106
+ **slice_kwargs,
107
+ )
108
+
109
+
110
+ class MaskedLabel(Label):
111
+ """Placeholder class for a label."""
112
+
113
+ def __init__(
114
+ self,
115
+ group_handler: ZarrGroupHandler,
116
+ path: str,
117
+ meta_handler: LabelMetaHandler | None,
118
+ label: Label,
119
+ masking_roi_table: MaskingROITable,
120
+ ) -> None:
121
+ """Initialize the Image at a single level.
122
+
123
+ Args:
124
+ group_handler: The Zarr group handler.
125
+ path: The path to the image in the omezarr file.
126
+ meta_handler: The image metadata handler.
127
+ label: The label image.
128
+ masking_roi_table: The masking ROI table.
129
+
130
+ """
131
+ super().__init__(
132
+ group_handler=group_handler, path=path, meta_handler=meta_handler
133
+ )
134
+ self._label = label
135
+ self._masking_roi_table = masking_roi_table
136
+
137
+ def get_roi(
138
+ self,
139
+ label: int,
140
+ zoom_factor: float = 1.0,
141
+ axes_order: Collection[str] | None = None,
142
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
143
+ **slice_kwargs: slice | int | Iterable[int],
144
+ ) -> ArrayLike:
145
+ """Return the array for a given ROI."""
146
+ roi = self._masking_roi_table.get(label)
147
+ roi = roi.zoom(zoom_factor)
148
+ return super().get_roi(
149
+ roi=roi, axes_order=axes_order, mode=mode, **slice_kwargs
150
+ )
151
+
152
+ def set_roi(
153
+ self,
154
+ label: int,
155
+ patch: ArrayLike,
156
+ zoom_factor: float = 1.0,
157
+ axes_order: Collection[str] | None = None,
158
+ **slice_kwargs: slice | int | Iterable[int],
159
+ ) -> None:
160
+ """Set the array for a given ROI."""
161
+ roi = self._masking_roi_table.get(label)
162
+ roi = roi.zoom(zoom_factor)
163
+ return super().set_roi(
164
+ roi=roi, patch=patch, axes_order=axes_order, **slice_kwargs
165
+ )
166
+
167
+ def get_roi_masked(
168
+ self,
169
+ label: int,
170
+ axes_order: Collection[str] | None = None,
171
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
172
+ zoom_factor: float = 1.0,
173
+ **slice_kwargs: slice | int | Iterable[int],
174
+ ) -> ArrayLike:
175
+ """Return the masked array for a given label."""
176
+ return get_masked_roi_pipe(
177
+ image=self,
178
+ label=label,
179
+ axes_order=axes_order,
180
+ mode=mode,
181
+ zoom_factor=zoom_factor,
182
+ **slice_kwargs,
183
+ )
184
+
185
+ def set_roi_masked(
186
+ self,
187
+ label: int,
188
+ patch: ArrayLike,
189
+ axes_order: Collection[str] | None = None,
190
+ zoom_factor: float = 1.0,
191
+ **slice_kwargs: slice | int | Iterable[int],
192
+ ) -> None:
193
+ """Set the masked array for a given label."""
194
+ return set_masked_roi_pipe(
195
+ image=self,
196
+ label=label,
197
+ patch=patch,
198
+ axes_order=axes_order,
199
+ zoom_factor=zoom_factor,
200
+ **slice_kwargs,
201
+ )
202
+
203
+
204
+ def get_masked_roi_pipe(
205
+ image: MaskedImage | MaskedLabel,
206
+ label: int,
207
+ axes_order: Collection[str] | None = None,
208
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
209
+ zoom_factor: float = 1.0,
210
+ **slice_kwargs: slice | int | Iterable[int],
211
+ ) -> ArrayLike:
212
+ """Return the masked array for a given label."""
213
+ roi = image._masking_roi_table.get(label)
214
+ roi = roi.zoom(zoom_factor)
215
+ slice_kwargs = roi_to_slice_kwargs(
216
+ roi=roi,
217
+ pixel_size=image.pixel_size,
218
+ dimensions=image.dimensions,
219
+ **slice_kwargs,
220
+ )
221
+ return get_masked_pipe(
222
+ array=image.zarr_array,
223
+ label_array=image._label.zarr_array,
224
+ label=label,
225
+ dimensions_array=image.dimensions,
226
+ dimensions_label=image._label.dimensions,
227
+ axes_order=axes_order,
228
+ mode=mode,
229
+ **slice_kwargs,
230
+ )
231
+
232
+
233
+ def set_masked_roi_pipe(
234
+ image: MaskedImage | MaskedLabel,
235
+ label: int,
236
+ patch: ArrayLike,
237
+ axes_order: Collection[str] | None = None,
238
+ zoom_factor: float = 1.0,
239
+ **slice_kwargs: slice | int | Iterable[int],
240
+ ) -> None:
241
+ """Set the masked array for a given label."""
242
+ roi = image._masking_roi_table.get(label)
243
+ roi = roi.zoom(zoom_factor)
244
+ slice_kwargs = roi_to_slice_kwargs(
245
+ roi=roi,
246
+ pixel_size=image.pixel_size,
247
+ dimensions=image.dimensions,
248
+ **slice_kwargs,
249
+ )
250
+ return set_masked_pipe(
251
+ array=image.zarr_array,
252
+ label_array=image._label.zarr_array,
253
+ label=label,
254
+ patch=patch,
255
+ dimensions_array=image.dimensions,
256
+ dimensions_label=image._label.dimensions,
257
+ axes_order=axes_order,
258
+ **slice_kwargs,
259
+ )