abczarr.ome.base
The version-independent OME-Zarr metadata model.
OME-Zarr, the NGFF specification, is a metadata convention for
bioimaging data stored in Zarr. It describes multiscale image
pyramids, high-content screening plates, segmentation labels, and
rendering settings, all as JSON attached to a Zarr group. abczarr
models that metadata as typed classes under abczarr.ome. One
package exists per NGFF version, v0_1 through v0_5, plus the 0.6
pre-release previews v0_6dev1 through v0_6dev4 and v0_6rc0.
The examples below target 0.5, the latest stable version. The Reference documents every version.
Describing a multiscale image
A multiscale image is a pyramid of resolution levels, each a Zarr array, described by Multiscale. It is built from a plain dict shaped like the JSON the spec defines:
>>> from abczarr.ome import v0_5
>>> multiscale = v0_5.Multiscale.from_json({
... "name": "nucleus-stain",
... "type": "gaussian",
... "axes": [
... {"name": "c", "type": "channel"},
... {"name": "y", "type": "space", "unit": "micrometer"},
... {"name": "x", "type": "space", "unit": "micrometer"},
... ],
... "datasets": [
... {
... "path": "0",
... "coordinateTransformations": [
... {"type": "scale", "scale": [1.0, 0.325, 0.325]}
... ],
... },
... {
... "path": "1",
... "coordinateTransformations": [
... {"type": "scale", "scale": [1.0, 0.65, 0.65]}
... ],
... },
... ],
... })
>>> [axis.name for axis in multiscale.axes]
['c', 'y', 'x']
>>> [dataset.path for dataset in multiscale.datasets]
['0', '1']
Each axis in axes becomes an
Axis, matched by its type field. The
y and x axes in this example become
SpaceAxis objects, since each
carries type="space". Each entry in datasets becomes a
Dataset. A dataset names an array
and carries the Scale
that places it in physical space, one value per axis, in that axis's
unit.
A Multiscale describes the pyramid, not the whole group. Wrapping
it in OMEImage produces the
metadata an image group actually carries, optionally alongside
Omero rendering settings:
from abczarr.ome import v0_5
image = v0_5.OMEImage(
version="0.5",
multiscales=[multiscale],
omero=v0_5.Omero.from_json({
"channels": [
{
"color": "00FF00",
"window": {"start": 0, "end": 1500, "min": 0, "max": 65535},
},
],
}),
)
The same shape covers the other kinds of OME-Zarr group: ImageLabel for a segmentation, and Plate / Well for a high-content screen, wrapped in OMEImageLabel, OMEPlate and OMEWell.
Reading and writing OME metadata on a group
OME-Zarr metadata lives in a group's user attributes, the same
attrs mapping any Zarr group exposes. NGFF 0.5 nests it all under
one "ome" key, so an object round-trips through
to_json and
from_json like this:
group.attrs["ome"] = image.to_json()
loaded = v0_5.OMEImage.from_json(group.attrs["ome"])
loaded.multiscales[0].axes[0].name # "c"
Earlier NGFF versions, 0.4 and before, write the same fields directly
at the top level of attrs instead of nesting them under "ome":
group.attrs.update(image.to_json()).
Converting between NGFF versions
OMEMetadata.to_version converts a piece of OME metadata, built against one NGFF version, to another. It works on the top-level container and on any nested piece of metadata alike:
>>> from abczarr.ome import v0_4
>>> old = v0_4.Multiscale.from_json({
... "version": "0.4",
... "axes": [
... {"name": "y", "type": "space"},
... {"name": "x", "type": "space"},
... ],
... "datasets": [
... {
... "path": "0",
... "coordinateTransformations": [
... {"type": "scale", "scale": [1.0, 1.0]}
... ],
... }
... ],
... })
>>> new = old.to_version("0.5")
>>> type(new).__module__
'abczarr.ome.v0_5.images'
>>> new.to_version("0.4") == old
True
Fields both versions share carry over unchanged. Converting forward, a
field only the newer version has gets a reasonable default. Axes, for
example, gained a type field in NGFF 0.4, defaulted from the axis's
name. Converting back, that field is dropped. Converting to a version
that would need information the source does not carry raises
ValueError rather than guessing.
Example
>>> from abczarr.ome import v0_2
>>> untyped = v0_2.Multiscale.from_json({
... "version": "0.2",
... "name": "x",
... "datasets": [{"path": "0"}],
... })
>>> untyped.to_version("0.3")
Traceback (most recent call last):
...
ValueError: cannot convert Multiscale from OME 0.2 to 0.3:
the target requires information OME 0.2 does not carry
Classes
OMEMetadata
OMEMetadata(*, extra_items: Iterable[tuple[str | bytes, Real | floating | bool | bool | int | str | bytes | Iterable[()] | None]] | Mapping[str | bytes, Real | floating | bool | bool | int | str | bytes | Iterable[()] | None] = FrozenDict({}))
Bases: FlexibleMetadata
The base of every OME-Zarr metadata class.
Every piece of OME-Zarr metadata is a subclass of this: a
multiscale pyramid, an axis, a plate, a rendering setting, and so
on. Each lives in the version package it belongs to, such as
abczarr.ome.v0_5.images. Build one with
from_json from the
JSON an OME-Zarr group carries, or with keyword arguments matching
its fields. to_json
serializes it back to that same shape, and any key it does not
recognize survives the round trip unchanged.
Use to_version to convert an object built against one NGFF version to another.
Methods:
to_version
Convert this OME metadata to another OME-NGFF version.
This method works on any piece of OME metadata, not only the top-level container. A Multiscale or an Omero converts just as well as an OMEImage. A field both versions carry is passed through unchanged. Converting forward, a field only the newer version has gets a reasonable default. Converting backward, that field is dropped.
Crossing the 0.5/0.6 boundary reshapes coordinate metadata.
Converting forward, 0.5's per-multiscale axes and per-dataset
scale and translation become 0.6's named coordinateSystems and
general coordinate transformations. Converting backward, a 0.6
transformation the stable model cannot express, such as an affine
or a rotation, is treated according to policy.
Between the 0.6 previews, a mapAxis or byDimension
transformation switches between addressing axes by name and
addressing them by index. Translating between axis names and axis
indices needs the axes of the coordinate systems involved. A
transformation converted on its own, apart from the multiscale
that declares those coordinate systems, raises ValueError when
that translation is needed. Later versions have no mapAxis
counterpart for a 0.6.dev1 mapAxis that drops or repeats an input
axis. Such a mapAxis becomes an equivalent affine that selects
the same input coordinates. A projectAxis, which first appears in
0.6rc0, becomes an equivalent affine in earlier versions in the
same way. Neither affine turns back into the original
transformation when converted again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version
|
str
|
The target OME-NGFF version, such as |
required |
policy
|
ConversionPolicy
|
How to treat information the target version cannot hold:
|
'warn'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
UnsupportedConversion
|
If |
OME
OME(version: str | bytes = '', *, extra_items: Iterable[tuple[str | bytes, Real | floating | bool | bool | int | str | bytes | Iterable[()] | None]] | Mapping[str | bytes, Real | floating | bool | bool | int | str | bytes | Iterable[()] | None] = FrozenDict({}))
Bases: OMEMetadata
The version-tagged, top-level OME-Zarr metadata for a group.
Every OME-Zarr group carries one of these: an image and its
multiscale pyramid, a collection of labels, a plate, or a well. Each
kind has its own subclass in every version's package. The version
field records which NGFF version the metadata is written against, and
with it what the rest of the fields mean.
Build the concrete class for the version in use instead of this one. See v0_5.OME and its siblings, including OMEImage and OMEPlate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version
|
str
|
The OME-NGFF version the metadata is written against. |
''
|
Attributes
Methods:
from_json
classmethod
Create an OME container from a JSON-serializable dict.
Called on a version's own class, such as v0_5.OME.from_json,
this picks the right image, plate, well, or label subclass for the
data, the same way any OME class does.
Called on this version-independent base class, it first reads the
version field to decide which NGFF version the data belongs
to, then hands the data to that version's own OME class. The
base class cannot make that choice on its own, because every
version's classes share it and it has no way to tell a v0.4 image
from a v0.5 one. Metadata that carries no version field is
therefore ambiguous and raises ValueError rather than being
guessed at.
Raises:
| Type | Description |
|---|---|
ValueError
|
If called on the version-independent base with data that has no
|