Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ jobs:
- name: ruff check
uses: astral-sh/ruff-action@v3
with:
version: "0.15.18"
args: "check"

- name: ruff format --check
uses: astral-sh/ruff-action@v3
with:
version: "0.15.18"
args: "format --check"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@ Optional extras:
pip install "patchworks[gpu]" # GPU VRAM querying (nvidia-ml-py)
pip install "patchworks[cellpose]" # Cellpose plugin
pip install "patchworks[bioio]" # convert any image format to OME-ZARR
pip install "patchworks[imaris]" # convert Imaris .ims files to OME-ZARR
pip install "patchworks[napari]" # interactive napari viewer plugin
pip install "patchworks[all]" # Everything (except napari GUI)
```

> `bioio` reads CZI/LIF/ND2/OME-TIFF/… The `[bioio]` extra bundles the common
> native readers (`bioio-nd2`, `bioio-ome-tiff`, `bioio-czi`, `bioio-tifffile`,
> `bioio-lif`) plus `bioio-bioformats`, the Bio-Formats catch-all reader (JVM).
> `[imaris]` adds native `.ims` support (HDF5, no JVM). Physical pixel
> calibration is read from the input and written into the OME-ZARR.

---

Expand Down
28 changes: 21 additions & 7 deletions docs/guide/ome_zarr_napari.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,33 @@ existed.

## Convert any image to OME-ZARR

`to_ome_zarr` accepts a dask/NumPy array, an existing `.zarr` store, or **any
file format** readable by [bioio](https://github.com/bioio-devs/bioio) (CZI,
LIF, ND2, OME-TIFF, …). File inputs are read **lazily** — pixels stream from
disk and are written level by level through dask, so terabyte images convert in
bounded RAM.
`to_ome_zarr` accepts a dask/NumPy array, an existing `.zarr` store, an
**Imaris `.ims`** file, or **any format** readable by
[bioio](https://github.com/bioio-devs/bioio) (CZI, LIF, ND2, OME-TIFF, …). File
inputs are read **lazily**.

```python
from patchworks.plugins.ome_zarr import to_ome_zarr

# From a proprietary microscope file (lazy, via bioio):
to_ome_zarr("scan.czi", "scan.zarr", n_levels=5)
to_ome_zarr("scan.czi", "scan.zarr", n_levels=5) # via bioio
to_ome_zarr("scan.ims", "scan.zarr") # Imaris, native HDF5
```

### Pixel calibration

The physical voxel size is read from the input — bioio's `physical_pixel_sizes`,
the Imaris resolution metadata, or an existing OME-ZARR's scale — and written
into the NGFF `coordinateTransformations` (in micrometers), so calibration is
preserved regardless of input. Override or supply it for bare arrays with
`pixel_size={"z": 2.0, "y": 0.32, "x": 0.32}`.

### Won't OOM

Each pyramid level is built by reading the **previous level back from disk**
and streaming the downsampled result out through dask with bounded chunks. The
graph never chains level-on-level and no whole plane/volume is held in RAM, so
terabyte images convert in bounded memory.

!!! note "Install the readers you need"
`pip install "patchworks[bioio]"` pulls `bioio` plus the `bioio-bioformats`
catch-all reader (needs a JVM). For speed, add native readers for your
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ bioio = [
"bioio-tifffile",
"bioio-lif",
]
# imaris reads .ims files natively (HDF5, no JVM) for OME-ZARR conversion.
imaris = ["imaris-ims-file-reader"]
# napari enables the interactive viewer plugin (GUI-heavy, kept out of [all]).
napari = ["napari[all]"]
dev = ["pytest", "pytest-cov", "scikit-image", "psutil", "tqdm"]
docs = ["mkdocs-material>=9.0", "mkdocstrings[python]>=0.24"]
all = ["patchworks[io,gpu,bioio]", "psutil", "tqdm", "scikit-image"]
all = ["patchworks[io,gpu,bioio,imaris]", "psutil", "tqdm", "scikit-image"]

[project.urls]
Homepage = "https://github.com/imcf/patchworks"
Expand Down
11 changes: 9 additions & 2 deletions src/patchworks/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ def load_ome_zarr(
(128, 2048, 2048)
"""
root = zarr.open_group(str(store_path), mode="r")
# OME-ZARR 0.5 nests under "ome" key; older stores use "multiscales" directly
_attrs = dict(root.attrs)
_ms = _attrs.get("multiscales") or _attrs.get("ome", {}).get("multiscales")
try:
path = root.attrs["multiscales"][0]["datasets"][level]["path"]
path = _ms[0]["datasets"][level]["path"]
except (KeyError, IndexError, TypeError) as exc:
raise ValueError(
f"Cannot read OME-ZARR multiscales metadata at level {level} "
Expand Down Expand Up @@ -157,8 +160,12 @@ def estimate_empty_tiles(
z_src: Any = None
if isinstance(image, (str, Path)):
_root = zarr.open_group(str(image), mode="r")
_rattr = dict(_root.attrs)
_rms = _rattr.get("multiscales") or _rattr.get("ome", {}).get(
"multiscales"
)
try:
_zpath = _root.attrs["multiscales"][0]["datasets"][level]["path"]
_zpath = _rms[0]["datasets"][level]["path"]
except (KeyError, IndexError, TypeError) as exc:
raise ValueError(
f"Cannot read OME-ZARR multiscales metadata at level {level} "
Expand Down
Loading
Loading