Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ngff): Support displaying "labels" for "multiscales" nodes #242

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

manzt
Copy link
Member

@manzt manzt commented Feb 25, 2025

Towards #45

demo: https://deploy-preview-242--vizarr.netlify.app/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr

Making progress—still a work in progress, but the initial plumbing is in place. That will need a refactor later alongside broader state management (probably at some latter point). However, things are showing up.

Key files:

  • src/layers/LabelLayer.ts – Custom deck.gl layer for rendering labels.
  • src/components/LayerController/Labels.tsx – UI controls for ImageLabelLayer, including the opacity slider.

With this setup, we can iterate on rendering and UI controls. This PR is in a good spot to move forward.

Screen.Recording.2025-02-25.at.23.31.28.mov

@will-moore @jluethi @joshmoore

@will-moore
Copy link
Collaborator

@manzt That's looking great, thanks.

I also tried testing with a different IDR sample at https://ome.github.io/ome-ngff-validator/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr.
This one actually has 2 sets of labels at https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr/labels/Cell/ and https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr/labels/Chromosomes/ so this would need to handle a list of labels layers rather than just a single layer.
It would also be cool if you could start at the image itself and find all the child labels, rather than needing to start at the labels themselves.
But I think I should be able to work on those changes (at some point) since they hopefully shouldn't be too tricky... Unless someone else would like to have a go?

@manzt
Copy link
Member Author

manzt commented Feb 26, 2025

It would also be cool if you could start at the image itself and find all the child labels, rather than needing to start at the labels themselves.

Agree this would be ideal. From an image root, where can one find the paths to the associated labels? I was only about to find paths in the one metadata pointing from labels up.

@will-moore
Copy link
Collaborator

You just have to check for the existence of /labels/.zattrs. That's what's happening at https://ome.github.io/ome-ngff-validator/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr to show the Cell & Chromosomes links.
So, we do that check for every image and just ignore if /labels/.zattrs is not found.

@manzt
Copy link
Member Author

manzt commented Feb 26, 2025

Ok, I updated the PR to handle this case. Now if you provide a labels node, it will just open as a regular multiscale array. But we will look for /labels/.zattrs.

@will-moore
Copy link
Collaborator

will-moore commented Feb 26, 2025

@manzt Nice - that's loading both labels now 👍 .

Screenshot 2025-02-26 at 14 33 15

I think there's an issue with the T-slider not updating labels? When I scroll through T I don't see any change in labels (but they do update when I scroll through Z).

I tried to check that the labels really do update through T by opening the label image itself...
But this gives Cannot read properties of null (reading '0') at

https://deploy-preview-242--vizarr.netlify.app/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr/labels/Chromosomes

This works in vanilla vizarr: https://hms-dbmi.github.io/vizarr/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0052A/5514375.zarr/labels/Chromosomes

@manzt
Copy link
Member Author

manzt commented Feb 26, 2025

I think there's an issue with the T-slider not updating labels? When I scroll through T I don't see any change in labels (but they do update when I scroll through Z).

Not totally sure. I can have a look.

@jluethi
Copy link

jluethi commented Feb 26, 2025

This is awesome to see @manzt ! ❤️

cc @tcompa @zonia3000 Do you have capacity to help here @zonia3000 ? Would be great to try this on our typical test data (e.g. this one with labels in both 2d & 3d images: https://zenodo.org/records/13305316). And then to see if we can help with some of the things mentioned as TODOs in this PR :)

@manzt
Copy link
Member Author

manzt commented Feb 26, 2025

Sounds good! First thing would be to see if you can get your own images (with labels) displayed with the current state of the PR. Then, with something visible we could try to iron out exactly what we need to prioritize to make it usable/useful, working backwards what code to write. For example, I know that we will at least need to fine a way to make ImageLabelLayer have a transparent background.

Some other ideas:

  • label opacity
  • label color(s)

We can then focus on making the rendering (ImageLabelLayer) parameterized by these properties, as well as make UI for adjusting them.

@manzt manzt force-pushed the manzt/labels branch 6 times, most recently from 26cb7f5 to 798405f Compare February 26, 2025 22:33
@tcompa
Copy link

tcompa commented Feb 27, 2025

Hi there, thanks a lot. As a first comment, I tried looking at one of the Fractal images - which is available at https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/

Sanity check 1: this image is viewed correctly in the standard vizarr viewer:

https://hms-dbmi.github.io/vizarr/?source=https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/

image


Sanity check 2: the image has a label array (under /labels/nuclei), which can be rendered by itself

https://deploy-preview-242--vizarr.netlify.app/?source=https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/labels/nuclei

image

Minor glitch in this page: I can add the same channel N times:
image


However something seems a bit off when I load the image+labels:

https://deploy-preview-242--vizarr.netlify.app/?source=https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/

image

The nuclei label is identified and showed in the top-left panel, but I cannot manage to get to see.
I also tried to disable the actual image channels:
image


My first guess for the explanation is that our label is a multiscale array.

The logs include a 404 response for the GET of https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/labels/nuclei/.zarray, but in fact there is no array at that path. There would be one at https://raw.githubusercontent.com/tcompa/hosting-ome-zarr-on-github/refs/heads/main/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0/labels/nuclei/0/.zarray instead (note the additional /0).

@will-moore
Copy link
Collaborator

In the last image + labels case, I'm seeing console errors IndexError: too many indicies for array; expected 3, got 4 which is likely due to the fact that the image has shape 3,1,2160,5120 whereas the labels have shape 1,540,1280.

@jluethi
Copy link

jluethi commented Feb 27, 2025

In the last image + labels case, I'm seeing console errors IndexError: too many indicies for array; expected 3, got 4 which is likely due to the fact that the image has shape 3,1,2160,5120 whereas the labels have shape 1,540,1280

Yes, these are the typical dimensions we'd have. Context:

  1. The x & y of the label are sometimes (like in this example) lower resolution than the image it is based on
  2. We don't include a channel axis c in the label image, as our labels don't (/can't) have channels. This is defined in the multiscales metadata of the label image

My questions from this:
a) are we reyling on the multiscales of the image to load the labels?
b) Do you typically save labels with the same axis, including a c axis @will-moore ?

@manzt
Copy link
Member Author

manzt commented Feb 27, 2025

we can support labels of different dimensions with the caveat that the axis labels MUST be a subset of the source image. Otherwise there is no way to align the selections across layers.

@will-moore
Copy link
Collaborator

@jluethi No, labels shouldn't need to have a C axis, although I see in the examples above we do have c:1 dimension.

@jluethi
Copy link

jluethi commented Feb 27, 2025

we can support labels of different dimensions with the caveat that the axis labels MUST be a subset of the source image. Otherwise there is no way to align the selections across layers.

That makes sense! And yes, I would agree that the axis of labels MUST be a subset. In our case, it's often czyx images & zyx labels.

There is the weird edge-case of arrays that have singleton dimensions. I think the limitation is fair that labels cannot introduce new singletons (e.g. a label shouldn't be saved as tzyx with singleton t dimension if the input image was czyx).

@manzt
Copy link
Member Author

manzt commented Feb 27, 2025

Great, thanks everyone! I forgot about the extensions feature and will give it a try. I think I'm also getting an error now due to a bug in Viv and latest deck.gl (#243). I'm going to revert those changes on this branch so we can keep iterating and not have it as a blocker.

@manzt manzt force-pushed the manzt/labels branch 3 times, most recently from 9cba886 to 87a665d Compare February 27, 2025 17:12
@manzt
Copy link
Member Author

manzt commented Mar 4, 2025

https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0047A/4496763.zarr/ Labels aren't showing. tile-layer.js:16 IndexError: index out of bounds for dimension with length 1 from tile-layer.js : 16

Not entirely sure what to do about this one. The labels have a z axis of length 1, while the source image has a z axis of length 25.

❯ curl -sL https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0047A/4496763.zarr/labels/0/0/.zarray | jq '.shape'
[
  1,
  1,
  2048,
  2048
]

❯ curl -sL https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0047A/4496763.zarr/0/.zarray | jq '.shape'
[
  4,
  25,
  2048,
  2048
]

we have logic to try to "align" selections across layers, but I'm not sure what the behavior would be here. I guess we could inspect the shape and ignore selections from dimensions that aren't exactly aligned.

@will-moore
Copy link
Collaborator

@manzt I wouldn't worry about trying to align labels for https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0047A/4496763.zarr/ - I think that is clearly invalid.

@manzt
Copy link
Member Author

manzt commented Mar 4, 2025

@manzt I wouldn't worry about trying to align labels for uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0047A/4496763.zarr - I think that is clearly invalid.

I actually was able to support this by generalizing the idea of special-casing the c axis. If the labels contain an dimension of length 1, it can only every be 0 in the selection, so we can safely ignore it.

image

@manzt
Copy link
Member Author

manzt commented Mar 4, 2025

https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0001A/2551.zarr/A/1/0/ fails to show labels, presumably because the colors: [] is an empty list (which is actually invalid at ome.github.io/ome-ngff-validator?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0001A/2551.zarr/A/1/0/labels/0/). So there's no need to fix that.

Was also trivial to support, although invalid. We can just treat empty colors the same as no colors. In a separate PR, it would be cool to have a "toast" like notification when some potentially invalid/ambiguous metadata is present with a link to the validator for more information.

@manzt
Copy link
Member Author

manzt commented Mar 4, 2025

Ok, so the implementation of the dynamic LUTs I made fails when the table is really large due to an issue with WebGL uniform sizes. I've updated the implementation to fallback to the default random LUT (and log a warning) if we can't use the colors defined in the OME-NGFF metadata.

I don't think that should be a blocker for this PR, and we can follow up separately to improve the shaders (i.e., sample from a texture rather than use uniforms). All the links you shared @will-moore seem to be working now.

Same for https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.3/idr0079A/9836998.zarr and a bunch of other older examples

One thing to note is that this one is very slow due to how much data we need to load because of the 3D chunks.

@will-moore
Copy link
Collaborator

Wow, really nice work. Thanks @manzt! They're looking great!

@manzt
Copy link
Member Author

manzt commented Mar 4, 2025

I'm not sure when I'll be able to push the additional UI changes through. However, I think in this state I'd vote to try to get this merged (maybe behind an ?experimental flag in the URL for now, or ?labels=true).

Then, we have separate, smaller PRs to clean up the UI for layer properties (opacity, on/off), change color palette if necessary, etc. Those will be easier to review and hopefully contribute.

The main outstanding issue IMO is that merging "as is" changes the default behavior of vizarr for older URLs, so I'm trying to think of how we could keep the previous behavior and maybe op-in to showing the labels? Does that make sense?

We could maybe show that the image has labels in the UI, but you need to toggle them on first?

@jluethi
Copy link

jluethi commented Mar 5, 2025

I just grabbed a random palette from Color Brewer, definitely not set on it. Happy to discuss other options, like the palette used in napari (I think it comes from scikit-image). However, I'd mostly like to choose a default and avoid exposing some configuration (outside of the NGFF spec).

Fully agreed on having one default colormap and not exposing this further for this effort here! I'd have a strong preference for the "large palette" one, because the smaller one repeats so often that neighboring labels with unique values often get the same color assigned in the example here.
Both the small & large shown above look much nicer in the saturation! :)

I'm not sure when I'll be able to push the additional UI changes through. However, I think in this state I'd vote to try to get this merged (maybe behind an ?experimental flag in the URL for now, or ?labels=true).

That would work for us!

We could maybe show that the image has labels in the UI, but you need to toggle them on first?

That would be even better! :) We can either directly start with this or start with the experimental flag and then add this. I think having them toggled off by default is fine and one can then activate them as wanted.

Then, we have separate, smaller PRs to clean up the UI for layer properties (opacity, on/off), change color palette if necessary, etc. Those will be easier to review and hopefully contribute.

We'd be happy to make contributions for this! :)

@manzt
Copy link
Member Author

manzt commented Mar 5, 2025

I'd have a strong preference for the "large palette" one

Alright, let's just go with that one for now and we can reassess in the future if/when something comes up.

That would be even better! :)

Ok, I'll push this PR to get some indicator of labels ("off" by default), which can be enabled (as a group) with opacity sliders. We could then add more granular controls to toggle on/off individual labels in a separate PR.

Will try to land by the end of the week, but hopefully the deploy preview URLs are usable for the time being to test out the feature futher.

@jluethi
Copy link

jluethi commented Mar 6, 2025

That's awesome! We've deployed the branch locally as well to use it with some non-public data and our authentication, I'll have a look if I spot any general issues coming up.

Very much looking forward to using this more, thanks a lot for your efforts here Trevor!

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.

5 participants