ome / ome/ngff

How to associate multiple image types at the same spatial location in HCS?

Open
#441 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
Python
Stars
171
Forks
75
Avg merge
2d 3h
Merged PRs (30d)
20

Description

We're building an image processing pipeline (brieflow) for optical pooled screens that has traditionally relied on tiffs as the intermediate file type. In switching to OME-Zarr v3 in HCS plate layout, we have had some questions on the best way to move forward. Each field of view produces multiple co-registered outputs with different dtypes and semantics:

  • Primary image: aligned multichannel fluorescence (uint16, 11ch)
  • Derived intermediates: filtered/transformed versions (float32, varying channels)
  • Segmentation masks: integer labels (int32)

These can't be combined into one multichannel array (different dtypes, different channel counts) and shouldn't be separate pyramid levels (they're different data, not downscaled versions). labels/ handles segmentation masks but not float-valued derived images.

We're unsure how to structure this within HCS. Here are the options we're considering:

Option A: Primary image at field level, derived images as siblings

The field itself is the primary multiscale image. Derived images sit beside it as named groups. Labels associate with the primary image per spec.

plate.zarr/
└── A/1/0/                              # field = primary multiscale image
    ├── zarr.json                       # multiscales + omero for aligned image
    ├── 0/, 1/, 2/, ...                 # pyramid levels (aligned data)
    ├── labels/
    │   ├── zarr.json                   # lists [nuclei, cells]
    │   ├── nuclei.zarr/               # label multiscale
    │   └── cells.zarr/
    ├── illumination_corrected.zarr/    # derived (own multiscales, no labels)
    ├── log_filtered.zarr/
    ├── max_filtered.zarr/
    ├── peaks.zarr/
    └── standard_deviation.zarr/

Pro: Field is a valid multiscale image. Labels are correctly placed. Everything is co-located.
Con: The derived .zarr groups sitting beside pyramid level directories (0/, 1/, ...) is unconventional. A strict reader might try to interpret them as pyramid levels or choke on unexpected children.

Option B: Primary image in HCS plate, derived images outside

Only the primary image lives in the plate zarr. Intermediates go into a parallel directory with matching structure. Relationship is implicit via matching paths.

sbs/
├── plate.zarr/                         # HCS-compliant plate
│   └── A/1/0/                          # field = primary multiscale image
│       ├── zarr.json                   # multiscales + omero
│       ├── 0/, 1/, 2/, ...             # pyramid levels
│       └── labels/
│           ├── nuclei.zarr/
│           └── cells.zarr/
└── intermediates/                      # non-HCS, parallel structure
    └── A/1/0/
        ├── illumination_corrected.zarr/
        ├── log_filtered.zarr/
        ├── max_filtered.zarr/
        ├── peaks.zarr/
        └── standard_deviation.zarr/

Pro: Plate zarr is fully spec-compliant. Viewers work out of the box.
Con: Spatial relationship between primary and derived images is not declared anywhere — only implied by matching directory paths. Two separate trees to manage.

Option C: Keep current layout, add field-level index metadata

Each field contains multiple zarr stores as we have today, but with a zarr.json at the field level that designates the primary image and lists the others.

plate.zarr/
└── A/1/0/                              # field directory
    ├── zarr.json                       # group metadata: declares primary + associated
    ├── aligned.zarr/                   # primary image (multiscales + omero)
    │   ├── 0/, 1/, 2/, ...
    ├── illumination_corrected.zarr/    # derived (own multiscales)
    ├── log_filtered.zarr/
    ├── max_filtered.zarr/
    ├── peaks.zarr/
    ├── standard_deviation.zarr/
    └── labels/                         # associated with aligned.zarr, not the others
        ├── zarr.json
        ├── nuclei.zarr/
        └── cells.zarr/

Pro: All data co-located. Relationships are explicit in metadata.
Con: Field is a group containing images, not a multiscale image itself. Well images[].path points to a group rather than a multiscale image, which doesn't match what viewers expect.

Is there a recommended pattern for this? Are others solving this differently? We envision a long term use case where a user could manually explore their data, for example, observing where peaks lie in relation to actual spots in the primary image, and evaluate where errors in the pipeline may have emerged.

Also ccing @exxpyy

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Compare the three proposed HCS layouts for OME-Zarr v3, including field-level images, labels, derived intermediates, and the images[].path expectation. Determine which pattern preserves valid viewer behavior while declaring relationships between co-registered data. Done means documenting or deciding a recommended pattern for this use case.

Written by the indexing model from the issue text.

Assessment

Domain
data
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.