Skip to content

4. Masked images and labels

Read and write image data one object at a time.

A masked image is an image paired with an instance segmentation. Instead of slicing by coordinates, you address the data by label id, and you can restrict reads and writes to the pixels belonging to that object.

From a label image to a single masked object Each object in a label image gets one row in a masking ROI table. Selecting a row returns just that object's bounding box, with the pixels outside the object masked out. LABEL IMAGE MASKING ROI TABLE ONE OBJECT 1 2 4 3 each object carries an integer id label bounding box 187 × 53 395 × 65 459 × 45 one row per object, in world units label 3 its own small array
from pathlib import Path

from ngio import open_ome_zarr_container
from ngio.utils import download_ome_zarr_dataset

# Download a sample dataset
download_dir = Path("./data").absolute()
hcs_path = download_ome_zarr_dataset("CardiomyocyteSmallMip", download_dir=download_dir)
image_path = hcs_path / "B" / "03" / "0"

# Open the OME-Zarr container
ome_zarr_container = open_ome_zarr_container(image_path)

Like the Image and Label objects, a MaskedImage is initialised from an OME-Zarr container, using the get_masked_image method.

Create a masked image from the nuclei label:

masked_image = ome_zarr_container.get_masked_image("nuclei")
print(masked_image)
MaskedImage(path=0, Dimensions(c: 3, z: 1, y: 4320, x: 5120), nuclei)

Since MaskedImage is a subclass of Image, you can use every method available on Image objects.

The two most notable exceptions are get_roi_as_numpy (or get_roi_as_dask) and set_roi, which now take an integer label instead of a roi object.

roi_data = masked_image.get_roi_as_numpy(label=1009, c=0)
print(roi_data.shape)
(1, 76, 83)

2026-08-11T12:16:19.700764 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ Label 1009 ROI

You can also use the zoom_factor argument to get more context around the ROI. For example, zoom out the ROI by a factor of 2:

roi_data = masked_image.get_roi_as_numpy(label=1009, c=0, zoom_factor=2)
print(roi_data.shape)
(1, 152, 167)
2026-08-11T12:16:19.780469 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ Label 1009 ROI - Zoomed out

Masked operations

get_roi_as_numpy returns the object's whole bounding box, neighbouring objects included. To read or write only the pixels that belong to the object, use the masked operations: get_roi_masked_as_numpy, get_roi_masked_as_dask and set_roi_masked. Everything outside the mask comes back zeroed, and on write is left untouched.

For example, read the masked data for one label:

masked_roi_data = masked_image.get_roi_masked_as_numpy(label=1009, c=0, zoom_factor=2)
print(masked_roi_data.shape)
(1, 152, 167)
2026-08-11T12:16:19.929438 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ Masked Label 1009 ROI

And write it back with set_roi_masked, which only touches the pixels inside the mask:

import numpy as np

masked_data = masked_image.get_roi_masked_as_numpy(label=1009, c=0)
masked_data = np.random.randint(0, 255, masked_data.shape, dtype=np.uint8)
masked_image.set_roi_masked(label=1009, c=0, patch=masked_data)
2026-08-11T12:16:20.117270 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ Masked Label 1009 ROI - After setting

Masked labels

The MaskedLabel class is a subclass of Label and provides the same functionality as the MaskedImage class.

Create a masked label from an OME-Zarr container using the get_masked_label method.

masked_label = ome_zarr_container.get_masked_label(
    label_name="wf_2_labels", masking_label_name="nuclei"
)
print(masked_label)
MaskedLabel(path=0, Dimensions(z: 1, y: 4320, x: 5120), nuclei)

Next steps

  • HCS plates — scale up from a single image to a whole plate.
  • Iterators — process every object or region without writing the loop yourself.