Skip to content

Conversation

@frasercl
Copy link
Collaborator

Review time: tiny (5min)

Resolves #359: fixes multiple bugs with loading multi-source zarr images where some sources are missing a channel (C) dimension.

This is useful to scientists with a multi-channel "original" image and a generated dataset written to a different zarr. E.g. if the original zarr has two channels and the scientist generates an additional segmentation layer in a different zarr, Vol-E can show it as a single three-channel image. But often, that single-channel segmentation image will be generated without an explicit "channels" dimension entirely (TZYX rather than TCZYX).

This PR fixes a couple spots where OMEZarrLoader currently doesn't account for this case.

@frasercl frasercl requested a review from a team as a code owner December 17, 2025 01:36
@frasercl frasercl requested review from ShrimpCryptid and interim17 and removed request for a team December 17, 2025 01:36
let channelCount = 0;
for (const s of sources) {
s.channelOffset = channelCount;
channelCount += s.omeroMetadata?.channels.length ?? s.scaleLevels[0].shape[s.axesTCZYX[1]];
Copy link
Contributor

Choose a reason for hiding this comment

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

So just to check my understanding, this was failing before because s.axesTCZYX[1] evaluated to -1, which then caused the statement to effectively become:

channelCount += undefined ?? undefined

Is that right? and now in that failure case we instead increment by 1

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup! And adding undefined to channelCount is what eventually produced the NaNs in chunk request URLs.

(Vaguely interesting: the NaNs only appeared if the image missing the channel dimension came before the image with it. Configurations with the URLs the opposite way around fell victim to the completely separate bug I fixed further down this file.)

const level = src.scaleLevels[scaleLevel];
const chunkDimsUnordered = level.shape.map((dim, idx) => Math.ceil(dim / level.chunks[idx]));
return this.orderByTCZYX(chunkDimsUnordered, 1);
return this.orderByTCZYX(chunkDimsUnordered, 1, idx);
Copy link
Contributor

Choose a reason for hiding this comment

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

What does adding idx to this line do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This specifies which source's dimension ordering we want to use. This used to just default to using the topology of the first source for everything. This was bad.

idx is also unnecessarily terse. I've renamed it sourceIndex.

Copy link
Contributor

@ShrimpCryptid ShrimpCryptid left a comment

Choose a reason for hiding this comment

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

Thanks for answering my questions! LGTM

@frasercl frasercl merged commit 19c10d0 into main Jan 14, 2026
3 checks passed
@frasercl frasercl deleted the fix/multi-source-zarr-missing-c branch January 14, 2026 00:21
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.

Cannot load multiple volumes if one is missing channels (TZYX and TCZYX)

4 participants