{ "cells": [ { "cell_type": "markdown", "id": "3de09aec", "metadata": {}, "source": [ "# WhipperSnapPy Tutorial\n", "\n", "This notebook demonstrates static and interactive 3D brain surface visualization\n", "using WhipperSnapPy. It covers single-view snapshots (`snap1`), four-view overview\n", "images (`snap4`), and interactive WebGL rendering (`plot3d`).\n", "\n", "**Tutorial data** from the Rhineland Study (Koch et al.),\n", "[Zenodo: https://doi.org/10.5281/zenodo.11186582](https://doi.org/10.5281/zenodo.11186582), CC BY 4.0." ] }, { "cell_type": "markdown", "id": "c7520b73", "metadata": {}, "source": [ "## Subject Directory\n", "\n", "Set `sdir` to your own FreeSurfer subject directory.\n", "If you leave it empty, the sample subject **sub-rs** (one anonymized subject\n", "from the Rhineland Study) is downloaded automatically (~20 MB, cached locally\n", "after the first run)." ] }, { "cell_type": "code", "execution_count": null, "id": "75a22f9c", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "from whippersnappy import fetch_sample_subject\n", "\n", "# Set sdir to your FreeSurfer subject directory.\n", "# Leave empty (\"\") to automatically use the sample subject (sub-rs,\n", "# one anonymized subject from the Rhineland Study). It is used directly\n", "# from the repository when available, otherwise downloaded (~20 MB) and\n", "# cached locally after the first run.\n", "sdir = \"\"\n", "# sdir = \"/path/to/your/subject\"\n", "\n", "if not sdir:\n", " sdir = fetch_sample_subject()[\"sdir\"]\n", "\n", "print(\"Subject directory:\", sdir)" ] }, { "cell_type": "markdown", "id": "06c859f0", "metadata": {}, "source": [ "### Derive file paths from `sdir`\n", "\n", "All paths are constructed from `sdir`, so switching between subjects only\n", "requires changing the single variable above." ] }, { "cell_type": "code", "execution_count": null, "id": "9ca0b13a", "metadata": {}, "outputs": [], "source": [ "# Surfaces\n", "lh_white = os.path.join(sdir, \"surf\", \"lh.white\")\n", "rh_white = os.path.join(sdir, \"surf\", \"rh.white\")\n", "\n", "# Curvature\n", "lh_curv = os.path.join(sdir, \"surf\", \"lh.curv\")\n", "rh_curv = os.path.join(sdir, \"surf\", \"rh.curv\")\n", "\n", "# Thickness overlay\n", "lh_thickness = os.path.join(sdir, \"surf\", \"lh.thickness\")\n", "rh_thickness = os.path.join(sdir, \"surf\", \"rh.thickness\")\n", "\n", "# Cortex label (mask for overlay)\n", "lh_label = os.path.join(sdir, \"label\", \"lh.cortex.label\")\n", "rh_label = os.path.join(sdir, \"label\", \"rh.cortex.label\")\n", "\n", "# Parcellation annotation (DKTatlas)\n", "lh_annot = os.path.join(sdir, \"label\", \"lh.aparc.DKTatlas.mapped.annot\")\n", "rh_annot = os.path.join(sdir, \"label\", \"rh.aparc.DKTatlas.mapped.annot\")" ] }, { "cell_type": "markdown", "id": "7fdab413", "metadata": {}, "source": [ "## snap1 \u2014 Basic Single View\n", "\n", "`snap1` renders a single static view of a surface mesh into a PIL Image.\n", "Here we render the left hemisphere with curvature texturing only (no overlay),\n", "which gives the classic sulcal depth shading." ] }, { "cell_type": "code", "execution_count": null, "id": "68e1a46a", "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", "\n", "from whippersnappy import snap1\n", "\n", "img = snap1(lh_white, bg_map=lh_curv)\n", "display(img)" ] }, { "cell_type": "markdown", "id": "fbb6b125", "metadata": {}, "source": [ "## snap1 \u2014 With Thickness Overlay\n", "\n", "By passing `overlay` and `roi`, the surface is colored by cortical\n", "thickness values, masked to the cortex label. The `view` parameter selects\n", "the lateral view of the left hemisphere." ] }, { "cell_type": "code", "execution_count": null, "id": "82ee25b8", "metadata": {}, "outputs": [], "source": [ "from whippersnappy.utils.types import ViewType\n", "\n", "img = snap1(\n", " lh_white,\n", " overlay=lh_thickness,\n", " bg_map=lh_curv,\n", " roi=lh_label,\n", " view=ViewType.LEFT,\n", ")\n", "display(img)" ] }, { "cell_type": "markdown", "id": "043273a1", "metadata": {}, "source": [ "## snap1 \u2014 With Parcellation Annotation\n", "\n", "`annot` accepts a FreeSurfer `.annot` file and colors each vertex by\n", "its parcellation label. This example uses the DKTatlas parcellation." ] }, { "cell_type": "code", "execution_count": null, "id": "3fd2b433", "metadata": {}, "outputs": [], "source": [ "img = snap1(\n", " lh_white,\n", " annot=lh_annot,\n", " bg_map=lh_curv,\n", ")\n", "display(img)" ] }, { "cell_type": "markdown", "id": "a3b6350b", "metadata": {}, "source": [ "## snap4 \u2014 Four-View Overview\n", "\n", "`snap4` renders lateral and medial views of both hemispheres and stitches\n", "them into a single composed image. Here we color both hemispheres by\n", "cortical thickness, masked to the cortex label." ] }, { "cell_type": "code", "execution_count": null, "id": "e0d0405b", "metadata": {}, "outputs": [], "source": [ "from whippersnappy import snap4\n", "\n", "img = snap4(\n", " sdir=sdir,\n", " lh_overlay=lh_thickness,\n", " rh_overlay=rh_thickness,\n", " colorbar=True,\n", " caption=\"Cortical Thickness (mm)\",\n", ")\n", "display(img)" ] }, { "cell_type": "markdown", "id": "f7006557", "metadata": {}, "source": [ "## plot3d \u2014 Interactive 3D Viewer\n", "\n", "`plot3d` creates an interactive Three.js/WebGL viewer that works in all\n", "Jupyter environments. You can rotate, zoom, and pan with the mouse.\n", "Requires `pip install 'whippersnappy[notebook]'`." ] }, { "cell_type": "code", "execution_count": null, "id": "ba49903c", "metadata": {}, "outputs": [], "source": [ "from whippersnappy import plot3d\n", "\n", "viewer = plot3d(\n", " mesh=lh_white,\n", " bg_map=lh_curv,\n", " overlay=lh_thickness,\n", ")\n", "display(viewer)" ] }, { "cell_type": "markdown", "id": "f2201a62", "metadata": {}, "source": [ "## snap_rotate \u2014 Rotating 360\u00b0 Animation\n", "\n", "`snap_rotate` renders a full 360\u00b0 rotation of the surface. We output an\n", "animated GIF so it displays inline in all Jupyter environments including\n", "PyCharm. Use `.mp4` as `outpath` instead for a smaller file when playing\n", "outside the notebook.\n", "This cell takes the longest to run \u2014 execute it last." ] }, { "cell_type": "code", "execution_count": null, "id": "15ea8375", "metadata": {}, "outputs": [], "source": [ "import os\n", "import tempfile\n", "\n", "from IPython.display import Image\n", "\n", "from whippersnappy import snap_rotate\n", "\n", "_gif_fd, outpath_gif = tempfile.mkstemp(suffix=\".gif\")\n", "os.close(_gif_fd)\n", "\n", "snap_rotate(\n", " mesh=lh_white,\n", " outpath=outpath_gif,\n", " overlay=lh_thickness,\n", " bg_map=lh_curv,\n", " roi=lh_label,\n", " n_frames=72,\n", " fps=24,\n", " width=800,\n", " height=600,\n", ")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "9f0b2db4", "metadata": {}, "outputs": [], "source": [ "display(Image(filename=outpath_gif))\n", "os.unlink(outpath_gif)\n" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 5 }