ngio 0.2.0a2__py3-none-any.whl → 0.2.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 (50) 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 +121 -57
  16. ngio/images/label.py +131 -86
  17. ngio/images/masked_image.py +259 -0
  18. ngio/images/omezarr_container.py +250 -77
  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 +174 -113
  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 +2 -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/__init__.py +3 -0
  39. ngio/utils/_datasets.py +4 -2
  40. ngio/utils/_fractal_fsspec_store.py +13 -0
  41. ngio/utils/_logger.py +3 -1
  42. ngio/utils/_zarr_utils.py +25 -2
  43. {ngio-0.2.0a2.dist-info → ngio-0.2.0b1.dist-info}/METADATA +4 -1
  44. ngio-0.2.0b1.dist-info/RECORD +54 -0
  45. ngio/ome_zarr_meta/_generic_handlers.py +0 -320
  46. ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
  47. ngio/tables/v1/_masking_roi_table.py +0 -175
  48. ngio-0.2.0a2.dist-info/RECORD +0 -53
  49. {ngio-0.2.0a2.dist-info → ngio-0.2.0b1.dist-info}/WHEEL +0 -0
  50. {ngio-0.2.0a2.dist-info → ngio-0.2.0b1.dist-info}/licenses/LICENSE +0 -0
ngio/images/label.py CHANGED
@@ -3,15 +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
+ PixelSize,
14
+ find_label_meta_handler,
13
15
  )
14
- from ngio.ome_zarr_meta.ngio_specs import SpaceUnits, TimeUnits
16
+ from ngio.tables import MaskingROITable
15
17
  from ngio.utils import (
16
18
  NgioValidationError,
17
19
  NgioValueError,
@@ -38,9 +40,7 @@ class Label(AbstractImage[LabelMetaHandler]):
38
40
 
39
41
  """
40
42
  if meta_handler is None:
41
- meta_handler = ImplementedLabelMetaHandlers().find_meta_handler(
42
- group_handler
43
- )
43
+ meta_handler = find_label_meta_handler(group_handler)
44
44
  super().__init__(
45
45
  group_handler=group_handler, path=path, meta_handler=meta_handler
46
46
  )
@@ -50,6 +50,10 @@ class Label(AbstractImage[LabelMetaHandler]):
50
50
  """Return the metadata."""
51
51
  return self._meta_handler.meta
52
52
 
53
+ def build_masking_roi_table(self) -> MaskingROITable:
54
+ """Compute the masking ROI table."""
55
+ return build_masking_roi_table(self)
56
+
53
57
  def consolidate(
54
58
  self,
55
59
  mode: Literal["dask", "numpy", "coarsen"] = "dask",
@@ -86,66 +90,62 @@ class LabelsContainer:
86
90
  attrs = self._group_handler.load_attrs()
87
91
  return attrs.get("labels", [])
88
92
 
89
- def get(self, name: str, path: str) -> Label:
90
- """Get a label from the group."""
93
+ def get(
94
+ self,
95
+ name: str,
96
+ path: str | None = None,
97
+ pixel_size: PixelSize | None = None,
98
+ strict: bool = False,
99
+ ) -> Label:
100
+ """Get a label from the group.
101
+
102
+ Args:
103
+ name (str): The name of the label.
104
+ path (str | None): The path to the image in the omezarr file.
105
+ pixel_size (PixelSize | None): The pixel size of the image.
106
+ strict (bool): Only used if the pixel size is provided. If True, the
107
+ pixel size must match the image pixel size exactly. If False, the
108
+ closest pixel size level will be returned.
109
+
110
+ """
91
111
  group_handler = self._group_handler.derive_handler(name)
92
- return Label(group_handler, path, None)
112
+ label_meta_handler = find_label_meta_handler(group_handler)
113
+ path = label_meta_handler.meta.get_dataset(
114
+ path=path, pixel_size=pixel_size, strict=strict
115
+ ).path
116
+ return Label(group_handler, path, label_meta_handler)
93
117
 
94
118
  def derive(
95
119
  self,
96
120
  name: str,
97
121
  ref_image: Image,
98
122
  shape: Collection[int] | None = None,
123
+ pixel_size: PixelSize | None = None,
124
+ axes_names: Collection[str] | None = None,
99
125
  chunks: Collection[int] | None = None,
100
- dtype: str = "uint16",
101
- xy_scaling_factor=2.0,
102
- z_scaling_factor=1.0,
126
+ dtype: str | None = None,
103
127
  overwrite: bool = False,
104
- ) -> None:
105
- """Add a label to the group."""
106
- existing_labels = self.list()
107
- if name in existing_labels and not overwrite:
108
- raise NgioValueError(
109
- f"Table '{name}' already exists in the group. "
110
- "Use overwrite=True to replace it."
111
- )
128
+ ) -> "Label":
129
+ """Create an empty OME-Zarr label from a reference image.
112
130
 
113
- label_group = self._group_handler.get_group(name, create_mode=True)
131
+ And add the label to the /labels group.
114
132
 
115
- _derive_label(
116
- ref_image=ref_image,
117
- store=label_group,
118
- shape=shape,
119
- chunks=chunks,
120
- dtype=dtype,
121
- xy_scaling_factor=xy_scaling_factor,
122
- z_scaling_factor=z_scaling_factor,
123
- overwrite=overwrite,
124
- )
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.
125
144
 
126
- if name not in existing_labels:
127
- existing_labels.append(name)
128
- self._group_handler.write_attrs({"labels": existing_labels})
145
+ Returns:
146
+ Label: The new label.
129
147
 
130
- def new(
131
- self,
132
- name: str,
133
- shape: Collection[int],
134
- xy_pixelsize: float,
135
- z_spacing: float = 1.0,
136
- time_spacing: float = 1.0,
137
- levels: "int | list[str]" = 5,
138
- xy_scaling_factor: float = 2.0,
139
- z_scaling_factor: float = 1.0,
140
- space_unit: SpaceUnits | str | None = None,
141
- time_unit: TimeUnits | str | None = None,
142
- axes_names: Collection[str] | None = None,
143
- chunks: Collection[int] | None = None,
144
- dtype: str = "uint16",
145
- overwrite: bool = False,
146
- version: str = "0.4",
147
- ) -> None:
148
- """Add a label to the group."""
148
+ """
149
149
  existing_labels = self.list()
150
150
  if name in existing_labels and not overwrite:
151
151
  raise NgioValueError(
@@ -155,82 +155,127 @@ class LabelsContainer:
155
155
 
156
156
  label_group = self._group_handler.get_group(name, create_mode=True)
157
157
 
158
- _create_empty_label(
158
+ _derive_label(
159
159
  store=label_group,
160
+ ref_image=ref_image,
161
+ name=name,
160
162
  shape=shape,
161
- xy_pixelsize=xy_pixelsize,
162
- z_spacing=z_spacing,
163
- time_spacing=time_spacing,
164
- levels=levels,
165
- xy_scaling_factor=xy_scaling_factor,
166
- z_scaling_factor=z_scaling_factor,
167
- space_unit=space_unit,
168
- time_unit=time_unit,
163
+ pixel_size=pixel_size,
169
164
  axes_names=axes_names,
170
165
  chunks=chunks,
171
166
  dtype=dtype,
172
167
  overwrite=overwrite,
173
- version=version,
174
168
  )
175
169
 
176
170
  if name not in existing_labels:
177
171
  existing_labels.append(name)
178
172
  self._group_handler.write_attrs({"labels": existing_labels})
179
173
 
174
+ return self.get(name)
175
+
180
176
 
181
177
  def _derive_label(
182
- ref_image: Image,
183
178
  store: StoreOrGroup,
179
+ ref_image: Image,
180
+ name: str,
184
181
  shape: Collection[int] | None = None,
182
+ pixel_size: PixelSize | None = None,
183
+ axes_names: Collection[str] | None = None,
185
184
  chunks: Collection[int] | None = None,
186
- dtype: str = "uint16",
187
- xy_scaling_factor=2.0,
188
- z_scaling_factor=1.0,
185
+ dtype: str | None = None,
189
186
  overwrite: bool = False,
190
187
  ) -> None:
191
- """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
+ """
192
206
  ref_meta = ref_image.meta
193
- # remove channls if present
194
- shape_ref = ref_image.shape
195
- chunks_ref = ref_image.chunks
196
- axes_names_ref = ref_image.dataset.axes_mapper.on_disk_axes_names
197
- c_axis = ref_image.dataset.axes_mapper.get_index("c")
198
- if c_axis is not None:
199
- shape_ref = shape_ref[:c_axis] + shape_ref[c_axis + 1 :]
200
- chunks_ref = chunks_ref[:c_axis] + chunks_ref[c_axis + 1 :]
201
- axes_names_ref = axes_names_ref[:c_axis] + axes_names_ref[c_axis + 1 :]
202
207
 
203
208
  if shape is None:
204
- shape = shape_ref
209
+ shape = ref_image.shape
205
210
 
206
- if chunks is None:
207
- chunks = chunks_ref
211
+ if pixel_size is None:
212
+ pixel_size = ref_image.pixel_size
208
213
 
209
- if len(shape) != len(shape_ref):
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
224
+
225
+ if len(axes_names) != len(shape):
210
226
  raise NgioValidationError(
211
- "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}."
212
229
  )
213
230
 
214
- if len(chunks) != len(chunks_ref):
231
+ if chunks is None:
232
+ chunks = ref_image.chunks
233
+
234
+ if len(chunks) != len(shape):
215
235
  raise NgioValidationError(
216
236
  "The chunks of the new image does not match the reference image."
237
+ f"Got {chunks} for shape {shape}."
217
238
  )
218
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
+
219
252
  _ = _create_empty_label(
220
253
  store=store,
221
254
  shape=shape,
222
- xy_pixelsize=ref_image.pixel_size.x,
255
+ pixelsize=ref_image.pixel_size.x,
223
256
  z_spacing=ref_image.pixel_size.z,
224
257
  time_spacing=ref_image.pixel_size.t,
225
258
  levels=ref_meta.levels,
226
- xy_scaling_factor=xy_scaling_factor,
227
- z_scaling_factor=z_scaling_factor,
259
+ yx_scaling_factor=ref_meta.yx_scaling(),
260
+ z_scaling_factor=ref_meta.z_scaling(),
228
261
  time_unit=ref_image.pixel_size.time_unit,
229
262
  space_unit=ref_image.pixel_size.space_unit,
230
- axes_names=axes_names_ref,
263
+ axes_names=axes_names,
231
264
  chunks=chunks,
232
265
  dtype=dtype,
233
266
  overwrite=overwrite,
234
267
  version=ref_meta.version,
268
+ name=name,
235
269
  )
236
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
+ )