From 29682b676d4d1051aa1e3c34a66f3e82817b1085 Mon Sep 17 00:00:00 2001 From: lorenzocerrone Date: Sat, 9 Nov 2024 09:23:32 +0000 Subject: [PATCH] deploy: 51dabac4dbc3bede9392d9b2dba1f52d00f4b2da --- api/core/index.html | 28 ++++++++++++++-------------- getting-started/index.html | 4 ++-- index.html | 4 ++-- notebooks/basic_usage/index.html | 4 ++-- notebooks/image/index.html | 4 ++-- notebooks/processing/index.html | 4 ++-- search/search_index.json | 2 +- sitemap.xml | 12 ++++++------ sitemap.xml.gz | Bin 264 -> 264 bytes 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/api/core/index.html b/api/core/index.html index 67054ea..4ed776f 100644 --- a/api/core/index.html +++ b/api/core/index.html @@ -1149,12 +1149,12 @@

_end_percentile = da.percentile( data, end_percentile, method="nearest" ).compute() - channel.extra_fields["window"] = { - "start": _start_percentile, - "end": _end_percentile, - "min": 0, - "max": max_dtype, - } + + channel.channel_visualisation.start = _start_percentile + channel.channel_visualisation.end = _end_percentile + channel.channel_visualisation.min = 0 + channel.channel_visualisation.max = max_dtype + ngio_logger.info( f"Updated window for channel {channel.label}. " f"Start: {start_percentile}, End: {end_percentile}" @@ -1822,12 +1822,12 @@

_end_percentile = da.percentile( data, end_percentile, method="nearest" ).compute() - channel.extra_fields["window"] = { - "start": _start_percentile, - "end": _end_percentile, - "min": 0, - "max": max_dtype, - } + + channel.channel_visualisation.start = _start_percentile + channel.channel_visualisation.end = _end_percentile + channel.channel_visualisation.min = 0 + channel.channel_visualisation.max = max_dtype + ngio_logger.info( f"Updated window for channel {channel.label}. " f"Start: {start_percentile}, End: {end_percentile}" @@ -1870,7 +1870,7 @@

- November 8, 2024 + November 9, 2024 @@ -1880,7 +1880,7 @@

- November 8, 2024 + November 9, 2024 diff --git a/getting-started/index.html b/getting-started/index.html index 004496b..573b5c2 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -676,7 +676,7 @@

Example Usage

- November 8, 2024 + November 9, 2024 @@ -686,7 +686,7 @@

Example Usage

- November 8, 2024 + November 9, 2024 diff --git a/index.html b/index.html index 20d0f1d..a1b4c5e 100644 --- a/index.html +++ b/index.html @@ -793,7 +793,7 @@

License

- November 8, 2024 + November 9, 2024 @@ -803,7 +803,7 @@

License

- November 8, 2024 + November 9, 2024 diff --git a/notebooks/basic_usage/index.html b/notebooks/basic_usage/index.html index 3e63e9f..2af52ca 100644 --- a/notebooks/basic_usage/index.html +++ b/notebooks/basic_usage/index.html @@ -1836,7 +1836,7 @@

Derive a new NgffImage - November 8, 2024 + November 9, 2024 @@ -1846,7 +1846,7 @@

Derive a new NgffImage - November 8, 2024 + November 9, 2024 diff --git a/notebooks/image/index.html b/notebooks/image/index.html index ae11857..35fe59b 100644 --- a/notebooks/image/index.html +++ b/notebooks/image/index.html @@ -2169,7 +2169,7 @@

Creating a new table - November 8, 2024 + November 9, 2024 @@ -2179,7 +2179,7 @@

Creating a new table - November 8, 2024 + November 9, 2024 diff --git a/notebooks/processing/index.html b/notebooks/processing/index.html index deac4ba..a045fbc 100644 --- a/notebooks/processing/index.html +++ b/notebooks/processing/index.html @@ -1967,7 +1967,7 @@

step 2: Run the workflow - November 8, 2024 + November 9, 2024 @@ -1977,7 +1977,7 @@

step 2: Run the workflow - November 8, 2024 + November 9, 2024 diff --git a/search/search_index.json b/search/search_index.json index 92ccb81..646e12e 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":"

The library can be installed from PyPI using pip:

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

The core extra installs the the zarr-python dependency. As of now, zarr-python is required to be installed separately, due to the transition to the new zarr-v3 library.

"},{"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__(\n        self, store: StoreLike, cache: bool = False, mode: AccessModeLiteral = \"r+\"\n    ) -> None:\n        \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n        self.store = store\n        self._mode = mode\n        self.group = open_group_wrapper(store=store, mode=self._mode)\n\n        if self.group.read_only:\n            self._mode = \"r\"\n\n        self._image_meta = get_ngff_image_meta_handler(\n            self.group, meta_mode=\"image\", cache=cache\n        )\n        self._metadata_cache = cache\n        self.table = TableGroup(self.group, mode=self._mode)\n        self.label = LabelGroup(self.group, image_ref=self.get_image(), mode=self._mode)\n\n        ngio_logger.info(f\"Opened image located in store: {store}\")\n        ngio_logger.info(f\"- Image number of levels: {self.num_levels}\")\n\n    @property\n    def image_meta(self) -> ImageMeta:\n        \"\"\"Get the image metadata.\"\"\"\n        meta = self._image_meta.load_meta()\n        assert isinstance(meta, ImageMeta)\n        return 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        image = Image(\n            store=self.group,\n            path=path,\n            pixel_size=pixel_size,\n            highest_resolution=highest_resolution,\n            label_group=LabelGroup(self.group, image_ref=None, mode=self._mode),\n            cache=self._metadata_cache,\n            mode=self._mode,\n        )\n        ngio_logger.info(f\"Opened image at path: {image.path}\")\n        ngio_logger.info(f\"- {image.dimensions}\")\n        ngio_logger.info(f\"- {image.pixel_size}\")\n        return image\n\n    def update_omero_window(\n        self, start_percentile: int = 5, end_percentile: int = 95\n    ) -> None:\n        \"\"\"Update the OMERO window.\n\n        This will setup percentiles based values for the window of each channel.\n\n        Args:\n            start_percentile (int): The start percentile.\n            end_percentile (int): The end percentile\n\n        \"\"\"\n        meta = self.image_meta\n\n        lowest_res_image = self.get_image(highest_resolution=True)\n        lowest_res_shape = lowest_res_image.shape\n        for path in self.levels_paths:\n            image = self.get_image(path=path)\n            if np.prod(image.shape) < np.prod(lowest_res_shape):\n                lowest_res_shape = image.shape\n                lowest_res_image = image\n\n        max_dtype = np.iinfo(image.on_disk_array.dtype).max\n        num_c = lowest_res_image.dimensions.get(\"c\", 1)\n\n        if meta.omero is None:\n            raise NotImplementedError(\n                \"OMERO metadata not found. \" \" Please add OMERO metadata to the image.\"\n            )\n\n        channel_list = meta.omero.channels\n        if len(channel_list) != num_c:\n            raise ValueError(\"The number of channels does not match the image.\")\n\n        for c, channel in enumerate(channel_list):\n            data = image.get_array(c=c, mode=\"dask\").ravel()\n            _start_percentile = da.percentile(\n                data, start_percentile, method=\"nearest\"\n            ).compute()\n            _end_percentile = da.percentile(\n                data, end_percentile, method=\"nearest\"\n            ).compute()\n            channel.extra_fields[\"window\"] = {\n                \"start\": _start_percentile,\n                \"end\": _end_percentile,\n                \"min\": 0,\n                \"max\": max_dtype,\n            }\n            ngio_logger.info(\n                f\"Updated window for channel {channel.label}. \"\n                f\"Start: {start_percentile}, End: {end_percentile}\"\n            )\n            meta.omero.channels[c] = channel\n\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: dict,\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        # Get the channel information if it exists\n        omero = self.image_meta.omero\n        if omero is not None:\n            channels = omero.channels\n            omero_kwargs = omero.extra_fields\n        else:\n            channels = []\n            omero_kwargs = {}\n\n        default_kwargs = {\n            \"store\": store,\n            \"on_disk_shape\": image_0.on_disk_shape,\n            \"chunks\": image_0.on_disk_array.chunks,\n            \"dtype\": image_0.on_disk_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            \"levels\": self.num_levels,\n            \"name\": name,\n            \"channel_labels\": image_0.channel_labels,\n            \"channel_wavelengths\": [ch.wavelength_id for ch in channels],\n            \"channel_visualization\": [ch.channel_visualisation for ch in channels],\n            \"omero_kwargs\": omero_kwargs,\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, cache: bool = False, mode: AccessModeLiteral = 'r+') -> None","text":"

Initialize the NGFFImage in read mode.

Source code in ngio/core/ngff_image.py
def __init__(\n    self, store: StoreLike, cache: bool = False, mode: AccessModeLiteral = \"r+\"\n) -> None:\n    \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n    self.store = store\n    self._mode = mode\n    self.group = open_group_wrapper(store=store, mode=self._mode)\n\n    if self.group.read_only:\n        self._mode = \"r\"\n\n    self._image_meta = get_ngff_image_meta_handler(\n        self.group, meta_mode=\"image\", cache=cache\n    )\n    self._metadata_cache = cache\n    self.table = TableGroup(self.group, mode=self._mode)\n    self.label = LabelGroup(self.group, image_ref=self.get_image(), mode=self._mode)\n\n    ngio_logger.info(f\"Opened image located in store: {store}\")\n    ngio_logger.info(f\"- Image number of levels: {self.num_levels}\")\n
"},{"location":"api/core/#ngio.core.NgffImage.derive_new_image","title":"derive_new_image(store: StoreLike, name: str, overwrite: bool = True, **kwargs: dict) -> 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 (dict, default: {} ) \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: dict,\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    # Get the channel information if it exists\n    omero = self.image_meta.omero\n    if omero is not None:\n        channels = omero.channels\n        omero_kwargs = omero.extra_fields\n    else:\n        channels = []\n        omero_kwargs = {}\n\n    default_kwargs = {\n        \"store\": store,\n        \"on_disk_shape\": image_0.on_disk_shape,\n        \"chunks\": image_0.on_disk_array.chunks,\n        \"dtype\": image_0.on_disk_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        \"levels\": self.num_levels,\n        \"name\": name,\n        \"channel_labels\": image_0.channel_labels,\n        \"channel_wavelengths\": [ch.wavelength_id for ch in channels],\n        \"channel_visualization\": [ch.channel_visualisation for ch in channels],\n        \"omero_kwargs\": omero_kwargs,\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    image = Image(\n        store=self.group,\n        path=path,\n        pixel_size=pixel_size,\n        highest_resolution=highest_resolution,\n        label_group=LabelGroup(self.group, image_ref=None, mode=self._mode),\n        cache=self._metadata_cache,\n        mode=self._mode,\n    )\n    ngio_logger.info(f\"Opened image at path: {image.path}\")\n    ngio_logger.info(f\"- {image.dimensions}\")\n    ngio_logger.info(f\"- {image.pixel_size}\")\n    return image\n
"},{"location":"api/core/#ngio.core.NgffImage.update_omero_window","title":"update_omero_window(start_percentile: int = 5, end_percentile: int = 95) -> None","text":"

Update the OMERO window.

This will setup percentiles based values for the window of each channel.

Parameters:

  • start_percentile (int, default: 5 ) \u2013

    The start percentile.

  • end_percentile (int, default: 95 ) \u2013

    The end percentile

Source code in ngio/core/ngff_image.py
def update_omero_window(\n    self, start_percentile: int = 5, end_percentile: int = 95\n) -> None:\n    \"\"\"Update the OMERO window.\n\n    This will setup percentiles based values for the window of each channel.\n\n    Args:\n        start_percentile (int): The start percentile.\n        end_percentile (int): The end percentile\n\n    \"\"\"\n    meta = self.image_meta\n\n    lowest_res_image = self.get_image(highest_resolution=True)\n    lowest_res_shape = lowest_res_image.shape\n    for path in self.levels_paths:\n        image = self.get_image(path=path)\n        if np.prod(image.shape) < np.prod(lowest_res_shape):\n            lowest_res_shape = image.shape\n            lowest_res_image = image\n\n    max_dtype = np.iinfo(image.on_disk_array.dtype).max\n    num_c = lowest_res_image.dimensions.get(\"c\", 1)\n\n    if meta.omero is None:\n        raise NotImplementedError(\n            \"OMERO metadata not found. \" \" Please add OMERO metadata to the image.\"\n        )\n\n    channel_list = meta.omero.channels\n    if len(channel_list) != num_c:\n        raise ValueError(\"The number of channels does not match the image.\")\n\n    for c, channel in enumerate(channel_list):\n        data = image.get_array(c=c, mode=\"dask\").ravel()\n        _start_percentile = da.percentile(\n            data, start_percentile, method=\"nearest\"\n        ).compute()\n        _end_percentile = da.percentile(\n            data, end_percentile, method=\"nearest\"\n        ).compute()\n        channel.extra_fields[\"window\"] = {\n            \"start\": _start_percentile,\n            \"end\": _end_percentile,\n            \"min\": 0,\n            \"max\": max_dtype,\n        }\n        ngio_logger.info(\n            f\"Updated window for channel {channel.label}. \"\n            f\"Start: {start_percentile}, End: {end_percentile}\"\n        )\n        meta.omero.channels[c] = channel\n\n    self._image_meta.write_meta(meta)\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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.shape=(3, 1, 1080, 1280)\nimage.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=True)\n
In\u00a0[4]: Copied!
print(\"List of Labels: \", ngff_image.label.list())\n\nlabel_nuclei = ngff_image.label.get_label(\"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_label(\"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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\n
In\u00a0[5]: Copied!
print(\"List of Tables: \", ngff_image.table.list())\nprint(\" - Feature tables: \", ngff_image.table.list(table_type='feature_table'))\nprint(\" - Roi tables: \", ngff_image.table.list(table_type='roi_table'))\nprint(\" - Masking Roi tables: \", ngff_image.table.list(table_type='masking_roi_table'))\n
print(\"List of Tables: \", ngff_image.table.list()) print(\" - Feature tables: \", ngff_image.table.list(table_type='feature_table')) print(\" - Roi tables: \", ngff_image.table.list(table_type='roi_table')) print(\" - Masking Roi tables: \", ngff_image.table.list(table_type='masking_roi_table'))
List of Tables:  ['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\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_table(\"regionprops_DAPI\")\nfeature_table.table\n
# Loading a table feature_table = ngff_image.table.get_table(\"regionprops_DAPI\") feature_table.table
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\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_table(\"FOV_ROI_table\")\n\nprint(f\"{roi_table.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_table(\"FOV_ROI_table\") print(f\"{roi_table.field_indexes=}\") print(f\"{roi_table.get_roi('FOV_1')=}\") roi_table.table
roi_table.field_indexes=['FOV_1', 'FOV_2', 'FOV_3', 'FOV_4']\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
"},{"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

You can download the example image (on Linux and Mac os) by running the following command:

bash setup_data.sh\n

from the root of the repository.

"},{"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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.channel_labels=['DAPI', 'nanog', 'Lamin B1']\nimage.dimensions=Dimensions(c=3, z=1, y=4320, x=5120)\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 dask array\ndask_array = image.on_disk_dask_array\ndask_array\n
# Get image as a dask array dask_array = image.on_disk_dask_array dask_array 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 .on_disk_array or .on_disk_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_array() method, which will return the image data in canonical order (TCZYX).

In\u00a0[4]: Copied!
image_numpy = image.get_array(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_array(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_table(\"FOV_ROI_table\")\nroi = roi_table.get_roi(\"FOV_1\")\nprint(f\"{roi=}\")\n\nimage_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\nimage_roi_1\n
roi_table = ngff_image.table.get_table(\"FOV_ROI_table\") roi = roi_table.get_roi(\"FOV_1\") print(f\"{roi=}\") image_roi_1 = image.get_array_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
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[5], line 5\n      2 roi = roi_table.get_roi(\"FOV_1\")\n      3 print(f\"{roi=}\")\n----> 5 image_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\n      6 image_roi_1\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_handler.py:105, in Image.get_array_from_roi(self, roi, c, t, mode, preserve_dimensions)\n    102 if isinstance(c, str):\n    103     c = self.get_channel_idx(label=c)\n--> 105 return self._get_array_from_roi(\n    106     roi=roi, t=t, c=c, mode=mode, preserve_dimensions=preserve_dimensions\n    107 )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'

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_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Get roi for lower resolution image\nimage_2_roi_1 = image_2.get_array_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_array_from_roi(roi=roi, c=0, preserve_dimensions=False) # Get roi for lower resolution image image_2_roi_1 = image_2.get_array_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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage_2.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\n
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[6], line 7\n      4 print(f\"{image_2.pixel_size=}\")\n      6 # Get roi for higher resolution image\n----> 7 image_1_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n      9 # Get roi for lower resolution image\n     10 image_2_roi_1 = image_2.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_handler.py:105, in Image.get_array_from_roi(self, roi, c, t, mode, preserve_dimensions)\n    102 if isinstance(c, str):\n    103     c = self.get_channel_idx(label=c)\n--> 105 return self._get_array_from_roi(\n    106     roi=roi, t=t, c=c, mode=mode, preserve_dimensions=preserve_dimensions\n    107 )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'
In\u00a0[7]: Copied!
import numpy as np\n\n# Get a small slice of the image\nsmall_slice = image.get_array(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_array(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_label(\"nuclei\")\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].imshow(nuclei.on_disk_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_array(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_array(x=slice(1000, 2000), y=slice(1000, 2000)) # Set the sample slice to zeros zeros_slice = np.zeros_like(small_slice) image.set_array(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_label(\"nuclei\") fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\") axs[1].imshow(nuclei.on_disk_array[0]) for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() # Add back the original slice to the image image.set_array(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.on_disk_array[0] > 100\nnew_label.on_disk_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.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].imshow(new_label.on_disk_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.on_disk_array[0] > 100 new_label.on_disk_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.on_disk_array[0, 0], cmap=\"gray\") axs[1].imshow(new_label.on_disk_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_label(\"new_label\", path=\"0\")\nlabel_2 = ngff_image.label.get_label(\"new_label\", path=\"2\")\n\nlabel_before_consolidation = label_2.on_disk_array[...]\n\n# Consolidate the label\nlabel_0.consolidate()\n\nlabel_after_consolidation = label_2.on_disk_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_label(\"new_label\", path=\"0\") label_2 = ngff_image.label.get_label(\"new_label\", path=\"2\") label_before_consolidation = label_2.on_disk_array[...] # Consolidate the label label_0.consolidate() label_after_consolidation = label_2.on_disk_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(table_type='feature_table')}\")\n\n\nnuclei = ngff_image.label.get_label('nuclei')\n\n# Create a table with random features for each nuclei in each ROI\nlist_of_records = []\nfor roi in roi_table.rois:\n    nuclei_in_roi = nuclei.get_array_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(\"FieldIndex\")}\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                     label_image='../nuclei',\n                     table_type='feature_table',\n                     overwrite=True)\n\nprint(f\"New list of feature table: {ngff_image.table.list(table_type='feature_table')}\")\nfeat_table.set_table(feat_df)\nfeat_table.consolidate()\n\nfeat_table.table\n
import numpy as np import pandas as pd print(f\"List of feature table: {ngff_image.table.list(table_type='feature_table')}\") nuclei = ngff_image.label.get_label('nuclei') # Create a table with random features for each nuclei in each ROI list_of_records = [] for roi in roi_table.rois: nuclei_in_roi = nuclei.get_array_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(\"FieldIndex\")} ) feat_df = pd.DataFrame.from_records(list_of_records) # Create a new feature table feat_table = ngff_image.table.new(name='new_feature_table', label_image='../nuclei', table_type='feature_table', overwrite=True) print(f\"New list of feature table: {ngff_image.table.list(table_type='feature_table')}\") feat_table.set_table(feat_df) feat_table.consolidate() feat_table.table
List of feature table: ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[10], line 12\n     10 list_of_records = []\n     11 for roi in roi_table.rois:\n---> 12     nuclei_in_roi = nuclei.get_array_from_roi(roi, mode='numpy')\n     13     for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n     14         list_of_records.append(\n     15             {\"label\": nuclei_id,\n     16              \"feat1\": np.random.rand(),\n     17              \"feat2\": np.random.rand(),\n     18          \"ROI\": roi.infos.get(\"FieldIndex\")}\n     19     )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/label_handler.py:89, in Label.get_array_from_roi(self, roi, t, mode, preserve_dimensions)\n     74 def get_array_from_roi(\n     75     self,\n     76     roi: WorldCooROI,\n   (...)\n     79     preserve_dimensions: bool = False,\n     80 ) -> ArrayLike:\n     81     \"\"\"Return the label data from a region of interest (ROI).\n     82 \n     83     Args:\n   (...)\n     87         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n     88     \"\"\"\n---> 89     return self._get_array_from_roi(\n     90         roi=roi, t=t, c=None, mode=mode, preserve_dimensions=preserve_dimensions\n     91     )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'
"},{"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 .array() we can use the .set_array() (or set_array_from_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))
\n---------------------------------------------------------------------------\nTypeError                                 Traceback (most recent call last)\nCell In[2], line 1\n----> 1 mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n      2                                        name=\"MIP\",\n      3                                        shape=(1, 1, 2160, 5120))\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/ngff_image.py:202, in NgffImage.derive_new_image(self, store, name, overwrite, **kwargs)\n    179 default_kwargs = {\n    180     \"store\": store,\n    181     \"on_disk_shape\": image_0.on_disk_shape,\n   (...)\n    197     \"version\": self.image_meta.version,\n    198 }\n    200 default_kwargs.update(kwargs)\n--> 202 create_empty_ome_zarr_image(\n    203     **default_kwargs,\n    204 )\n    205 return NgffImage(store=store)\n\nTypeError: create_empty_ome_zarr_image() got an unexpected keyword argument 'shape'
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_table(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.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.rois:\n    print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n    patch = source_image.get_array_from_roi(roi)\n    mip_patch = patch.max(axis=1, keepdims=True)\n    mip_image.set_array_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.on_disk_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_table(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.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.rois: print(f\" - Processing ROI {roi.infos.get('field_index')}\") patch = source_image.get_array_from_roi(roi) mip_patch = patch.max(axis=1, keepdims=True) mip_image.set_array_from_roi(patch=mip_patch, roi=roi) print(\"MIP image saved\") plt.figure(figsize=(5, 5)) plt.title(\"Mip\") plt.imshow(mip_image.on_disk_array[0, 0, :, :], cmap=\"gray\") plt.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 2, 2160, 5120)\n
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[3], line 6\n      3 print(\"Source image loaded with shape:\", source_image.shape)\n      5 # Get the MIP image\n----> 6 mip_image = mip_ngff.get_image()\n      7 print(\"MIP image loaded with shape:\", mip_image.shape)\n      9 # Get a ROI table\n\nNameError: name 'mip_ngff' is not defined
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_array(c=0, z=0)\n\n# Consolidate the pyramid\nmip_image.consolidate()\n\nimage_after_consolidation = mip_image_2.get_array(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_array(c=0, z=0) # Consolidate the pyramid mip_image.consolidate() image_after_consolidation = mip_image_2.get_array(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()
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[4], line 2\n      1 # Get the MIP image at a lower resolution\n----> 2 mip_image_2 = mip_ngff.get_image(path=\"2\")\n      4 image_before_consolidation = mip_image_2.get_array(c=0, z=0)\n      6 # Consolidate the pyramid\n\nNameError: name 'mip_ngff' is not defined
In\u00a0[5]: Copied!
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n\nroi_list = []\nfor roi in roi_table.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.set_rois(roi_list, overwrite=True)\nmip_roi_table.consolidate()\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.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.set_rois(roi_list, overwrite=True) mip_roi_table.consolidate() mip_roi_table.table
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[5], line 1\n----> 1 mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n      3 roi_list = []\n      4 for roi in roi_table.rois:\n\nNameError: name 'mip_ngff' is not defined
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)
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[7], line 1\n----> 1 nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True)\n\nNameError: name 'mip_ngff' is not defined
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_table(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.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.rois:\n    print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n    patch = source_image.get_array_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_array_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.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].set_title(\"Nuclei segmentation\")\naxs[1].imshow(nuclei_image.on_disk_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_table(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.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.rois: print(f\" - Processing ROI {roi.infos.get('field_index')}\") patch = source_image.get_array_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_array_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.on_disk_array[0, 0], cmap=\"gray\") axs[1].set_title(\"Nuclei segmentation\") axs[1].imshow(nuclei_image.on_disk_array[0], cmap=rand_cmap, interpolation='nearest') for ax in axs: ax.axis('off') plt.tight_layout() plt.show()
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[8], line 2\n      1 # Get the source imag\n----> 2 source_image = mip_ngff.get_image()\n      3 print(\"Source image loaded with shape:\", source_image.shape)\n      5 # Get a ROI table\n\nNameError: name 'mip_ngff' is not defined
In\u00a0[\u00a0]: Copied!
\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":"

The library can be installed from PyPI using pip:

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

The core extra installs the the zarr-python dependency. As of now, zarr-python is required to be installed separately, due to the transition to the new zarr-v3 library.

"},{"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__(\n        self, store: StoreLike, cache: bool = False, mode: AccessModeLiteral = \"r+\"\n    ) -> None:\n        \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n        self.store = store\n        self._mode = mode\n        self.group = open_group_wrapper(store=store, mode=self._mode)\n\n        if self.group.read_only:\n            self._mode = \"r\"\n\n        self._image_meta = get_ngff_image_meta_handler(\n            self.group, meta_mode=\"image\", cache=cache\n        )\n        self._metadata_cache = cache\n        self.table = TableGroup(self.group, mode=self._mode)\n        self.label = LabelGroup(self.group, image_ref=self.get_image(), mode=self._mode)\n\n        ngio_logger.info(f\"Opened image located in store: {store}\")\n        ngio_logger.info(f\"- Image number of levels: {self.num_levels}\")\n\n    @property\n    def image_meta(self) -> ImageMeta:\n        \"\"\"Get the image metadata.\"\"\"\n        meta = self._image_meta.load_meta()\n        assert isinstance(meta, ImageMeta)\n        return 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        image = Image(\n            store=self.group,\n            path=path,\n            pixel_size=pixel_size,\n            highest_resolution=highest_resolution,\n            label_group=LabelGroup(self.group, image_ref=None, mode=self._mode),\n            cache=self._metadata_cache,\n            mode=self._mode,\n        )\n        ngio_logger.info(f\"Opened image at path: {image.path}\")\n        ngio_logger.info(f\"- {image.dimensions}\")\n        ngio_logger.info(f\"- {image.pixel_size}\")\n        return image\n\n    def update_omero_window(\n        self, start_percentile: int = 5, end_percentile: int = 95\n    ) -> None:\n        \"\"\"Update the OMERO window.\n\n        This will setup percentiles based values for the window of each channel.\n\n        Args:\n            start_percentile (int): The start percentile.\n            end_percentile (int): The end percentile\n\n        \"\"\"\n        meta = self.image_meta\n\n        lowest_res_image = self.get_image(highest_resolution=True)\n        lowest_res_shape = lowest_res_image.shape\n        for path in self.levels_paths:\n            image = self.get_image(path=path)\n            if np.prod(image.shape) < np.prod(lowest_res_shape):\n                lowest_res_shape = image.shape\n                lowest_res_image = image\n\n        max_dtype = np.iinfo(image.on_disk_array.dtype).max\n        num_c = lowest_res_image.dimensions.get(\"c\", 1)\n\n        if meta.omero is None:\n            raise NotImplementedError(\n                \"OMERO metadata not found. \" \" Please add OMERO metadata to the image.\"\n            )\n\n        channel_list = meta.omero.channels\n        if len(channel_list) != num_c:\n            raise ValueError(\"The number of channels does not match the image.\")\n\n        for c, channel in enumerate(channel_list):\n            data = image.get_array(c=c, mode=\"dask\").ravel()\n            _start_percentile = da.percentile(\n                data, start_percentile, method=\"nearest\"\n            ).compute()\n            _end_percentile = da.percentile(\n                data, end_percentile, method=\"nearest\"\n            ).compute()\n\n            channel.channel_visualisation.start = _start_percentile\n            channel.channel_visualisation.end = _end_percentile\n            channel.channel_visualisation.min = 0\n            channel.channel_visualisation.max = max_dtype\n\n            ngio_logger.info(\n                f\"Updated window for channel {channel.label}. \"\n                f\"Start: {start_percentile}, End: {end_percentile}\"\n            )\n            meta.omero.channels[c] = channel\n\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: dict,\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        # Get the channel information if it exists\n        omero = self.image_meta.omero\n        if omero is not None:\n            channels = omero.channels\n            omero_kwargs = omero.extra_fields\n        else:\n            channels = []\n            omero_kwargs = {}\n\n        default_kwargs = {\n            \"store\": store,\n            \"on_disk_shape\": image_0.on_disk_shape,\n            \"chunks\": image_0.on_disk_array.chunks,\n            \"dtype\": image_0.on_disk_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            \"levels\": self.num_levels,\n            \"name\": name,\n            \"channel_labels\": image_0.channel_labels,\n            \"channel_wavelengths\": [ch.wavelength_id for ch in channels],\n            \"channel_visualization\": [ch.channel_visualisation for ch in channels],\n            \"omero_kwargs\": omero_kwargs,\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, cache: bool = False, mode: AccessModeLiteral = 'r+') -> None","text":"

Initialize the NGFFImage in read mode.

Source code in ngio/core/ngff_image.py
def __init__(\n    self, store: StoreLike, cache: bool = False, mode: AccessModeLiteral = \"r+\"\n) -> None:\n    \"\"\"Initialize the NGFFImage in read mode.\"\"\"\n    self.store = store\n    self._mode = mode\n    self.group = open_group_wrapper(store=store, mode=self._mode)\n\n    if self.group.read_only:\n        self._mode = \"r\"\n\n    self._image_meta = get_ngff_image_meta_handler(\n        self.group, meta_mode=\"image\", cache=cache\n    )\n    self._metadata_cache = cache\n    self.table = TableGroup(self.group, mode=self._mode)\n    self.label = LabelGroup(self.group, image_ref=self.get_image(), mode=self._mode)\n\n    ngio_logger.info(f\"Opened image located in store: {store}\")\n    ngio_logger.info(f\"- Image number of levels: {self.num_levels}\")\n
"},{"location":"api/core/#ngio.core.NgffImage.derive_new_image","title":"derive_new_image(store: StoreLike, name: str, overwrite: bool = True, **kwargs: dict) -> 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 (dict, default: {} ) \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: dict,\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    # Get the channel information if it exists\n    omero = self.image_meta.omero\n    if omero is not None:\n        channels = omero.channels\n        omero_kwargs = omero.extra_fields\n    else:\n        channels = []\n        omero_kwargs = {}\n\n    default_kwargs = {\n        \"store\": store,\n        \"on_disk_shape\": image_0.on_disk_shape,\n        \"chunks\": image_0.on_disk_array.chunks,\n        \"dtype\": image_0.on_disk_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        \"levels\": self.num_levels,\n        \"name\": name,\n        \"channel_labels\": image_0.channel_labels,\n        \"channel_wavelengths\": [ch.wavelength_id for ch in channels],\n        \"channel_visualization\": [ch.channel_visualisation for ch in channels],\n        \"omero_kwargs\": omero_kwargs,\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    image = Image(\n        store=self.group,\n        path=path,\n        pixel_size=pixel_size,\n        highest_resolution=highest_resolution,\n        label_group=LabelGroup(self.group, image_ref=None, mode=self._mode),\n        cache=self._metadata_cache,\n        mode=self._mode,\n    )\n    ngio_logger.info(f\"Opened image at path: {image.path}\")\n    ngio_logger.info(f\"- {image.dimensions}\")\n    ngio_logger.info(f\"- {image.pixel_size}\")\n    return image\n
"},{"location":"api/core/#ngio.core.NgffImage.update_omero_window","title":"update_omero_window(start_percentile: int = 5, end_percentile: int = 95) -> None","text":"

Update the OMERO window.

This will setup percentiles based values for the window of each channel.

Parameters:

  • start_percentile (int, default: 5 ) \u2013

    The start percentile.

  • end_percentile (int, default: 95 ) \u2013

    The end percentile

Source code in ngio/core/ngff_image.py
def update_omero_window(\n    self, start_percentile: int = 5, end_percentile: int = 95\n) -> None:\n    \"\"\"Update the OMERO window.\n\n    This will setup percentiles based values for the window of each channel.\n\n    Args:\n        start_percentile (int): The start percentile.\n        end_percentile (int): The end percentile\n\n    \"\"\"\n    meta = self.image_meta\n\n    lowest_res_image = self.get_image(highest_resolution=True)\n    lowest_res_shape = lowest_res_image.shape\n    for path in self.levels_paths:\n        image = self.get_image(path=path)\n        if np.prod(image.shape) < np.prod(lowest_res_shape):\n            lowest_res_shape = image.shape\n            lowest_res_image = image\n\n    max_dtype = np.iinfo(image.on_disk_array.dtype).max\n    num_c = lowest_res_image.dimensions.get(\"c\", 1)\n\n    if meta.omero is None:\n        raise NotImplementedError(\n            \"OMERO metadata not found. \" \" Please add OMERO metadata to the image.\"\n        )\n\n    channel_list = meta.omero.channels\n    if len(channel_list) != num_c:\n        raise ValueError(\"The number of channels does not match the image.\")\n\n    for c, channel in enumerate(channel_list):\n        data = image.get_array(c=c, mode=\"dask\").ravel()\n        _start_percentile = da.percentile(\n            data, start_percentile, method=\"nearest\"\n        ).compute()\n        _end_percentile = da.percentile(\n            data, end_percentile, method=\"nearest\"\n        ).compute()\n\n        channel.channel_visualisation.start = _start_percentile\n        channel.channel_visualisation.end = _end_percentile\n        channel.channel_visualisation.min = 0\n        channel.channel_visualisation.max = max_dtype\n\n        ngio_logger.info(\n            f\"Updated window for channel {channel.label}. \"\n            f\"Start: {start_percentile}, End: {end_percentile}\"\n        )\n        meta.omero.channels[c] = channel\n\n    self._image_meta.write_meta(meta)\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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.shape=(3, 1, 4320, 5120)\nimage.pixel_size=PixelSize(x=0.1625, y=0.1625, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.shape=(3, 1, 1080, 1280)\nimage.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=True)\n
In\u00a0[4]: Copied!
print(\"List of Labels: \", ngff_image.label.list())\n\nlabel_nuclei = ngff_image.label.get_label(\"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_label(\"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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\n
In\u00a0[5]: Copied!
print(\"List of Tables: \", ngff_image.table.list())\nprint(\" - Feature tables: \", ngff_image.table.list(table_type='feature_table'))\nprint(\" - Roi tables: \", ngff_image.table.list(table_type='roi_table'))\nprint(\" - Masking Roi tables: \", ngff_image.table.list(table_type='masking_roi_table'))\n
print(\"List of Tables: \", ngff_image.table.list()) print(\" - Feature tables: \", ngff_image.table.list(table_type='feature_table')) print(\" - Roi tables: \", ngff_image.table.list(table_type='roi_table')) print(\" - Masking Roi tables: \", ngff_image.table.list(table_type='masking_roi_table'))
List of Tables:  ['FOV_ROI_table', 'nuclei_ROI_table', 'well_ROI_table', 'regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\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_table(\"regionprops_DAPI\")\nfeature_table.table\n
# Loading a table feature_table = ngff_image.table.get_table(\"regionprops_DAPI\") feature_table.table
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\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_table(\"FOV_ROI_table\")\n\nprint(f\"{roi_table.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_table(\"FOV_ROI_table\") print(f\"{roi_table.field_indexes=}\") print(f\"{roi_table.get_roi('FOV_1')=}\") roi_table.table
roi_table.field_indexes=['FOV_1', 'FOV_2', 'FOV_3', 'FOV_4']\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
"},{"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

You can download the example image (on Linux and Mac os) by running the following command:

bash setup_data.sh\n

from the root of the repository.

"},{"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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage.channel_labels=['DAPI', 'nanog', 'Lamin B1']\nimage.dimensions=Dimensions(c=3, z=1, y=4320, x=5120)\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 dask array\ndask_array = image.on_disk_dask_array\ndask_array\n
# Get image as a dask array dask_array = image.on_disk_dask_array dask_array 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 .on_disk_array or .on_disk_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_array() method, which will return the image data in canonical order (TCZYX).

In\u00a0[4]: Copied!
image_numpy = image.get_array(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_array(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_table(\"FOV_ROI_table\")\nroi = roi_table.get_roi(\"FOV_1\")\nprint(f\"{roi=}\")\n\nimage_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\nimage_roi_1\n
roi_table = ngff_image.table.get_table(\"FOV_ROI_table\") roi = roi_table.get_roi(\"FOV_1\") print(f\"{roi=}\") image_roi_1 = image.get_array_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
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[5], line 5\n      2 roi = roi_table.get_roi(\"FOV_1\")\n      3 print(f\"{roi=}\")\n----> 5 image_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=True, mode=\"dask\")\n      6 image_roi_1\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_handler.py:105, in Image.get_array_from_roi(self, roi, c, t, mode, preserve_dimensions)\n    102 if isinstance(c, str):\n    103     c = self.get_channel_idx(label=c)\n--> 105 return self._get_array_from_roi(\n    106     roi=roi, t=t, c=c, mode=mode, preserve_dimensions=preserve_dimensions\n    107 )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'

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_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\n# Get roi for lower resolution image\nimage_2_roi_1 = image_2.get_array_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_array_from_roi(roi=roi, c=0, preserve_dimensions=False) # Get roi for lower resolution image image_2_roi_1 = image_2.get_array_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=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\nimage_2.pixel_size=PixelSize(x=0.65, y=0.65, z=1.0, unit=<SpaceUnits.micrometer: 'micrometer'>, virtual=False)\n
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[6], line 7\n      4 print(f\"{image_2.pixel_size=}\")\n      6 # Get roi for higher resolution image\n----> 7 image_1_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n      9 # Get roi for lower resolution image\n     10 image_2_roi_1 = image_2.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_handler.py:105, in Image.get_array_from_roi(self, roi, c, t, mode, preserve_dimensions)\n    102 if isinstance(c, str):\n    103     c = self.get_channel_idx(label=c)\n--> 105 return self._get_array_from_roi(\n    106     roi=roi, t=t, c=c, mode=mode, preserve_dimensions=preserve_dimensions\n    107 )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'
In\u00a0[7]: Copied!
import numpy as np\n\n# Get a small slice of the image\nsmall_slice = image.get_array(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_array(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_label(\"nuclei\")\nfig, axs = plt.subplots(1, 2, figsize=(10, 5))\naxs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].imshow(nuclei.on_disk_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_array(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_array(x=slice(1000, 2000), y=slice(1000, 2000)) # Set the sample slice to zeros zeros_slice = np.zeros_like(small_slice) image.set_array(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_label(\"nuclei\") fig, axs = plt.subplots(1, 2, figsize=(10, 5)) axs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\") axs[1].imshow(nuclei.on_disk_array[0]) for ax in axs: ax.axis(\"off\") plt.tight_layout() plt.show() # Add back the original slice to the image image.set_array(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.on_disk_array[0] > 100\nnew_label.on_disk_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.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].imshow(new_label.on_disk_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.on_disk_array[0] > 100 new_label.on_disk_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.on_disk_array[0, 0], cmap=\"gray\") axs[1].imshow(new_label.on_disk_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_label(\"new_label\", path=\"0\")\nlabel_2 = ngff_image.label.get_label(\"new_label\", path=\"2\")\n\nlabel_before_consolidation = label_2.on_disk_array[...]\n\n# Consolidate the label\nlabel_0.consolidate()\n\nlabel_after_consolidation = label_2.on_disk_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_label(\"new_label\", path=\"0\") label_2 = ngff_image.label.get_label(\"new_label\", path=\"2\") label_before_consolidation = label_2.on_disk_array[...] # Consolidate the label label_0.consolidate() label_after_consolidation = label_2.on_disk_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(table_type='feature_table')}\")\n\n\nnuclei = ngff_image.label.get_label('nuclei')\n\n# Create a table with random features for each nuclei in each ROI\nlist_of_records = []\nfor roi in roi_table.rois:\n    nuclei_in_roi = nuclei.get_array_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(\"FieldIndex\")}\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                     label_image='../nuclei',\n                     table_type='feature_table',\n                     overwrite=True)\n\nprint(f\"New list of feature table: {ngff_image.table.list(table_type='feature_table')}\")\nfeat_table.set_table(feat_df)\nfeat_table.consolidate()\n\nfeat_table.table\n
import numpy as np import pandas as pd print(f\"List of feature table: {ngff_image.table.list(table_type='feature_table')}\") nuclei = ngff_image.label.get_label('nuclei') # Create a table with random features for each nuclei in each ROI list_of_records = [] for roi in roi_table.rois: nuclei_in_roi = nuclei.get_array_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(\"FieldIndex\")} ) feat_df = pd.DataFrame.from_records(list_of_records) # Create a new feature table feat_table = ngff_image.table.new(name='new_feature_table', label_image='../nuclei', table_type='feature_table', overwrite=True) print(f\"New list of feature table: {ngff_image.table.list(table_type='feature_table')}\") feat_table.set_table(feat_df) feat_table.consolidate() feat_table.table
List of feature table: ['regionprops_DAPI', 'nuclei_measurements_wf3', 'nuclei_measurements_wf4', 'nuclei_lamin_measurements_wf4']\n
\n---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[10], line 12\n     10 list_of_records = []\n     11 for roi in roi_table.rois:\n---> 12     nuclei_in_roi = nuclei.get_array_from_roi(roi, mode='numpy')\n     13     for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n     14         list_of_records.append(\n     15             {\"label\": nuclei_id,\n     16              \"feat1\": np.random.rand(),\n     17              \"feat2\": np.random.rand(),\n     18          \"ROI\": roi.infos.get(\"FieldIndex\")}\n     19     )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/label_handler.py:89, in Label.get_array_from_roi(self, roi, t, mode, preserve_dimensions)\n     74 def get_array_from_roi(\n     75     self,\n     76     roi: WorldCooROI,\n   (...)\n     79     preserve_dimensions: bool = False,\n     80 ) -> ArrayLike:\n     81     \"\"\"Return the label data from a region of interest (ROI).\n     82 \n     83     Args:\n   (...)\n     87         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n     88     \"\"\"\n---> 89     return self._get_array_from_roi(\n     90         roi=roi, t=t, c=None, mode=mode, preserve_dimensions=preserve_dimensions\n     91     )\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:344, in ImageLike._get_array_from_roi(self, roi, t, c, mode, preserve_dimensions)\n    327 def _get_array_from_roi(\n    328     self,\n    329     roi: WorldCooROI,\n   (...)\n    333     preserve_dimensions: bool = False,\n    334 ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:\n    335     \"\"\"Return the image data from a region of interest (ROI).\n    336 \n    337     Args:\n   (...)\n    342         preserve_dimensions (bool): Whether to preserve the dimensions of the data.\n    343     \"\"\"\n--> 344     data_pipe = self._build_roi_pipe(\n    345         roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions\n    346     )\n    347     return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)\n    348     return return_array\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/image_like_handler.py:291, in ImageLike._build_roi_pipe(self, roi, t, c, preserve_dimensions)\n    283 def _build_roi_pipe(\n    284     self,\n    285     roi: WorldCooROI,\n   (...)\n    288     preserve_dimensions: bool = False,\n    289 ) -> DataTransformPipe:\n    290     \"\"\"Build the data transform pipe for a region of interest (ROI).\"\"\"\n--> 291     roi_coo = roi.to_raster_coo(\n    292         pixel_size=self.dataset.pixel_size, dimensions=self.dimensions\n    293     )\n    295     slicer = RoiSlicer(\n    296         on_disk_axes_name=self.dataset.on_disk_axes_names,\n    297         axes_order=self.dataset.axes_order,\n   (...)\n    301         preserve_dimensions=preserve_dimensions,\n    302     )\n    303     return DataTransformPipe(slicer=slicer)\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/roi.py:44, in WorldCooROI.to_raster_coo(self, pixel_size, dimensions)\n     39 def to_raster_coo(\n     40     self, pixel_size: PixelSize, dimensions: Dimensions\n     41 ) -> \"RasterCooROI\":\n     42     \"\"\"Convert to raster coordinates.\"\"\"\n     43     return RasterCooROI(\n---> 44         x=_to_raster(self.x, pixel_size.x, dimensions.x),\n     45         y=_to_raster(self.y, pixel_size.y, dimensions.y),\n     46         z=_to_raster(self.z, pixel_size.z, dimensions.z),\n     47         x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x),\n     48         y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y),\n     49         z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z),\n     50         original_roi=self,\n     51     )\n\nAttributeError: 'Dimensions' object has no attribute 'x'
"},{"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 .array() we can use the .set_array() (or set_array_from_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))
\n---------------------------------------------------------------------------\nTypeError                                 Traceback (most recent call last)\nCell In[2], line 1\n----> 1 mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n      2                                        name=\"MIP\",\n      3                                        shape=(1, 1, 2160, 5120))\n\nFile /opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/ngio/core/ngff_image.py:202, in NgffImage.derive_new_image(self, store, name, overwrite, **kwargs)\n    179 default_kwargs = {\n    180     \"store\": store,\n    181     \"on_disk_shape\": image_0.on_disk_shape,\n   (...)\n    197     \"version\": self.image_meta.version,\n    198 }\n    200 default_kwargs.update(kwargs)\n--> 202 create_empty_ome_zarr_image(\n    203     **default_kwargs,\n    204 )\n    205 return NgffImage(store=store)\n\nTypeError: create_empty_ome_zarr_image() got an unexpected keyword argument 'shape'
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_table(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.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.rois:\n    print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n    patch = source_image.get_array_from_roi(roi)\n    mip_patch = patch.max(axis=1, keepdims=True)\n    mip_image.set_array_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.on_disk_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_table(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.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.rois: print(f\" - Processing ROI {roi.infos.get('field_index')}\") patch = source_image.get_array_from_roi(roi) mip_patch = patch.max(axis=1, keepdims=True) mip_image.set_array_from_roi(patch=mip_patch, roi=roi) print(\"MIP image saved\") plt.figure(figsize=(5, 5)) plt.title(\"Mip\") plt.imshow(mip_image.on_disk_array[0, 0, :, :], cmap=\"gray\") plt.axis('off') plt.tight_layout() plt.show()
Source image loaded with shape: (1, 2, 2160, 5120)\n
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[3], line 6\n      3 print(\"Source image loaded with shape:\", source_image.shape)\n      5 # Get the MIP image\n----> 6 mip_image = mip_ngff.get_image()\n      7 print(\"MIP image loaded with shape:\", mip_image.shape)\n      9 # Get a ROI table\n\nNameError: name 'mip_ngff' is not defined
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_array(c=0, z=0)\n\n# Consolidate the pyramid\nmip_image.consolidate()\n\nimage_after_consolidation = mip_image_2.get_array(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_array(c=0, z=0) # Consolidate the pyramid mip_image.consolidate() image_after_consolidation = mip_image_2.get_array(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()
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[4], line 2\n      1 # Get the MIP image at a lower resolution\n----> 2 mip_image_2 = mip_ngff.get_image(path=\"2\")\n      4 image_before_consolidation = mip_image_2.get_array(c=0, z=0)\n      6 # Consolidate the pyramid\n\nNameError: name 'mip_ngff' is not defined
In\u00a0[5]: Copied!
mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n\nroi_list = []\nfor roi in roi_table.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.set_rois(roi_list, overwrite=True)\nmip_roi_table.consolidate()\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.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.set_rois(roi_list, overwrite=True) mip_roi_table.consolidate() mip_roi_table.table
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[5], line 1\n----> 1 mip_roi_table = mip_ngff.table.new(\"FOV_ROI_table\", overwrite=True)\n      3 roi_list = []\n      4 for roi in roi_table.rois:\n\nNameError: name 'mip_ngff' is not defined
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)
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[7], line 1\n----> 1 nuclei_image = mip_ngff.label.derive(name=\"nuclei\", overwrite=True)\n\nNameError: name 'mip_ngff' is not defined
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_table(\"FOV_ROI_table\")\nprint(\"ROI table loaded with\", len(roi_table.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.rois:\n    print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n    patch = source_image.get_array_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_array_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.on_disk_array[0, 0], cmap=\"gray\")\naxs[1].set_title(\"Nuclei segmentation\")\naxs[1].imshow(nuclei_image.on_disk_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_table(\"FOV_ROI_table\") print(\"ROI table loaded with\", len(roi_table.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.rois: print(f\" - Processing ROI {roi.infos.get('field_index')}\") patch = source_image.get_array_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_array_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.on_disk_array[0, 0], cmap=\"gray\") axs[1].set_title(\"Nuclei segmentation\") axs[1].imshow(nuclei_image.on_disk_array[0], cmap=rand_cmap, interpolation='nearest') for ax in axs: ax.axis('off') plt.tight_layout() plt.show()
\n---------------------------------------------------------------------------\nNameError                                 Traceback (most recent call last)\nCell In[8], line 2\n      1 # Get the source imag\n----> 2 source_image = mip_ngff.get_image()\n      3 print(\"Source image loaded with shape:\", source_image.shape)\n      5 # Get a ROI table\n\nNameError: name 'mip_ngff' is not defined
In\u00a0[\u00a0]: Copied!
\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 49f2dc0..1712c59 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,26 +2,26 @@ https://github.com/fractal-analytics-platform/ngio.git/ - 2024-11-08 + 2024-11-09 https://github.com/fractal-analytics-platform/ngio.git/getting-started/ - 2024-11-08 + 2024-11-09 https://github.com/fractal-analytics-platform/ngio.git/api/core/ - 2024-11-08 + 2024-11-09 https://github.com/fractal-analytics-platform/ngio.git/notebooks/basic_usage/ - 2024-11-08 + 2024-11-09 https://github.com/fractal-analytics-platform/ngio.git/notebooks/image/ - 2024-11-08 + 2024-11-09 https://github.com/fractal-analytics-platform/ngio.git/notebooks/processing/ - 2024-11-08 + 2024-11-09 \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 3abe19d94706f99ddc8fb2c9e443e350011c7368..9ff3bab0e4e9b4ea58d0108c128f63edcd9f4078 100644 GIT binary patch delta 71 zcmV-N0J#5%0*C?!ABzYG0H!XH2Ok3Ts*xx+B~hIFL%ZZama8I8)QO3Cegat6Rgq^K d^-K!012R)d2<}Cw)k%?rdavO delta 71 zcmV-N0J#5%0*C?!ABzYGfK)A!2Ok1ltC1)-B_z)Mpcqr6KLM=ks>rjA ddM1V00hy^J1otA;>QRw|d