diff --git a/.binder/requirements.txt b/.binder/requirements.txt index 2b31e4b7..cc22d0a0 100644 --- a/.binder/requirements.txt +++ b/.binder/requirements.txt @@ -4,12 +4,11 @@ jupyterlab>=2.2.0 imageio ipywidgets>=7.5.1 ipympl>=0.5.7 +ngff-zarr>=0.12.0 numpy torch>=2.0 monai>=1.2.0 matplotlib==3.3.1 -PyQt5==5.15.0 -PyQt5-sip==12.8.0 -QtPy==1.9.0 +PySide2 voila tqdm diff --git a/.gitignore b/.gitignore index dac7be19..f7a43a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ wasm/typescript/demo/ wasm/test micromamba/ .pixi/ +examples/result.ome.zarr/ diff --git a/examples/ITK_Example23_OMEZarr.ipynb b/examples/ITK_Example23_OMEZarr.ipynb new file mode 100644 index 00000000..d6bd8390 --- /dev/null +++ b/examples/ITK_Example23_OMEZarr.ipynb @@ -0,0 +1,561 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 23. OME-Zarr image registration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### OME-Zarr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[OME-Zarr](https://ngff.openmicroscopy.org/) is a cloud-optimized file format designed for storing and managing large-scale bioimaging data [1][2]. \n", + "\n", + "## Core Features\n", + "\n", + "**Storage Architecture**\n", + "- Stores N-dimensional typed arrays in individually accessible chunks\n", + "- Uses JSON for metadata storage and binary data in chunk-files\n", + "- Supports up to 5 dimensions in version 0.4 (time, channel, z, y, x)\n", + "\n", + "**Performance Optimization**\n", + "- Implements Google Maps-style multi-resolution pyramids for smooth zooming\n", + "- Offers configurable chunk compression using algorithms like GZIP or Blosc\n", + "- Enables efficient data access through colocated pixel storage\n", + "\n", + "**Data Organization**\n", + "- Uses hierarchical Zarr \"groups\" to organize multiple multi-dimensional pyramids\n", + "- Allows metadata attachment at each hierarchy level using JSON files\n", + "- Supports grouping of related data (raw images, deconvolutions, segmentations)\n", + "\n", + "## Spatial Metadata Support\n", + "\n", + "Version 0.4 introduced significant spatial metadata capabilities:\n", + "- Supports multi-dimensional raster images with associated volumetric data\n", + "- Enables spatial transformations for dataset alignment\n", + "\n", + "[1] https://www.biorxiv.org/content/10.1101/2023.02.17.528834v2.full\n", + "[2] https://pmc.ncbi.nlm.nih.gov/articles/PMC9980008/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ngff-zarr\n", + "\n", + "[ngff-zarr](https://ngff-zarr.readthedocs.io) is lean and kind OME-Zarr implementation.\n", + "\n", + "## ✨ Features\n", + "\n", + "- Minimal dependencies\n", + "- Work with arbitrary Zarr store types\n", + "- Lazy, parallel, and web ready -- no local filesystem required\n", + "- Process extremely large datasets\n", + "- Conversion of most bioimaging file formats\n", + "- Multiple downscaling methods\n", + "- Supports Python>=3.9\n", + "- Reads OME-Zarr v0.1 to v0.5 into simple Python data classes with Dask arrays\n", + "- Optional OME-Zarr data model validation during reading\n", + "- Writes OME-Zarr v0.4 to v0.5\n", + "- Optional writing via [tensorstore](https://google.github.io/tensorstore/)\n", + "\n", + "And interoperability with [ITK's Python bindings](https://docs.itk.org/en/latest/learn/python_quick_start.html) and [ITK-Wasm](https://wasm.itk.org) that work with [itk-elastix](https://pypi.org/project/itk-elastix/) and [itkwasm-elastix](https://pypi.org/project/itkwasm-elastix/)\n", + "\n", + "In this notebook, we'll register OME-Zarr images with `itk-elastix`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import itk\n", + "from itkwidgets import view\n", + "import ngff_zarr as nz\n", + "from rich import print\n", + "from zarr.storage import FSStore, LRUStoreCache\n", + "import zarr\n", + "import copy\n", + "import dask.array as da\n", + "import dask" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Multiscales(\n", + " images=[\n", + " NgffImage(\n", + " data=dask.array<from-zarr, shape=(79, 201, 333, 333), dtype=uint16, chunksize=(1, 1, 333, 333), \n", + "chunktype=numpy.ndarray>,\n", + " dims=['t', 'z', 'y', 'x'],\n", + " scale={'t': 1.0, 'z': 1.0, 'y': 1.0, 'x': 1.0},\n", + " translation={'t': 0.0, 'z': 0.0, 'y': 0.0, 'x': 0.0},\n", + " name='image',\n", + " axes_units={'t': None, 'z': None, 'y': None, 'x': None},\n", + " computed_callbacks=[]\n", + " ),\n", + " NgffImage(\n", + " data=dask.array<from-zarr, shape=(79, 201, 166, 166), dtype=uint16, chunksize=(1, 1, 166, 166), \n", + "chunktype=numpy.ndarray>,\n", + " dims=['t', 'z', 'y', 'x'],\n", + " scale={'t': 1.0, 'z': 1.0, 'y': 1.0, 'x': 1.0},\n", + " translation={'t': 0.0, 'z': 0.0, 'y': 0.0, 'x': 0.0},\n", + " name='image',\n", + " axes_units={'t': None, 'z': None, 'y': None, 'x': None},\n", + " computed_callbacks=[]\n", + " ),\n", + " NgffImage(\n", + " data=dask.array<from-zarr, shape=(79, 201, 83, 83), dtype=uint16, chunksize=(1, 1, 83, 83), \n", + "chunktype=numpy.ndarray>,\n", + " dims=['t', 'z', 'y', 'x'],\n", + " scale={'t': 1.0, 'z': 1.0, 'y': 1.0, 'x': 1.0},\n", + " translation={'t': 0.0, 'z': 0.0, 'y': 0.0, 'x': 0.0},\n", + " name='image',\n", + " axes_units={'t': None, 'z': None, 'y': None, 'x': None},\n", + " computed_callbacks=[]\n", + " )\n", + " ],\n", + " metadata=Metadata(\n", + " axes=[\n", + " Axis(name='t', type='time', unit=None),\n", + " Axis(name='z', type='space', unit=None),\n", + " Axis(name='y', type='space', unit=None),\n", + " Axis(name='x', type='space', unit=None)\n", + " ],\n", + " datasets=[\n", + " Dataset(path='0', coordinateTransformations=[]),\n", + " Dataset(path='1', coordinateTransformations=[]),\n", + " Dataset(path='2', coordinateTransformations=[])\n", + " ],\n", + " coordinateTransformations=None,\n", + " omero=Omero(\n", + " channels=[\n", + " OmeroChannel(color='808080', window=OmeroWindow(min=0.0, max=65535.0, start=200.0, end=1500.0))\n", + " ]\n", + " ),\n", + " name='image',\n", + " version='0.3'\n", + " ),\n", + " scale_factors=None,\n", + " method=None,\n", + " chunks=None\n", + ")\n", + "\n" + ], + "text/plain": [ + "\u001b[1;35mMultiscales\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mimages\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mNgffImage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[35mdask\u001b[0m.array\u001b[1m<\u001b[0m\u001b[1;95mfrom-zarr\u001b[0m\u001b[39m, \u001b[0m\u001b[33mshape\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m79\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m201\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, \u001b[0m\u001b[33mdtype\u001b[0m\u001b[39m=\u001b[0m\u001b[35muint16\u001b[0m\u001b[39m, \u001b[0m\u001b[33mchunksize\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m1\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, \u001b[0m\n", + "\u001b[33mchunktype\u001b[0m\u001b[39m=\u001b[0m\u001b[35mnumpy\u001b[0m\u001b[39m.ndarray>,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mdims\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m't'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'z'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'y'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'x'\u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mscale\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m't'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m1.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'z'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m1.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'y'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m1.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'x'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m1.0\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mtranslation\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m't'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'z'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'y'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'x'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mname\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'image'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33maxes_units\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m't'\u001b[0m\u001b[39m: \u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'z'\u001b[0m\u001b[39m: \u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'y'\u001b[0m\u001b[39m: \u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'x'\u001b[0m\u001b[39m: \u001b[0m\u001b[3;35mNone\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mcomputed_callbacks\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m[\u001b[0m\u001b[1;39m]\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[1;35mNgffImage\u001b[0m\u001b[1;39m(\u001b[0m\n", + "\u001b[39m \u001b[0m\u001b[33mdata\u001b[0m\u001b[39m=\u001b[0m\u001b[35mdask\u001b[0m\u001b[39m.array
NgffImage(\n", + " data=dask.array<from-zarr, shape=(79, 201, 333, 333), dtype=uint16, chunksize=(1, 1, 333, 333), \n", + "chunktype=numpy.ndarray>,\n", + " dims=['t', 'z', 'y', 'x'],\n", + " scale={'t': 1.0, 'z': 1.0, 'y': 1.0, 'x': 1.0},\n", + " translation={'t': 0.0, 'z': 0.0, 'y': 0.0, 'x': 0.0},\n", + " name='image',\n", + " axes_units={'t': None, 'z': None, 'y': None, 'x': None},\n", + " computed_callbacks=[]\n", + ")\n", + "\n" + ], + "text/plain": [ + "\u001b[1;35mNgffImage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[35mdask\u001b[0m.array\u001b[1m<\u001b[0m\u001b[1;95mfrom-zarr\u001b[0m\u001b[39m, \u001b[0m\u001b[33mshape\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m79\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m201\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, \u001b[0m\u001b[33mdtype\u001b[0m\u001b[39m=\u001b[0m\u001b[35muint16\u001b[0m\u001b[39m, \u001b[0m\u001b[33mchunksize\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m1\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m333\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, \u001b[0m\n", + "\u001b[33mchunktype\u001b[0m\u001b[39m=\u001b[0m\u001b[35mnumpy\u001b[0m\u001b[39m.ndarray\u001b[0m\u001b[1m>\u001b[0m,\n", + " \u001b[33mdims\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m't'\u001b[0m, \u001b[32m'z'\u001b[0m, \u001b[32m'y'\u001b[0m, \u001b[32m'x'\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mscale\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m't'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'z'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'y'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'x'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mtranslation\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m't'\u001b[0m: \u001b[1;36m0.0\u001b[0m, \u001b[32m'z'\u001b[0m: \u001b[1;36m0.0\u001b[0m, \u001b[32m'y'\u001b[0m: \u001b[1;36m0.0\u001b[0m, \u001b[32m'x'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'image'\u001b[0m,\n", + " \u001b[33maxes_units\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m't'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'z'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'y'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'x'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mcomputed_callbacks\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Working with the first scale\n", + "ngff_image = ome_zarr.images[0]\n", + "\n", + "# For visualization\n", + "vmin = ome_zarr.metadata.omero.channels[0].window.start\n", + "vmax = ome_zarr.metadata.omero.channels[0].window.end\n", + "\n", + "print(ngff_image)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " " + ], + "text/plain": [ + "