Metadata-Version: 2.5
Name: ioimage
Version: 0.1.3
Summary: Read and write microscopy images (TIFF, OME-TIFF, OME-Zarr) with one API, lazy region reads and complete metadata
Project-URL: Documentation, https://ioimage.readthedocs.io/
Project-URL: Homepage, https://github.com/VirtualEmbryo/ioimage
Project-URL: Bug Tracker, https://github.com/VirtualEmbryo/ioimage/issues
Project-URL: Changelog, https://github.com/VirtualEmbryo/ioimage/blob/main/CHANGELOG.md
Author-email: Hervé Turlier <herve.turlier@cnrs.fr>, Matthieu Perez <matthieu.perez@cnrs.fr>
Maintainer-email: Matthieu Perez <matthieu.perez@cnrs.fr>
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: numpy>=2.0
Requires-Dist: tifffile>=2026.5.2
Provides-Extra: codecs
Requires-Dist: imagecodecs>=2026.1.1; extra == 'codecs'
Provides-Extra: docs
Requires-Dist: myst-parser>=0.13; extra == 'docs'
Requires-Dist: sphinx-autoapi; extra == 'docs'
Requires-Dist: sphinx-book-theme>=0.1.0; extra == 'docs'
Requires-Dist: sphinx-copybutton; extra == 'docs'
Requires-Dist: sphinx>=4.0; extra == 'docs'
Provides-Extra: test
Requires-Dist: imagecodecs>=2026.1.1; extra == 'test'
Requires-Dist: ome-zarr>=0.19; extra == 'test'
Requires-Dist: psutil>=6; extra == 'test'
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
Requires-Dist: pytest>=7.2.0; extra == 'test'
Requires-Dist: scikit-image>=0.24; extra == 'test'
Requires-Dist: zarr>=3.2; extra == 'test'
Provides-Extra: zarr
Requires-Dist: zarr>=3.2; extra == 'zarr'
Description-Content-Type: text/markdown

# ioimage

[![ci](https://github.com/VirtualEmbryo/ioimage/actions/workflows/ci.yml/badge.svg)](https://github.com/VirtualEmbryo/ioimage/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/ioimage)](https://pypi.org/project/ioimage/)

Read and write microscopy images — TIFF, BigTIFF, OME-TIFF, ImageJ TIFF and OME-Zarr — with one API.

- Lazy region reads: opening an image reads only headers and metadata, and a region read touches only the pages or chunks it needs.
- Pyramids and sharding for OME-Zarr, with bounded memory for conversion.
- A format-independent metadata model that extracts everything a file carries (anisotropic voxel size, channels, time, objective, acquisition, the original metadata blocks verbatim) and carries it through every conversion.
- A shared frame with [iorec](https://github.com/VirtualEmbryo/iorec), which stores `.rec` meshes: both use the same vocabulary (`coordinate_frame`, `axis_order`, `spacing`, `spacing_unit`, `source_shape`, `crop_offset`), so a mesh and its image live in the same frame.

Licence: GPL-3.0-or-later.

## Installation

```bash
pip install ioimage
```

The core depends on `numpy` and `tifffile` only. OME-Zarr needs the `zarr` extra (zarr-python ≥ 3.2), and compressed TIFF codecs beyond zlib need the `codecs` extra (imagecodecs):

```bash
pip install "ioimage[zarr,codecs]"
```

Python ≥ 3.12.

## Usage

### Python

```python
import ioimage

# open lazily: only headers and metadata are read
with ioimage.open("stack.ome.zarr") as img:  # or stack.tif / stack.ome.tif
    img.shape, img.dtype, img.axes, img.levels  # (1024, 2048, 2048) uint16 'zyx' 3
    img.meta.spacing, img.meta.spacing_unit  # (2.0, 0.325, 0.325) 'micrometer' — or None, never a default
    crop = img.read(region=(slice(100, 356), slice(0, 512), slice(512, 1024)))  # only those chunks are read
    low = img.read(level=2)  # a pyramid level
    one = img.read(channel=0, timepoint=3)  # on 4-D / 5-D images: selects and drops the axis
    crop, crop_meta = img.read_with_metadata(region=(slice(100, 356),))  # metadata of the crop (origin, crop_offset)

array, meta = ioimage.read("stack.tif")  # whole image in one call

# write: the format follows the suffix, or format="tiff" | "imagej" | "ome-tiff" | "ome-zarr"
ioimage.write("out.ome.zarr", array, meta, chunks=(64, 256, 256), shards=(256, 1024, 1024), levels=3)
ioimage.write("out.ome.tif", array, meta)
ioimage.convert("in.tif", "out.ome.zarr", levels=3)  # streamed, bounded memory

# the frame shared with iorec
iorec.save_rec("mesh.rec", points, triangles, labels, metadata=crop_meta.to_iorec())
```

### Command line

```bash
ioimage info stack.ome.zarr            # shape, dtype, axes, levels, chunks, metadata summary
ioimage meta stack.ome.tif --json      # the full structured metadata (add --raw for the raw blocks)
ioimage convert in.tif out.ome.zarr --chunks 64,256,256 --shards 256,1024,1024 --levels 3
```

## Metadata

`ImageMetadata` holds, in array-axis order: `axes` (a subset of `tczyx`), `shape`, `dtype`, `spacing` per space axis with `spacing_unit`, `origin`, `time_interval`/`timestamps`, `channels` (name, colour, excitation and emission wavelengths), `objective` (magnification, numerical aperture, immersion, refractive index), `acquisition` (date, instrument, detector, software, exposure), `significant_bits`, the iorec frame keys, and `raw`: the original OME-XML, ImageJ description, TIFF tags, vendor blocks and NGFF attributes, verbatim.

Rules: missing metadata stays missing (`spacing is None`, never a guessed value); anisotropic spacing is carried exactly, never resampled; a crop or a pyramid level updates the derived fields (`shape`, `origin`, `spacing`, `crop_offset`, `source_shape`); when a target format cannot hold a field, `write` warns (`MetadataDropped`) and records the list in the output (`dropped_fields`).

| Target | holds everything? | what is dropped |
|---|---|---|
| OME-Zarr 0.5 | yes (structured fields as attributes, OME-XML in `OME/METADATA.ome.xml`) | — |
| OME-TIFF | yes (standard OME-XML elements plus `MapAnnotation`s for the raw blocks and the frame) | — |
| ImageJ TIFF | partly | origin, channel colours and wavelengths, raw blocks other than ImageJ's |
| plain TIFF | structured fields (JSON description) | raw blocks |

## Development

Tooling: `uv`, `just`, `ruff`, `ty`, `pytest`, `pre-commit`, Sphinx.

```bash
uv sync                      # dev group: tests, ruff, ty, sphinx
just lint && just typecheck && just test
just test -m slow            # the large-stack memory test (writes an 8 GiB-equivalent store in a temp folder)
IOIMAGE_REFERENCE_DATA=/path/to/reference_data just test -m reference   # optional comparison with scikit-image (layout in tests/test_reference_data.py)
```

See [CONTRIBUTING.md](https://github.com/VirtualEmbryo/ioimage/blob/main/CONTRIBUTING.md) before opening a pull request. To cite ioimage, see [CITATION.cff](https://github.com/VirtualEmbryo/ioimage/blob/main/CITATION.cff).
