Skip to content

Use your own data

Use your own surface maps, contact coordinates, or label images with the recipes below. Files combined in one scene must share a coordinate space; anatplot does not register or resample them. Datasets lists compatible public surfaces and atlases.

Draw a cortical map

For a cortical map, supply one value per vertex of each display mesh. Match the arrays and meshes by template, density, and exact vertex order within each hemisphere; equal lengths do not establish correspondence. The recipes below read .npy arrays and GIFTI meshes. anatplot does not apply embedded GIFTI coordinate transforms, so check the source geometry before using them.

import numpy as np

from anatplot import SurfacePlot

left_values = np.load("left-values.npy")
right_values = np.load("right-values.npy")

with SurfacePlot(
    left="left.inflated.surf.gii",
    right="right.inflated.surf.gii",
) as plot:
    plot.add_layer({"left": left_values, "right": right_values}, cbar=True)
    fig = plot.to_figure()

fig.savefig("my-map.png", dpi=300)

For contacts on an inflated map, projection_surface locates contacts on anatomical pial or midthickness meshes before displaying them on the inflated meshes. Each anatomical mesh must correspond vertex by vertex to its display mesh, with identical ordered triangles.

Verify that contacts and anatomical meshes share one RAS millimetre frame. Each xyz row is [R, A, S], with right, anterior, and superior positive; record the source transform and units. A shared label such as “MNI” does not establish alignment.

import numpy as np

from anatplot import SurfacePlot

left_values = np.load("left-values.npy")
right_values = np.load("right-values.npy")
xyz = np.load("contacts-ras-mm.npy")

with SurfacePlot(
    left="left.inflated.surf.gii",
    right="right.inflated.surf.gii",
    projection_surface=(
        "left.midthickness.surf.gii",
        "right.midthickness.surf.gii",
    ),
) as plot:
    plot.add_layer({"left": left_values, "right": right_values}, cbar=True)
    plot.add_contacts(xyz, color="royalblue", radius=2)
    fig = plot.to_figure()

fig.savefig("my-map-and-contacts.png", dpi=300)

Missing values (NaN or masked vertices) leave the surface underneath visible. Zero remains visible; mask it only if it means background in your data.

When comparing the same statistic across plots, pass the same color_range to each add_layer() call.

For discrete vertex labels, use plot.add_parcellation({"left": left_labels, "right": right_labels}) and mask a background label if needed. Vertex colors blend across triangles, so their edges are not exact parcel boundaries. The surface tutorial shows an atlas example.

Contacts on an inflated map

With the anatomical projection_surface pair shown above, SurfacePlot locates each contact on the nearest anatomical triangle and displays its marker on the corresponding inflated triangle. Without that pair, it projects onto the display meshes themselves, which does not recover anatomical distances when the display is inflated.

Projection discards depth and does not establish cortical membership. Contacts more than 6 mm from the localization surface are skipped with a warning by default.

For deep contacts, ROIPlot or VolumePlot retains their supplied positions when their anatomy shares the coordinate frame. On SurfacePlot, project=False also keeps supplied coordinates and skips the distance filter. It does not move contacts onto an inflated display surface, so use it with an undeformed surface when you need their original positions. The surface tutorial uses a matching pair supplied by the dataset fetcher.

Show contacts within a whole-brain surface

VolumePlot gives contacts a translucent whole-brain surface background. It displays surface meshes, not image voxels, and keeps contact positions as supplied. Use anatomical pial meshes and contacts whose source records confirm the same RAS millimetre frame:

import numpy as np

from anatplot import VolumePlot

xyz = np.load("contacts-ras-mm.npy")

with VolumePlot(left="left.pial.surf.gii", right="right.pial.surf.gii") as plot:
    plot.add_contacts(xyz, radius=2, color="royalblue")
    fig = plot.to_figure()

fig.savefig("my-contacts.png", dpi=300)

Use a template surface only when its registration to your contacts is confirmed; use subject-specific pial surfaces for contacts in subject space. If contacts fall outside the initial camera frame, call plot.plotter.reset_camera() before to_figure(). See the VolumePlot tutorial for a complete example.

Draw regions from a label image

ROIPlot accepts a 3D NIfTI image whose entire data array contains finite, nonnegative integers. Zero is background; choose positive IDs present in the image. The NIfTI header must declare spatial units of mm, meter, or micron—unknown units are rejected. Its affine and units place extracted regions in RAS millimetres. This image differs from the one-dimensional surface vertex labels used by add_parcellation().

import nibabel as nib
import numpy as np

from anatplot import ROIPlot

image = nib.load("my-labels.nii.gz")
ids = np.unique(np.asanyarray(image.dataobj))
print(ids[ids > 0])  # Choose positive IDs present in your image.

with ROIPlot(image) as plot:
    plot.add_roi([1, 2])  # Replace with IDs from the printed list.
    fig = plot.to_figure()

fig.savefig("my-regions.png", dpi=300)

Use the source atlas's label table to map IDs to anatomical names. Selected IDs in one add_roi() call form a union without internal borders. If the affine contains shear, use add_roi(..., smoothing=False) or resample the image beforehand. Inspect thin regions after extraction. The ROI tutorial shows how to find IDs by region name.

For figure size, 3D viewing, HTML, and camera choices, see Save and display. Full shapes and options are in the plotting API.