diff --git a/api/core/index.html b/api/core/index.html index ad7e8ea..8fb5f8d 100644 --- a/api/core/index.html +++ b/api/core/index.html @@ -1507,7 +1507,7 @@

- October 7, 2024 + October 8, 2024 @@ -1517,7 +1517,7 @@

- October 7, 2024 + October 8, 2024 diff --git a/getting-started/index.html b/getting-started/index.html index 390941c..da6aa9b 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -677,7 +677,7 @@

Example Usage

- October 7, 2024 + October 8, 2024 @@ -687,7 +687,7 @@

Example Usage

- October 7, 2024 + October 8, 2024 diff --git a/index.html b/index.html index 77ad3b6..0c5cd22 100644 --- a/index.html +++ b/index.html @@ -793,7 +793,7 @@

License

- October 7, 2024 + October 8, 2024 @@ -803,7 +803,7 @@

License

- October 7, 2024 + October 8, 2024 diff --git a/notebooks/basic_usage/index.html b/notebooks/basic_usage/index.html index d74378b..50901f7 100644 --- a/notebooks/basic_usage/index.html +++ b/notebooks/basic_usage/index.html @@ -1859,7 +1859,7 @@

Derive a new NgffImage - October 7, 2024 + October 8, 2024 @@ -1869,7 +1869,7 @@

Derive a new NgffImage - October 7, 2024 + October 8, 2024 diff --git a/notebooks/image/index.html b/notebooks/image/index.html index 0130457..b605392 100644 --- a/notebooks/image/index.html +++ b/notebooks/image/index.html @@ -1170,7 +1170,7 @@

ImagesΒΆ

Images image.axes_names=['c', 'z', 'y', 'x'] image.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer) image.channel_labels=['DAPI', 'nanog', 'Lamin B1'] -image.dimensions=<ngio.core.dimensions.Dimensions object at 0x7f06fcdbdd60> +image.dimensions=<ngio.core.dimensions.Dimensions object at 0x7f65aed0d400> @@ -2087,7 +2087,7 @@

Creating a new table - October 7, 2024 + October 8, 2024 @@ -2097,7 +2097,7 @@

Creating a new table - October 7, 2024 + October 8, 2024 diff --git a/notebooks/processing/index.html b/notebooks/processing/index.html index 76d614b..d3d8e45 100644 --- a/notebooks/processing/index.html +++ b/notebooks/processing/index.html @@ -1903,7 +1903,7 @@

step 2: Run the workflow @@ -1939,7 +1939,7 @@

step 2: Run the workflow - October 7, 2024 + October 8, 2024 @@ -1949,7 +1949,7 @@

step 2: Run the workflow - October 7, 2024 + October 8, 2024 diff --git a/search/search_index.json b/search/search_index.json index d69bb1f..d5667a2 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to NGIO","text":"

NGIO is a Python library to streamline OME-Zarr image analysis workflows.

Main Goals:

  • Abstract object base API for handling OME-Zarr files
  • Powefull iterators for processing data using common access patterns
  • Tight integration with Fractal's Table Fractal
  • Validate OME-Zarr files

To get started, check out the Getting Started guide.

"},{"location":"#ngio-is-under-active-development","title":"\ud83d\udea7 Ngio is Under active Development \ud83d\udea7","text":""},{"location":"#roadmap","title":"Roadmap","text":"Feature Status ETA Description Metadata Handling \u2705 Read, Write, Validate OME-Zarr Metadata (0.4 supported, 0.5 ready) OME-Zarr Validation \u2705 Validate OME-Zarr files for compliance with the OME-Zarr Specification + Compliance between Metadata and Data Base Image Handling \u2705 Load data from OME-Zarr files, retrieve basic metadata, and write data ROI Handling \u2705 Common ROI models Label Handling \u2705 Mid-September Based on Image Handling Table Validation \u2705 Mid-September Validate Table fractal V1 + Compliance between Metadata and Data Table Handling \u2705 Mid-September Read, Write ROI, Features, and Masked Tables Basic Iterators Ongoing End-September Read and Write Iterators for common access patterns Base Documentation \u2705 End-September API Documentation and Examples Beta Ready Testing \u2705 End-September Beta Testing; Library is ready for testing, but the API is not stable Mask Iterators Ongoing October Iterators over Masked Tables Advanced Iterators Not started October Iterators for advanced access patterns Parallel Iterators Not started End of the Year Concurrent Iterators for parallel read and write Full Documentation Not started End of the Year Complete Documentation Release 1.0 (Commitment to API) Not started End of the Year API is stable; breaking changes will be avoided"},{"location":"#contributors","title":"Contributors","text":"

ngio is developed at the BioVisionCenter at the University of Zurich. The main contributors are: @lorenzocerrone, @jluethi.

"},{"location":"#license","title":"License","text":"

ngio is released according to the BSD-3-Clause License. See LICENSE

"},{"location":"getting-started/","title":"Getting Started","text":"

Warning

The library is still in development and is not yet stable. The API is subject to change, bugs and breaking changes are expected.

Warning

The documentation is still under development. It is not yet complete and may contain errors and inaccuracies.

"},{"location":"getting-started/#installation","title":"Installation","text":"

Currently, ngio is not available on PyPI. You can install it from the source code.

pip install \"ngio[v2]\"\n

The v2 extra installs the latest version of zarr-python from the v2 branch. ngio is currently not completely compatible with the v3 release of zarr-python.

"},{"location":"getting-started/#ngio-api-overview","title":"ngio API Overview","text":"

ngio implements an abstract object base API for handling OME-Zarr files. The three main objects are NgffImage, Image (Label), and ROITables.

  • NgffImage is the main entry point to the library. It is used to open an OME-Zarr Image and manage its metadata. This object can not be used to access the data directly. but it can be used to access and create the Image, Label, and Tables objects. Moreover it can be used to derive a new Ngff images based on the current one.
  • Image and Label are used to access \"ImageLike\" objects. They are the main objects to access the data in the OME-Zarr file, manage the metadata, and write data.
  • ROITables can be used to access specific region of interest in the image. They are tightly integrated with the Image and Label objects.
"},{"location":"getting-started/#example-usage","title":"Example Usage","text":"

Currently, the library is not yet stable. However, you can see some example usage in our demo notebooks.

  • Basic Usage
  • Image/Label/Tables
  • Processing
"},{"location":"api/core/","title":"ngio.core","text":""},{"location":"api/core/#ngio.core","title":"ngio.core","text":"

Core classes for the ngio library.

"},{"location":"api/core/#ngffimage","title":"NGFFImage","text":""},{"location":"api/core/#ngio.core.NgffImage","title":"ngio.core.NgffImage","text":"

A class to handle OME-NGFF images.

Source code in ngio/core/ngff_image.py
class NgffImage:\n    \"\"\"A class to handle OME-NGFF images.\"\"\"\n\n    def __init__(self, store: StoreLike) -> None:\n        \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n        self.store = store\n        self.group = open_group_wrapper(store=store, mode=\"r+\")\n        self._image_meta = get_ngff_image_meta_handler(\n            self.group, meta_mode=\"image\", cache=False\n        )\n\n        self.table = TableGroup(self.group)\n        self.label = LabelGroup(self.group, image_ref=self.get_image())\n\n    @property\n    def image_meta(self) -> ImageMeta:\n        \"\"\"Get the image metadata.\"\"\"\n        return self._image_meta.load_meta()\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"Get the number of levels in the image.\"\"\"\n        return self.image_meta.num_levels\n\n    @property\n    def levels_paths(self) -> list[str]:\n        \"\"\"Get the paths of the levels in the image.\"\"\"\n        return self.image_meta.levels_paths\n\n    def get_image(\n        self,\n        *,\n        path: str | None = None,\n        pixel_size: PixelSize | None = None,\n        highest_resolution: bool = True,\n    ) -> Image:\n        \"\"\"Get an image handler for the given level.\n\n        Args:\n            path (str | None, optional): The path to the level.\n            pixel_size (tuple[float, ...] | list[float] | None, optional): The pixel\n                size of the level.\n            highest_resolution (bool, optional): Whether to get the highest\n                resolution level\n\n        Returns:\n            ImageHandler: The image handler.\n        \"\"\"\n        if path is not None or pixel_size is not None:\n            highest_resolution = False\n\n        return Image(\n            store=self.group,\n            path=path,\n            pixel_size=pixel_size,\n            highest_resolution=highest_resolution,\n        )\n\n    def _update_omero_window(self) -> None:\n        \"\"\"Update the OMERO window.\"\"\"\n        meta = self.image_meta\n        image = self.get_image(highest_resolution=True)\n        max_dtype = np.iinfo(image.array.dtype).max\n        start, end = image.dask_array.min().compute(), image.dask_array.max().compute()\n\n        channel_list = meta.omero.channels\n\n        new_channel_list = []\n        for channel in channel_list:\n            channel.extra_fields[\"window\"] = {\n                \"start\": start,\n                \"end\": end,\n                \"min\": 0,\n                \"max\": max_dtype,\n            }\n            new_channel_list.append(channel)\n\n        meta.omero.channels = new_channel_list\n        self._image_meta.write_meta(meta)\n\n    def derive_new_image(\n        self,\n        store: StoreLike,\n        name: str,\n        overwrite: bool = True,\n        **kwargs,\n    ) -> \"NgffImage\":\n        \"\"\"Derive a new image from the current image.\n\n        Args:\n            store (StoreLike): The store to create the new image in.\n            name (str): The name of the new image.\n            overwrite (bool): Whether to overwrite the image if it exists\n            **kwargs: Additional keyword arguments.\n                Follow the same signature as `create_empty_ome_zarr_image`.\n\n        Returns:\n            NgffImage: The new image.\n        \"\"\"\n        image_0 = self.get_image(highest_resolution=True)\n\n        default_kwargs = {\n            \"store\": store,\n            \"shape\": image_0.on_disk_shape,\n            \"chunks\": image_0.array.chunks,\n            \"dtype\": image_0.array.dtype,\n            \"on_disk_axis\": image_0.dataset.on_disk_axes_names,\n            \"pixel_sizes\": image_0.pixel_size,\n            \"xy_scaling_factor\": self.image_meta.xy_scaling_factor,\n            \"z_scaling_factor\": self.image_meta.z_scaling_factor,\n            \"time_spacing\": image_0.dataset.time_spacing,\n            \"time_units\": image_0.dataset.time_axis_unit,\n            \"num_levels\": self.num_levels,\n            \"name\": name,\n            \"channel_labels\": image_0.channel_labels,\n            \"channel_wavelengths\": None,\n            \"channel_kwargs\": None,\n            \"omero_kwargs\": None,\n            \"overwrite\": overwrite,\n            \"version\": self.image_meta.version,\n        }\n\n        default_kwargs.update(kwargs)\n\n        create_empty_ome_zarr_image(\n            **default_kwargs,\n        )\n        return NgffImage(store=store)\n
"},{"location":"api/core/#ngio.core.NgffImage.image_meta","title":"image_meta: ImageMeta property","text":"

Get the image metadata.

"},{"location":"api/core/#ngio.core.NgffImage.levels_paths","title":"levels_paths: list[str] property","text":"

Get the paths of the levels in the image.

"},{"location":"api/core/#ngio.core.NgffImage.num_levels","title":"num_levels: int property","text":"

Get the number of levels in the image.

"},{"location":"api/core/#ngio.core.NgffImage.__init__","title":"__init__(store: StoreLike) -> None","text":"

Initialize the NGFFImage in read mode.

Source code in ngio/core/ngff_image.py
def __init__(self, store: StoreLike) -> None:\n    \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n    self.store = store\n    self.group = open_group_wrapper(store=store, mode=\"r+\")\n    self._image_meta = get_ngff_image_meta_handler(\n        self.group, meta_mode=\"image\", cache=False\n    )\n\n    self.table = TableGroup(self.group)\n    self.label = LabelGroup(self.group, image_ref=self.get_image())\n
"},{"location":"api/core/#ngio.core.NgffImage.derive_new_image","title":"derive_new_image(store: StoreLike, name: str, overwrite: bool = True, **kwargs) -> NgffImage","text":"

Derive a new image from the current image.

Parameters:

  • store (StoreLike) \u2013

    The store to create the new image in.

  • name (str) \u2013

    The name of the new image.

  • overwrite (bool, default: True ) \u2013

    Whether to overwrite the image if it exists

  • **kwargs \u2013

    Additional keyword arguments. Follow the same signature as create_empty_ome_zarr_image.

Returns:

  • NgffImage ( NgffImage ) \u2013

    The new image.

Source code in ngio/core/ngff_image.py
def derive_new_image(\n    self,\n    store: StoreLike,\n    name: str,\n    overwrite: bool = True,\n    **kwargs,\n) -> \"NgffImage\":\n    \"\"\"Derive a new image from the current image.\n\n    Args:\n        store (StoreLike): The store to create the new image in.\n        name (str): The name of the new image.\n        overwrite (bool): Whether to overwrite the image if it exists\n        **kwargs: Additional keyword arguments.\n            Follow the same signature as `create_empty_ome_zarr_image`.\n\n    Returns:\n        NgffImage: The new image.\n    \"\"\"\n    image_0 = self.get_image(highest_resolution=True)\n\n    default_kwargs = {\n        \"store\": store,\n        \"shape\": image_0.on_disk_shape,\n        \"chunks\": image_0.array.chunks,\n        \"dtype\": image_0.array.dtype,\n        \"on_disk_axis\": image_0.dataset.on_disk_axes_names,\n        \"pixel_sizes\": image_0.pixel_size,\n        \"xy_scaling_factor\": self.image_meta.xy_scaling_factor,\n        \"z_scaling_factor\": self.image_meta.z_scaling_factor,\n        \"time_spacing\": image_0.dataset.time_spacing,\n        \"time_units\": image_0.dataset.time_axis_unit,\n        \"num_levels\": self.num_levels,\n        \"name\": name,\n        \"channel_labels\": image_0.channel_labels,\n        \"channel_wavelengths\": None,\n        \"channel_kwargs\": None,\n        \"omero_kwargs\": None,\n        \"overwrite\": overwrite,\n        \"version\": self.image_meta.version,\n    }\n\n    default_kwargs.update(kwargs)\n\n    create_empty_ome_zarr_image(\n        **default_kwargs,\n    )\n    return NgffImage(store=store)\n
"},{"location":"api/core/#ngio.core.NgffImage.get_image","title":"get_image(*, path: str | None = None, pixel_size: PixelSize | None = None, highest_resolution: bool = True) -> Image","text":"

Get an image handler for the given level.

Parameters:

  • path (str | None, default: None ) \u2013

    The path to the level.

  • pixel_size (tuple[float, ...] | list[float] | None, default: None ) \u2013

    The pixel size of the level.

  • highest_resolution (bool, default: True ) \u2013

    Whether to get the highest resolution level

Returns:

  • ImageHandler ( Image ) \u2013

    The image handler.

Source code in ngio/core/ngff_image.py
def get_image(\n    self,\n    *,\n    path: str | None = None,\n    pixel_size: PixelSize | None = None,\n    highest_resolution: bool = True,\n) -> Image:\n    \"\"\"Get an image handler for the given level.\n\n    Args:\n        path (str | None, optional): The path to the level.\n        pixel_size (tuple[float, ...] | list[float] | None, optional): The pixel\n            size of the level.\n        highest_resolution (bool, optional): Whether to get the highest\n            resolution level\n\n    Returns:\n        ImageHandler: The image handler.\n    \"\"\"\n    if path is not None or pixel_size is not None:\n        highest_resolution = False\n\n    return Image(\n        store=self.group,\n        path=path,\n        pixel_size=pixel_size,\n        highest_resolution=highest_resolution,\n    )\n
"},{"location":"notebooks/basic_usage/","title":"Basic Usage","text":"In\u00a0[1]: Copied!
from ngio.core import NgffImage\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")\n
from ngio.core import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")

The ngff_image contains several attributes and methods to interact with the OME-NGFF (OME-Zarr) file on the storage.

In\u00a0[2]: Copied!
# Explore object metadata\nprint(\"Levels: \", ngff_image.levels_paths)\nprint(\"Num Levels: \", ngff_image.num_levels)\n
# Explore object metadata print(\"Levels: \", ngff_image.levels_paths) print(\"Num Levels: \", ngff_image.num_levels)
Levels:  ['0', '1', '2', '3', '4']\nNum Levels:  5\n

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.

In\u00a0[3]: Copied!
from ngio.ngff_meta import PixelSize\n\n# 1. Get image from highest resolution (default)\nimage = ngff_image.get_image()\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n\n# 2. Get image from a specific level using the path keyword\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n\n# 3. Get image from a specific pixel size using the pixel_size keyword\nimage = ngff_image.get_image(pixel_size=PixelSize(x=0.65, y=0.65, z=1))\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n
from ngio.ngff_meta import PixelSize # 1. Get image from highest resolution (default) image = ngff_image.get_image() print(f\"{image.shape=}\") print(f\"{image.pixel_size=}\") # 2. Get image from a specific level using the path keyword print(f\"{image.shape=}\") print(f\"{image.pixel_size=}\") # 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(f\"{image.shape=}\") print(f\"{image.pixel_size=}\")
image.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.shape=(3, 1, 1080, 1280)\nimage.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer)\n
In\u00a0[4]: Copied!
print(\"List of Labels: \", ngff_image.label.list())\n\nlabel_nuclei = ngff_image.label.get(\"nuclei\", path=\"0\")\nprint(f\"{label_nuclei.shape=}\")\nprint(f\"{label_nuclei.pixel_size=}\")\n
print(\"List of Labels: \", ngff_image.label.list()) label_nuclei = ngff_image.label.get(\"nuclei\", path=\"0\") print(f\"{label_nuclei.shape=}\") print(f\"{label_nuclei.pixel_size=}\")
List of Labels:  ['nuclei', 'wf_2_labels', 'wf_3_labels', 'wf_4_labels']\nlabel_nuclei.shape=(1, 4320, 5120)\nlabel_nuclei.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\n
In\u00a0[5]: Copied!
print(\"List of Tables: \", ngff_image.table.list())\nprint(\" - Feature tables: \", ngff_image.table.list(type='feature_table'))\nprint(\" - Roi tables: \", ngff_image.table.list(type='roi_table'))\nprint(\" - Masking Roi tables: \", ngff_image.table.list(type='masking_roi_table'))\n
print(\"List of Tables: \", ngff_image.table.list()) print(\" - Feature tables: \", ngff_image.table.list(type='feature_table')) print(\" - Roi tables: \", ngff_image.table.list(type='roi_table')) print(\" - Masking Roi tables: \", ngff_image.table.list(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']\n - Feature tables:  ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n - Roi tables:  ['FOV_ROI_table', 'well_ROI_table']\n - Masking Roi tables:  ['nuclei_ROI_table']\n
In\u00a0[6]: Copied!
# Loading a table\nfeature_table = ngff_image.table.get(\"regionprops_DAPI\")\nfeature_table.table\n
# Loading a table feature_table = ngff_image.table.get(\"regionprops_DAPI\") feature_table.table
/opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\n  warnings.warn(\"Transforming to str index.\", ImplicitModificationWarning)\n
Out[6]: 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 \u00d7 7 columns

In\u00a0[7]: Copied!
# Loading a roi table\nroi_table = ngff_image.table.get(\"FOV_ROI_table\")\n\nprint(f\"{roi_table.list_field_indexes=}\")\nprint(f\"{roi_table.get_roi(\"FOV_1\")=}\")\n\nroi_table.table\n
# Loading a roi table roi_table = ngff_image.table.get(\"FOV_ROI_table\") print(f\"{roi_table.list_field_indexes=}\") print(f\"{roi_table.get_roi(\"FOV_1\")=}\") roi_table.table
roi_table.list_field_indexes=['FOV_1', 'FOV_2', 'FOV_3', 'FOV_4']\nroi_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)\n
Out[7]: 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 In\u00a0[8]: Copied!
new_ngff_image = ngff_image.derive_new_image(\"../../data/new_ome.zarr\", name=\"new_image\")\n\nprint(f\"{new_ngff_image.store=}\")\nprint(f\"{new_ngff_image.levels_paths=}\")\nprint(f\"{new_ngff_image.num_levels=}\")\n
new_ngff_image = ngff_image.derive_new_image(\"../../data/new_ome.zarr\", name=\"new_image\") print(f\"{new_ngff_image.store=}\") print(f\"{new_ngff_image.levels_paths=}\") print(f\"{new_ngff_image.num_levels=}\")
new_ngff_image.store='../../data/new_ome.zarr'\nnew_ngff_image.levels_paths=['0', '1', '2', '3', '4']\nnew_ngff_image.num_levels=5\n
In\u00a0[\u00a0]: Copied!
\n
"},{"location":"notebooks/basic_usage/#basic-usage","title":"Basic Usage\u00b6","text":"

In this notebook we will show how to use the 'ngffImage' class to manage a OME-NGFF image.

For this example we will use a small example image that can be downloaded from the following link: example ome-zarr

"},{"location":"notebooks/basic_usage/#ngffimage","title":"NgffImage\u00b6","text":"

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 zarr store) or from a zarr.Group object.

"},{"location":"notebooks/basic_usage/#labels","title":"Labels\u00b6","text":"

The NgffImage can also be used to load labels from a OME-NGFF file.

"},{"location":"notebooks/basic_usage/#tables","title":"Tables\u00b6","text":"

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.
"},{"location":"notebooks/basic_usage/#derive-a-new-ngffimage","title":"Derive a new NgffImage\u00b6","text":"

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).

"},{"location":"notebooks/image/","title":"Images/Labels/Tables","text":"In\u00a0[1]: Copied!
import matplotlib.pyplot as plt\n\nfrom ngio.core.ngff_image import NgffImage\n\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")\n
import matplotlib.pyplot as plt from ngio.core.ngff_image import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\") In\u00a0[2]: Copied!
image = ngff_image.get_image()\n\nprint(\"Image information:\")\nprint(f\"{image.shape=}\")\nprint(f\"{image.axes_names=}\")\nprint(f\"{image.pixel_size=}\")\nprint(f\"{image.channel_labels=}\")\nprint(f\"{image.dimensions=}\")\n
image = ngff_image.get_image() print(\"Image information:\") print(f\"{image.shape=}\") print(f\"{image.axes_names=}\") print(f\"{image.pixel_size=}\") print(f\"{image.channel_labels=}\") print(f\"{image.dimensions=}\")
Image information:\nimage.shape=(3, 1, 4320, 5120)\nimage.axes_names=['c', 'z', 'y', 'x']\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.channel_labels=['DAPI', 'nanog', 'Lamin B1']\nimage.dimensions=<ngio.core.dimensions.Dimensions object at 0x7f06fcdbdd60>\n

The Image object created is a lazy object, meaning that the image is not loaded into memory until it is needed. To get the image data from disk we can use the .array attribute or we can get it as a dask.array object using the .dask_array attribute.

In\u00a0[3]: Copied!
# Get image as a zarr.Array\narray = image.array\nprint(f\"{array}\")\n\ndask_array = image.dask_array\ndask_array\n
# Get image as a zarr.Array array = image.array print(f\"{array}\") dask_array = image.dask_array dask_array
<zarr.core.Array '/0' (3, 1, 4320, 5120) uint16>\n
Out[3]: Array Chunk Bytes 126.56 MiB 10.55 MiB Shape (3, 1, 4320, 5120) (1, 1, 2160, 2560) Dask graph 12 chunks in 2 graph layers Data type uint16 numpy.ndarray 3 1 5120 4320 1

Note, directly accessing the .array or .dask_array attributes will load the image as stored in the file.

Since in principle the images can have different axes order. A safer way to access the image data is to use the .get_data() method, which will return the image data in canonical order (TCZYX).

In\u00a0[4]: Copied!
image_numpy = image.get_data(c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\")\n\nprint(f\"{image_numpy.shape=}\")\n
image_numpy = image.get_data(c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\") print(f\"{image_numpy.shape=}\")
image_numpy.shape=(1, 250, 250)\n
In\u00a0[5]: Copied!
roi_table = ngff_image.table.get(\"FOV_ROI_table\")\nroi = roi_table.get_roi(\"FOV_1\")\nprint(f\"{roi=}\")\n\nimage_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\nimage_roi_1\n
roi_table = ngff_image.table.get(\"FOV_ROI_table\") roi = roi_table.get_roi(\"FOV_1\") print(f\"{roi=}\") image_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\") image_roi_1
roi=WorldCooROI(x_length=416.0, y_length=351.0, z_length=1.0, x=0.0, y=0.0, z=0.0)\n
Out[5]: Array Chunk Bytes 10.55 MiB 10.55 MiB Shape (1, 1, 2160, 2560) (1, 1, 2160, 2560) Dask graph 1 chunks in 3 graph layers Data type uint16 numpy.ndarray 1 1 2560 2160 1

The roi object can is defined in physical coordinates, and can be used to extract the region of interest from the image or label at any resolution.

In\u00a0[6]: Copied!
image_2 = ngff_image.get_image(path=\"2\")\n# Two images at different resolutions\nprint(f\"{image.pixel_size=}\")\nprint(f\"{image_2.pixel_size=}\")\n\n# Get roi for higher resolution image\nimage_1_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Get roi for lower resolution image\nimage_2_roi_1 = image_2.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Plot the two images side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image_1_roi_1[0], cmap=\"gray\")\naxs[1].imshow(image_2_roi_1[0], cmap=\"gray\")\nplt.show()\n
image_2 = ngff_image.get_image(path=\"2\") # Two images at different resolutions print(f\"{image.pixel_size=}\") print(f\"{image_2.pixel_size=}\") # Get roi for higher resolution image image_1_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False) # Get roi for lower resolution image image_2_roi_1 = image_2.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False) # Plot the two images side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image_1_roi_1[0], cmap=\"gray\") axs[1].imshow(image_2_roi_1[0], cmap=\"gray\") plt.show()
image.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage_2.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer)\n
In\u00a0[7]: Copied!
import numpy as np\n\n# Get a small slice of the image\nsmall_slice = image.get_data(x=slice(1000, 2000), y=slice(1000, 2000))\n\n# Set the sample slice to zeros\nzeros_slice = np.zeros_like(small_slice)\nimage.set_data(patch=zeros_slice, x=slice(1000, 2000), y=slice(1000, 2000))\n\n\n# Load the image from disk and show the edited image\nnuclei = ngff_image.label.get(\"nuclei\")\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.array[0, 0], cmap=\"gray\")\naxs[1].imshow(nuclei.array[0])\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n\n# Add back the original slice to the image\nimage.set_data(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000))\n
import numpy as np # Get a small slice of the image small_slice = image.get_data(x=slice(1000, 2000), y=slice(1000, 2000)) # Set the sample slice to zeros zeros_slice = np.zeros_like(small_slice) image.set_data(patch=zeros_slice, x=slice(1000, 2000), y=slice(1000, 2000)) # Load the image from disk and show the edited image nuclei = ngff_image.label.get(\"nuclei\") fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.array[0, 0], cmap=\"gray\") axs[1].imshow(nuclei.array[0]) for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() # Add back the original slice to the image image.set_data(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000)) In\u00a0[8]: Copied!
# Create a a new label object and set it to a simple segmentation\nnew_label = ngff_image.label.derive(\"new_label\", overwrite=True)\n\nsimple_segmentation = image.array[0] > 100\nnew_label.array[...] = simple_segmentation\n\n# make a subplot with two image show side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.array[0, 0], cmap=\"gray\")\naxs[1].imshow(new_label.array[0], cmap=\"gray\")\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n
# Create a a new label object and set it to a simple segmentation new_label = ngff_image.label.derive(\"new_label\", overwrite=True) simple_segmentation = image.array[0] > 100 new_label.array[...] = simple_segmentation # make a subplot with two image show side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.array[0, 0], cmap=\"gray\") axs[1].imshow(new_label.array[0], cmap=\"gray\") for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() In\u00a0[9]: Copied!
label_0 = ngff_image.label.get(\"new_label\", path=\"0\")\nlabel_2 = ngff_image.label.get(\"new_label\", path=\"2\")\n\nlabel_before_consolidation = label_2.array[...]\n\n# Consolidate the label\nlabel_0.consolidate()\n\nlabel_after_consolidation = label_2.array[...]\n\n\n# make a subplot with two image show side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(label_before_consolidation[0], cmap=\"gray\")\naxs[1].imshow(label_after_consolidation[0], cmap=\"gray\")\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n
label_0 = ngff_image.label.get(\"new_label\", path=\"0\") label_2 = ngff_image.label.get(\"new_label\", path=\"2\") label_before_consolidation = label_2.array[...] # Consolidate the label label_0.consolidate() label_after_consolidation = label_2.array[...] # make a subplot with two image show side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(label_before_consolidation[0], cmap=\"gray\") axs[1].imshow(label_after_consolidation[0], cmap=\"gray\") for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() In\u00a0[10]: Copied!
import numpy as np\nimport pandas as pd\n\nprint(f\"List of feature table: {ngff_image.table.list(type='feature_table')}\")\n\n\nnuclei = ngff_image.label.get('nuclei')\n\n# Create a table with random features for each nuclei in each ROI\nlist_of_records = []\nfor roi in roi_table.list_rois:\n    nuclei_in_roi = nuclei.get_data_from_roi(roi, mode='numpy')\n    for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n        list_of_records.append(\n            {\"label\": nuclei_id,\n             \"feat1\": np.random.rand(),\n             \"feat2\": np.random.rand(),\n         \"ROI\": roi.infos.get(\"field_index\")}\n    )\n\nfeat_df = pd.DataFrame.from_records(list_of_records)\n\n# Create a new feature table\nfeat_table = ngff_image.table.new(name='new_feature_table',\n                     region='../nuclei',\n                     table_type='feature_table',\n                     overwrite=True)\n\nprint(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\")\nfeat_table.table = feat_df\nfeat_table.write()\n\nfeat_table.table\n
import numpy as np import pandas as pd print(f\"List of feature table: {ngff_image.table.list(type='feature_table')}\") nuclei = ngff_image.label.get('nuclei') # Create a table with random features for each nuclei in each ROI list_of_records = [] for roi in roi_table.list_rois: nuclei_in_roi = nuclei.get_data_from_roi(roi, mode='numpy') for nuclei_id in np.unique(nuclei_in_roi)[1:]: list_of_records.append( {\"label\": nuclei_id, \"feat1\": np.random.rand(), \"feat2\": np.random.rand(), \"ROI\": roi.infos.get(\"field_index\")} ) feat_df = pd.DataFrame.from_records(list_of_records) # Create a new feature table feat_table = ngff_image.table.new(name='new_feature_table', region='../nuclei', table_type='feature_table', overwrite=True) print(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\") feat_table.table = feat_df feat_table.write() feat_table.table
List of feature table: ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n
\n---------------------------------------------------------------------------\nTypeError                                 Traceback (most recent call last)\nCell In[10], line 24\n     21 feat_df = pd.DataFrame.from_records(list_of_records)\n     23 # Create a new feature table\n---> 24 feat_table = ngff_image.table.new(name='new_feature_table',\n     25                      region='../nuclei',\n     26                      table_type='feature_table',\n     27                      overwrite=True)\n     29 print(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\")\n     30 feat_table.table = feat_df\n\nFile /opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/ngio/tables/tables_group.py:146, in TableGroup.new(self, name, table_type, overwrite, version, **type_specific_kwargs)\n    143     list_of_tables.remove(name)\n    145 table_impl = _find_table_impl(table_type=table_type, version=version)\n--> 146 new_table = table_impl._new(\n    147     parent_group=self._group,\n    148     name=name,\n    149     overwrite=overwrite,\n    150     **type_specific_kwargs,\n    151 )\n    153 self._group.attrs[\"tables\"] = [*list_of_tables, name]\n    155 return new_table\n\nTypeError: FeatureTableV1._new() got an unexpected keyword argument 'region'
In\u00a0[\u00a0]: Copied!
\n
"},{"location":"notebooks/image/#imageslabelstables","title":"Images/Labels/Tables\u00b6","text":"

In this notebook we will show how to use the Image, Label and Table objects.

"},{"location":"notebooks/image/#images","title":"Images\u00b6","text":"

Images can be loaded from a NgffImage object.

"},{"location":"notebooks/image/#data-loading","title":"Data Loading\u00b6","text":""},{"location":"notebooks/image/#roitableimage-interaction","title":"RoiTable/Image Interaction\u00b6","text":"

roi objects from a roi_table can be used to extract a region of interest from an image or a label.

"},{"location":"notebooks/image/#writing-images","title":"Writing Images\u00b6","text":"

Similarly to the .get_data() we can use the .set_data() (or set_data_roi) method to write part of an image to disk.

"},{"location":"notebooks/image/#deriving-a-new-label","title":"Deriving a new label\u00b6","text":"

When doing image analysis, we often need to create new labels or tables. The ngff_image allows us to simply create new labels and tables.

"},{"location":"notebooks/image/#image-consolidation","title":"Image Consolidation\u00b6","text":"

Every time we modify a label or a image, we are modifying the on-disk data on one layer only. This means that if we have the image saved in multiple resolutions, we need to consolidate the changes to all resolutions. To do so, we can use the .consolidate() method.

"},{"location":"notebooks/image/#creating-a-new-table","title":"Creating a new table\u00b6","text":"

We can simply create a new table by create a new Table object from a pandas dataframe. For a simple feature table the only reuiremnt is to have a integer column named label that will be used to link the table to the objects in the image.

"},{"location":"notebooks/processing/","title":"Processing","text":"In\u00a0[1]: Copied!
import matplotlib.pyplot as plt\n\nfrom ngio.core import NgffImage\n\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0\")\n
import matplotlib.pyplot as plt from ngio.core import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0\") In\u00a0[2]: Copied!
mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n                                       name=\"MIP\",\n                                       shape=(1, 1, 2160, 5120))\n
mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\", name=\"MIP\", shape=(1, 1, 2160, 5120)) In\u00a0[3]: Copied!
# Get the source imag\nsource_image = ngff_image.get_image()\nprint(\"Source image loaded with shape:\", source_image.shape)\n\n# Get the MIP image\nmip_image = mip_ngff.get_image()\nprint(\"MIP image loaded with shape:\", mip_image.shape)\n\n# Get a ROI table\nroi_table = ngff_image.table.get(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\")\n\n# For each ROI in the table\n# - get the data from the source image\n# - calculate the MIP\n# - set the data in the MIP image\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    patch = source_image.get_data_from_roi(roi)\n    mip_patch = patch.max(axis=1, keepdims=True)\n    mip_image.set_data_from_roi(patch=mip_patch, roi=roi)\n    \nprint(\"MIP image saved\")\n\nplt.figure(figsize=(5, 5))\nplt.title(\"Mip\")\nplt.imshow(mip_image.array[0, 0, :, :], cmap=\"gray\")\nplt.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the source imag source_image = ngff_image.get_image() print(\"Source image loaded with shape:\", source_image.shape) # Get the MIP image mip_image = mip_ngff.get_image() print(\"MIP image loaded with shape:\", mip_image.shape) # Get a ROI table roi_table = ngff_image.table.get(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\") # For each ROI in the table # - get the data from the source image # - calculate the MIP # - set the data in the MIP image for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") patch = source_image.get_data_from_roi(roi) mip_patch = patch.max(axis=1, keepdims=True) mip_image.set_data_from_roi(patch=mip_patch, roi=roi) print(\"MIP image saved\") plt.figure(figsize=(5, 5)) plt.title(\"Mip\") plt.imshow(mip_image.array[0, 0, :, :], cmap=\"gray\") plt.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 2, 2160, 5120)\nMIP image loaded with shape: (1, 1, 2160, 5120)\nROI table loaded with 2 ROIs\n - Processing ROI FOV_1\n - Processing ROI FOV_2\nMIP image saved\n
In\u00a0[4]: Copied!
# Get the MIP image at a lower resolution\nmip_image_2 = mip_ngff.get_image(path=\"2\")\n\nimage_before_consolidation = mip_image_2.get_data(c=0, z=0)\n\n# Consolidate the pyramid\nmip_image.consolidate()\n\nimage_after_consolidation = mip_image_2.get_data(c=0, z=0)\n\nfig, axs = plt.subplots(2, 1, figsize=(10, 5))\naxs[0].set_title(\"Before consolidation\")\naxs[0].imshow(image_before_consolidation, cmap=\"gray\")\naxs[1].set_title(\"After consolidation\")\naxs[1].imshow(image_after_consolidation, cmap=\"gray\")\nfor ax in axs:\n    ax.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the MIP image at a lower resolution mip_image_2 = mip_ngff.get_image(path=\"2\") image_before_consolidation = mip_image_2.get_data(c=0, z=0) # Consolidate the pyramid mip_image.consolidate() image_after_consolidation = mip_image_2.get_data(c=0, z=0) fig, axs = plt.subplots(2, 1, figsize=(10, 5)) axs[0].set_title(\"Before consolidation\") axs[0].imshow(image_before_consolidation, cmap=\"gray\") axs[1].set_title(\"After consolidation\") axs[1].imshow(image_after_consolidation, cmap=\"gray\") for ax in axs: ax.axis('off') plt.tight_layout() plt.show() In\u00a0[5]: Copied!
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n\nroi_list = []\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    roi.z_length = 1 # In the MIP image, the z dimension is 1\n    roi_list.append(roi)\n\nmip_roi_table.append_rois(roi_list)\nmip_roi_table.write()\n\nmip_roi_table.table\n
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True) roi_list = [] for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") roi.z_length = 1 # In the MIP image, the z dimension is 1 roi_list.append(roi) mip_roi_table.append_rois(roi_list) mip_roi_table.write() mip_roi_table.table
 - Processing ROI FOV_1\n - Processing ROI FOV_2\n
Out[5]: x_micrometer y_micrometer z_micrometer len_x_micrometer len_y_micrometer len_z_micrometer FieldIndex FOV_1 0.0 0.0 0.0 416.0 351.0 1 FOV_2 416.0 0.0 0.0 416.0 351.0 1 In\u00a0[6]: Copied!
# Setup a simple segmentation function\n\nimport numpy as np\nfrom matplotlib.colors import ListedColormap\nfrom skimage.filters import threshold_otsu\nfrom skimage.measure import label\n\nrand_cmap = np.random.rand(1000, 3)\nrand_cmap[0] = 0\nrand_cmap = ListedColormap(rand_cmap)\n\n\ndef otsu_threshold_segmentation(image: np.ndarray, max_label:int) -> np.ndarray:\n    \"\"\"Simple segmentation using Otsu thresholding.\"\"\"\n    threshold = threshold_otsu(image)\n    binary = image > threshold\n    label_image = label(binary)\n    label_image += max_label\n    label_image = np.where(binary, label_image, 0)\n    return label_image\n
# Setup a simple segmentation function import numpy as np from matplotlib.colors import ListedColormap from skimage.filters import threshold_otsu from skimage.measure import label rand_cmap = np.random.rand(1000, 3) rand_cmap[0] = 0 rand_cmap = ListedColormap(rand_cmap) def otsu_threshold_segmentation(image: np.ndarray, max_label:int) -> np.ndarray: \"\"\"Simple segmentation using Otsu thresholding.\"\"\" threshold = threshold_otsu(image) binary = image > threshold label_image = label(binary) label_image += max_label label_image = np.where(binary, label_image, 0) return label_image In\u00a0[7]: Copied!
nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True)\n
nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True) In\u00a0[8]: Copied!
# Get the source imag\nsource_image = mip_ngff.get_image()\nprint(\"Source image loaded with shape:\", source_image.shape)\n\n# Get a ROI table\nroi_table = mip_ngff.table.get(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\")\n\n# Find the DAPI channel\ndapi_idx = source_image.get_channel_idx(label=\"DAPI\")\n\n# For each ROI in the table\n# - get the data from the source image\n# - calculate the Segmentation\n# - set the data in segmentation image\nmax_label = 0\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    patch = source_image.get_data_from_roi(roi, c=dapi_idx)\n    segmentation = otsu_threshold_segmentation(patch, max_label)\n\n    # Add the max label of the previous segmentation to avoid overlapping labels\n    max_label = segmentation.max()\n\n    nuclei_image.set_data_from_roi(patch=segmentation, roi=roi)\n\n# Consolidate the segmentation image\nnuclei_image.consolidate()\n\nprint(\"Segmentation image saved\")\nfig, axs = plt.subplots(2, 1, figsize=(10, 5))\naxs[0].set_title(\"MIP\")\naxs[0].imshow(source_image.array[0, 0], cmap=\"gray\")\naxs[1].set_title(\"Nuclei segmentation\")\naxs[1].imshow(nuclei_image.array[0], cmap=rand_cmap, interpolation='nearest')\nfor ax in axs:\n    ax.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the source imag source_image = mip_ngff.get_image() print(\"Source image loaded with shape:\", source_image.shape) # Get a ROI table roi_table = mip_ngff.table.get(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\") # Find the DAPI channel dapi_idx = source_image.get_channel_idx(label=\"DAPI\") # For each ROI in the table # - get the data from the source image # - calculate the Segmentation # - set the data in segmentation image max_label = 0 for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") patch = source_image.get_data_from_roi(roi, c=dapi_idx) segmentation = otsu_threshold_segmentation(patch, max_label) # Add the max label of the previous segmentation to avoid overlapping labels max_label = segmentation.max() nuclei_image.set_data_from_roi(patch=segmentation, roi=roi) # Consolidate the segmentation image nuclei_image.consolidate() print(\"Segmentation image saved\") fig, axs = plt.subplots(2, 1, figsize=(10, 5)) axs[0].set_title(\"MIP\") axs[0].imshow(source_image.array[0, 0], cmap=\"gray\") axs[1].set_title(\"Nuclei segmentation\") axs[1].imshow(nuclei_image.array[0], cmap=rand_cmap, interpolation='nearest') for ax in axs: ax.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 1, 2160, 5120)\nROI table loaded with 2 ROIs\n - Processing ROI FOV_1\n - Processing ROI FOV_2\n
Segmentation image saved\n
"},{"location":"notebooks/processing/#processing","title":"Processing\u00b6","text":"

In this notebook we will implement a couple of mock image analysis workflows using ngio.

"},{"location":"notebooks/processing/#maximum-intensity-projection","title":"Maximum intensity projection\u00b6","text":"

In this workflow we will read a volumetric image and create a maximum intensity projection (MIP) along the z-axis.

"},{"location":"notebooks/processing/#step-1-create-a-ngff-image","title":"step 1: Create a ngff image\u00b6","text":"

For this example we will use the following publicly available image

"},{"location":"notebooks/processing/#step-2-create-a-new-ngff-image-to-store-the-mip","title":"step 2: Create a new ngff image to store the MIP\u00b6","text":""},{"location":"notebooks/processing/#step-3-run-the-workflow","title":"step 3: Run the workflow\u00b6","text":"

For each roi in the image, create a MIP and store it in the new image

"},{"location":"notebooks/processing/#step-4-consolidate-the-results-important","title":"step 4: Consolidate the results (!!! Important)\u00b6","text":"

In this we wrote the mip image to a single level of the image pyramid. To truly consolidate the results we would need to write the mip to all levels of the image pyramid. We can do this by calling the .consolidate() method on the image.

"},{"location":"notebooks/processing/#step-5-create-a-new-roi-table","title":"step 5: Create a new ROI table\u00b6","text":"

As a final step we will create a new ROI table that contains the MIPs as ROIs. Where we correct the z bounds of the ROIs to reflect the MIP.

"},{"location":"notebooks/processing/#image-segmentation","title":"Image segmentation\u00b6","text":"

Now we can use the MIP image to segment the image using a simple thresholding algorithm.

"},{"location":"notebooks/processing/#step-1-derive-a-new-label-image-from-the-mip-image","title":"step 1: Derive a new label image from the MIP image\u00b6","text":""},{"location":"notebooks/processing/#step-2-run-the-workflow","title":"step 2: Run the workflow\u00b6","text":""}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to NGIO","text":"

NGIO is a Python library to streamline OME-Zarr image analysis workflows.

Main Goals:

  • Abstract object base API for handling OME-Zarr files
  • Powefull iterators for processing data using common access patterns
  • Tight integration with Fractal's Table Fractal
  • Validate OME-Zarr files

To get started, check out the Getting Started guide.

"},{"location":"#ngio-is-under-active-development","title":"\ud83d\udea7 Ngio is Under active Development \ud83d\udea7","text":""},{"location":"#roadmap","title":"Roadmap","text":"Feature Status ETA Description Metadata Handling \u2705 Read, Write, Validate OME-Zarr Metadata (0.4 supported, 0.5 ready) OME-Zarr Validation \u2705 Validate OME-Zarr files for compliance with the OME-Zarr Specification + Compliance between Metadata and Data Base Image Handling \u2705 Load data from OME-Zarr files, retrieve basic metadata, and write data ROI Handling \u2705 Common ROI models Label Handling \u2705 Mid-September Based on Image Handling Table Validation \u2705 Mid-September Validate Table fractal V1 + Compliance between Metadata and Data Table Handling \u2705 Mid-September Read, Write ROI, Features, and Masked Tables Basic Iterators Ongoing End-September Read and Write Iterators for common access patterns Base Documentation \u2705 End-September API Documentation and Examples Beta Ready Testing \u2705 End-September Beta Testing; Library is ready for testing, but the API is not stable Mask Iterators Ongoing October Iterators over Masked Tables Advanced Iterators Not started October Iterators for advanced access patterns Parallel Iterators Not started End of the Year Concurrent Iterators for parallel read and write Full Documentation Not started End of the Year Complete Documentation Release 1.0 (Commitment to API) Not started End of the Year API is stable; breaking changes will be avoided"},{"location":"#contributors","title":"Contributors","text":"

ngio is developed at the BioVisionCenter at the University of Zurich. The main contributors are: @lorenzocerrone, @jluethi.

"},{"location":"#license","title":"License","text":"

ngio is released according to the BSD-3-Clause License. See LICENSE

"},{"location":"getting-started/","title":"Getting Started","text":"

Warning

The library is still in development and is not yet stable. The API is subject to change, bugs and breaking changes are expected.

Warning

The documentation is still under development. It is not yet complete and may contain errors and inaccuracies.

"},{"location":"getting-started/#installation","title":"Installation","text":"

Currently, ngio is not available on PyPI. You can install it from the source code.

pip install \"ngio[v2]\"\n

The v2 extra installs the latest version of zarr-python from the v2 branch. ngio is currently not completely compatible with the v3 release of zarr-python.

"},{"location":"getting-started/#ngio-api-overview","title":"ngio API Overview","text":"

ngio implements an abstract object base API for handling OME-Zarr files. The three main objects are NgffImage, Image (Label), and ROITables.

  • NgffImage is the main entry point to the library. It is used to open an OME-Zarr Image and manage its metadata. This object can not be used to access the data directly. but it can be used to access and create the Image, Label, and Tables objects. Moreover it can be used to derive a new Ngff images based on the current one.
  • Image and Label are used to access \"ImageLike\" objects. They are the main objects to access the data in the OME-Zarr file, manage the metadata, and write data.
  • ROITables can be used to access specific region of interest in the image. They are tightly integrated with the Image and Label objects.
"},{"location":"getting-started/#example-usage","title":"Example Usage","text":"

Currently, the library is not yet stable. However, you can see some example usage in our demo notebooks.

  • Basic Usage
  • Image/Label/Tables
  • Processing
"},{"location":"api/core/","title":"ngio.core","text":""},{"location":"api/core/#ngio.core","title":"ngio.core","text":"

Core classes for the ngio library.

"},{"location":"api/core/#ngffimage","title":"NGFFImage","text":""},{"location":"api/core/#ngio.core.NgffImage","title":"ngio.core.NgffImage","text":"

A class to handle OME-NGFF images.

Source code in ngio/core/ngff_image.py
class NgffImage:\n    \"\"\"A class to handle OME-NGFF images.\"\"\"\n\n    def __init__(self, store: StoreLike) -> None:\n        \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n        self.store = store\n        self.group = open_group_wrapper(store=store, mode=\"r+\")\n        self._image_meta = get_ngff_image_meta_handler(\n            self.group, meta_mode=\"image\", cache=False\n        )\n\n        self.table = TableGroup(self.group)\n        self.label = LabelGroup(self.group, image_ref=self.get_image())\n\n    @property\n    def image_meta(self) -> ImageMeta:\n        \"\"\"Get the image metadata.\"\"\"\n        return self._image_meta.load_meta()\n\n    @property\n    def num_levels(self) -> int:\n        \"\"\"Get the number of levels in the image.\"\"\"\n        return self.image_meta.num_levels\n\n    @property\n    def levels_paths(self) -> list[str]:\n        \"\"\"Get the paths of the levels in the image.\"\"\"\n        return self.image_meta.levels_paths\n\n    def get_image(\n        self,\n        *,\n        path: str | None = None,\n        pixel_size: PixelSize | None = None,\n        highest_resolution: bool = True,\n    ) -> Image:\n        \"\"\"Get an image handler for the given level.\n\n        Args:\n            path (str | None, optional): The path to the level.\n            pixel_size (tuple[float, ...] | list[float] | None, optional): The pixel\n                size of the level.\n            highest_resolution (bool, optional): Whether to get the highest\n                resolution level\n\n        Returns:\n            ImageHandler: The image handler.\n        \"\"\"\n        if path is not None or pixel_size is not None:\n            highest_resolution = False\n\n        return Image(\n            store=self.group,\n            path=path,\n            pixel_size=pixel_size,\n            highest_resolution=highest_resolution,\n        )\n\n    def _update_omero_window(self) -> None:\n        \"\"\"Update the OMERO window.\"\"\"\n        meta = self.image_meta\n        image = self.get_image(highest_resolution=True)\n        max_dtype = np.iinfo(image.array.dtype).max\n        start, end = image.dask_array.min().compute(), image.dask_array.max().compute()\n\n        channel_list = meta.omero.channels\n\n        new_channel_list = []\n        for channel in channel_list:\n            channel.extra_fields[\"window\"] = {\n                \"start\": start,\n                \"end\": end,\n                \"min\": 0,\n                \"max\": max_dtype,\n            }\n            new_channel_list.append(channel)\n\n        meta.omero.channels = new_channel_list\n        self._image_meta.write_meta(meta)\n\n    def derive_new_image(\n        self,\n        store: StoreLike,\n        name: str,\n        overwrite: bool = True,\n        **kwargs,\n    ) -> \"NgffImage\":\n        \"\"\"Derive a new image from the current image.\n\n        Args:\n            store (StoreLike): The store to create the new image in.\n            name (str): The name of the new image.\n            overwrite (bool): Whether to overwrite the image if it exists\n            **kwargs: Additional keyword arguments.\n                Follow the same signature as `create_empty_ome_zarr_image`.\n\n        Returns:\n            NgffImage: The new image.\n        \"\"\"\n        image_0 = self.get_image(highest_resolution=True)\n\n        default_kwargs = {\n            \"store\": store,\n            \"shape\": image_0.on_disk_shape,\n            \"chunks\": image_0.array.chunks,\n            \"dtype\": image_0.array.dtype,\n            \"on_disk_axis\": image_0.dataset.on_disk_axes_names,\n            \"pixel_sizes\": image_0.pixel_size,\n            \"xy_scaling_factor\": self.image_meta.xy_scaling_factor,\n            \"z_scaling_factor\": self.image_meta.z_scaling_factor,\n            \"time_spacing\": image_0.dataset.time_spacing,\n            \"time_units\": image_0.dataset.time_axis_unit,\n            \"num_levels\": self.num_levels,\n            \"name\": name,\n            \"channel_labels\": image_0.channel_labels,\n            \"channel_wavelengths\": None,\n            \"channel_kwargs\": None,\n            \"omero_kwargs\": None,\n            \"overwrite\": overwrite,\n            \"version\": self.image_meta.version,\n        }\n\n        default_kwargs.update(kwargs)\n\n        create_empty_ome_zarr_image(\n            **default_kwargs,\n        )\n        return NgffImage(store=store)\n
"},{"location":"api/core/#ngio.core.NgffImage.image_meta","title":"image_meta: ImageMeta property","text":"

Get the image metadata.

"},{"location":"api/core/#ngio.core.NgffImage.levels_paths","title":"levels_paths: list[str] property","text":"

Get the paths of the levels in the image.

"},{"location":"api/core/#ngio.core.NgffImage.num_levels","title":"num_levels: int property","text":"

Get the number of levels in the image.

"},{"location":"api/core/#ngio.core.NgffImage.__init__","title":"__init__(store: StoreLike) -> None","text":"

Initialize the NGFFImage in read mode.

Source code in ngio/core/ngff_image.py
def __init__(self, store: StoreLike) -> None:\n    \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n    self.store = store\n    self.group = open_group_wrapper(store=store, mode=\"r+\")\n    self._image_meta = get_ngff_image_meta_handler(\n        self.group, meta_mode=\"image\", cache=False\n    )\n\n    self.table = TableGroup(self.group)\n    self.label = LabelGroup(self.group, image_ref=self.get_image())\n
"},{"location":"api/core/#ngio.core.NgffImage.derive_new_image","title":"derive_new_image(store: StoreLike, name: str, overwrite: bool = True, **kwargs) -> NgffImage","text":"

Derive a new image from the current image.

Parameters:

  • store (StoreLike) \u2013

    The store to create the new image in.

  • name (str) \u2013

    The name of the new image.

  • overwrite (bool, default: True ) \u2013

    Whether to overwrite the image if it exists

  • **kwargs \u2013

    Additional keyword arguments. Follow the same signature as create_empty_ome_zarr_image.

Returns:

  • NgffImage ( NgffImage ) \u2013

    The new image.

Source code in ngio/core/ngff_image.py
def derive_new_image(\n    self,\n    store: StoreLike,\n    name: str,\n    overwrite: bool = True,\n    **kwargs,\n) -> \"NgffImage\":\n    \"\"\"Derive a new image from the current image.\n\n    Args:\n        store (StoreLike): The store to create the new image in.\n        name (str): The name of the new image.\n        overwrite (bool): Whether to overwrite the image if it exists\n        **kwargs: Additional keyword arguments.\n            Follow the same signature as `create_empty_ome_zarr_image`.\n\n    Returns:\n        NgffImage: The new image.\n    \"\"\"\n    image_0 = self.get_image(highest_resolution=True)\n\n    default_kwargs = {\n        \"store\": store,\n        \"shape\": image_0.on_disk_shape,\n        \"chunks\": image_0.array.chunks,\n        \"dtype\": image_0.array.dtype,\n        \"on_disk_axis\": image_0.dataset.on_disk_axes_names,\n        \"pixel_sizes\": image_0.pixel_size,\n        \"xy_scaling_factor\": self.image_meta.xy_scaling_factor,\n        \"z_scaling_factor\": self.image_meta.z_scaling_factor,\n        \"time_spacing\": image_0.dataset.time_spacing,\n        \"time_units\": image_0.dataset.time_axis_unit,\n        \"num_levels\": self.num_levels,\n        \"name\": name,\n        \"channel_labels\": image_0.channel_labels,\n        \"channel_wavelengths\": None,\n        \"channel_kwargs\": None,\n        \"omero_kwargs\": None,\n        \"overwrite\": overwrite,\n        \"version\": self.image_meta.version,\n    }\n\n    default_kwargs.update(kwargs)\n\n    create_empty_ome_zarr_image(\n        **default_kwargs,\n    )\n    return NgffImage(store=store)\n
"},{"location":"api/core/#ngio.core.NgffImage.get_image","title":"get_image(*, path: str | None = None, pixel_size: PixelSize | None = None, highest_resolution: bool = True) -> Image","text":"

Get an image handler for the given level.

Parameters:

  • path (str | None, default: None ) \u2013

    The path to the level.

  • pixel_size (tuple[float, ...] | list[float] | None, default: None ) \u2013

    The pixel size of the level.

  • highest_resolution (bool, default: True ) \u2013

    Whether to get the highest resolution level

Returns:

  • ImageHandler ( Image ) \u2013

    The image handler.

Source code in ngio/core/ngff_image.py
def get_image(\n    self,\n    *,\n    path: str | None = None,\n    pixel_size: PixelSize | None = None,\n    highest_resolution: bool = True,\n) -> Image:\n    \"\"\"Get an image handler for the given level.\n\n    Args:\n        path (str | None, optional): The path to the level.\n        pixel_size (tuple[float, ...] | list[float] | None, optional): The pixel\n            size of the level.\n        highest_resolution (bool, optional): Whether to get the highest\n            resolution level\n\n    Returns:\n        ImageHandler: The image handler.\n    \"\"\"\n    if path is not None or pixel_size is not None:\n        highest_resolution = False\n\n    return Image(\n        store=self.group,\n        path=path,\n        pixel_size=pixel_size,\n        highest_resolution=highest_resolution,\n    )\n
"},{"location":"notebooks/basic_usage/","title":"Basic Usage","text":"In\u00a0[1]: Copied!
from ngio.core import NgffImage\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")\n
from ngio.core import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")

The ngff_image contains several attributes and methods to interact with the OME-NGFF (OME-Zarr) file on the storage.

In\u00a0[2]: Copied!
# Explore object metadata\nprint(\"Levels: \", ngff_image.levels_paths)\nprint(\"Num Levels: \", ngff_image.num_levels)\n
# Explore object metadata print(\"Levels: \", ngff_image.levels_paths) print(\"Num Levels: \", ngff_image.num_levels)
Levels:  ['0', '1', '2', '3', '4']\nNum Levels:  5\n

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.

In\u00a0[3]: Copied!
from ngio.ngff_meta import PixelSize\n\n# 1. Get image from highest resolution (default)\nimage = ngff_image.get_image()\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n\n# 2. Get image from a specific level using the path keyword\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n\n# 3. Get image from a specific pixel size using the pixel_size keyword\nimage = ngff_image.get_image(pixel_size=PixelSize(x=0.65, y=0.65, z=1))\nprint(f\"{image.shape=}\")\nprint(f\"{image.pixel_size=}\")\n
from ngio.ngff_meta import PixelSize # 1. Get image from highest resolution (default) image = ngff_image.get_image() print(f\"{image.shape=}\") print(f\"{image.pixel_size=}\") # 2. Get image from a specific level using the path keyword print(f\"{image.shape=}\") print(f\"{image.pixel_size=}\") # 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(f\"{image.shape=}\") print(f\"{image.pixel_size=}\")
image.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.shape=(3, 1, 1080, 1280)\nimage.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer)\n
In\u00a0[4]: Copied!
print(\"List of Labels: \", ngff_image.label.list())\n\nlabel_nuclei = ngff_image.label.get(\"nuclei\", path=\"0\")\nprint(f\"{label_nuclei.shape=}\")\nprint(f\"{label_nuclei.pixel_size=}\")\n
print(\"List of Labels: \", ngff_image.label.list()) label_nuclei = ngff_image.label.get(\"nuclei\", path=\"0\") print(f\"{label_nuclei.shape=}\") print(f\"{label_nuclei.pixel_size=}\")
List of Labels:  ['nuclei', 'wf_2_labels', 'wf_3_labels', 'wf_4_labels']\nlabel_nuclei.shape=(1, 4320, 5120)\nlabel_nuclei.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\n
In\u00a0[5]: Copied!
print(\"List of Tables: \", ngff_image.table.list())\nprint(\" - Feature tables: \", ngff_image.table.list(type='feature_table'))\nprint(\" - Roi tables: \", ngff_image.table.list(type='roi_table'))\nprint(\" - Masking Roi tables: \", ngff_image.table.list(type='masking_roi_table'))\n
print(\"List of Tables: \", ngff_image.table.list()) print(\" - Feature tables: \", ngff_image.table.list(type='feature_table')) print(\" - Roi tables: \", ngff_image.table.list(type='roi_table')) print(\" - Masking Roi tables: \", ngff_image.table.list(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']\n - Feature tables:  ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n - Roi tables:  ['FOV_ROI_table', 'well_ROI_table']\n - Masking Roi tables:  ['nuclei_ROI_table']\n
In\u00a0[6]: Copied!
# Loading a table\nfeature_table = ngff_image.table.get(\"regionprops_DAPI\")\nfeature_table.table\n
# Loading a table feature_table = ngff_image.table.get(\"regionprops_DAPI\") feature_table.table
/opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\n  warnings.warn(\"Transforming to str index.\", ImplicitModificationWarning)\n
Out[6]: 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 \u00d7 7 columns

In\u00a0[7]: Copied!
# Loading a roi table\nroi_table = ngff_image.table.get(\"FOV_ROI_table\")\n\nprint(f\"{roi_table.list_field_indexes=}\")\nprint(f\"{roi_table.get_roi(\"FOV_1\")=}\")\n\nroi_table.table\n
# Loading a roi table roi_table = ngff_image.table.get(\"FOV_ROI_table\") print(f\"{roi_table.list_field_indexes=}\") print(f\"{roi_table.get_roi(\"FOV_1\")=}\") roi_table.table
roi_table.list_field_indexes=['FOV_1', 'FOV_2', 'FOV_3', 'FOV_4']\nroi_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)\n
Out[7]: 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 In\u00a0[8]: Copied!
new_ngff_image = ngff_image.derive_new_image(\"../../data/new_ome.zarr\", name=\"new_image\")\n\nprint(f\"{new_ngff_image.store=}\")\nprint(f\"{new_ngff_image.levels_paths=}\")\nprint(f\"{new_ngff_image.num_levels=}\")\n
new_ngff_image = ngff_image.derive_new_image(\"../../data/new_ome.zarr\", name=\"new_image\") print(f\"{new_ngff_image.store=}\") print(f\"{new_ngff_image.levels_paths=}\") print(f\"{new_ngff_image.num_levels=}\")
new_ngff_image.store='../../data/new_ome.zarr'\nnew_ngff_image.levels_paths=['0', '1', '2', '3', '4']\nnew_ngff_image.num_levels=5\n
In\u00a0[\u00a0]: Copied!
\n
"},{"location":"notebooks/basic_usage/#basic-usage","title":"Basic Usage\u00b6","text":"

In this notebook we will show how to use the 'ngffImage' class to manage a OME-NGFF image.

For this example we will use a small example image that can be downloaded from the following link: example ome-zarr

"},{"location":"notebooks/basic_usage/#ngffimage","title":"NgffImage\u00b6","text":"

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 zarr store) or from a zarr.Group object.

"},{"location":"notebooks/basic_usage/#labels","title":"Labels\u00b6","text":"

The NgffImage can also be used to load labels from a OME-NGFF file.

"},{"location":"notebooks/basic_usage/#tables","title":"Tables\u00b6","text":"

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.
"},{"location":"notebooks/basic_usage/#derive-a-new-ngffimage","title":"Derive a new NgffImage\u00b6","text":"

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).

"},{"location":"notebooks/image/","title":"Images/Labels/Tables","text":"In\u00a0[1]: Copied!
import matplotlib.pyplot as plt\n\nfrom ngio.core.ngff_image import NgffImage\n\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\")\n
import matplotlib.pyplot as plt from ngio.core.ngff_image import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\") In\u00a0[2]: Copied!
image = ngff_image.get_image()\n\nprint(\"Image information:\")\nprint(f\"{image.shape=}\")\nprint(f\"{image.axes_names=}\")\nprint(f\"{image.pixel_size=}\")\nprint(f\"{image.channel_labels=}\")\nprint(f\"{image.dimensions=}\")\n
image = ngff_image.get_image() print(\"Image information:\") print(f\"{image.shape=}\") print(f\"{image.axes_names=}\") print(f\"{image.pixel_size=}\") print(f\"{image.channel_labels=}\") print(f\"{image.dimensions=}\")
Image information:\nimage.shape=(3, 1, 4320, 5120)\nimage.axes_names=['c', 'z', 'y', 'x']\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage.channel_labels=['DAPI', 'nanog', 'Lamin B1']\nimage.dimensions=<ngio.core.dimensions.Dimensions object at 0x7f65aed0d400>\n

The Image object created is a lazy object, meaning that the image is not loaded into memory until it is needed. To get the image data from disk we can use the .array attribute or we can get it as a dask.array object using the .dask_array attribute.

In\u00a0[3]: Copied!
# Get image as a zarr.Array\narray = image.array\nprint(f\"{array}\")\n\ndask_array = image.dask_array\ndask_array\n
# Get image as a zarr.Array array = image.array print(f\"{array}\") dask_array = image.dask_array dask_array
<zarr.core.Array '/0' (3, 1, 4320, 5120) uint16>\n
Out[3]: Array Chunk Bytes 126.56 MiB 10.55 MiB Shape (3, 1, 4320, 5120) (1, 1, 2160, 2560) Dask graph 12 chunks in 2 graph layers Data type uint16 numpy.ndarray 3 1 5120 4320 1

Note, directly accessing the .array or .dask_array attributes will load the image as stored in the file.

Since in principle the images can have different axes order. A safer way to access the image data is to use the .get_data() method, which will return the image data in canonical order (TCZYX).

In\u00a0[4]: Copied!
image_numpy = image.get_data(c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\")\n\nprint(f\"{image_numpy.shape=}\")\n
image_numpy = image.get_data(c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\") print(f\"{image_numpy.shape=}\")
image_numpy.shape=(1, 250, 250)\n
In\u00a0[5]: Copied!
roi_table = ngff_image.table.get(\"FOV_ROI_table\")\nroi = roi_table.get_roi(\"FOV_1\")\nprint(f\"{roi=}\")\n\nimage_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\nimage_roi_1\n
roi_table = ngff_image.table.get(\"FOV_ROI_table\") roi = roi_table.get_roi(\"FOV_1\") print(f\"{roi=}\") image_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\") image_roi_1
roi=WorldCooROI(x_length=416.0, y_length=351.0, z_length=1.0, x=0.0, y=0.0, z=0.0)\n
Out[5]: Array Chunk Bytes 10.55 MiB 10.55 MiB Shape (1, 1, 2160, 2560) (1, 1, 2160, 2560) Dask graph 1 chunks in 3 graph layers Data type uint16 numpy.ndarray 1 1 2560 2160 1

The roi object can is defined in physical coordinates, and can be used to extract the region of interest from the image or label at any resolution.

In\u00a0[6]: Copied!
image_2 = ngff_image.get_image(path=\"2\")\n# Two images at different resolutions\nprint(f\"{image.pixel_size=}\")\nprint(f\"{image_2.pixel_size=}\")\n\n# Get roi for higher resolution image\nimage_1_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Get roi for lower resolution image\nimage_2_roi_1 = image_2.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Plot the two images side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image_1_roi_1[0], cmap=\"gray\")\naxs[1].imshow(image_2_roi_1[0], cmap=\"gray\")\nplt.show()\n
image_2 = ngff_image.get_image(path=\"2\") # Two images at different resolutions print(f\"{image.pixel_size=}\") print(f\"{image_2.pixel_size=}\") # Get roi for higher resolution image image_1_roi_1 = image.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False) # Get roi for lower resolution image image_2_roi_1 = image_2.get_data_from_roi(roi=roi, c=0, preserve_dimensions=False) # Plot the two images side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image_1_roi_1[0], cmap=\"gray\") axs[1].imshow(image_2_roi_1[0], cmap=\"gray\") plt.show()
image.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=micrometer)\nimage_2.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=micrometer)\n
In\u00a0[7]: Copied!
import numpy as np\n\n# Get a small slice of the image\nsmall_slice = image.get_data(x=slice(1000, 2000), y=slice(1000, 2000))\n\n# Set the sample slice to zeros\nzeros_slice = np.zeros_like(small_slice)\nimage.set_data(patch=zeros_slice, x=slice(1000, 2000), y=slice(1000, 2000))\n\n\n# Load the image from disk and show the edited image\nnuclei = ngff_image.label.get(\"nuclei\")\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.array[0, 0], cmap=\"gray\")\naxs[1].imshow(nuclei.array[0])\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n\n# Add back the original slice to the image\nimage.set_data(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000))\n
import numpy as np # Get a small slice of the image small_slice = image.get_data(x=slice(1000, 2000), y=slice(1000, 2000)) # Set the sample slice to zeros zeros_slice = np.zeros_like(small_slice) image.set_data(patch=zeros_slice, x=slice(1000, 2000), y=slice(1000, 2000)) # Load the image from disk and show the edited image nuclei = ngff_image.label.get(\"nuclei\") fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.array[0, 0], cmap=\"gray\") axs[1].imshow(nuclei.array[0]) for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() # Add back the original slice to the image image.set_data(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000)) In\u00a0[8]: Copied!
# Create a a new label object and set it to a simple segmentation\nnew_label = ngff_image.label.derive(\"new_label\", overwrite=True)\n\nsimple_segmentation = image.array[0] > 100\nnew_label.array[...] = simple_segmentation\n\n# make a subplot with two image show side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.array[0, 0], cmap=\"gray\")\naxs[1].imshow(new_label.array[0], cmap=\"gray\")\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n
# Create a a new label object and set it to a simple segmentation new_label = ngff_image.label.derive(\"new_label\", overwrite=True) simple_segmentation = image.array[0] > 100 new_label.array[...] = simple_segmentation # make a subplot with two image show side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.array[0, 0], cmap=\"gray\") axs[1].imshow(new_label.array[0], cmap=\"gray\") for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() In\u00a0[9]: Copied!
label_0 = ngff_image.label.get(\"new_label\", path=\"0\")\nlabel_2 = ngff_image.label.get(\"new_label\", path=\"2\")\n\nlabel_before_consolidation = label_2.array[...]\n\n# Consolidate the label\nlabel_0.consolidate()\n\nlabel_after_consolidation = label_2.array[...]\n\n\n# make a subplot with two image show side by side\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(label_before_consolidation[0], cmap=\"gray\")\naxs[1].imshow(label_after_consolidation[0], cmap=\"gray\")\nfor ax in axs:\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n
label_0 = ngff_image.label.get(\"new_label\", path=\"0\") label_2 = ngff_image.label.get(\"new_label\", path=\"2\") label_before_consolidation = label_2.array[...] # Consolidate the label label_0.consolidate() label_after_consolidation = label_2.array[...] # make a subplot with two image show side by side fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(label_before_consolidation[0], cmap=\"gray\") axs[1].imshow(label_after_consolidation[0], cmap=\"gray\") for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() In\u00a0[10]: Copied!
import numpy as np\nimport pandas as pd\n\nprint(f\"List of feature table: {ngff_image.table.list(type='feature_table')}\")\n\n\nnuclei = ngff_image.label.get('nuclei')\n\n# Create a table with random features for each nuclei in each ROI\nlist_of_records = []\nfor roi in roi_table.list_rois:\n    nuclei_in_roi = nuclei.get_data_from_roi(roi, mode='numpy')\n    for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n        list_of_records.append(\n            {\"label\": nuclei_id,\n             \"feat1\": np.random.rand(),\n             \"feat2\": np.random.rand(),\n         \"ROI\": roi.infos.get(\"field_index\")}\n    )\n\nfeat_df = pd.DataFrame.from_records(list_of_records)\n\n# Create a new feature table\nfeat_table = ngff_image.table.new(name='new_feature_table',\n                     region='../nuclei',\n                     table_type='feature_table',\n                     overwrite=True)\n\nprint(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\")\nfeat_table.table = feat_df\nfeat_table.write()\n\nfeat_table.table\n
import numpy as np import pandas as pd print(f\"List of feature table: {ngff_image.table.list(type='feature_table')}\") nuclei = ngff_image.label.get('nuclei') # Create a table with random features for each nuclei in each ROI list_of_records = [] for roi in roi_table.list_rois: nuclei_in_roi = nuclei.get_data_from_roi(roi, mode='numpy') for nuclei_id in np.unique(nuclei_in_roi)[1:]: list_of_records.append( {\"label\": nuclei_id, \"feat1\": np.random.rand(), \"feat2\": np.random.rand(), \"ROI\": roi.infos.get(\"field_index\")} ) feat_df = pd.DataFrame.from_records(list_of_records) # Create a new feature table feat_table = ngff_image.table.new(name='new_feature_table', region='../nuclei', table_type='feature_table', overwrite=True) print(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\") feat_table.table = feat_df feat_table.write() feat_table.table
List of feature table: ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n
\n---------------------------------------------------------------------------\nTypeError                                 Traceback (most recent call last)\nCell In[10], line 24\n     21 feat_df = pd.DataFrame.from_records(list_of_records)\n     23 # Create a new feature table\n---> 24 feat_table = ngff_image.table.new(name='new_feature_table',\n     25                      region='../nuclei',\n     26                      table_type='feature_table',\n     27                      overwrite=True)\n     29 print(f\"New list of feature table: {ngff_image.table.list(type='feature_table')}\")\n     30 feat_table.table = feat_df\n\nFile /opt/hostedtoolcache/Python/3.12.6/x64/lib/python3.12/site-packages/ngio/tables/tables_group.py:146, in TableGroup.new(self, name, table_type, overwrite, version, **type_specific_kwargs)\n    143     list_of_tables.remove(name)\n    145 table_impl = _find_table_impl(table_type=table_type, version=version)\n--> 146 new_table = table_impl._new(\n    147     parent_group=self._group,\n    148     name=name,\n    149     overwrite=overwrite,\n    150     **type_specific_kwargs,\n    151 )\n    153 self._group.attrs[\"tables\"] = [*list_of_tables, name]\n    155 return new_table\n\nTypeError: FeatureTableV1._new() got an unexpected keyword argument 'region'
In\u00a0[\u00a0]: Copied!
\n
"},{"location":"notebooks/image/#imageslabelstables","title":"Images/Labels/Tables\u00b6","text":"

In this notebook we will show how to use the Image, Label and Table objects.

"},{"location":"notebooks/image/#images","title":"Images\u00b6","text":"

Images can be loaded from a NgffImage object.

"},{"location":"notebooks/image/#data-loading","title":"Data Loading\u00b6","text":""},{"location":"notebooks/image/#roitableimage-interaction","title":"RoiTable/Image Interaction\u00b6","text":"

roi objects from a roi_table can be used to extract a region of interest from an image or a label.

"},{"location":"notebooks/image/#writing-images","title":"Writing Images\u00b6","text":"

Similarly to the .get_data() we can use the .set_data() (or set_data_roi) method to write part of an image to disk.

"},{"location":"notebooks/image/#deriving-a-new-label","title":"Deriving a new label\u00b6","text":"

When doing image analysis, we often need to create new labels or tables. The ngff_image allows us to simply create new labels and tables.

"},{"location":"notebooks/image/#image-consolidation","title":"Image Consolidation\u00b6","text":"

Every time we modify a label or a image, we are modifying the on-disk data on one layer only. This means that if we have the image saved in multiple resolutions, we need to consolidate the changes to all resolutions. To do so, we can use the .consolidate() method.

"},{"location":"notebooks/image/#creating-a-new-table","title":"Creating a new table\u00b6","text":"

We can simply create a new table by create a new Table object from a pandas dataframe. For a simple feature table the only reuiremnt is to have a integer column named label that will be used to link the table to the objects in the image.

"},{"location":"notebooks/processing/","title":"Processing","text":"In\u00a0[1]: Copied!
import matplotlib.pyplot as plt\n\nfrom ngio.core import NgffImage\n\nngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0\")\n
import matplotlib.pyplot as plt from ngio.core import NgffImage ngff_image = NgffImage(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0\") In\u00a0[2]: Copied!
mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n                                       name=\"MIP\",\n                                       shape=(1, 1, 2160, 5120))\n
mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\", name=\"MIP\", shape=(1, 1, 2160, 5120)) In\u00a0[3]: Copied!
# Get the source imag\nsource_image = ngff_image.get_image()\nprint(\"Source image loaded with shape:\", source_image.shape)\n\n# Get the MIP image\nmip_image = mip_ngff.get_image()\nprint(\"MIP image loaded with shape:\", mip_image.shape)\n\n# Get a ROI table\nroi_table = ngff_image.table.get(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\")\n\n# For each ROI in the table\n# - get the data from the source image\n# - calculate the MIP\n# - set the data in the MIP image\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    patch = source_image.get_data_from_roi(roi)\n    mip_patch = patch.max(axis=1, keepdims=True)\n    mip_image.set_data_from_roi(patch=mip_patch, roi=roi)\n    \nprint(\"MIP image saved\")\n\nplt.figure(figsize=(5, 5))\nplt.title(\"Mip\")\nplt.imshow(mip_image.array[0, 0, :, :], cmap=\"gray\")\nplt.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the source imag source_image = ngff_image.get_image() print(\"Source image loaded with shape:\", source_image.shape) # Get the MIP image mip_image = mip_ngff.get_image() print(\"MIP image loaded with shape:\", mip_image.shape) # Get a ROI table roi_table = ngff_image.table.get(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\") # For each ROI in the table # - get the data from the source image # - calculate the MIP # - set the data in the MIP image for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") patch = source_image.get_data_from_roi(roi) mip_patch = patch.max(axis=1, keepdims=True) mip_image.set_data_from_roi(patch=mip_patch, roi=roi) print(\"MIP image saved\") plt.figure(figsize=(5, 5)) plt.title(\"Mip\") plt.imshow(mip_image.array[0, 0, :, :], cmap=\"gray\") plt.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 2, 2160, 5120)\nMIP image loaded with shape: (1, 1, 2160, 5120)\nROI table loaded with 2 ROIs\n - Processing ROI FOV_1\n - Processing ROI FOV_2\nMIP image saved\n
In\u00a0[4]: Copied!
# Get the MIP image at a lower resolution\nmip_image_2 = mip_ngff.get_image(path=\"2\")\n\nimage_before_consolidation = mip_image_2.get_data(c=0, z=0)\n\n# Consolidate the pyramid\nmip_image.consolidate()\n\nimage_after_consolidation = mip_image_2.get_data(c=0, z=0)\n\nfig, axs = plt.subplots(2, 1, figsize=(10, 5))\naxs[0].set_title(\"Before consolidation\")\naxs[0].imshow(image_before_consolidation, cmap=\"gray\")\naxs[1].set_title(\"After consolidation\")\naxs[1].imshow(image_after_consolidation, cmap=\"gray\")\nfor ax in axs:\n    ax.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the MIP image at a lower resolution mip_image_2 = mip_ngff.get_image(path=\"2\") image_before_consolidation = mip_image_2.get_data(c=0, z=0) # Consolidate the pyramid mip_image.consolidate() image_after_consolidation = mip_image_2.get_data(c=0, z=0) fig, axs = plt.subplots(2, 1, figsize=(10, 5)) axs[0].set_title(\"Before consolidation\") axs[0].imshow(image_before_consolidation, cmap=\"gray\") axs[1].set_title(\"After consolidation\") axs[1].imshow(image_after_consolidation, cmap=\"gray\") for ax in axs: ax.axis('off') plt.tight_layout() plt.show() In\u00a0[5]: Copied!
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n\nroi_list = []\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    roi.z_length = 1 # In the MIP image, the z dimension is 1\n    roi_list.append(roi)\n\nmip_roi_table.append_rois(roi_list)\nmip_roi_table.write()\n\nmip_roi_table.table\n
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True) roi_list = [] for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") roi.z_length = 1 # In the MIP image, the z dimension is 1 roi_list.append(roi) mip_roi_table.append_rois(roi_list) mip_roi_table.write() mip_roi_table.table
 - Processing ROI FOV_1\n - Processing ROI FOV_2\n
Out[5]: x_micrometer y_micrometer z_micrometer len_x_micrometer len_y_micrometer len_z_micrometer FieldIndex FOV_1 0.0 0.0 0.0 416.0 351.0 1 FOV_2 416.0 0.0 0.0 416.0 351.0 1 In\u00a0[6]: Copied!
# Setup a simple segmentation function\n\nimport numpy as np\nfrom matplotlib.colors import ListedColormap\nfrom skimage.filters import threshold_otsu\nfrom skimage.measure import label\n\nrand_cmap = np.random.rand(1000, 3)\nrand_cmap[0] = 0\nrand_cmap = ListedColormap(rand_cmap)\n\n\ndef otsu_threshold_segmentation(image: np.ndarray, max_label:int) -> np.ndarray:\n    \"\"\"Simple segmentation using Otsu thresholding.\"\"\"\n    threshold = threshold_otsu(image)\n    binary = image > threshold\n    label_image = label(binary)\n    label_image += max_label\n    label_image = np.where(binary, label_image, 0)\n    return label_image\n
# Setup a simple segmentation function import numpy as np from matplotlib.colors import ListedColormap from skimage.filters import threshold_otsu from skimage.measure import label rand_cmap = np.random.rand(1000, 3) rand_cmap[0] = 0 rand_cmap = ListedColormap(rand_cmap) def otsu_threshold_segmentation(image: np.ndarray, max_label:int) -> np.ndarray: \"\"\"Simple segmentation using Otsu thresholding.\"\"\" threshold = threshold_otsu(image) binary = image > threshold label_image = label(binary) label_image += max_label label_image = np.where(binary, label_image, 0) return label_image In\u00a0[7]: Copied!
nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True)\n
nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True) In\u00a0[8]: Copied!
# Get the source imag\nsource_image = mip_ngff.get_image()\nprint(\"Source image loaded with shape:\", source_image.shape)\n\n# Get a ROI table\nroi_table = mip_ngff.table.get(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\")\n\n# Find the DAPI channel\ndapi_idx = source_image.get_channel_idx(label=\"DAPI\")\n\n# For each ROI in the table\n# - get the data from the source image\n# - calculate the Segmentation\n# - set the data in segmentation image\nmax_label = 0\nfor roi in roi_table.list_rois:\n    print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\")\n    patch = source_image.get_data_from_roi(roi, c=dapi_idx)\n    segmentation = otsu_threshold_segmentation(patch, max_label)\n\n    # Add the max label of the previous segmentation to avoid overlapping labels\n    max_label = segmentation.max()\n\n    nuclei_image.set_data_from_roi(patch=segmentation, roi=roi)\n\n# Consolidate the segmentation image\nnuclei_image.consolidate()\n\nprint(\"Segmentation image saved\")\nfig, axs = plt.subplots(2, 1, figsize=(10, 5))\naxs[0].set_title(\"MIP\")\naxs[0].imshow(source_image.array[0, 0], cmap=\"gray\")\naxs[1].set_title(\"Nuclei segmentation\")\naxs[1].imshow(nuclei_image.array[0], cmap=rand_cmap, interpolation='nearest')\nfor ax in axs:\n    ax.axis('off')\nplt.tight_layout()\nplt.show()\n
# Get the source imag source_image = mip_ngff.get_image() print(\"Source image loaded with shape:\", source_image.shape) # Get a ROI table roi_table = mip_ngff.table.get(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.list_rois), \"ROIs\") # Find the DAPI channel dapi_idx = source_image.get_channel_idx(label=\"DAPI\") # For each ROI in the table # - get the data from the source image # - calculate the Segmentation # - set the data in segmentation image max_label = 0 for roi in roi_table.list_rois: print(f\" - Processing ROI {roi.infos.get(\"field_index\")}\") patch = source_image.get_data_from_roi(roi, c=dapi_idx) segmentation = otsu_threshold_segmentation(patch, max_label) # Add the max label of the previous segmentation to avoid overlapping labels max_label = segmentation.max() nuclei_image.set_data_from_roi(patch=segmentation, roi=roi) # Consolidate the segmentation image nuclei_image.consolidate() print(\"Segmentation image saved\") fig, axs = plt.subplots(2, 1, figsize=(10, 5)) axs[0].set_title(\"MIP\") axs[0].imshow(source_image.array[0, 0], cmap=\"gray\") axs[1].set_title(\"Nuclei segmentation\") axs[1].imshow(nuclei_image.array[0], cmap=rand_cmap, interpolation='nearest') for ax in axs: ax.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 1, 2160, 5120)\nROI table loaded with 2 ROIs\n - Processing ROI FOV_1\n - Processing ROI FOV_2\n
Segmentation image saved\n
"},{"location":"notebooks/processing/#processing","title":"Processing\u00b6","text":"

In this notebook we will implement a couple of mock image analysis workflows using ngio.

"},{"location":"notebooks/processing/#maximum-intensity-projection","title":"Maximum intensity projection\u00b6","text":"

In this workflow we will read a volumetric image and create a maximum intensity projection (MIP) along the z-axis.

"},{"location":"notebooks/processing/#step-1-create-a-ngff-image","title":"step 1: Create a ngff image\u00b6","text":"

For this example we will use the following publicly available image

"},{"location":"notebooks/processing/#step-2-create-a-new-ngff-image-to-store-the-mip","title":"step 2: Create a new ngff image to store the MIP\u00b6","text":""},{"location":"notebooks/processing/#step-3-run-the-workflow","title":"step 3: Run the workflow\u00b6","text":"

For each roi in the image, create a MIP and store it in the new image

"},{"location":"notebooks/processing/#step-4-consolidate-the-results-important","title":"step 4: Consolidate the results (!!! Important)\u00b6","text":"

In this we wrote the mip image to a single level of the image pyramid. To truly consolidate the results we would need to write the mip to all levels of the image pyramid. We can do this by calling the .consolidate() method on the image.

"},{"location":"notebooks/processing/#step-5-create-a-new-roi-table","title":"step 5: Create a new ROI table\u00b6","text":"

As a final step we will create a new ROI table that contains the MIPs as ROIs. Where we correct the z bounds of the ROIs to reflect the MIP.

"},{"location":"notebooks/processing/#image-segmentation","title":"Image segmentation\u00b6","text":"

Now we can use the MIP image to segment the image using a simple thresholding algorithm.

"},{"location":"notebooks/processing/#step-1-derive-a-new-label-image-from-the-mip-image","title":"step 1: Derive a new label image from the MIP image\u00b6","text":""},{"location":"notebooks/processing/#step-2-run-the-workflow","title":"step 2: Run the workflow\u00b6","text":""}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 93cc529..ef78439 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,26 +2,26 @@ https://github.com/fractal-analytics-platform/ngio.git/ - 2024-10-07 + 2024-10-08 https://github.com/fractal-analytics-platform/ngio.git/getting-started/ - 2024-10-07 + 2024-10-08 https://github.com/fractal-analytics-platform/ngio.git/api/core/ - 2024-10-07 + 2024-10-08 https://github.com/fractal-analytics-platform/ngio.git/notebooks/basic_usage/ - 2024-10-07 + 2024-10-08 https://github.com/fractal-analytics-platform/ngio.git/notebooks/image/ - 2024-10-07 + 2024-10-08 https://github.com/fractal-analytics-platform/ngio.git/notebooks/processing/ - 2024-10-07 + 2024-10-08 \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 6230631..7b25fbf 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ