WhipperSnapPy Tutorial

This notebook demonstrates static and interactive 3D brain surface visualization using WhipperSnapPy. It covers single-view snapshots (snap1), four-view overview images (snap4), and interactive WebGL rendering (plot3d).

Tutorial data from the Rhineland Study (Koch et al.), Zenodo: https://doi.org/10.5281/zenodo.11186582, CC BY 4.0.

Subject Directory

Set sdir to your own FreeSurfer subject directory. If you leave it empty, the sample subject sub-rs (one anonymized subject from the Rhineland Study) is downloaded automatically (~20 MB, cached locally after the first run).

[1]:
import os

from whippersnappy import fetch_sample_subject

# Set sdir to your FreeSurfer subject directory.
# Leave empty ("") to automatically use the sample subject (sub-rs,
# one anonymized subject from the Rhineland Study). It is used directly
# from the repository when available, otherwise downloaded (~20 MB) and
# cached locally after the first run.
sdir = ""
# sdir = "/path/to/your/subject"

if not sdir:
    sdir = fetch_sample_subject()["sdir"]

print("Subject directory:", sdir)
Subject directory: /home/runner/.cache/whippersnappy/sub-rs

Derive file paths from sdir

All paths are constructed from sdir, so switching between subjects only requires changing the single variable above.

[2]:
# Surfaces
lh_white     = os.path.join(sdir, "surf", "lh.white")
rh_white     = os.path.join(sdir, "surf", "rh.white")

# Curvature
lh_curv      = os.path.join(sdir, "surf", "lh.curv")
rh_curv      = os.path.join(sdir, "surf", "rh.curv")

# Thickness overlay
lh_thickness = os.path.join(sdir, "surf", "lh.thickness")
rh_thickness = os.path.join(sdir, "surf", "rh.thickness")

# Cortex label (mask for overlay)
lh_label     = os.path.join(sdir, "label", "lh.cortex.label")
rh_label     = os.path.join(sdir, "label", "rh.cortex.label")

# Parcellation annotation (DKTatlas)
lh_annot     = os.path.join(sdir, "label", "lh.aparc.DKTatlas.mapped.annot")
rh_annot     = os.path.join(sdir, "label", "rh.aparc.DKTatlas.mapped.annot")

snap1 — Basic Single View

snap1 renders a single static view of a surface mesh into a PIL Image. Here we render the left hemisphere with curvature texturing only (no overlay), which gives the classic sulcal depth shading.

[3]:
from IPython.display import display

from whippersnappy import snap1

img = snap1(lh_white, bg_map=lh_curv)
display(img)
../_images/tutorials_whippersnappy_tutorial_6_0.png

snap1 — With Thickness Overlay

By passing overlay and roi, the surface is colored by cortical thickness values, masked to the cortex label. The view parameter selects the lateral view of the left hemisphere.

[4]:
from whippersnappy.utils.types import ViewType

img = snap1(
    lh_white,
    overlay=lh_thickness,
    bg_map=lh_curv,
    roi=lh_label,
    view=ViewType.LEFT,
)
display(img)
../_images/tutorials_whippersnappy_tutorial_8_0.png

snap1 — With Parcellation Annotation

annot accepts a FreeSurfer .annot file and colors each vertex by its parcellation label. This example uses the DKTatlas parcellation.

[5]:
img = snap1(
    lh_white,
    annot=lh_annot,
    bg_map=lh_curv,
)
display(img)
../_images/tutorials_whippersnappy_tutorial_10_0.png

snap4 — Four-View Overview

snap4 renders lateral and medial views of both hemispheres and stitches them into a single composed image. Here we color both hemispheres by cortical thickness, masked to the cortex label.

[6]:
from whippersnappy import snap4

img = snap4(
    sdir=sdir,
    lh_overlay=lh_thickness,
    rh_overlay=rh_thickness,
    colorbar=True,
    caption="Cortical Thickness (mm)",
)
display(img)
../_images/tutorials_whippersnappy_tutorial_12_0.png

plot3d — Interactive 3D Viewer

plot3d creates an interactive Three.js/WebGL viewer that works in all Jupyter environments. You can rotate, zoom, and pan with the mouse. Requires pip install 'whippersnappy[notebook]'.

[7]:
from whippersnappy import plot3d

viewer = plot3d(
    mesh=lh_white,
    bg_map=lh_curv,
    overlay=lh_thickness,
)
display(viewer)

snap_rotate — Rotating 360° Animation

snap_rotate renders a full 360° rotation of the surface. We output an animated GIF so it displays inline in all Jupyter environments including PyCharm. Use .mp4 as outpath instead for a smaller file when playing outside the notebook. This cell takes the longest to run — execute it last.

[8]:
import os
import tempfile

from IPython.display import Image

from whippersnappy import snap_rotate

_gif_fd, outpath_gif = tempfile.mkstemp(suffix=".gif")
os.close(_gif_fd)

snap_rotate(
    mesh=lh_white,
    outpath=outpath_gif,
    overlay=lh_thickness,
    bg_map=lh_curv,
    roi=lh_label,
    n_frames=72,
    fps=24,
    width=800,
    height=600,
)

[8]:
'/tmp/tmp5j860e5z.gif'
[9]:
display(Image(filename=outpath_gif))
os.unlink(outpath_gif)

../_images/tutorials_whippersnappy_tutorial_17_0.gif