Skip to main content

A package to plot and capture publication-ready surfaces with overlays, for example, FastSurfer/FreeSurfer brain data.

Project description

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 — GIF works without this):

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 on Linux uses EGL with Mesa's llvmpipe CPU software renderer — no GPU or display server required. The log reports:

EGL context active — CPU software rendering (llvmpipe (...), ...)

When a GPU is accessible (native install, Docker with --gpus all, or Singularity with --nv), EGL selects it automatically:

EGL context active — GPU rendering (...)

OSMesa (libosmesa6) is a last-resort CPU fallback used only when EGL itself cannot initialise (e.g. libegl1 not installed).

On Windows, GLFW creates an invisible window; a GPU driver is sufficient. On macOS, a real display connection is required (NSGL does not support headless rendering). See the Docker/Singularity guide for container usage.

Command-Line Usage

After installation the following commands are available:

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 / GIfTI and plain-text overlays
whippersnap1 --mesh mesh.off --overlay values.txt -o snap1.png
whippersnap1 --mesh surface.surf.gii --overlay overlay.func.gii -o snap1.png

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

Rotation video (whippersnap1 --rotate)

Renders a 360° animation of any triangular surface mesh. GIF output uses pure PIL (no extra install); MP4/WebM requires pip install 'whippersnappy[video]'.

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

whippersnap1 --mesh $SUBJECT_DIR/surf/lh.white \
             --rotate \
             -o rotation.gif

Desktop GUI (whippersnap)

Launches an interactive Qt window with live threshold controls and mouse-driven rotation, pan, and zoom. Requires pip install 'whippersnappy[gui]'.

General mode — any triangular mesh:

whippersnap --mesh mesh.off --overlay values.txt
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 whippersnap1 --help, whippersnap4 --help, or whippersnap --help.

Python API

from whippersnappy import snap1, snap4, snap_rotate, ViewType, get_view_matrix
from whippersnappy import plot3d  # requires whippersnappy[notebook]
Function / Class 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
ViewType Enum of camera presets used by snap1 and snap_rotate
get_view_matrix Return the 4×4 view matrix for a ViewType preset

ViewType values — pass to the view parameter of snap1 or the start_view parameter of snap_rotate:

Value Description
ViewType.LEFT Left lateral view (default)
ViewType.RIGHT Right lateral view
ViewType.FRONT Frontal / anterior view
ViewType.BACK Posterior view
ViewType.TOP Superior / dorsal view
ViewType.BOTTOM Inferior / ventral view

Both snap1 and snap_rotate also accept a raw 4×4 NumPy matrix for view / start_view. Use get_view_matrix to start from a preset and modify it:

import numpy as np
import pyrr
from whippersnappy import snap1, snap_rotate, ViewType, get_view_matrix

# Tilt the left-lateral preset slightly downward
mat = get_view_matrix(ViewType.LEFT).copy()
tilt = np.array(pyrr.Matrix44.from_x_rotation(np.radians(10)), dtype=np.float32)
mat = tilt @ mat

img = snap1('lh.white', overlay='lh.thickness', view=mat)
snap_rotate('lh.white', 'rotation.mp4', start_view=mat)

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 (.mgh, .mgz), plain text / CSV (.txt, .csv), NumPy (.npy, .npz), GIfTI functional/label (.func.gii, .label.gii).

Examples

from whippersnappy import snap1, snap4, ViewType

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

# Specific view
img = snap1('lh.white', overlay='lh.thickness', view=ViewType.FRONT)
img.save('snap1_front.png')

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

# OFF / VTK / PLY / GIfTI mesh with a plain-text overlay
img = snap1('mesh.off', overlay='values.txt')
img = snap1('surface.surf.gii', overlay='overlay.func.gii')

# Array inputs (e.g. from LaPy or trimesh)
import numpy as np
v = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]], dtype=np.float32)
f = np.array([[0,2,1],[0,1,3],[0,3,2],[1,2,3]], dtype=np.uint32)
overlay = np.array([0.1, 0.5, 0.9, 0.3], dtype=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 rendering environment using EGL — CPU software rendering by default, GPU rendering with --gpus all (NVIDIA). See DOCKER.md for details.

API Documentation

https://deep-mi.org/WhipperSnapPy

Links

Lab webpage: https://deep-mi.org

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

whippersnappy-2.1.0.tar.gz (179.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

whippersnappy-2.1.0-py3-none-any.whl (182.7 kB view details)

Uploaded Python 3

File details

Details for the file whippersnappy-2.1.0.tar.gz.

File metadata

  • Download URL: whippersnappy-2.1.0.tar.gz
  • Upload date:
  • Size: 179.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for whippersnappy-2.1.0.tar.gz
Algorithm Hash digest
SHA256 1217a016eeee6d30bf2ea40c95eb46bb34071212c73fcd3dfd7a0b3141181c1e
MD5 cc7510c9d1d829675432e5a33d9201f7
BLAKE2b-256 6b5c9b9b6a85595952ca4af1e7cc67b6d77e02c927cb8c1b8c04fb306033a39d

See more details on using hashes here.

File details

Details for the file whippersnappy-2.1.0-py3-none-any.whl.

File metadata

  • Download URL: whippersnappy-2.1.0-py3-none-any.whl
  • Upload date:
  • Size: 182.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for whippersnappy-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 94282b6011791e3e91ed30b0483897794a48896f360f55a59d483acda4a1872b
MD5 07b474e2dcf5015275bffd7339d15912
BLAKE2b-256 c211a81cd9316e2d4b92cdb162d9299b680f78edf494f695d9d1aed3d3cf770a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page