Repository navigation
fix(ngff): write the labels list to the labels group - #485
Merged
Merged
Conversation
Position.create_labels_group created `labels/` with empty attributes and
_update_labels_metadata wrote the list of label images into the
position's own metadata (`ome.labels` on the image). OME-NGFF puts that
list on the labels group itself: `labels/zarr.json` as
`{"ome": {"version": "0.5", "labels": [...]}}` for v0.5 and
`labels/.zattrs` as `{"labels": [...]}` for v0.4. Readers that follow the
spec, such as ome-zarr-models, failed to open a position with labels
(KeyError 'ome' from Labels.from_zarr).
Write the list to the labels group instead. ImagesMeta.labels stays in
the model so stores written by older iohub versions still parse, and the
legacy position-level list is dropped the next time labels are updated so
the two locations cannot disagree.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 8, 2026
iohub never reads the labels list (label_names() lists the labels group's children), and ImagesMeta allows extra keys, so stores written by older iohub still open without the typed field. Remove ImagesMeta.labels and the migration that cleared it, keeping only the write to the labels group. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
@srivarra I want to get your review on this one since it comes down to how we implement the OME-NGFF spec. Please double check that these changes make sense. |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The implementation matches the NGFF layouts for both supported versions and includes focused regression coverage.
0 open findings
What changed in this PR
Corrects OME-NGFF label metadata placement so standards-compliant readers can discover labels.
Changes:
- Writes label lists to the labels group for NGFF 0.4 and 0.5.
- Removes the position-level typed
labelsfield. - Updates tests for metadata layout and copying behavior.
| File | Description |
|---|---|
src/iohub/ngff/nodes.py |
Writes label metadata to the labels group. |
src/iohub/ngff/models.py |
Removes position-level label metadata. |
tests/ngff/test_ngff.py |
Verifies exact on-disk metadata placement. |
tests/ngff/test_ngff_utils.py |
Updates metadata-copy coverage. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
srivarra
approved these changes
Oct 8, 2026
Collaborator
|
This makes sense to me, thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Supports Biohub/biahub#360 (DCA v0.2 compliance, Biohub/biahub#363).
Summary
Position.create_labels_groupcreatedlabels/with empty attributes, and_update_labels_metadatawrote the list of label images into the position's own metadata (ome.labelson the image). OME-NGFF puts that list on the labels group:<fov>/labels/zarr.json→{"ome": {"version": "0.5", "labels": [...]}}<fov>/labels/.zattrs→{"labels": [...]}Spec-following readers therefore could not open a position with labels:
ome_zarr_models.open_ome_zarr/ v05Image.from_zarrfailed withKeyError: 'ome'fromLabels.from_zarr.This PR:
_update_labels_metadata), for both versions;ImagesMeta.labelsfield.Stores written by older iohub
No migration is attempted, and none is needed to open them:
label_names(),labels()andget_label()list the labels group's children.ImagesMetaallows extra keys, so an old position-levellabelsentry still parses, just untyped.Such stores open, and their labels stay readable. Adding a label writes the full list to the labels group. The stale list on the position is left in place and carried through later metadata dumps, but nothing reads it. Spec-following readers like ome-zarr-models read only the labels group, so they can open an old store once its labels group has been written.
Testing
test_labels_group_metadata_on_disk[0.4|0.5]asserts the exactlabels/zarr.json/labels/.zattrscontent and that the position no longer carrieslabels.test_labels_metadata_structureto match.test_create_empty_plate_copy_metadata_from: it now writes the dangling label reference straight into the source position's attributes instead of through the removed field.uv run pytest tests/ngff -q→ 216 passed, 1 skipped, 2 failed. The failures,test_apply_transform_to_czyx_and_saveandtest_apply_transform_to_tczyx_and_save(RuntimeError: incompatible offset), fail the same way onmainin this environment (Python 3.14).label_names()works, and adding a label writes["cells", "nuclei"]tolabels/zarr.jsonwhile the stale position list stays put.ome-zarr-models(not a test dependency, so not added to the suite):ome_zarr_models.open_ome_zarron a position with a label now returns anImagewithLabelsfor both 0.4 and 0.5. Onmainboth fail.Checklist
#486 builds on this PR and targets this branch. Used by Biohub/biahub#413 (pinned via
dev/dca-labels).🤖 Generated with Claude Code