Giter Club home page Giter Club logo

dash-slicer's Introduction

CI

dash_slicer

A volume slicer for Dash

Create slice views along a given dimension, and allows multiple such views to be linked, to help with navigation. Supports anisotropic data, mask overlays, and more.

Status

This work is marked as alpha - some essential features are still in development, and some parts of the API may change in future releases.

Installation

$ pip install dash-slicer

Dash-slicer depends on Python 3.6+ plus some dependencies.

Usage example

import dash
from dash import html
from dash_slicer import VolumeSlicer
import imageio

app = dash.Dash(__name__, update_title=None)

vol = imageio.volread("imageio:stent.npz")
slicer = VolumeSlicer(app, vol)

app.layout = html.Div([slicer.graph, slicer.slider, *slicer.stores])

if __name__ == "__main__":
    app.run_server(debug=True, dev_tools_props_check=False)

See the guide for more examples and explanations. A complete app that uses dash-slicer is dash-vocid-xray (source).

License

This code is distributed under the MIT license.

Developers

  • Make sure that you have Python with the appropriate dependencies installed, e.g. via venv.

  • Run pip install -e . to do an in-place install of the package.

  • Run the examples using e.g. python examples/all_features.py

  • Use black . to autoformat.

  • Use flake8 . to lint.

  • Use pytest . to run the tests.

  • Use python update_docs_in_readme.py to update the readme when needed.

On every PR, an app with the same name as your branch is deployed to the Dash playground instance so that you can change whether your changes did not break the package.

Release procedure:

  • Bump version in __init__.py (and commit this change).
  • Run git tag v?.? && git push origin v?.?
  • On GH, turn that tag into a release and write release notes.
  • Clear the dist dir.
  • Run python setup.py sdist bdist_wheel
  • Run twine upload dist/*
  • Bump version of dash-slicer in dash-docs.

Reference

The VolumeSlicer class

class VolumeSlicer(app, volume, *, spacing=None, origin=None, axis=0, reverse_y=True, clim=None, scene_id=None, color=None, thumbnail=True)

A slicer object to show 3D image data in Dash. Upon instantiation one can provide the following parameters:

  • app (dash.Dash): the Dash application instance.
  • volume (ndarray): the 3D numpy array to slice through. The dimensions are assumed to be in zyx order. If this is not the case, you can use np.swapaxes to make it so.
  • spacing (tuple of float): the distance between voxels for each dimension (zyx). The spacing and origin are applied to make the slice drawn in "scene space" rather than "voxel space".
  • origin (tuple of float): the offset for each dimension (zyx).
  • axis (int): the dimension to slice in. Default 0.
  • reverse_y (bool): whether to reverse the y-axis, so that the origin of the slice is in the top-left, rather than bottom-left. Default True. Note: setting this to False affects performance, see #12. This has been fixed, but the fix has not yet been released with Dash.
  • clim (tuple of float): the (initial) contrast limits. Default the min and max of the volume.
  • scene_id (str): the scene that this slicer is part of. Slicers that have the same scene-id show each-other's positions with line indicators. By default this is derived from id(volume).
  • color (str): the color for this slicer. By default the color is a shade of blue, orange, or green, depending on the axis. Set to empty string to prevent drawing indicators for this slicer.
  • thumbnail (int or bool): the preferred size of low-resolution data to be uploaded to the client. If False, the full-resolution data are uploaded client-side. If True (default), a default value of 32 is used.

Note that this is not a Dash Component. The components that make up the slicer (and which must be present in the layout) are: slicer.graph, slicer.slider, and slicer.stores.

method VolumeSlicer.create_overlay_data(mask, color=None)

Given a 3D mask array, create an object that can be used as output for slicer.overlay_data. Set mask to None to clear the mask. The color can be a hex color or an rgb/rgba tuple. Alternatively, color can be a list of such colors, defining a colormap.

property VolumeSlicer.axis (int): The axis to slice.

property VolumeSlicer.clim: A dcc.Store to be used as Output, representing the contrast limits as a 2-element tuple. This value should probably not be changed too often (e.g. on slider drag) because the thumbnail data is recreated on each change.

property VolumeSlicer.extra_traces: A dcc.Store to be used as an Output to define additional traces to be shown in this slicer. The data must be a list of dictionaries, with each dict representing a raw trace object.

property VolumeSlicer.graph: The dcc.Graph for this slicer. Use graph.figure to access the Plotly Figure object.

property VolumeSlicer.nslices (int): The number of slices for this slicer.

property VolumeSlicer.overlay_data: A dcc.Store to be used an Output for the overlay data. The form of this data is considered an implementation detail; users are expected to use create_overlay_data to create it.

property VolumeSlicer.scene_id (str): The id of the "virtual scene" for this slicer. Slicers that have the same scene_id show each-other's positions.

property VolumeSlicer.slider: The dcc.Slider to change the index for this slicer. If you don't want to use the slider, wrap it in a div with style display: none.

property VolumeSlicer.state: A dcc.Store representing the current state of the slicer (present in slicer.stores). This store is intended for use as State or Input. Its data is a dict with the fields:

  • "index": the integer slice index.
  • "index_changed": a bool indicating whether the index changed since last time.
  • "xrange": the view range (2 floats) in the x-dimension (2D).
  • "yrange": the view range (2 floats) in the y-dimension (2D).
  • "zpos": the float position aling the axis, in scene coordinates.
  • "axis": the axis (int) for this slicer.
  • "color": the color (str) for this slicer.

The id of the store is a dictionary so it can be used in a pattern matching Input. Its field are: context, scene, name. Where scene is the scene_id and name is "state".

property VolumeSlicer.stores: A list of dcc.Store objects that the slicer needs to work. These must be added to the app layout. Note that public stores like state and extra_traces are also present in this list.

Reacting to slicer state

It is possible to get notified of updates to slicer position and view ranges. To get this for all slicers with a specific scene_id, create a pattern matching input like this:

Input({"scene": scene_id, "context": ALL, "name": "state"})

See the state property for details.

Setting slicer positions

To programatically set the position of the slicer, create a dcc.Store with a dictionary-id that has the following fields:

  • 'context': a unique name for this store.
  • 'scene': the scene_id of the slicer objects to set the position for.
  • 'name': 'setpos'

The value in the store must be an 3-element tuple (x, y, z) in scene coordinates. To apply the position for one dimension only, use e.g (None, None, x).

Performance tips

There tends to be a lot of interaction in an application that contains slicer objects. To realize a smooth user experience, performance matters. Here are some tips to help with that:

  • Most importantly, when running the server in debug mode, consider setting dev_tools_props_check=False.
  • Also consider creating the Dash application with update_title=None.
  • Setting reverse_y to False negatively affects performance. This will be fixed in a future version of Plotly/Dash.
  • For a smooth experience, avoid triggering unnecessary figure updates.
  • When adding a callback that uses the slicer position, use the (rate limited) state store rather than the slider value.

dash-slicer's People

Contributors

almarklein avatar emmanuelle avatar gvwilson avatar n-friederich avatar surchs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dash-slicer's Issues

API to specify overlay data

The slicer needs to be able to overlay segmentation data (e.g. masks). Two suggestions:

slicer = VolumeSlice(volume, mask, ...)

# Suggestion 1: provide a mask shaped LxMxNx4 (it includes an alpha channel)
slicer.setOverlay(mask)

# Suggestion 2: provide a mask shaped LxMxN plus a colormap. 
slicer.setOverlay(mask, colormap=[(0, 0, 0, 0), (255, 0, 0, 100)])

With 1) the user has somewhat more flexibility, but the user would have to compose the mask, and the memory cost is high. With 2) we can still support multiple "layers", by allowing the mask to be integer, we can provide a default colormap or allow providing a single color to make single-layer masks easier.

Maybe there are other ideas?

Can we remove these margins?

When you zoom into a plot, a large part of the image is cut off, even though these is plenty of space. Can we fix that?

afbeelding

Add option to display brain images in neurological orientation

Human brain MRI volumes are generally organized in the R-A-S orientation in voxel space, i.e. moving along the volume axes moves through the "real world" image in Right-Left, Anterior-Posterior, Superior-Inferior directions respectively. And if the data were not oriented as expected, nilearn offers a way to try to get them there.

To display slices from these volumes in the expected display conventions (that differ somewhat between radiologists and neurologists): , we need to flip them by 90 degrees. Here is a short example to show this:

import numpy as np
from nilearn import datasets, plotting
import nibabel as nib
import matplotlib.pyplot as plt

# Get the commonly used MNI152 brain template
mni152 = datasets.fetch_icbm152_2009()
print(mni152.description.decode())

# Load the Nibabel image object
# And the volume as a numpy array
mni152_t1_img = nib.load(mni152.t1)
mni152_t1 = mni152_t1_img.get_fdata()

# Check that the data are in RAS orientation
nib.aff2axcodes(mni152_t1_img.affine)

# Display a slice
a_slice = mni152_t1[:, :, 90]
fig, axes = plt.subplots(1, 2)
axes[0].imshow(a_slice, cmap="gray")
axes[1].imshow(np.rot90(a_slice), cmap="gray")

This looks like so:
image

This "flip the slice by 90 degrees before displaying" is also what nilearn does in it's plotting tools:

# Note that nilearn displays slice indices in MNI/real-world coordinates
plotting.plot_anat(mni152_t1_img)

image

Since we aren't displaying static images of single slices, I think it'd be best to allow the user to ask us to flip the slices before displaying on the dash-slicer side of things.

How to run dash-slicer in the @app.callback?

Hi,
I am strugling to make dash-slicer work in the app.callback. Here is my code:

import dash
import dash_html_components as html
import imageio
from dash_slicer import VolumeSlicer

app = dash.Dash(__name__, update_title=None)

app.layout = html.Div(
    children=[
        html.Button('Randomize', id='random_button', n_clicks=0),
        html.Div(id='series_viewer')
    ]
)


@app.callback(
    dash.dependencies.Output('series_viewer', 'children'),
    dash.dependencies.Input('random_button', 'n_clicks')
)
def show(n_clicks):
    vol = imageio.volread("imageio:stent.npz")
    slicer = VolumeSlicer(app, vol)
    slicer.graph.config["scrollZoom"] = False
    return slicer.graph, slicer.slider, *slicer.stores


if __name__ == "__main__":
    app.run_server(debug=True, dev_tools_props_check=False)

What I am getting is:
obraz

Where there should be an image over the slider. Is there any way to make it work?

This code is based on the example from: Introduction to Dash Slicer

Image flickering when the y-axes is *not* reversed

The Image trace is quite efficient when a base64-encoded png is used as the source. However, when the current implementation (of the Image trace in Plotly) needs extra steps (including per-pixel blitting) when the image is not aligned with the screen.

Possible solutions:

Allow for negative spacing / stepsize

The spacing and origin arguments are used to allow the slices to be drawn in "scene space" rather than voxel space. This is a really useful feature. For example when displaying a brain volume, there is typically an affine matrix that translates voxel coordinates into some standard reference space (see e.g. here: https://nipy.org/nibabel/coordinate_systems.html).

One use case here would be to have a negative stepsize / spacing. I.e. walking along one volume axis would decrease the corresponding position in the standard / scene space. This is the case for example for some volume organizations of data in the MNI stereotaxic space, where the origin of the coordinate system (0, 0, 0) is by convention in the center of the brain (anterior commissure). Setting negative values for the origin works well, but at the moment, a negative spacing leads to odd behaviour of the slicers (outline traces are flipped outside, cross-linking positions doesn't seem to work correctly).
edit: cross-linking positions is working with the sliders but does not work correctly when clicking.

import dash
from jupyter_dash import JupyterDash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State, ALL
from dash_slicer import VolumeSlicer

import imageio

app = JupyterDash(__name__)
server = app.server

vol = imageio.volread("imageio:stent.npz")
origin = [1000, -1000, 0]
spacing = [-1, 1, 1]

saggital_slicer = VolumeSlicer(app, vol, spacing=spacing, origin=origin, axis=0, scene_id="brain")
coronal_slicer = VolumeSlicer(app, vol, spacing=spacing, origin=origin, axis=1, scene_id="brain")
axial_slicer = VolumeSlicer(app, vol, spacing=spacing, origin=origin, axis=2, scene_id="brain")

slicer_list = []
for slicer in [saggital_slicer, coronal_slicer, axial_slicer]:
    slicer.graph.figure.update_layout(dragmode=False, plot_bgcolor="rgb(0, 0, 0)")
    slicer_list.append(html.Div([
        slicer.graph,
        html.Div(slicer.slider),#, style={"display": "none"}),
        *slicer.stores,
    ]))
    

style={
        "display": "grid",
        "gridTemplateColumns": "33% 33% 33%",
    }
app.layout = html.Div(style=style, children=slicer_list)

app.run_server(debug=True, dev_tools_props_check=False, mode="jupyterlab")

This is probably not a high priority feature but could be nice to have.

Add method to clear overlay data

At the moment it's possible to clear the overlay data of a slicer by passing an array or zeros but it would be faster to return a list of None. This occurred to me when updating the chest X-ray app where I had to use this mask of zeros to clear the overlay data.

Allow image data to be shown with colormaps

The image data is now greyscale. Being able to show it in e.g. Viridis would be nice. We should consider making it possible to create a colorbar in this case.

There are different ways to implement this. Ranging from leveraging numb, scikit-image, or Pillow. One very interesting approach is to produce paletted PNG's, with the palette being the colormap. This can be done via e.g. Pillow or pypng.

Allow specifying overlay colors using css color names

There are two parts where we deal with color:

  • The slicers color property, used for the indicators. Supports anything compatible with CSS.
  • The colormap for overlays. Currently supports RGB tuples, RGBA tuples, and hex colors.

Could use example on how to read from and write to slicer index positions from interactive element

Hey,

I am trying to have a simple app where I show the current slicer index positions in a datatable or similar element and allow for a user to change the slicer values from this datatable (this could also just be Input fields or something like NumericInput from dash-daq). My goal is something like this:
image

Here is the general strategy I am using:
Suppose I have three slicers:

slicer0 = VolumeSlicer(app, vol, axis=0)
slicer1 = VolumeSlicer(app, vol, axis=1)
slicer2 = VolumeSlicer(app, vol, axis=2)

then to

Write to slicer position:

  1. Make store for slicer positions like described in the examples:
setpos_store = dcc.Store(
    id={"context": "app", "scene": slicer0.scene_id, "name": "setpos"}
)
  1. Write new index positions to the store like so:
return x, y, z 
...
Output(setpos_store.id, "data")

Read from slicer position

This is more difficult. I cannot just listen to Input({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data"), as described in the examples, because that gives me circular dependencies. As per the new devnotes, I tried listening to the slider values instead like so:

[
        Input(slicer0.slider.id, "value"),
        Input(slicer1.slider.id, "value"),
        Input(slicer2.slider.id, "value"),
    ],

However, this also results in a circular dependency problem (i.e. the values in the datatable both depend on and affect the slider positions).

A nice workaround proposed by @emmanuelle is to listen to the clickData events from the slicer.graph objects as the trigger, and have the slicer position info as state. This is possible. For example like so:

[
        Input(slicer0.graph.id, "clickData"),
        Input(slicer1.graph.id, "clickData"),
        Input(slicer2.graph.id, "clickData"),
    ],
    [
        State({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data"),
    ],

This works, but the slicer position reported by the State here lags one user click event behind the true position. In other words: the coordinates I get in this way are from the position before the current click event. So also not ideal, particularly not if I want to use the coordinates as starting points to let the user make changes. Here is a brief example to show the latter problem:

import dash
import dash_html_components as html
import dash_core_components as dcc
from dash_slicer import VolumeSlicer
from dash.dependencies import Input, Output, State, ALL
import imageio
import dash_daq as daq

app = dash.Dash(__name__, update_title=None)
server = app.server

# Read volume
vol = imageio.volread("imageio:stent.npz")
vol = vol[::3, :, :]
spacing = 1, 1, 1
ori = 0, 0, 0

# Create slicer objects
slicer0 = VolumeSlicer(app, vol, spacing=spacing, origin=ori, axis=0, thumbnail=False)
slicer1 = VolumeSlicer(
    app, vol, spacing=spacing, origin=ori, axis=1, thumbnail=8, reverse_y=False
)
slicer2 = VolumeSlicer(app, vol, spacing=spacing, origin=ori, axis=2, color="#00ff99")

setpos_store = dcc.Store(
    id={"context": "app", "scene": slicer0.scene_id, "name": "setpos"}
)

app.layout = html.Div(style={
    "display": "grid",
    "gridTemplateColumns": "33% 33% 33%",
}, children=[
    html.Div(
        [
            slicer0.graph,
            html.Div(slicer0.slider, style={"display": "none"}),
            *slicer0.stores
        ]
    ),
    html.Div(
        [
            slicer1.graph,
            html.Div(slicer1.slider, style={"display": "none"}),
            *slicer1.stores
        ]
    ),
    html.Div(
        [
            slicer2.graph,
            html.Div(slicer2.slider, style={"display": "none"}),
            *slicer2.stores
        ]
    ),
    html.Pre(id="slicer_click_output"),
    html.Pre(id="slicer_direct_output"),
]
)


# Show the current slicer coordinates in the numeric input fields
@app.callback(
    Output("slicer_click_output", "children"),
    [
        Input(slicer0.graph.id, "clickData"),
        Input(slicer1.graph.id, "clickData"),
        Input(slicer2.graph.id, "clickData"),
    ],
    [
        State({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data"),
    ],
    prevent_initial_call=True
)
def read_slicer_pos(fire1, fire2, fire3, slicer_index):
    if slicer_index is None or any([i is None for i in slicer_index]):
        return dash.no_update
    z_pos = slicer_index[0]["index"]
    y_pos = slicer_index[1]["index"]
    x_pos = slicer_index[2]["index"]
    return f"ClickEvent output from slicers\nx: {x_pos}\ny: {y_pos}\nz: {z_pos}"


@app.callback(
    Output("slicer_direct_output", "children"),
    Input({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data"),
    prevent_initial_call=True
)
def write_coords(slicer_index):
    if slicer_index is None or any([i is None for i in slicer_index]):
        return dash.no_update
    z_pos = slicer_index[0]["index"]
    y_pos = slicer_index[1]["index"]
    x_pos = slicer_index[2]["index"]
    return f"Direct output from slicers\nx: {x_pos}\ny: {y_pos}\nz: {z_pos}"


if __name__ == "__main__":
    app.run_server(debug=True, dev_tools_props_check=False)

Summary

It's not clear (to me) how to both listen to and change through user input the slicer positions with the same interactive component (e.g. datatable or input field) in an app. It would probably be good to add an example that illustrates this.

Todo

To keep track of progress:

  • id's defined must be made unique so one can use multiple slicers in one app.
  • Figure out how to "connect" multiple views on the same data. #4
  • Add indicators for other slices. #4
  • Use image traces, see #2 (comment) #3
  • Support anisotropic data and flipping axis'. #8
  • callbacks are now defined before the layout, which is not supposed to work? -> I read it the wrong way.
  • Make it possible to show low-res data as a placeholder. #3
  • Look into that React error / warning. -> It seems to be gone :)
  • Different behavior for slider drag and slider release:
  • Release
  • Docs
  • Allow indicators to be different colors? #29
  • API to show extra traces in the figure. #40
  • Support for setting contrast limits. #47
  • Make it configurable what data is uploaded at the start (none, low-res, full-res). #34
  • Colormaps. #49

Ideas maybe worth considering:

  • More ways to control the slice index -> #11
  • Define slice using a plane instead of an axis + index (needs interpolation with e.g. numba). -> probably out of scope - if going this way, keep the views orthogonal.

Init volume in a callback

In all the given examples, the volume and the slicer are global variables. It would be useful to be able to declare these variables in a callback. It would allow the user to select the file to vizualize during the session. The code below doesn't work: the slicer are well designed but the images remain completely blank.

  def layout(slicer0, slicer1, slicer2):

    layout = html.Div([
        html.Div(
            [
                html.Center(html.H3("Axial")),
                slicer0.graph,
                html.Br(),
                slicer0.slider,
                *slicer0.stores,
            ]
        ),
        html.Div(
            [
                html.Center(html.H3("Coronal")),
                slicer1.graph,
                html.Br(),
                slicer1.slider,
                *slicer1.stores,
            ]
        ),
        html.Div(
            [
                html.Center(html.H3("Sagittal")),
                slicer2.graph,
                html.Br(),
                slicer2.slider,
                *slicer2.stores,
            ]
        ),
    ],     style={
        "display": "grid",
        "gridTemplateColumns": "50% 50%",
    },
    )
    return layout


app = dash.Dash(
    __name__,
    meta_tags=[{
        "name": "viewport",
        "content": "width=device-width, initial-scale=1"
    }]
)

app.layout = html.Div([
        dbc.Button("Apply", id="display_ct"),
        html.Div(id="frame_ct_display"),
])

@app.callback(
    [Output("frame_ct_display", "children")],
    [Input("display_ct","n_clicks"),]
)
def display_ct(n_clicks):

    if n_clicks is None:
         raise PreventUpdate

    vol = imageio.volread("imageio:stent.npz")
    slicer0 = VolumeSlicer(app, vol, axis=0)
    slicer1 = VolumeSlicer(app, vol, axis=1)
    slicer2 = VolumeSlicer(app, vol, axis=2)

    return [layout(slicer0, slicer1, slicer2)]


if __name__ == '__main__':
    app.run_server(debug=True, dev_tools_props_check=False)

UserWarning: The dash_core_components package is deprecated.

If you use dash-slicer with the current plotly.version, you get the following warning:

UserWarning: 
The dash_html_components package is deprecated. Please replace
`import dash_html_components as html` with `from dash import html`
  import dash_html_components as html

Therefore, it would be important to adjust the imports accordingly.

More ways to control the slice index?

Possible ways to control the slice index:

  • A slider.
  • Clicking in one view sets position of other slicers of the same scene.
  • Scrolling with modifier?
  • Keyboard shortcuts?
  • Next/Previous buttons? -> Can be added by user if needed. We have an example that does this.
  • Numeric input? -> tricky because of recursion. Not so much needed?

Annotation tool

Is it possible to interactively annotate slices of a volume in dash-slicer?

Caching of full-res slices clientside

In #24 I removed the (very rudimentary) caching mechanism from the code. To not lose the knowledge, here's some background and code.

Reasoning for removing the code (for now)

For caching to become useful it would have to be smarter than what we have now. Further, because we have the low-res slices, the need of caching is lower.

After we've implemented more features that affect the managing of data (like contrast limits), we should revisit whether the additional complexity of a caching mechanism is worthwhile.

Outline

To perform caching, we'd keep a per-slicer client-side dictionary (e.g. on the slicer_state object that #24 introduced). At the moment the server callback that serves slices uses the index.data as an input. We'd need an extra store req-index that will copy over the index.data, except when the cache has data for that index. Something like this:

        self._clientside_callback(
            """
        function update_req_index(index) {
            let slicer_state;  // filled in
            slicer_state.cache = slicer_state.cache || {};
            return slicer_state.cache[index] ? dash_clientside.no_update : index;
        }
        """,
            Output(self._req_index.id, "data"),
            [Input(self._index.id, "data")],
        )

Then when a new slice is received from the server, store the incoming slice:

        self._clientside_callback(
            """
        function update_image_traces(index, server_data, overlays, lowres, info, current_traces) {
            let slicer_state;  // filled in
            slicer_state.cache = slicer_state.cache || {};
            for (let trigger of dash_clientside.callback_context.triggered) {
                if (trigger.prop_id.indexOf('server-data') >= 0) {
                    slicer_state.cache[server_data.index] = server_data;
                    break;
                }
            }
    ...

This will restore more or less the caching mechanism that we had earlier. But more features should be added to make it useful, e.g. obtaining neighboring slices, and purging slices from the cache if needed to preserve memory.

Applying colormap to a Slicer object

Hi,
I am trying to apply a colormap to already segmented images, i.e. I want to be able to color the labels when I change the slice index but I dont know how to incorporate input argument "value" in my function or do I even need that since I based my code on the example threshold_overlay.py but I don't need the contrast limit.
This is the code snippet:

@app.callback(
    [
        Output(slicer1.overlay_data.id, "data")
    ],
    [Input("main-slider", "value")]
)
def apply_colors(value):
    print(value)
    mask = mask.astype(np.uint8)

    return slicer1.create_overlay_data(mask, colormap_gt)

I have three Slicer objects and I want on the two of those three to color the labels.
The thing is my mask is already defined, i.e. my segmented images with 8 possible values (from 1 through 8).

I would really appreciate if someone could help me with this.

When I run the app in the browser, it displays the error:

dash.exceptions.InvalidCallbackReturnValue: Invalid number of output values for ..slicer2-overlay.data...
Expected 1, got 139

But my mask is already the same shape as the volume.

Reduce noise in the callback graph

[...] with the long id names, it's quite painful to look at the callback graph in the developer tools (which you can see with debug=True). This is not specific to this project, but it might be worth submitting a PR to Dash to represent long ids in a different format (see for example plotly/dash#1179 for the last modifications of the code generating the callback graph).

In #4, the id's where changed to be dict id's, but these are very long e.g. when shown in the callback graph debugger tool. Some options (both are probably a good idea):

  • Somehow display such id's more efficiently in the callback graph.
  • Use only dict-id's for objects that need it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.