Voxel Performance Tradeoffs¶

NiiVue is designed to deliver smooth, interactive graphics on a wide range of devices, including low-power phones, tablets, and computers. Some visualization options, however, require additional resources and can reduce rendering performance—especially when displaying high resolution volume rendering. This notebook intentionally demonstrates these performance trade-offs, allowing you to explore them interactively through various controls. Features such as anti-aliasing, high-DPI rendering, and reflective volume modes are particularly demanding and can significantly affect speed on slower hardware.

This notebook mirrors the JavaScript web page.

In [1]:
import json
import ipywidgets as widgets
from ipyniivue import NiiVue
from ipyniivue.constants import ShowRender

## create user interface

status_bar = widgets.Label(value="")

aa_check = widgets.Checkbox(value=True, description="Anti Alias")
dpi_check = widgets.Checkbox(value=True, description="High DPI")
render_mode = widgets.Dropdown(
    options=[
        ("slices", "-1"),
        ("matte", "0"),
        ("low", "0.3"),
        ("medium", "0.6"),
        ("high", "1.0"),
    ],
    value="0.6",
    description="Render Mode",
)
atlas_slider = widgets.IntSlider(
    value=64, min=0, max=255, description="Atlas", readout=False
)
stat_slider = widgets.IntSlider(
    value=255, min=0, max=255, description="Statistics", readout=False
)

# create niivue instance

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

nv = None  # Global variable to hold the NiiVue instance


def create_nv(anti_alias):
    """Create new NiiVue instance."""
    global nv

    if nv:
        # Don't forget to dispose of the previous nv if it exists
        nv.close()

    nv = NiiVue(
        back_color=(1, 1, 1, 1),
        show_3d_crosshair=True,
        is_anti_alias=anti_alias,
    )

    @nv.on_location_change
    def handle_location_change(location):
        """Display location."""
        status_bar.value = location["string"]

    nv.opts.multiplanar_show_render = ShowRender.ALWAYS
    nv.opts.is_colorbar = True

    volume_list = [
        {"path": "../images/mni152.nii.gz"},
        {"path": "../images/aal.nii.gz"},
        {
            "path": "../images/stats.nv_demo_mskd.nii.gz",
            "colormap": "warm",
            "colormap_negative": "winter",
            "frame_4d": 1,
            "cal_min": 3.3641,
            "cal_max": 6,
        },
    ]
    nv.load_volumes(volume_list)

    # Hide colorbars for volumes 0 and 1
    nv.volumes[0].colorbar_visible = False
    nv.volumes[1].colorbar_visible = False
    nv.volumes[1].set_colormap_label(cmap)

    return nv


# Create initial nv
nv = create_nv(aa_check.value)

## Define event handlers

def on_dpi_check_change(change):
    """Set high resolution capable."""
    nv.set_high_resolution_capable(change["new"])


def on_render_mode_change(change):
    """Set volume render illumination."""
    nv.set_volume_render_illumination(float(change["new"]))


def on_atlas_slider_change(change):
    """Set 2nd volume opacity."""
    nv.volumes[1].opacity = change["new"] / 255.0


def on_stat_slider_change(change):
    """Set 3rd volume opacity."""
    nv.volumes[2].opacity = change["new"] / 255.0


def on_aa_check_change(change):
    """Recreate nv with new anti_alias setting and re-apply settings."""
    anti_alias = change["new"]
    # Create new nv
    new_nv = create_nv(anti_alias)

    # Update the UI container
    ui_container.children = (
        widgets.HBox([aa_check, dpi_check, render_mode]),
        widgets.HBox([atlas_slider, stat_slider]),
        new_nv,
        status_bar,
    )

    # Re-apply settings, except for antialias, because doing
    # so would result in an endless loop here
    on_dpi_check_change({"new": dpi_check.value})
    on_atlas_slider_change({"new": atlas_slider.value})
    on_stat_slider_change({"new": stat_slider.value})

## Set up observers

dpi_check.observe(on_dpi_check_change, names="value")
render_mode.observe(on_render_mode_change, names="value")
atlas_slider.observe(on_atlas_slider_change, names="value")
stat_slider.observe(on_stat_slider_change, names="value")
aa_check.observe(on_aa_check_change, names="value")

## Apply initial settings

# Except for antialias, because doing so would result in an endless loop here
# Also, not doing on_render_mode_change because canvas has not been attached yet
# so gl does not exist yet.
on_dpi_check_change({"new": dpi_check.value})
on_atlas_slider_change({"new": atlas_slider.value})
on_stat_slider_change({"new": stat_slider.value})

## Display all

ui_container = widgets.VBox(
    [
        widgets.HBox([aa_check, dpi_check, render_mode]),
        widgets.HBox([atlas_slider, stat_slider]),
        nv,
        status_bar,
    ]
)

display(ui_container)
In [ ]:
 
In [ ]: