Atlas Demo¶

NiiVue can display brain atlases interactively, showing the region name when you hover over an area and adjusting opacity to highlight the selected region.

This notebook mirrors the Atlas live demo web page

In [1]:
import json
import ipywidgets as widgets
from IPython.display import display
from ipyniivue import NiiVue, ShowRender

## Create NiiVue Instance

nv = NiiVue(
    show_3d_crosshair=True,
    back_color=(0.5, 0.5, 0.5, 1),
)

nv.set_interpolation(True)
nv.opts.crosshair_gap = 12
nv.opts.multiplanar_show_render = ShowRender.ALWAYS
nv.opts.drag_mode = "PAN"
nv.opts.yoke_3d_to_2d_zoom = True

nv.load_volumes(
    [
        {"path": "../images/mni152.nii.gz"},
        {"path": "../images/aal.nii.gz"},
    ]
)

## Load a custom colormap

with open("../images/aal.json") as f:
    cmap = json.load(f)

nv.volumes[1].set_colormap_label(cmap)

clut = nv.volumes[1].colormap_label.lut.copy()

# Make all regions translucent by setting alpha values to 96
for i in range(3, len(clut), 4):
    clut[i] = 96

# Update the colormap label with the modified lut
nv.volumes[1].colormap_label.lut = clut

## Add other widgets

interp_checkbox = widgets.Checkbox(
    value=True,
    description="Jagged",
)

outline_slider = widgets.IntSlider(
    min=0,
    max=255,
    value=1,
    description="Outline",
)

alpha_slider = widgets.IntSlider(
    min=1,
    max=255,
    value=150,
    description="Opacity",
)

pad_slider = widgets.IntSlider(
    min=0,
    max=10,
    value=5,
    description="Padding",
)

gap_slider = widgets.IntSlider(
    min=0,
    max=36,
    value=12,
    description="Crosshair Gap",
    style={"description_width": "initial"}
)

## Setup observer functions

def on_outline_change(change):
    """Handle changes in the outline slider."""
    nv.set_atlas_outline(change["new"] / 255)


def on_alpha_change(change):
    """Handle changes in the opacity slider."""
    nv.volumes[1].opacity = change["new"] / 255


def on_pad_change(change):
    """Handle changes in the padding slider."""
    nv.opts.multiplanar_pad_pixels = change["new"]


def on_gap_change(change):
    """Handle changes in the crosshair gap slider."""
    nv.opts.crosshair_gap = change["new"]


def on_interp_change(change):
    """Handle changes in the interpolation checkbox."""
    nv.set_interpolation(change["new"])

# Observe changes in widget values and call the respective functions
outline_slider.observe(on_outline_change, names="value")
alpha_slider.observe(on_alpha_change, names="value")
pad_slider.observe(on_pad_change, names="value")
gap_slider.observe(on_gap_change, names="value")
interp_checkbox.observe(on_interp_change, names="value")

# Initialize the NiiVue instance with the current widget values
on_alpha_change({"new": alpha_slider.value})
on_outline_change({"new": outline_slider.value})
nv.opts.multiplanar_pad_pixels = pad_slider.value
nv.opts.crosshair_gap = gap_slider.value
nv.set_interpolation(interp_checkbox.value)

## Setup hover and click updates

output = widgets.HTML("Hover:<br>Clicked:")

active_idx = -1


@nv.on_hover_idx_change
def on_hover_idx_change(data):
    """Handle hover updates."""
    global active_idx
    idx_values = data["idxValues"]
    idx = idx_values[1]["idx"]
    if idx is not None and idx != active_idx:
        nv.opts.atlas_active_index = idx
        label = cmap["labels"][idx] if idx < len(cmap["labels"]) else ""
        clicked_line = output.value.split("Clicked:")[1]
        output.value = f"Hover: {label}<br>Clicked:{clicked_line}"


@nv.on_location_change
def handle_location_change(location):
    """Handle mouse clicks."""
    hover_line = output.value.split("Hover:")[1].split("<br>")[0]
    output.value = f"Hover:{hover_line}<br>Clicked: {location['string']}"

## Display all

controls = widgets.VBox(
    [
        interp_checkbox,
        outline_slider,
        alpha_slider,
        pad_slider,
        gap_slider,
        output,
    ]
)

display(
    widgets.VBox(
        [
            controls,
            nv,
        ]
    )
)
In [ ]: