Voxel Mosaics 2¶
Choose axial (A), coronal (C) or sagittal (S) slices. Modify with cross slices (X) and renderings (R).
This notebook mirrors the NiiVue web page recipe.
In [1]:
import ipywidgets as widgets
from IPython.display import display
from ipyniivue import NiiVue
nv = NiiVue(height=600, back_color=(1, 1, 1, 1), is_colorbar=True, drag_mode="CONTRAST")
nv.load_volumes(
[
{
"path": "../images/fslmean.nii.gz",
"colormap": "gray",
"colorbar_visible": False,
},
{
"path": "../images/fslt.nii.gz",
"colormap": "warm",
"colormap_negative": "winter",
"cal_min": 2,
"cal_max": 6,
},
]
)
initial_mosaic = (
"A 0 L+ 50 L- 60 C -10 0 S 40; A X 0 S X 0 C X 0 R A X 0 R S X 0 R C X 0"
)
nv.set_slice_mosaic_string(initial_mosaic)
## Create interactive controls
# mosaic string input
mosaic_text = widgets.Text(
value=initial_mosaic,
placeholder="Enter mosaic string",
description="Mosaic string:",
style={"description_width": "initial"},
layout=widgets.Layout(width="70%"),
)
# help button
help_button = widgets.Button(
description="Help",
button_style="info",
tooltip="Click for information about mosaic strings",
layout=widgets.Layout(width="80px"),
)
# radiological convention checkbox
radio_check = widgets.Checkbox(value=False, description="Radiological", indent=False)
# world space checkbox
mm_check = widgets.Checkbox(value=False, description="World space", indent=False)
# ruler checkbox
ruler_check = widgets.Checkbox(value=False, description="Ruler", indent=False)
# nose left checkbox
sag_check = widgets.Checkbox(value=False, description="Nose left", indent=False)
# colorbar checkbox
colorbar_check = widgets.Checkbox(value=True, description="Colorbar", indent=False)
# negative colors checkbox
negative_check = widgets.Checkbox(
value=True, description="Negative colors", indent=False
)
# orient cube checkbox
cube_check = widgets.Checkbox(value=False, description="Cube", indent=False)
# high DPI checkbox
dpi_check = widgets.Checkbox(value=True, description="HighDPI", indent=False)
# gamma slider
gamma_slider = widgets.FloatSlider(
value=1.0, min=0.1, max=2.0, step=0.1, description="Gamma:", continuous_update=True, readout=False
)
# drag mode dropdown
drag_mode = widgets.Dropdown(
options=[
("none", 0),
("contrast", 1),
("measurement", 2),
],
value=1,
description="Drag mode:",
style={"description_width": "initial"},
)
back_color_picker = widgets.ColorPicker(
concise=False,
description='Back color',
value='white',
disabled=False,
continuous_update= True
)
N = 11 # replace with your actual upper index
options = list(range(N + 1))
stat_range = widgets.SelectionRangeSlider(
options=options,
index=(2, 6),
description="Threshold:",
disabled=False,
readout=False
)
# output for messages
output = widgets.Output()
## Setup Event Handlers
def on_mosaic_change(change):
"""Handle mosaic string changes."""
nv.set_slice_mosaic_string(change["new"])
def on_help_click(b):
"""Display help information."""
with output:
output.clear_output()
print("Mosaic String Help:")
print("=" * 50)
print("Choose axial (A), coronal (C) or sagittal (S) slices.")
print("Modify with cross slices (X) and renderings (R).")
print("\nExample patterns:")
print(" A 0 20 40 - Axial slices at positions 0, 20, 40")
print(" C -10 0 10 - Coronal slices at positions -10, 0, 10")
print(" S 0 - Sagittal slice at position 0")
print(" A X 0 - Axial slice with cross at position 0")
print(" R - 3D rendering")
print(" L+ 50 - Left hemisphere at position 50")
print(" L- 60 - Right hemisphere at position 60")
def on_radio_change(change):
"""Handle radiological convention toggle."""
nv.set_radiological_convention(change["new"])
def on_mm_change(change):
"""Handle world space toggle."""
nv.set_slice_mm(change["new"])
def on_ruler_change(change):
"""Handle ruler toggle."""
nv.opts.is_ruler = change["new"]
def on_sag_change(change):
"""Handle sagittal nose left toggle."""
nv.opts.sagittal_nose_left = change["new"]
def on_colorbar_change(change):
"""Handle colorbar toggle."""
nv.opts.is_colorbar = change["new"]
def on_negative_change(change):
"""Handle negative colormap toggle."""
if change["new"]:
nv.set_colormap_negative(nv.volumes[1].id, "winter")
else:
nv.set_colormap_negative(nv.volumes[1].id, "")
def on_cube_change(change):
"""Handle orient cube toggle."""
nv.opts.is_orient_cube = change["new"]
def on_dpi_change(change):
"""Handle high DPI toggle."""
nv.set_high_resolution_capable(change["new"])
def on_gamma_change(change):
"""Handle gamma adjustment."""
nv.set_gamma(change["new"])
def on_drag_mode_change(change):
"""Handle drag mode change."""
nv.opts.drag_mode = change["new"]
def on_color_change(change):
"""Convert hex color (e.g. '#1f1f23') to normalized RGBA tuple (0..1)."""
hexstr = change["new"].lstrip("#") # remove leading '#'
if len(hexstr) != 6:
print("Invalid hex color:", change["new"])
return
r = int(hexstr[0:2], 16) / 255.0
g = int(hexstr[2:4], 16) / 255.0
b = int(hexstr[4:6], 16) / 255.0
a = 1.0
nv.opts.back_color = (r, g, b, a)
#nv.draw_scene()
def on_stat_change(change):
"""Set threshold for statistical overlay."""
low, high = change["new"] # extract the two values
nv.volumes[1].cal_min = low
nv.volumes[1].cal_max = high
# attach event handlers
mosaic_text.observe(on_mosaic_change, names="value")
help_button.on_click(on_help_click)
radio_check.observe(on_radio_change, names="value")
mm_check.observe(on_mm_change, names="value")
ruler_check.observe(on_ruler_change, names="value")
sag_check.observe(on_sag_change, names="value")
colorbar_check.observe(on_colorbar_change, names="value")
negative_check.observe(on_negative_change, names="value")
cube_check.observe(on_cube_change, names="value")
dpi_check.observe(on_dpi_change, names="value")
gamma_slider.observe(on_gamma_change, names="value")
drag_mode.observe(on_drag_mode_change, names="value")
back_color_picker.observe(on_color_change, names="value")
stat_range.observe(on_stat_change, names="value")
## Display controls and image
# organize controls
mosaic_row = widgets.HBox([mosaic_text, help_button])
checkbox_row1 = widgets.HBox([radio_check, mm_check, ruler_check, sag_check])
checkbox_row2 = widgets.HBox([colorbar_check, negative_check, cube_check, dpi_check])
slider_row = widgets.HBox([gamma_slider, drag_mode, back_color_picker, stat_range])
# create main layout
controls = widgets.VBox([mosaic_row, checkbox_row1, checkbox_row2, slider_row, output])
# display everything
display(widgets.VBox([controls, nv]))
In [ ]: