OME-Zarr Image Exploration¶
In this notebook we will show how to use the 'NgffImage' class to explore and manage an OME-NGFF image.
For this example we will use a small example image that can be downloaded from the following link: example ome-zarr
Setup¶
You can download the example image (on Linux and Mac os) by running the following command:
bash setup_data.sh
from the root of the repository.
NgffImage¶
The NgffImage
provides a high-level interface to read, write and manipulate NGFF images.
A NgffImage
can be created from a storelike object (e.g. a path to a directory, or a url) or from a zarr.Group
object.
from ngio.core import NgffImage
# Ngio can stream data from any fsspec-compatible store
path = "../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0"
ngff_image = NgffImage(path, "r")
The ngff_image
object provides a high-level interface to read, write and manipulate OME-Zarr images.
Print the image will show some overview information like:
- The path to the image
- The multiscale pyramid paths
- The labels contained in the image
- The tables contained in the imag
print(ngff_image)
NGFFImage(group_path=/home/runner/work/ngio/ngio/data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/, paths=['0', '1', '2', '3', '4'], labels=['nuclei', 'wf_2_labels', 'wf_3_labels', 'wf_4_labels'], tables=['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4'], )
From the NgffImage
object we can easily access access the image data (at any resolution level), the labels and the tables.
Get a single level
of the image pyramid as Image
(to know more about the Image
class, please refer to the Image notebook
The Image
object is the main object to interact with the image.
It contains methods to interact with the image data and metadata.
from ngio.ngff_meta import PixelSize
# 1. Get image from highest resolution (default)
image = ngff_image.get_image()
print(image)
# 2. Get image from a specific level using the path keyword
image = ngff_image.get_image(path="1")
print(image)
# 3. Get image from a specific pixel size using the pixel_size keyword
image = ngff_image.get_image(pixel_size=PixelSize(x=0.65, y=0.65, z=1))
print(image)
Image(group_path=/home/runner/work/ngio/ngio/data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/, path=0, PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer), Dimensions(c=3, z=1, y=4320, x=5120), ) Image(group_path=/home/runner/work/ngio/ngio/data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/, path=1, PixelSize(x=0.325, y=0.325, z=1.0, unit=micrometer), Dimensions(c=3, z=1, y=2160, x=2560), ) Image(group_path=/home/runner/work/ngio/ngio/data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/, path=2, PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer), Dimensions(c=3, z=1, y=1080, x=1280), )
The Image
object provides a high-level interface to read and write image data at a specific resolution level.
print("Shape", image.shape)
print("Axes", image.axes_names)
print("PixelSize", image.pixel_size)
print("Dimensions", image.dimensions)
print("Channel Names", image.channel_labels)
Shape (3, 1, 1080, 1280) Axes ['c', 'z', 'y', 'x'] PixelSize PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer) Dimensions Dimensions(c=3, z=1, y=1080, x=1280) Channel Names ['DAPI', 'nanog', 'Lamin B1']
# Get data as a numpy array or a dask array
data = image.get_array(c=0, mode="numpy")
print(data)
dask_data = image.get_array(c=0, mode="dask")
dask_data
[[[280 258 223 ... 252 248 274] [288 284 269 ... 206 236 259] [334 358 351 ... 212 240 259] ... [ 70 87 102 ... 2 5 4] [186 184 209 ... 2 4 4] [209 220 223 ... 4 1 7]]]
|
ngio
design is to always provide the data in a canonical axis order (t
, c
, z
, y
, x
) no matter what is the order on disk.
The Image
object provides methods to access the data in this order.
If you want to access data or metadata in the on-disk order, you can by using on_disk_{method_name}
methods.
print("On-disk shape", image.on_disk_shape)
print("On-disk array", image.on_disk_array)
print("On-disk dask array", image.on_disk_dask_array)
On-disk shape (3, 1, 1080, 1280) On-disk array <zarr.core.Array '/2' (3, 1, 1080, 1280) uint16> On-disk dask array dask.array<from-zarr, shape=(3, 1, 1080, 1280), dtype=uint16, chunksize=(1, 1, 1080, 1280), chunktype=numpy.ndarray>
Labels¶
The NgffImage
can also be used to load labels from a OME-NGFF
file and behave similarly to the Image
object.
print("List of Labels: ", ngff_image.labels.list())
label_nuclei = ngff_image.labels.get_label("nuclei", path="0")
print(label_nuclei)
List of Labels: ['nuclei', 'wf_2_labels', 'wf_3_labels', 'wf_4_labels'] Label(group_path=/home/runner/work/ngio/ngio/data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/labels/nuclei, path=0, name=nuclei, PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer), Dimensions(z=1, y=4320, x=5120), )
Tables¶
The NgffImage
can also be used to load tables from a OME-NGFF
file.
ngio
supports three types of tables:
features table
A simple table to store features associated with a label.roi table
A table to store regions of interest.masking roi tables
A table to store single objects bounding boxes associated with a label.
print("List of Tables: ", ngff_image.tables.list())
print(" - Feature tables: ", ngff_image.tables.list(table_type='feature_table'))
print(" - Roi tables: ", ngff_image.tables.list(table_type='roi_table'))
print(" - Masking Roi tables: ", ngff_image.tables.list(table_type='masking_roi_table'))
List of Tables: ['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4'] - Feature tables: ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4'] - Roi tables: ['FOV_ROI_table', 'well_ROI_table'] - Masking Roi tables: ['nuclei_ROI_table']
# Loading a table
feature_table = ngff_image.tables.get_table("regionprops_DAPI")
feature_table.table
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index. warnings.warn("Transforming to str index.", ImplicitModificationWarning)
area | bbox_area | equivalent_diameter | max_intensity | mean_intensity | min_intensity | standard_deviation_intensity | |
---|---|---|---|---|---|---|---|
label | |||||||
1 | 2120.0 | 2655.0 | 15.938437 | 476.0 | 278.635864 | 86.0 | 54.343792 |
2 | 327.0 | 456.0 | 8.547709 | 604.0 | 324.162079 | 118.0 | 90.847092 |
3 | 1381.0 | 1749.0 | 13.816510 | 386.0 | 212.682114 | 60.0 | 50.169601 |
4 | 2566.0 | 3588.0 | 16.985800 | 497.0 | 251.731491 | 61.0 | 53.307186 |
5 | 4201.0 | 5472.0 | 20.019413 | 466.0 | 223.862885 | 51.0 | 56.719025 |
... | ... | ... | ... | ... | ... | ... | ... |
3002 | 1026.0 | 1288.0 | 12.513618 | 589.0 | 308.404480 | 132.0 | 64.681778 |
3003 | 859.0 | 1080.0 | 11.794101 | 400.0 | 270.349243 | 107.0 | 49.040470 |
3004 | 508.0 | 660.0 | 9.899693 | 314.0 | 205.043304 | 82.0 | 33.249981 |
3005 | 369.0 | 440.0 | 8.899028 | 376.0 | 217.970184 | 82.0 | 50.978519 |
3006 | 278.0 | 330.0 | 8.097459 | 339.0 | 217.996399 | 100.0 | 38.510067 |
3006 rows × 7 columns
# Loading a roi table
roi_table = ngff_image.tables.get_table("FOV_ROI_table")
print(f"{roi_table.field_indexes=}")
print(f"{roi_table.get_roi('FOV_1')=}")
roi_table.table
roi_table.field_indexes=['FOV_1', 'FOV_2', 'FOV_3', 'FOV_4'] roi_table.get_roi('FOV_1')=WorldCooROI(x_length=416.0, y_length=351.0, z_length=1.0, x=0.0, y=0.0, z=0.0)
x_micrometer | y_micrometer | z_micrometer | len_x_micrometer | len_y_micrometer | len_z_micrometer | x_micrometer_original | y_micrometer_original | |
---|---|---|---|---|---|---|---|---|
FieldIndex | ||||||||
FOV_1 | 0.0 | 0.0 | 0.0 | 416.0 | 351.0 | 1.0 | -1448.300049 | -1517.699951 |
FOV_2 | 416.0 | 0.0 | 0.0 | 416.0 | 351.0 | 1.0 | -1032.300049 | -1517.699951 |
FOV_3 | 0.0 | 351.0 | 0.0 | 416.0 | 351.0 | 1.0 | -1448.300049 | -1166.699951 |
FOV_4 | 416.0 | 351.0 | 0.0 | 416.0 | 351.0 | 1.0 | -1032.300049 | -1166.699951 |
Rois can be used to index image and label data.
import matplotlib.pyplot as plt
# Plotting a single ROI
roi = roi_table.get_roi("FOV_1")
roi_data = image.get_array_from_roi(roi, c=0, mode="numpy")
plt.title("ROI: FOV_1")
plt.imshow(roi_data[0], cmap="gray")
plt.axis("off")
plt.show()
Derive a new NgffImage¶
When processing an image, it is often useful to derive a new image from the original image.
The NgffImage
class provides a method to derive a new image from the original image.
When deriving a new image, a new NgffImage
object is created with the same metadata as the original image. Optionally the
user can specify different metadata for the new image(.e.g. different channels names).
new_ngff_image = ngff_image.derive_new_image("../../data/new_ome.zarr", name="new_image")
print(new_ngff_image)
NGFFImage(group_path=/home/runner/work/ngio/ngio/data/new_ome.zarr/, paths=['0', '1', '2', '3', '4'], labels=[], tables=[], )
Steam an NgffImage over HTTP¶
The NgffImage
class can also be used to stream an image over HTTP. This is useful when the image is stored on a remote server and you want to access it without downloading the entire image. All features of the NgffImage
class are available when streaming an image over HTTP (besides anything that requires writing to the image).
from ngio.core.utils import get_fsspec_http_store
# Ngio can stream data from any fsspec-compatible store
url = "https://raw.githubusercontent.com/fractal-analytics-platform/fractal-ome-zarr-examples/refs/heads/main/v04/20200812-CardiomyocyteDifferentiation14-Cycle1_B_03_mip.zarr/"
store = get_fsspec_http_store(url)
ngff_image = NgffImage(store, "r")
print(ngff_image)
NGFFImage(group_path=https://raw.githubusercontent.com/fractal-analytics-platform/fractal-ome-zarr-examples/refs/heads/main/v04/20200812-CardiomyocyteDifferentiation14-Cycle1_B_03_mip.zarr/, paths=['0', '1', '2', '3'], labels=['nuclei'], tables=['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI'], )