Alpha Threshold¶
NiiVue supports asymmetric positive and negative statistical thresholds and can render sub-threshold voxels as translucent. This feature is useful because brain activity maps are often thresholded to correct for multiple comparisons, which can reduce statistical power. Visualizing sub-threshold values helps reveal underlying trends that might otherwise be hidden.
Note that smoothing an image will erode peaks, generally reducing the number of voxels that exceed a specified threshold.
This Python notebook mirrors a JavaScript web demo.
In [1]:
import ipywidgets as widgets
from IPython.display import display
from ipyniivue import NiiVue, ShowRender, SliceType
# Create NiiVue instance with specific settings
nv = NiiVue(
loading_text="waiting",
back_color=(1, 1, 1, 1),
show_3d_crosshair=True,
is_colorbar=True,
multiplanar_show_render=ShowRender.ALWAYS,
)
# Set initial configuration
nv.set_radiological_convention(False)
nv.set_slice_type(SliceType.MULTIPLANAR)
nv.set_slice_mm(False)
nv.set_interpolation(True)
# Load 4D volume with paired HEAD and BRIK files
nv.load_volumes(
[
{
"path": "../images/fslmean.nii.gz",
},
{
"path": "../images/fslt.nii.gz",
"colormap": "warm",
"colormap_negative": "winter",
"cal_min": 3,
"cal_max": 6,
"cal_min_neg": -6,
"cal_max_neg": -3,
},
]
)
# Hide colorbar for anatomical scan
nv.volumes[0].colorbar_visible = False
# Set initial overlay outline
nv.overlay_outline_width = 0.25
# High DPI checkbox
dpi_checkbox = widgets.Checkbox(
value=True,
description="High DPI",
tooltip="Higher resolution for 'retina' displays",
)
# Negative colors checkbox
negative_checkbox = widgets.Checkbox(value=True, description="Negative Colors")
# Smooth checkbox
smooth_checkbox = widgets.Checkbox(
value=False,
description="Smooth",
tooltip=(
"Trilinear interpolation blurs data, "
"but can change which voxels survive a threshold"
),
)
# World space checkbox
world_checkbox = widgets.Checkbox(value=False, description="World Space")
# Outline width slider
outline_slider = widgets.IntSlider(
min=0, max=4, value=1, description="Outline", continuous_update=True, readout=False
)
# Alpha mode dropdown
alpha_dropdown = widgets.Dropdown(
options=[
("Restrict colorbar to range", 0),
("Colorbar from 0, transparent subthreshold", 1),
("Colorbar from 0, translucent subthreshold", 2),
],
value=0,
description="Alpha Mode:",
)
# threshold sliders
pos_stat_range = widgets.IntRangeSlider(
value=[5, 7],
min=1,
max=10,
step=1,
description='+Threshold:',
continuous_update=True,
readout=False,
)
neg_stat_range = widgets.IntRangeSlider(
value=[5, 7],
min=1,
max=10,
step=1,
description='-Threshold:',
continuous_update=True,
readout=False,
)
# Location display
location_output = widgets.HTML(value=" ")
## Setup Event Handlers
def on_dpi_change(change):
"""Handle DPI checkbox changes."""
nv.set_high_resolution_capable(change["new"])
def on_negative_change(change):
"""Handle negative colormap checkbox changes."""
neg_stat_range.disabled = not change["new"]
if change["new"]:
nv.set_colormap_negative(nv.volumes[1].id, "winter")
else:
nv.set_colormap_negative(nv.volumes[1].id, "")
def on_smooth_change(change):
"""Handle smooth interpolation checkbox changes."""
nv.set_interpolation(not change["new"])
def on_world_change(change):
"""Handle world space checkbox changes."""
nv.set_slice_mm(change["new"])
def on_outline_change(change):
"""Handle outline width slider changes."""
nv.overlay_outline_width = 0.25 * change["new"]
def on_alpha_mode_change(change):
"""Handle alpha mode dropdown changes."""
nv.volumes[1].colormap_type = change["new"]
def on_pos_stat_change(change):
"""Set threshold for statistical overlay."""
low, high = change["new"]
nv.volumes[1].cal_min = low
nv.volumes[1].cal_max = high
def on_neg_stat_change(change):
"""Set threshold for statistical overlay."""
low, high = change["new"]
nv.volumes[1].cal_min_neg = -low
nv.volumes[1].cal_max_neg = -high
@nv.on_location_change
def handle_location_change(data):
"""Update location display when crosshair moves."""
location_output.value = f" {data['string']}"
# Attach event handlers
dpi_checkbox.observe(on_dpi_change, names="value")
negative_checkbox.observe(on_negative_change, names="value")
smooth_checkbox.observe(on_smooth_change, names="value")
world_checkbox.observe(on_world_change, names="value")
outline_slider.observe(on_outline_change, names="value")
alpha_dropdown.observe(on_alpha_mode_change, names="value")
# Initialize values
on_outline_change({"new": outline_slider.value})
on_alpha_mode_change({"new": alpha_dropdown.value})
pos_stat_range.observe(on_pos_stat_change, names="value")
neg_stat_range.observe(on_neg_stat_change, names="value")
## Display All
# Organize controls
controls_row1 = widgets.HBox(
[dpi_checkbox, negative_checkbox, smooth_checkbox, world_checkbox]
)
controls_row2 = widgets.HBox([neg_stat_range, pos_stat_range])
controls_row3 = widgets.HBox([outline_slider, alpha_dropdown])
# Create main layout
controls = widgets.VBox(
[controls_row1, controls_row2, controls_row3, location_output]
)
# Display everything
display(widgets.VBox([controls, nv]))
In [ ]: