Metadata-Version: 2.5
Name: omeify
Version: 0.23.1
Summary: Predictable, metadata-minimized OME-TIFF and OME-Zarr image I/O
Project-URL: Homepage, https://github.com/jason-weirather/omeify
Project-URL: Repository, https://github.com/jason-weirather/omeify
Project-URL: Issues, https://github.com/jason-weirather/omeify/issues
Author: Jason L Weirather
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: H&E,MITI,OME-TIFF,OME-Zarr,SCN,SVS,microscopy,multiplex imaging,pathology,whole slide imaging
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.11
Requires-Dist: click<9,>=8.1
Requires-Dist: fsspec[http]<2027,>=2024.6
Requires-Dist: imagecodecs<2026.5.10,>=2024.6.1; python_version == '3.11'
Requires-Dist: imagecodecs<2027,>=2024.6.1; python_version >= '3.12'
Requires-Dist: jsonschema<5,>=4.20
Requires-Dist: lxml<7,>=5
Requires-Dist: numpy<3,>=1.26.4
Requires-Dist: ome-schema>=0.1.0
Requires-Dist: pillow<13,>=10.4
Requires-Dist: referencing<1,>=0.31
Requires-Dist: tifffile<2026.4.11,>=2024.8.30; python_version == '3.11'
Requires-Dist: tifffile<2027,>=2024.8.30; python_version >= '3.12'
Requires-Dist: tqdm<5,>=4.66
Requires-Dist: zarr<4,>=3.1.3
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: packaging>=22; extra == 'dev'
Requires-Dist: pytest<10,>=8; extra == 'dev'
Requires-Dist: ruff>=0.12; extra == 'dev'
Provides-Extra: intelligence
Requires-Dist: sheetbend[llm]<0.6,>=0.5.0; extra == 'intelligence'
Provides-Extra: interop
Requires-Dist: ome-zarr-models<2,>=1.8.1; extra == 'interop'
Provides-Extra: s3
Requires-Dist: s3fs<2027,>=2024.6; extra == 's3'
Description-Content-Type: text/markdown

# omeify

**Predictable OME-TIFF and OME-Zarr image I/O for microscopy.**

Omeify is a command-line tool and Python library for **inspecting, converting,
cropping, RGB reconstruction, concatenating, and deliberately modifying** microscopy images. It reads
supported vendor TIFFs, OME-TIFF, and OME-Zarr, and writes calibrated, pyramidal
**OME-TIFF** or **OME-Zarr** with consistent pixel interpretation, controlled
metadata, and verified construction. OME-TIFF is the default output.

Microscopy metadata can carry loose text in vendor descriptions, scanner fields,
file paths, and annotations, sometimes including patient or specimen identifiers.
Copying it uncritically can propagate that information into later derivatives.
Omeify **rebuilds output metadata from a documented, constrained set of fields**
instead of forwarding arbitrary source descriptions alongside the pixels.
**Controlling this propagation is an essential step in deidentification pipelines**,
but not deidentification on its own: retained channel and ROI names, explicitly
supplied provenance, and image pixels still require review. RGB output uses
a fixed sRGB profile instead of copying the scanner's ICC metadata.

The goal is a dependable home base for images from different instruments and
workflows. Omeify produces documented profiles of open formats, not private
Omeify files: an OME-aware application such as **QuPath** can use a compatible
OME-TIFF without installing Omeify.

[Installation](#installation) · [Quick start](#quick-start) ·
[Commands](#command-line) ·
[Supported inputs](#supported-file-conversions) ·
[OME-TIFF contract](#the-ome-tiff-home-base) ·
[OME-Zarr contract](#the-ome-zarr-home-base) ·
[Metadata and MITI](#metadata-minimization-and-miti-alignment) ·
[Python library](#python-library) · [Charter](CHARTER.md) · [Reference][reference]

## Z/T image stacks (0.23)

`Image` remains a two-dimensional observation. `open_dataset()` adds persistent
OME-TIFF/OME-Zarr source sessions, first-class Z/T axis metadata, borrowed plane
Images, canonical logical block axes (`TCZYX`, `TZYXS`, `TZYX`) distinct from
native storage axes, shared bounded caching and explicit optional read-ahead. Both writers
have `write_dataset()` for regular stacks; Zarr adds explicit XY/XYZ and
anisotropy-aware pyramids. `convert` preserves complete selected Z/T series by default;
`mutate` makes explicit numerical changes over the selected extent. Multi-file TIFF is not included.

See [Z/T source sessions, examples, and limitations](docs/z_t_support.md).
Version 0.23 adds dependency-backed Z/T tests and 2D compatibility regressions.
The [release checks](docs/development.md) describe baseline and independent interoperability
validation. Existing 2D examples, defaults and methods below retain their meaning.
Stack paths do not claim full parity for every existing CLI workflow or
irregular acquisition data.

## Installation

Omeify requires **Python 3.11 or newer**. A normal installation includes both
OME-TIFF and OME-Zarr support, including reading Zarr over HTTP(S).
From this repository checkout:

```bash
python -m pip install .
omeify version
```

The package-index release may lag this checkout; use the revision matching the
API documented here. For scientific environments that must remain on Python
3.10, **0.21.0** is the compatibility checkpoint.

For development, install `python -m pip install -e '.[dev]'` and run
`python -m pytest -q`. [Development checks](docs/development.md) documents the
supported environments and independent interoperability tests. Ordinary image
I/O does not require an AI model or optional intelligence dependencies.

## Command line

Each command has a distinct job:

| Command | Purpose |
|---|---|
| `omeify inspect` | Examine TIFF or OME-Zarr structure, channels, calibration, and metadata without decoding image pixels. |
| `omeify convert` | Reconstruct the selected image/series in OME-TIFF or OME-Zarr; preserve its Z/T extent and scalar intensity scale by default. |
| `omeify crop` | Export pixel-coordinate rectangles or GeoJSON regions, optionally selecting/relabeling channels. |
| `omeify extract-rgb` | Reconstruct a real RGB image from three named scalar channels, with explicit bit-depth/background policies. |
| `omeify concat` | Combine selected, optionally renamed **OME-TIFF image series** in a chosen order. |
| `omeify mutate` | Intentionally change floating-point image values or float32 precision, with a report. |
| `omeify version` | Show the package version; `--json` also reports image-I/O dependency versions. |

`convert`, `crop`, `extract-rgb`, and `mutate` can write either output format with
`--output-format ome_tiff|ome_zarr`. `concat` writes OME-TIFF only.

OME ROI annotations are **omitted by default** in `convert` and `mutate`, keeping
image products metadata-minimized. To retain linked ROI geometry and plane
assignments, use `--preserve-rois` with **OME-TIFF input and output**:

```bash
omeify convert input.ome.tif -o kept.ome.tif --type ome_tiff --preserve-rois
omeify mutate float.ome.tif -o kept-float.ome.tif --type ome_tiff \
  --float32-mantissa-bits 11 --preserve-rois
```

Preservation requires unchanged X/Y/Z/T/C geometry; partial Z/T selections and
ROI-linked external annotations are not supported. The operation refuses unsafe
preservation rather than silently dropping or misplacing annotations. Source ROI
names and geometry may be sensitive. OME-Zarr output has no equivalent ROI
preservation contract in this release; `--preserve-rois` there is rejected.
`inspect` reports ROI, shape and image-reference counts without decoding masks or
listing every ROI by default.

### Quick start

Inspect a slide, turn it into a standardized OME-TIFF, then inspect the output:

```bash
omeify inspect slide.svs
omeify convert slide.svs -o slide.ome.tif --type svs --no-overwrite
omeify inspect slide.ome.tif
```

To write OME-Zarr instead, choose the output format explicitly:

```bash
omeify convert slide.ome.tif -o slide.ome.zarr \
  --type ome_tiff --output-format ome_zarr
```

The selected input profile (`--type`) determines how pixels and calibration are
interpreted; a filename suffix alone is not enough. `convert` preserves the
source's numerical type and intensity scale, although lossy output compression
and sRGB color conversion can change RGB values. Existing OME-TIFF and supported OME-Zarr images
can also be converted into a fresh, normalized output.

### Z-axis calibration for conversions

If the selected output has **more than one Z section**, `convert` and `mutate`
require either known physical Z spacing from the source or an explicit user
choice. A missing value is **not** silently assumed to be ordinal. The two
supported declarations are:

```bash
# Known physical step. The separate Z unit defaults to µm when omitted.
omeify convert input.zarr --type ome_zarr --z-spacing-size 500 \
  --z-spacing-unit nm -o calibrated.ome.tiff

# Intentionally index-only Z: preserve all sections, with no physical Z scale.
omeify convert input.zarr --type ome_zarr --z-spacing-ordinal \
  -o ordinal.ome.tiff
```

`--z-spacing-size` is a deliberate override, even if the source has a physical
spacing. The output canonicalizes known physical spacing to µm; ordinal output
omits `PhysicalSizeZ`/`PhysicalSizeZUnit` in TIFF and the Z-axis physical unit
in Zarr. Both workflows preserve T, C, and all unselected Z sections. Selecting
one Z section needs no Z spacing. `--z-spacing-ordinal` cannot override a source
that already provides usable physical spacing. The operation report records
`input.z_spacing_policy` as `source`, `override`, `ordinal`, or `not_applicable`,
and records the caller's explicit size/unit in `input.z_spacing_override` when
one was supplied.
The Python `convert()`/`mutate()` arguments are `z_spacing_size`,
`z_spacing_unit`, and `z_spacing_ordinal`; the low-level dataset view retains its
existing canonical `z_spacing_um` representation. These flags are not a substitute
for studying an acquisition's actual physical sampling or irregular plane positions.

### Supported file conversions

| Input | `--type` | Image representation |
|---|---|---|
| Akoya multiplex QPTIFF | `qptiff_mif` | Planar multiplex |
| Akoya Fusion multiplex QPTIFF | `qptiff_fusion` | Planar multiplex |
| Indica Labs/HALO mIF TIFF | `indica_mif` | Planar multiplex |
| Akoya H&E QPTIFF | `qptiff_he` | RGB |
| Aperio SVS | `svs` | RGB |
| Leica SCN brightfield/H&E | `scn_he` | RGB |
| OME-TIFF | `ome_tiff` | Scalar, multiplex, or RGB |
| OME-Zarr | `ome_zarr` | Selected scalar, multiplex, RGB, or label image |

Source support is intentionally specific, not a promise to convert every TIFF or
all variations of a vendor format. `--series` selects an image where supported;
Both OME inputs accept `--z-index` and `--t-index` to restrict a conversion;
with no Z/T selectors the complete selected series is retained. OME-Zarr also
accepts `--group`. The [Z/T guide](docs/z_t_support.md) describes scopes. See [supported inputs and calibration][supported-inputs]
for the exact rules. Ordinary TIFF `inspect` can describe metadata from layouts
that `convert` does not support.

### Crop an image or a set of regions

```bash
# CLI bounds are X0 Y0 X1 Y1, in full-resolution pixels.
omeify crop slide.ome.tif -o one-region.ome.tif --bounds 10000 5000 12048 7048

# QuPath-style pixel-coordinate GeoJSON can describe several regions.
omeify crop slide.ome.tif -o regions.ome.tif --geojson regions.geojson
```

By default, every region becomes a separately named **image series in one
output**; `-o` names that exact output, not a prefix. Use `--shatter by_index` to
write one file per region, or `--shatter by_name` to group regions into files by
name. GeoJSON polygons now mask pixels outside their true shapes, including
holes and disjoint MultiPolygon parts, inside their minimal rectangular raster.
`--mask-background keep` restores the former full-bounding-box behavior.
OME-Zarr collections use the same ordered-region model.

Select a subset of logical channels, reorder them, or assign new channel names in
one crop without making an intermediate image:

```bash
omeify crop slide.ome.tif -o two-markers.ome.tif --bounds 0 0 2048 2048 \
  --channel DAPI --named-channel CD8a CD8
```

`--channel SOURCE` and `--named-channel OUTPUT_NAME SOURCE` can be intermixed
and repeated in output order. `SOURCE` is an exact channel name or a zero-based
index; use `name:0` for a channel literally named `0`. With no selection flags,
all channels are retained. For renaming without selecting, `crop` also supports
the same `--rename-channels-json` and `--rename-channels-by name|index` options
as `convert`. For a polygon, `--mask-background auto` (the default) writes white
outside RGB/H&E and black outside scalar signals. Use `--mask-background black|white|keep`
explicitly when needed; `--padding N` adds a colored margin, and `--pad-outside`
allows the crop to extend beyond the scanned image (otherwise use `--clip` to trim).
The output report and embedded provenance record source series, the selected
Z/T plane, the requested Zarr group (where applicable), and the ordered source
to output channel mapping. RGB is one indivisible logical channel, not three
separate color planes. See the [cropping guide](docs/regions.md).

With the optional intelligence integration, a vision-capable model can propose
GeoJSON rectangles from a bounded preview. This explicitly sends image pixels
to the selected model source and needs human review:

```bash
omeify inspect slide.ome.tif -i --geojson \
  -q "Find the right tissue and name it right." > right.geojson
omeify crop slide.ome.tif -o right.ome.tif --geojson right.geojson
```

[Cropping guide](docs/regions.md) covers geometry, naming, preview setup,
and output options.

### Recover an RGB image from separate color channels

Some multiplex OME-TIFFs store a displayed RGB image as three independent
`RED`, `GREEN`, `BLUE` scalar channels alongside fluorescence markers. Those
channels are **not** an OME RGB image. `extract-rgb` deliberately assembles
an interleaved uint8 `YXS` image without loading the complete slide:

```bash
omeify extract-rgb multiplex.ome.tiff -o recovered.he.ome.tiff --name "H&E"
# Preview five small samples of the stored byte pattern without writing.
omeify extract-rgb multiplex.ome.tiff --probe
```

For uint16 input, `--encoding auto` (default) takes the high byte. This
reverses both `value8*256` and `value8*257` expansions, but can discard real
16-bit precision. Choose `--encoding rounded` for continuous full-range
uint16 data or `low-byte` when values are entirely 0..255. uint8 channels
are copied directly. By default, original all-zero RGB triples become white;
`--background keep` retains true black. This fill is **not** tissue detection.

The output uses **lossless Deflate** and Omeify's automatic RGB SubIFD pyramid
(512-pixel tiles) by default, not the normal RGB JPEG storage policy. It
retains pixel calibration, and the versioned operation report records the
chosen source channels, bit-depth mapping, and background policy. Other
formats can be selected explicitly using `--type` and `--output-format`.
This is a deliberately transforming operation, **not** `convert`.

### Concatenate OME-TIFF series

```bash
omeify concat \
  --series slide.cells.ome.tif 0 \
  --named-series "Nuclei" slide.cells.ome.tif 1 \
  --named-series "Appearance" slide.regions.ome.tif 0 \
  -o slide.combined.ome.tif --no-overwrite
```

`concat` combines **complete OME Images**, not channels or mosaics. The order of
`--series` and `--named-series` arguments is the output order; the indices are
zero-based and output names must be unique. Different dimensions, pixel types,
and physical resolutions are allowed, with each image kept as its own series.
Full-resolution decoded pixels are rewritten with lossless compression; RGB
samples are first mapped to sRGB if the input carries another ICC profile.
Pyramids are rebuilt. The operation does not register or align images.
`concat` currently writes OME-TIFF only.

### Make an intentional numeric change

```bash
# Input must contain supported floating-point pixels.
omeify mutate float-image.ome.tif -o compact.ome.tif \
  --type ome_tiff --dtype uint16 --range-mode auto \
  --no-overwrite --output-json compact.mutation.json
```

`mutate` also supports deliberate float32 mantissa trimming using
`--float32-mantissa-bits N`. Its integer `auto` mapping may rescale each channel
independently, so it is **not** a neutral conversion or cohort normalization.
Keep these numerical changes separate from ordinary `convert`.
[Mutation options][commands] documents the mappings and precision policy.

### Reports and command help

Writing commands use **`-o` / `--output`** for their destination and print a
short completion message by default. Use `-v` for progress, `-vv` for diagnostics,
or `-vvv` for the full report. `--output-json FILE` saves a full structured
report, while `--output-json -` writes JSON-only stdout. `inspect` prints its
report by default; `--json` and `-o` control its representation and destination.

Use `omeify COMMAND --help` for installed options and the [report guide](docs/reports.md)
for the report schemas. TIFF destinations can be replaced after verification by
default; `--no-overwrite` refuses replacement. **OME-Zarr output always requires
a new local directory**, regardless of overwrite flags. Reports can contain
source paths and identifying metadata, so review them before sharing.

## Why use omeify instead of tifffile directly?

Omeify uses [tifffile] for TIFF I/O. Use tifffile directly when you need to
control a TIFF's low-level structure and metadata yourself. Use Omeify when you
want a supported source to follow the same rules for pixel layout, calibration,
channel identity, metadata minimization, pyramids, and safe writing every time.
You need not recreate vendor-specific plane mappings or OME-XML generation in
each analysis script.

Its two output formats remain open standards. The **home base** is a clear,
narrower construction contract, rather than a new format or a requirement to
use an Omeify reader downstream.

## The OME-TIFF home base

**Every OME-TIFF written by Omeify follows these rules.** Pixel values,
compression, and content can differ, but the construction is predictable.

| Aspect | Omeify's output contract |
|---|---|
| Container | Self-contained, little-endian BigTIFF with OME 2016-06 XML. Explicit image origins use a linked, controlled geometry annotation. |
| Image layout | Grayscale (`YX`), multiplex channel stacks (`CYX`), or interleaved RGB (`YXS`). RGB is three samples within one logical channel, not three fluorescence channels. |
| Pixel types | Scalar images support 8-, 16-, and 32-bit signed or unsigned integers and 32- or 64-bit floats. RGB requires `uint8`. |
| Pixel values | `convert` preserves scalar dtype and intensity scale; RGB is mapped to sRGB, which can change color samples. Lossless encoding retains output values; JPEG is lossy. `mutate` makes requested numerical changes. |
| Calibration | X and Y physical pixel sizes are required and stored in **µm**. Multi-Z `convert`/`mutate` additionally require physical Z spacing or an explicit ordinal declaration. No missing measurement is invented. |
| Pyramids | Tiled SubIFDs are rebuilt, not copied from the source. Intensity images default to 2× mean reduction; declared label images use nearest-neighbor reduction. |
| Metadata | Fresh, controlled OME-XML instead of arbitrary vendor descriptions; crop outputs also retain region names and source-image coordinates. |
| Verification | Generated metadata, TIFF organization, canonical ICC bytes, encoding, and sampled pixels are checked before the temporary output replaces the destination. |

By default, reduced-resolution levels are built until the image fits in one
tile. **RGB OME-TIFF** defaults to JPEG quality 90, 4:2:2 chroma sampling, and
512 × 512 tiles. **Grayscale, multiplex, and label images** default to lossless
LZW and 1024 × 1024 tiles. These defaults apply to CLI and Python writers;
explicit settings take precedence. Choose a lossless codec such as
`--compression Deflate` to avoid JPEG loss after sRGB conversion.

The contract governs construction, **not scientific accuracy**. Calibration can
be faithfully recorded without having been correctly measured at acquisition.
See [storage and compression][jpeg-policy] and [verification][verification].

## The OME-Zarr home base

Omeify's canonical OME-Zarr output is **NGFF 0.5 on Zarr v3**. It uses fresh,
allowlisted JSON metadata, calibrated spatial coordinates, 512-pixel spatial
chunks, and lossless Zstd level 3 by default. Pyramids are rebuilt through the
same image model as TIFF. Zarr output is lossless by default even for RGB,
unlike the TIFF JPEG default.

The reader supports selected **OME-Zarr 0.4, 0.5, and 0.6** images, including
local stores and HTTP(S) roots. S3 reads are available with the optional
`omeify[s3]` installation; neither HTTP(S) nor S3 writes are supported. A reader
can select a 2D image or an explicit Z/T plane, but this is not arbitrary scene
registration or a complete implementation of every NGFF spatial relationship.
For 0.6 images with additional spatial context, export requires deliberate
`--intrinsic-only` selection rather than silently dropping that context.

OME-Zarr output is staged, validated, and published as a **new local directory**;
an existing store is never replaced. The writer currently produces unsharded
chunks, although supported sharded input can be read. The
[OME-Zarr guide](docs/ome_zarr.md) has remote examples, coordinate rules,
compatibility boundaries, and the output-version rationale.

## Metadata minimization and MITI alignment

Microscopy files may contain names, paths, specimen identifiers, vendor XML,
and other acquisition metadata that do not belong in every derived image.
Omeify builds **new OME-XML or NGFF JSON** from a controlled set of fields instead
of copying those descriptions wholesale. This limits the paths through which
unreviewed free text can spread into downstream image products, while keeping
metadata needed to interpret the pixels.

Its OME-TIFF minimum-information policy draws on:

[Schapiro D, Yapp C, Sokolov A, et al. **MITI Minimum Information guidelines for highly
multiplexed tissue images.** *Nature Methods.* 2022;19:262-267.
doi:10.1038/s41592-022-01415-4.](https://doi.org/10.1038/s41592-022-01415-4)

MITI covers the broader imaging study, including biospecimen, reagent,
instrument, acquisition, processing, and analysis records. Omeify addresses a
**narrower image-header contract**: retain what is necessary to interpret the
pixels, and leave other experimental records alongside the image rather than
inventing them in the header. This is MITI-guided construction with a deliberate,
explicitly declared ordinal-Z exception, **not a claim of exact compliance with
MITI's published multi-Z calibration requirement or study-wide MITI compliance**.

### What can cross from the source?

| Source information | Omeify policy | Why |
|---|---|---|
| Pixel dtype and image geometry | Preserved by `convert`; numerical changes require explicit mutation | Defines the numeric image being interpreted. |
| Channel names | Read from recognized source fields and optionally renamed | Retains logical channel identities without guessing biomarkers. |
| Physical pixel size | Taken from supported calibration declarations or an explicit override | Required for quantitative spatial interpretation. |
| RGB ICC profile | Interpreted to convert RGB pixels to sRGB, then discarded | Output carries only Omeify's fixed, audited sRGB profile, never source ICC metadata. |
| Vendor descriptions, filenames, scanner fields, dates, user names, unrelated free text | Not copied into generated OME-XML or NGFF JSON | Avoids an uncontrolled metadata passthrough. |
| `Plane/@ExposureTime`, `Plane/@PositionXYZ` | Deliberately omitted | Detector and microscope-stage measurements belong in separate acquisition records. |
| Biospecimen, reagent, acquisition, processing, and analysis metadata | Not inferred or manufactured | Belongs in deliberate accompanying study records. |

### What does Omeify put in OME-TIFF?

The generated header is checked against
`omeify/schemas/miti_ome_tiff_header.schema.json`. The minimum includes:

| Header value | Omeify policy | Purpose |
|---|---|---|
| `Image ID`, `Pixels ID` | Generated | Unambiguous internal OME references without copying source IDs. |
| `BigEndian`, `DimensionOrder`, `Interleaved` | Required and storage-checked | Describes the pixel layout. |
| `PhysicalSizeX`, `PhysicalSizeY`, units | Required | Carries physical calibration. |
| `PhysicalSizeZ`, `PhysicalSizeZUnit` | When known or explicitly supplied; both omitted only for declared ordinal multi-Z conversion | Never labels section indices as physical distances. |
| `TimeIncrement`, `TimeIncrementUnit` | When known | Regular T interval; no invented playback rate. |
| `Plane/@DeltaT`, `DeltaTUnit` | Retained when provided, per T/C/Z observation | Captures actual timing, including irregular acquisition. Not required when TimeIncrement suffices. |
| `SizeX`, `SizeY`, `SizeC`, `SizeZ`, `SizeT` | Required | Declares image dimensions. |
| `Type`, `SignificantBits` | Required and sample-checked | Declares pixel representation and precision. |
| Channel `ID`, `Name`, `SamplesPerPixel` | Required | Identifies logical channels and RGB sample grouping. |
| `TiffData IFD`, `PlaneCount` | Required and checked | Maps logical OME planes to stored TIFF directories. |
| `Image/@Name` | Omitted normally; named in multi-series output | Provides intentional series identity without arbitrary source names. |
| Root `UUID` | Generated by default; optional | Supplies a file-level identifier. |
| Root `Creator` | Generated from the Omeify version | Identifies the software constructing the header. |
| Empty `LightPath` | Generated without acquisition claims | Meets the supported OME channel structure without inventing instrument details. |
| Pyramid `MapAnnotation` | Generated when reduced levels exist | Describes the rebuilt resolution hierarchy. |
| ICC profile | A fixed sRGB profile is generated for every RGB image | Source profile data cannot pass into the output. |
| Multi-image annotations | Added for explicit provenance or crop records | Describes intentional output context. |

Passing the header check establishes these minimum fields, not a privacy
certificate. In particular, **metadata minimization is not deidentification**:
retained channel/ROI names, caller-supplied provenance, source coordinates,
and pixels (including burned-in labels) still need independent review before
sharing. `inspect` exposes original metadata; it does not minimize or rewrite
it. Use `omeify inspect image.ome.tiff --show-miti` to append the actual
curated header fields and values to the regular inspection tree and MITI check.
The [metadata retention and sharing policy](docs/reports.md#metadata-retention-and-sharing)
describes these boundaries for both output formats.

## Python library

The same **Image API** can open files, wrap arrays, or read from external
regional image providers. Images describe their channels, shape, dtype, pixel
size, and pyramid levels; readers return only requested regions rather than
implicitly materializing whole slides.

### Write an image, then read a region

This example creates a synthetic multiplex image, saves a calibrated OME-TIFF,
and reads a DAPI patch back. Use a new filename when rerunning because
`overwrite=False` refuses to replace the example output.

```python
from pathlib import Path

import numpy as np
from omeify import MultichannelImage, OMETiffReader, OMETiffWriter, PixelSize

path = Path("omeify-demo.ome.tif")
values = np.arange(2 * 256 * 384, dtype=np.uint32).reshape(2, 256, 384)
pixels = (values % 4096).astype(np.uint16)

with MultichannelImage.from_array(
    pixels,
    axes="CYX",
    channel_names=("DAPI", "CD3"),
    pixel_size=PixelSize(0.5, 0.5, "µm"),
) as image:
    report = OMETiffWriter(
        path, compression="Deflate", tile_size=128, overwrite=False,
    ).write(image)

with OMETiffReader(path) as image:
    print(image.axes, image.shape, image.channel_names, image.pixel_size)
    dapi_patch = image.get_by_name("DAPI").read_region(0, 128, 0, 128)

np.testing.assert_array_equal(dapi_patch, pixels[0, :128, :128])
```

Image metadata belongs to the **Image**; compression, tiles, and publication
settings belong to the **writer**. Constructors such as `.from_array()` return
unopened images, so use a context manager or explicit `open()` / `close()`.
Regional patches own their returned pixels; lazy channel and crop views still
need the original image open. `.asarray()` deliberately requests a whole image.

The same regional surface works with OME-Zarr:

```python
from omeify import OMEZarrReader

with OMEZarrReader("slide.ome.zarr") as image:
    print(image.channel_names, image.pixel_size, image.levels)
    patch = image.read_region(0, 256, 0, 256)
```

`RGBImage.from_array(...)` and `LabelImage.from_array(...)` represent explicit
RGB and categorical images; three scalar channels are never guessed to be RGB.
`MultichannelImage.from_channels(...)` assembles lazy channel views, and
`image.with_metadata(...)` supplies deliberate calibration or naming changes.
`OMEMultiSeriesWriter` and `OMEZarrCollectionWriter` write named heterogeneous
image collections; callers can implement `ImageSource` to generate only requested
rectangles without building an intermediate TIFF.

[Images and sources][image-sources] describes the shared API, ownership,
coordinates, and external providers. The [Python reference][python-api] and
[Zarr examples](docs/ome_zarr.md#python-access) cover the individual writers.

## Interoperability with QuPath and other applications

OME-TIFF output targets ordinary OME-aware consumers, including **QuPath**,
**Bio-Formats**, and **tifffile**. Omeify's explicit channel mapping, calibrated
tiled planes, and SubIFD pyramids are meant to make images usable outside the
Python library. The receiving application still needs to support the selected
dtype, compression, and image/series layout; no universal viewer certification
is implied. See [QuPath's TIFF guidance][qupath-formats] and the
[OME-TIFF specification][ome-tiff-spec].

**RGB is not an H&E declaration.** QuPath can recognize RGB pixels while
classifying the image as Fluorescence. For known H&E images, choose Brightfield
(H&E) when needed. Omeify does not invent stain identities or acquisition
metadata, and the receiving application's display choice does not alter stored
pixels. Use lossless compression to avoid further RGB changes after sRGB
conversion; the [JPEG policy][jpeg-policy]
documents encoding choices and older-file considerations.

## Reproducibility and verification

Omeify promises **documented construction, explicit transformations, and
recorded checks**, not byte-identical output across runs or versions. TIFF
writers validate generated OME-XML against a local OME XSD and the minimum
header policy, then verify structure, calibration, encoding, and representative
pixels. Zarr writers validate metadata and arrays, check their planned dense
chunk inventory, and decode representative pixels before publication.

Pixel checks are **sampled**, including checks at reduced levels. They are not
an exhaustive pixel audit or proof of correct input interpretation, specimen
quality, or physical microscope calibration. Keep original acquisition data,
processing parameters, reports, and `omeify version --json` with the study.

Both backends work in bounded image regions instead of loading the whole slide,
but large TIFF pyramids need temporary scratch disk space. TIFF writes are
staged and checked before atomic replacement, and Zarr creates a new verified
store without replacing an existing directory. See [verification][verification],
[TIFF I/O and failure behavior][io-behavior], and
[Zarr safe writing](docs/ome_zarr.md#safe-writing-and-verification).

## Scope and further documentation

Ordinary Image writing uses scalar (`YX`), multiplex (`CYX`), and RGB (`YXS`)
observations. Explicit `write_dataset()` and scoped convert/mutate operations
also write regular Z/T stacks; RGB still requires three `uint8` samples.
Registered scenes and multi-file TIFF are not supported. Omeify does **not** perform segmentation, registration, stain
normalization, object measurements, or scientific quality assessment.

Optional `inspect -i` can request advisory metadata answers using a configured
Sheetbend source and the `omeify[intelligence]` extra (Python 3.13+).
`inspect -i --visual` or `--geojson` explicitly transmits a bounded image
preview to the selected model source, while ordinary inspection and metadata
questions do not send pixels. The visual GeoJSON proposal can feed a subsequent
`crop`; it is not an automatic crop or a tissue-segmentation result. Read the
[intelligence reference][intelligence] and
[visual region guide](docs/regions.md#ask-for-visual-geojson) for setup and
privacy considerations.

For deeper details:

- [Command-line and Python reference][reference]: source profiles, mutations,
  storage options, and complete parameter descriptions.
- [OME-Zarr guide](docs/ome_zarr.md): versions, selected planes, remote reads,
  geometry, and compatibility limits.
- [Images and sources][image-sources]: channels, arrays, image providers, and
  API migration.
- [Reports and metadata retention](docs/reports.md): report schemas, output
  behavior, privacy boundaries, and report migrations.
- [Cropping and visual regions](docs/regions.md): coordinate conventions,
  GeoJSON, splitting, and optional visual localization.
- [Development and release checks](docs/development.md): test environments and
  packaged-wheel verification.

This README describes the current pre-1.0 contracts rather than a promise of
backward compatibility with every earlier release.

## License

Apache License 2.0. See [LICENSE][license].

[tifffile]: https://github.com/cgohlke/tifffile
[qupath-formats]: https://qupath.readthedocs.io/en/stable/docs/intro/formats.html#tiff
[ome-tiff-spec]: https://docs.openmicroscopy.org/ome-model/6.2.2/ome-tiff/specification.html
[reference]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md
[supported-inputs]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#supported-inputs
[commands]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#command-line-reference
[python-api]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#python-api
[jpeg-policy]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#compression-policy
[verification]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#validation-and-verification
[io-behavior]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#io-scratch-space-and-failure-behavior
[image-sources]: https://github.com/jason-weirather/omeify/blob/main/docs/image_sources.md
[intelligence]: https://github.com/jason-weirather/omeify/blob/main/docs/reference.md#optional-metadata-intelligence
[license]: https://github.com/jason-weirather/omeify/blob/main/LICENSE
