Skip to content

fix(ngff): write the labels list to the labels group - #485

Merged
ieivanov merged 2 commits into
mainfrom
fix/labels-group-metadata
Oct 9, 2026
Merged

ieivanov merged 2 commits into
mainfrom
fix/labels-group-metadata

Conversation

@ieivanov

@ieivanov ieivanov commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Related issue

Supports Biohub/biahub#360 (DCA v0.2 compliance, Biohub/biahub#363).

Summary

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:

  • v0.5: <fov>/labels/zarr.json → {"ome": {"version": "0.5", "labels": [...]}}
  • v0.4: <fov>/labels/.zattrs → {"labels": [...]}

Spec-following readers therefore could not open a position with labels: ome_zarr_models.open_ome_zarr / v05 Image.from_zarr failed with KeyError: 'ome' from Labels.from_zarr.

This PR:

  • writes the labels list to the labels group (_update_labels_metadata), for both versions;
  • stops writing it onto the position, and removes the ImagesMeta.labels field.

Stores written by older iohub

No migration is attempted, and none is needed to open them:

  • iohub never reads the labels list: label_names(), labels() and get_label() list the labels group's children.
  • ImagesMeta allows extra keys, so an old position-level labels entry 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

  • New test_labels_group_metadata_on_disk[0.4|0.5] asserts the exact labels/zarr.json / labels/.zattrs content and that the position no longer carries labels.
  • Updated test_labels_metadata_structure to match.
  • Updated 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_save and test_apply_transform_to_tczyx_and_save (RuntimeError: incompatible offset), fail the same way on main in this environment (Python 3.14).
  • Checked by hand that a store in the old layout still opens: label_names() works, and adding a label writes ["cells", "nuclei"] to labels/zarr.json while the stale position list stays put.
  • Checked by hand with ome-zarr-models (not a test dependency, so not added to the suite): ome_zarr_models.open_ome_zarr on a position with a label now returns an Image with Labels for both 0.4 and 0.5. On main both fail.

Checklist

  • I added or updated tests, or this change does not require tests.
  • I updated the documentation, or this change does not require documentation.
  • I confirmed this change does not include confidential or sensitive information.

#486 builds on this PR and targets this branch. Used by Biohub/biahub#413 (pinned via dev/dca-labels).

🤖 Generated with Claude Code

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>
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>
@ieivanov
ieivanov marked this pull request as ready for review October 8, 2026 23:25
@ieivanov
ieivanov requested review from srivarra and a balanced review from Copilot October 8, 2026 23:25
@ieivanov

ieivanov commented Oct 8, 2026

Copy link
Copy Markdown
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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 labels field.
  • 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

srivarra commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

This makes sense to me, thanks!

@ieivanov
ieivanov merged commit 52b29ed into main Oct 9, 2026
20 checks passed
@ieivanov
ieivanov deleted the fix/labels-group-metadata branch October 9, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants