plotly / dash-slicer

A volume slicer for Dash
https://dash.plotly.com/slicer
MIT License
24 stars 7 forks source link

How does create_overlay_data work? #56

Open xhluca opened 3 years ago

xhluca commented 3 years ago

It would be nice to have a working example in the dash docs

rafmudaf commented 3 years ago

I'm struggling to get create_overlay_data to use a colormap. It seems to always take the first color in the color list.

Starting with the all_features example, I've modified this line to be

def update_overlay(level):
    R, G, B = "#FF0000", "#00FF00", "#0000FF"
    # R, G, B = (255, 0, 0), (0, 255, 0), (0, 0, 255)
    color = [R, G, B]
    return slicer1.create_overlay_data(vol > level, color)

But the example shows only an overlay in red.

Looking at the implementation, I'm wondering if somehow the data is not being properly mapped to the color map or if its not passed into the function correctly. I've verified that the colormap variable is created by copying lines 90 to 120 into my script; the value of colormap is:

[(0, 0, 0, 0), (255, 0, 0, 100), (0, 255, 0, 100), (0, 0, 255, 100)]
almarklein commented 3 years ago

There is this example (referenced from the readme) that uses create_overlay_data(). But that example is indeed a bit large - it would be nice to have a simple example focusing on this feature.

@rafmudaf what does your mask look like? A minimal working example demonstrating the problem would help narrow things down :)

rafmudaf commented 3 years ago

Hi @almarklein Thanks for pointing out that example. I looked through but it is quite complex.

I actually found that one of the examples in the slicer repo has multiple colors in the overlay: https://github.com/plotly/dash-slicer/blob/main/examples/threshold_overlay.py. This example pointed out the critical point that the mask should be successively added to in order to map bins of data to the index of their color in the colormap. The mask is essentially an index-mapping of all points in the data set to a color in the colormap. In retrospect, this is clear, but it isn't obvious when you don't know what you're looking for.

I've opened a pull request showing this at https://github.com/plotly/dash-slicer/pull/57. No need to merge, I just wanted to show you the code.

rafmudaf commented 3 years ago

For future reference, here's a code snippet of mapping a mask to a colormap:

COLORMAP = px.colors.sequential.Turbo

def apply_levels(level):
    n_bins = len(COLORMAP)
    mask = np.zeros(vol.shape, np.uint8)
    data_range = vol.max() - vol.min()
    thresholds = [ vol.min() + i * data_range / n_bins for i in range(1,n_bins + 1) ]

    for i in range(n_bins):
        mask += vol < thresholds[i]
    return slicer.create_overlay_data(mask, COLORMAP)
almarklein commented 3 years ago

The mask is essentially an index-mapping of all points in the data set to a color in the colormap.

Indeed. I'm glad that you figured it out! Nevertheless, it looks like this is something that we could document more clearly. Would you be interested in creating a PR to update the docstring of create_overlay_data?

Out of curiosity, how did you (initially) think that the colors would be applied based on the mask?