pyrametric
Lazy, memory-bounded connected-components labeling and per-object measurement for OME-Zarr pyramids, with OME-NGFF label-image output.
pyrametric is the OME-Zarr-aware layer over tilewise-ccl: it
labels the level-0 binary mask of a Pyramid, propagates the labels down the
resolution levels, measures per-object features, and writes the result as a
spec-compliant NGFF label image (labels/<name>/ with image-label
metadata). It works on already-segmented masks — thresholding/segmentation
lives in pyrops. It provides:
label_pyramid— label a maskPyramid→ a lazy, writable labelPyramid(optionally with a per-object properties table).extract_features/ObjectFeatures— regionprops-like per-object measurement (area, bbox, moments, physical units, ...) over a label pyramid, computed lazily and memory-bounded (validated against skimage in 2D and 3D).write_label_pyramid— write a label pyramid as an OME-Zarr label image (colors, properties,sourceback-reference; defaultlabels/<name>/location).label_array— re-exported fromtilewise-cclfor array-level labeling.
The heavy lifting (fast tile-wise connected components, memory-bounded, exact,
returning a lazy dask array) lives in tilewise-ccl; this package adds the
pyramid, axis-handling, measurement, and NGFF I/O concerns on top.
Installation
pip install pyrametric
# or, from a checkout:
pip install -e .
Depends on tilewise-ccl, ome_zarr_pyramid, plus numpy/scipy/dask/zarr.
Quick start
The input's level 0 must already be a binary mask (background 0 + one foreground value) — there is no thresholding here (binarize beforehand).
from ome_zarr_pyramid.core.io import IO
from pyrametric import label_pyramid, write_label_pyramid
io = IO()
mask_pyr = io.read_pyramid("mask.zarr") # level 0 = a binary mask
# label + propagate down the pyramid -> a lazy, writable label Pyramid
label_pyr = label_pyramid(mask_pyr, connectivity=2, n_workers=8)
# ...write it as a plain multiscale array,
io.write_pyramid(label_pyr, "labels.zarr", overwrite=True)
With properties + an OME-Zarr label image
# also measure per-object properties (area + bounding box) - FREE from the labeling
# metadata pass; they are stamped onto the returned pyramid's image-label metadata
label_pyr = label_pyramid(mask_pyr, properties=True, n_workers=8)
# write a spec-compliant label image under the source's labels/ group:
# mask.zarr/labels/cells/ (multiscales + image-label: colors, properties, source)
write_label_pyramid(label_pyr, source="mask.zarr", name="cells")
Attaching labels to the source image, written together
Pyramid.add_image_label attaches a label pyramid to its source image pyramid,
so the two travel together and a single write_pyramid emits the image plus its
labels/<name>/ sub-group in one OME-Zarr store — the common "image + its
segmentation, side by side" layout that viewers open as one dataset:
image_pyr = io.read_pyramid("image.zarr") # the intensity image
mask_pyr = io.read_pyramid("mask.zarr") # its binary mask (segment/threshold upstream)
# label the mask, build its multiscale, name it, then attach it to the image
label_pyr = label_pyramid(mask_pyr, properties=True).downscale().rename("cells")
combined = image_pyr.add_image_label(label_pyr, name="cells")
io.write_pyramid(combined, "image_with_labels.zarr", overwrite=True)
Labeling a sub-region
To label only part of the image, select it first with Pyramid.isel (any axis,
int/slice/list) — the label matches that sub-region's geometry:
label_pyr = label_pyramid(mask_pyr.isel(c=1, z=slice(100, 150)))
API
label_pyramid(pyramid, tile_shape=None, connectivity=2, n_workers=1, properties=False, verbose=False)
Label a binary-mask Pyramid's level 0 and propagate the labels down the
resolution levels (nearest-neighbour / stride downsampling — the correct choice
for categorical labels). To label a sub-region, select it first with
Pyramid.isel (e.g. mask.isel(c=1, z=slice(100, 150))).
Returns a lazy label Pyramid (dtype int32 unless the object count exceeds
int32), mirroring the source's axes, units, per-level shapes and scales. With
properties=True the same pyramid additionally carries its OME image-label
metadata (per-object colors + an area/bbox properties table), so it is write-ready.
| Parameter | Type | Default | Description |
|---|---|---|---|
pyramid |
Pyramid |
— | Source pyramid; level 0 is a binary mask (not validated — see Notes). Axes may be any subset of tczyx. |
tile_shape |
sequence of int | None |
Tile size for tilewise-ccl, one entry per spatial axis. None → (303,) * n_spatial. |
connectivity |
int | 2 |
Spatial scipy.ndimage connectivity (2D: 1=4-, 2=8-conn; 3D: 1=6-, 2=18-, 3=26-conn). |
n_workers |
int | 1 |
Threads for each labeling metadata pass. |
properties |
bool | False |
Also measure per-object area + bbox (free from the labeling pass) and stamp them onto the returned pyramid's image-label metadata (colors + properties). The return type is unchanged — always a Pyramid. |
verbose |
bool | False |
Forwarded to tilewise-ccl.label_array. |
When properties=True, label_pyramid measures each object's area (voxel
count) and bbox (half-open bounding box, per axis, in the label's own
coordinates) and stamps them onto the returned pyramid's image-label metadata
(alongside deterministic per-object colors), so it is write-ready. For richer
per-object measurement (intensity / physical / shape features, filtering) pass the
label pyramid to extract_features(...).
write_label_pyramid(label_pyramid, source=None, name="labels_0", output=None, write_colors=True, write_props=True, overwrite=False, **write_kwargs)
Write a label pyramid as an OME-Zarr label image, serialising the image-label
metadata already on the pyramid (built by label_pyramid(..., properties=True) or
Pyramid.set_image_label). This writer only persists it — it does not measure.
Returns the path written.
| Parameter | Type | Default | Description |
|---|---|---|---|
label_pyramid |
Pyramid |
— | The label pyramid, carrying its image-label metadata. |
source |
str | Path | Pyramid |
None |
The source image the labels belong to. Required when output is None: labels go to <source>/labels/<name>/, registered in the source's labels group, with image-label.source.image = "../../". |
name |
str | "labels_0" |
Label-image name (the labels/<name> subgroup). |
output |
str | Path | None |
Explicit output path; overrides the default labels/<name> location (written standalone). |
write_colors |
bool | True |
Include the pyramid's colors in the written metadata; False drops that key at write time (a lighter write for a very large object count). |
write_props |
bool | True |
Include the pyramid's properties table (label-value, area (pixels), object-coordinates); False drops it at write time. |
overwrite |
bool | False |
Overwrite an existing label image. |
**write_kwargs |
Forwarded to IO.write_labels / write_pyramid (backend, workers, ...). |
The emitted image-label metadata (OME-NGFF
labels spec)
contains version, colors ({label-value, rgba}), properties
({label-value, "area (pixels)", "object-coordinates": {axis: [start, stop]}}),
and source. Both NGFF 0.4 and 0.5 layouts are written correctly.
Notes
- Binary mask required, not validated. The level-0 array must be a binary
mask; this is the caller's responsibility. Validating it would force a full
scan of a potentially huge array, so it is deliberately skipped. (Re-labeling
an already-labeled dataset will be possible later, once
tilewise-cclgrows a relabel function.) - Downsampling is nearest-neighbour (stride). Categorical labels must not be averaged; coarser levels are produced by striding, matching the source pyramid's per-level shapes and scales.
- Batch axes give globally-unique ids. When multiple
(t, c)volumes are labeled, each is labeled independently and its ids are offset so the whole output is a single, globally-unique label image. - Colors/properties at scale. For very large object counts, writing explicit
per-object colors and properties bloats the metadata (pyrametric warns) — pass
write_colors=False/write_props=Falsetowrite_label_pyramidto drop them. Viewers such as napari auto-randomise label colors when nocolorslist is present, so skipping them is usually fine. - Memory. Labeling and property computation are memory-bounded by tile size
(see
tilewise-ccl); the only object-count-proportional cost is the in-memory properties table.
See also
tilewise-ccl— the storage-agnostic array-level engine (label_array), with its own README and benchmarks.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyrametric-0.0.1.tar.gz.
File metadata
- Download URL: pyrametric-0.0.1.tar.gz
- Upload date:
- Size: 34.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a44790e29108b97e88150cc45abffea65fee3ad6b03ec8eedceb902394e24f00
|
|
| MD5 |
f6aea1eaa83f75538a8b8da3e06d6dcb
|
|
| BLAKE2b-256 |
59b6c5ce47f95beb93a09aedb02ebb47744b70d485254d6e10cab622048936e7
|
Provenance
The following attestation bundles were made for pyrametric-0.0.1.tar.gz:
Publisher:
publish.yml on bugraoezdemir/pyrametric
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrametric-0.0.1.tar.gz -
Subject digest:
a44790e29108b97e88150cc45abffea65fee3ad6b03ec8eedceb902394e24f00 - Sigstore transparency entry: 2521425858
- Sigstore integration time:
-
Permalink:
bugraoezdemir/pyrametric@eda943fb129f9e90fdb8ed80e99b532ead1c0da2 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/bugraoezdemir
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eda943fb129f9e90fdb8ed80e99b532ead1c0da2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyrametric-0.0.1-py3-none-any.whl.
File metadata
- Download URL: pyrametric-0.0.1-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63a75570aa777d596ed48ba1a2872b39bd4d4f731defd8f8debd2b727092239c
|
|
| MD5 |
9dfdf5101456ece91ee904cf7364dec8
|
|
| BLAKE2b-256 |
8ea2a59169cf79ad52b497e1e04b7576faf95941049ab79f4e92191599a6d431
|
Provenance
The following attestation bundles were made for pyrametric-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on bugraoezdemir/pyrametric
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrametric-0.0.1-py3-none-any.whl -
Subject digest:
63a75570aa777d596ed48ba1a2872b39bd4d4f731defd8f8debd2b727092239c - Sigstore transparency entry: 2521425976
- Sigstore integration time:
-
Permalink:
bugraoezdemir/pyrametric@eda943fb129f9e90fdb8ed80e99b532ead1c0da2 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/bugraoezdemir
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eda943fb129f9e90fdb8ed80e99b532ead1c0da2 -
Trigger Event:
release
-
Statement type: