Skip to content

Render labels#38

Open
will-moore wants to merge 15 commits into
BioNGFF:mainfrom
will-moore:render_labels
Open

Render labels#38
will-moore wants to merge 15 commits into
BioNGFF:mainfrom
will-moore:render_labels

Conversation

@will-moore

@will-moore will-moore commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Fixes #37

This improves the rendering of Labels with look-up tables, by avoiding the scaling of input values by min/max settings...
Currently, if we set a LUT (even such as glasbey where adjacent colors are distinctive), the LUT is scaled over the whole range of pixel values, so if the pixel range is large, you'll get a load of adjacent label values rendered the same color:

(label image is at https://ome.github.io/ome-ngff-validator/?source=https://livingobjects.ebi.ac.uk/idr/webatlas/dataset_161/morphology.ome.zarr/0/labels/segmentation/)

Screenshot 2026-06-02 at 17 01 23

We want to use label values as indices of the LUT list, using modulo transform for when we have larger label value range than colors in our LUT.
Another design goal here is to avoid proliferation of parameters for render functions.

At the lowest level, the rendering of luts is handled by:

renderChunkWithLUT(chunk, lut, {range...})

We want to support 2 different behaviours here:

  • Existing behavior: for pixel values within the range, scale the value to an equivalent position/index within the LUT
  • New behaviour: use the pixel value as a LUT index directly, using modulo to handle larger values

The main parameter difference between these 2 behaviours is that the first needs a range and the 2nd doesnt.
So, in this PR, if you don't supply a range to renderChunkWithLUT() then you get the 2nd behaviour.

The other question we discussed at #37 was how to specify a background 0 color for the LUT, without it getting reused for other label values (where value % lut.length is 0).
I have tweaked the modulo behaviour to base it on 1 instead of 0.
So if I have a lut of [transparent, cyan, magenta, yellow] then pixel values will be assigned as follows:

0: transparent
1: cyan
2: magenta
3: yellow
4: cyan
5: magenta
6: yellow
...

This also provides an easy way to achieve another useful feature: render ALL labels the same color, and the background a different color. Simply use a lut like [transparent, cyan].

With this change to renderChunkWithLUT() the higher-level function renderRGBA (soon to be renamed) needed an additional option, since it currently calculates a range if one is not provided, using the minMax range of the chunk. That option is calcMinMaxForRange and must be true to replicate previous behaviour.

The naming of this parameter is a bit tricky and doesn't make for a very user-friendly API.
So I have subclassed NgffImage to create LabelsImage. The calcMinMaxForRange defaults to true for NgffImage and false for LabelsImage, so that when you are rendering labels you get the desired behaviour.

To test, build the docs and read the labels page, try the demo etc.
Also can try testing/editing the new code in index.html.

Using this PR, that image above can be rendered like this (both specify background as black):

Screenshot 2026-06-02 at 17 46 00 Screenshot 2026-06-02 at 17 46 07

@will-moore

Copy link
Copy Markdown
Collaborator Author

@jwindhager it would be great to hear your thoughts / review of this PR - see description above, thx

@will-moore

Copy link
Copy Markdown
Collaborator Author

This branch is working quite nicely for me over at ome/omero-iviewer#532 (copied the build dist over from this branch). See screenshot there:

  • Rendering all labels same color (background transparent)
  • Rendering all labels with a LUT (background transparent)
  • Rendering labels with a colorMap

Since these are not actual min/max values. Also more consistent with use of range elsewhere
@will-moore

Copy link
Copy Markdown
Collaborator Author

@jwindhager (or @lubianat) - I've added a few tests and I think this is looking ready to be merged and released (once also renamed renderRGBA() to renderArray()) so it would be great to hear any feedback / review of the changes here. Any time you can spend on this would be much appreciated, thanks!

@lubianat

Copy link
Copy Markdown
Contributor

Hi, @will-moore , I looked at it in general, it looks okay, but I don't feel super confident on evaluating it.

I noticed when building the docs that "fill color" was working before (web version):
grafik

but now it isn't (my build)

grafik

if I select one of the first two, it does not work, but if I select "data", it does.

grafik

So I guess "fill color" is only passed in the last option, which was not intuitive for me I think

@lubianat

Copy link
Copy Markdown
Contributor

So, it looks good to me in general, but I am not super confident on my review

@will-moore

Copy link
Copy Markdown
Collaborator Author

@lubianat Thanks - you are correct - Fill color only applies to the "Data" option.

I tweaked the layout to hopefully improve clarity there. The controls for "Data" are only enabled when that option is selected.
I also added a code line to each option to help explanation of the API used in each case. I don't know if that is helpful or confusing?

Screenshot 2026-06-22 at 16 24 12

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.

ColorMap for "auto" label

2 participants