Skip to main content

OME-TIFF and OME-ZARR writer APIs designed for microscopy acquisition

Project description

ome-writers

License PyPI Python Version CI codecov codspeed

OME-TIFF and OME-ZARR writer APIs designed for microscopy acquisition.

Documentation

Detailed documentation is available at https://pymmcore-plus.github.io/ome-writers/.

Purpose

ome-writers provides a unified interface for writing microscopy image data to OME-compliant formats (OME-TIFF and OME-Zarr) using various different backends. It is designed for streaming acquisition: receiving 2D camera frames one at a time and writing them to multi-dimensional arrays with proper metadata.

The core problem ome-writers solves:

Map a stream of 2D frames (arriving in acquisition order) to storage locations in multi-dimensional arrays, while generating OME-compliant metadata for both TIFF and Zarr formats.

We prioritize:

  • โœ… Correctness: Strict adherence to both OME-TIFF and OME-Zarr specifications.
  • ๐Ÿ’ฏ Completeness: We want all the metadata to go to its proper place.
  • ๐Ÿš€ Performance: Very minimal, native-backed hot-path logic when appending frames.
  • ๐Ÿคธโ€โ™‚๏ธ Flexibility: Pick from 5+ array backends, suiting your dependency preferences.
  • ๐Ÿ“– Usability: Relatively small, well organized API, with extensive documentation.
  • ๐Ÿ’ช Stability: Minimal dependencies, exhaustive testing and validation.

Installation

You can install ome-writers via pip. You must also select select at least one backend extra:

pip install ome-writers[<backend>]

...where <backend> is a comma-separated list of one or more of the following:

  • tensorstore โ€” Uses tensorstore, supports OME-Zarr v0.5.
  • acquire-zarr โ€” Uses acquire-zarr, supports OME-Zarr v0.5.
  • zarr-python โ€” Uses zarr-python, supports OME-Zarr v0.5.
  • zarrs-python โ€” Uses zarrs-python, supports OME-Zarr v0.5.
  • tifffile โ€” Uses tifffile, supports OME-TIFF.
  • all โ€” install all backends.

[!Note] All zarr-backends use yaozarrs to generate OME-Zarr metadata and create zarr hierarchies (only array-writing is handled by the selected backend).

(Developers using uv sync will end up with all backends installed by default.)

Basic Usage

[!Note] More complete usage examples are available in the usage documentation

from ome_writers import AcquisitionSettings, Dimension, create_stream

settings = AcquisitionSettings(
    root_path="example_5d_image.ome.zarr",
    dimensions=[
        Dimension(name="t", count=10, chunk_size=1, type="time"),
        Dimension(name="c", count=2, chunk_size=1, type="channel"),
        Dimension(name="z", count=5, chunk_size=1, type="space", scale=5),
        Dimension(name="y", count=256, chunk_size=64, type="space", scale=0.1),
        Dimension(name="x", count=256, chunk_size=64, type="space", scale=0.1),
    ],
    dtype="uint16",
    overwrite=True,
)

with create_stream(settings) as stream:
    for frame in ...:
        stream.append(frame)

High-Level Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  AcquisitionSettings  โ”‚โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚   FrameRouter   โ”‚โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  ArrayBackend         โ”‚
โ”‚                       โ”‚      โ”‚                 โ”‚      โ”‚                       โ”‚
โ”‚  Declarative model    โ”‚      โ”‚  __next__() ->  โ”‚      โ”‚  write(pos,idx,frame) โ”‚
โ”‚  of acquisition order โ”‚      โ”‚    (pos, idx)   โ”‚      โ”‚  finalize()           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

AcquisitionSettings (schema.py)

The schema is the declarative description of what to create. In addition to other storage details such as data types, chunking, compression, and other metadata, it must fully describe the dimensionality of the data and the exact order in which frames will arrive.

Explicit non-goal: ome-writers does not attempt to handle non-deterministic acquisition patterns (e.g., event-driven acquisitions where data shape is unknown ahead of time). However, we do support an unbounded first dimension (e.g., time or whatever). For this case, we recommend a flat 3D structure (e.g., FYX with unbounded F) where F is "any frame", storing metadata for mapping frames to logical dimensions externally.

It answers:

  • What dimensions exist? (T, C, Z, Y, X, positions, plates, etc.)
  • What is the acquisition order? (how will frames arrive)
  • What is the storage order? (how should axes be arranged on disk)
  • Data types, chunking, compression, sharding, etc.

The schema separates acquisition order (the order dimensions appear in the dimensions list) from storage order (controlled by the storage_order field). This allows data to arrive in one order (e.g., TZCYX) but be stored in another (e.g., TCZYX for NGFF compliance).

FrameRouter

The router is the stateful iterator that maps frame numbers to storage locations. It:

  1. Reads the schema to understand both acquisition and storage order
  2. Maintains iteration state (which frame are we on?)
  3. Computes the permutation from acquisition order to storage order
  4. Yields (position_key, storage_index) tuples for each frame

The router is the only component that knows about both orderings. It iterates in acquisition order (because that's how frames arrive) and emits storage-order indices (because that's what backends need).

ArrayBackend

Backends are format-specific writers that handle the actual I/O. They:

  1. Create arrays/files based on the schema
  2. Write frames to specified locations
  3. Generate format-appropriate metadata
  4. Handle finalization (flushing, closing)

Supported backends:

  • tensorstore โ€” OME-Zarr v0.5 via yaozarrs
  • zarr-python โ€” OME-Zarr v0.5 via yaozarrs
  • acquire-zarr โ€” OME-Zarr v0.5 via yaozarrs
  • tifffile โ€” OME-TIFF

Backends receive indices in storage order and don't need to know about acquisition order.

Design Principles

  1. Schema is declarative โ€” describes the target structure, not how to build it
  2. Router handles the mapping โ€” single place for acquisitionโ†’storage order logic
  3. Backends are simple adapters โ€” receive storage-order indices, write bytes
  4. Position is a meta-dimension โ€” appears in iteration but becomes separate arrays/files, not an array axis

Why this layer of abstraction?

The separation of schema, router, and backend allows us to leave the performance-critical tasks to C++ libraries (like tensorstore, acquire-zarr), while keeping "fiddly" metadata logic and frame routing in Python (where it's easier to maintain).

The API of this library is heavily inspired by the acquire-zarr API (declare deterministic experiment with schema, append frames with single append() calls). But we also:

  • want to support both zarr and tiff formats (OME-TIFF)
  • want to support other zarr array libraries, such as tensorstore.
  • want to take advantage of Python for metadata management (e.g. ome-types for OME-XML generation and yaozarrs for OME-Zarr metadata)

Supported Use Cases

  • Single 5D image (TCZYX or any permutation) โ€” the common case
  • Multi-position acquisition โ€” separate arrays/files per stage position
  • Well plates โ€” hierarchical plate/well/field structure with explicit acquisition order
  • Unbounded first dimension โ€” e.g., streaming time-lapse with unknown total frames

Currently Unsupported Edge Cases

  • Jagged arrays: E.g.
    • one channel does Z-stacks while another does single planes. In other words, the outer array is regular, but some inner frames are missing/skipped.
    • different positions have different shapes (nT, nZ, etc), such as is possible when using subsequences in useq-schema. (maybe this is just the responsibility of the user to create multiple streams).
  • Multi-camera setups, particularly with different image shapes or data types. (here too... the caller could just call append() in the right order for each buffer)
  • What happens if you want to skip a frame at runtime, maybe append(None)?

Contributing

We welcome contributions to ome-writers! See our contributing guide for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ome_writers-0.2.0.tar.gz (152.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ome_writers-0.2.0-py3-none-any.whl (86.2 kB view details)

Uploaded Python 3

File details

Details for the file ome_writers-0.2.0.tar.gz.

File metadata

  • Download URL: ome_writers-0.2.0.tar.gz
  • Upload date:
  • Size: 152.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ome_writers-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ea5a240659b3cad285a257f7f3c53fdad811f36ccad0047793cf61f1c409af98
MD5 cfa84f54c6a89300e336d6e4490cb08e
BLAKE2b-256 0e6bff6899799fbf7a7a76c3f76c8f976c9918df22bef41a9603d7e7b307c6a0

See more details on using hashes here.

Provenance

The following attestation bundles were made for ome_writers-0.2.0.tar.gz:

Publisher: ci.yml on pymmcore-plus/ome-writers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ome_writers-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: ome_writers-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 86.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ome_writers-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bbabae08b0883bab0a22f015f8947c92c4dad840e89dca3073ede4c55a655ece
MD5 e485345241b977c4b1d86542b7086a21
BLAKE2b-256 53b509bc1e9902d8e41f52a092f8cc2c13e4fe034259b30ce85bd23753b74ec9

See more details on using hashes here.

Provenance

The following attestation bundles were made for ome_writers-0.2.0-py3-none-any.whl:

Publisher: ci.yml on pymmcore-plus/ome-writers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page