WhipperSnapPy

WhipperSnapPy is a Python/OpenGL tool to render triangular surface meshes with color overlays or parcellations and generate screenshots — from the command line, in Jupyter notebooks, or via a desktop GUI.

It works with FreeSurfer and FastSurfer brain surfaces as well as any triangle mesh in OFF, legacy ASCII VTK PolyData, ASCII PLY, or GIfTI (.gii, .surf.gii) format, or passed directly as a NumPy (vertices, faces) tuple.

Installation

pip install whippersnappy

For rotation video support (MP4/WebM):

pip install 'whippersnappy[video]'

For the interactive desktop GUI:

pip install 'whippersnappy[gui]'

For interactive 3D in Jupyter notebooks:

pip install 'whippersnappy[notebook]'

Off-screen (headless) rendering is supported natively via EGL on Linux — no xvfb required. See the Docker guide for headless usage.

Command-Line Usage

After installation the following commands are available:

Four-view snapshot (whippersnap4)

Renders lateral and medial views of both hemispheres into a single composed image:

whippersnap4 -lh $LH_OVERLAY \
             -rh $RH_OVERLAY \
             -sd $SUBJECT_DIR \
             --fmax 4 --fthresh 2 \
             --caption "Cortical Thickness" \
             -o snap4.png

Single-view snapshot (whippersnap1)

Renders one view of any triangular surface mesh:

whippersnap1 --mesh $SUBJECT_DIR/surf/lh.white \
             --overlay $LH_OVERLAY \
             --bg-map  $SUBJECT_DIR/surf/lh.curv \
             --roi     $SUBJECT_DIR/label/lh.cortex.label \
             --view left \
             -o snap1.png

# Also works with OFF / VTK / PLY
whippersnap1 --mesh mesh.off --overlay values.mgh -o snap1.png
whippersnap1 --mesh surface.surf.gii --overlay overlay.func.gii -o snap1.png

Rotation video (whippersnap1 --rotate)

Renders a 360° animation of any triangular surface mesh:

whippersnap1 --mesh $SUBJECT_DIR/surf/lh.white \
             --overlay $LH_OVERLAY \
             --rotate \
             -o rotation.mp4

Desktop GUI (whippersnap)

Launches an interactive Qt window with live threshold controls.

General mode — any triangular mesh:

pip install 'whippersnappy[gui]'
whippersnap --mesh mesh.off --overlay values.mgh
whippersnap --mesh lh.white --overlay lh.thickness --bg-map lh.curv

FreeSurfer shortcut — derive all paths from a subject directory:

whippersnap -sd $SUBJECT_DIR --hemi lh -lh $LH_OVERLAY
whippersnap -sd $SUBJECT_DIR --hemi rh --annot rh.aparc.annot

For all options run whippersnap4 --help, whippersnap1 --help, or whippersnap --help.

Python API

from whippersnappy import snap1, snap4, snap_rotate, plot3d

Function

Description

snap1

Single-view snapshot of any triangular mesh → PIL Image

snap4

Four-view composed image (FreeSurfer subject, lateral/medial both hemispheres)

snap_rotate

360° rotation video of any triangular surface mesh (MP4, WebM, or GIF)

plot3d

Interactive 3D WebGL viewer for Jupyter notebooks

Supported mesh inputs for snap1, snap_rotate, and plot3d: FreeSurfer binary surfaces (e.g. lh.white), OFF (.off), legacy ASCII VTK PolyData (.vtk), ASCII PLY (.ply), GIfTI surface (.gii, .surf.gii), or a (vertices, faces) NumPy array tuple.

Supported overlay/label inputs: FreeSurfer morph (.curv, .thickness), MGH/MGZ, ASCII (.txt, .csv), NumPy (.npy, .npz), GIfTI functional/label (.func.gii, .label.gii, .gii).

Example

from whippersnappy import snap1, snap4

# FreeSurfer surface with overlay
img = snap1('lh.white',
            overlay='lh.thickness',
            bg_map='lh.curv',
            roi='lh.cortex.label')
img.save('snap1.png')

# Four-view overview (FreeSurfer subject directory)
img = snap4(sdir='/path/to/subject',
            lh_overlay='/path/to/lh.thickness',
            rh_overlay='/path/to/rh.thickness',
            colorbar=True, caption='Cortical Thickness (mm)')
img.save('snap4.png')

# OFF / VTK / PLY / GIfTI mesh
img = snap1('mesh.off', overlay='values.mgh')
img = snap1('surface.surf.gii', overlay='overlay.func.gii')

# Array inputs (e.g. from LaPy or trimesh)
import numpy as np
v = np.random.randn(1000, 3).astype(np.float32)
f = np.array([[0, 1, 2]], dtype=np.uint32)
overlay = np.random.randn(1000).astype(np.float32)
img = snap1((v, f), overlay=overlay)

See tutorials/whippersnappy_tutorial.ipynb for complete notebook examples.

Docker

The Docker image provides a fully headless EGL rendering environment — no display server or xvfb required. See DOCKER.md for details.

API Documentation

https://deep-mi.org/WhipperSnapPy