Making a surface plot of a 3D statistical map#

In this example, we will project a 3D statistical map onto a cortical mesh using vol_to_surf, display a surface plot of the projected map using plot_surf_stat_map with different plotting engines, and add contours of regions of interest using plot_surf_contours.

Get a statistical map#

Get a cortical mesh#

Sample the 3D data around each node of the mesh#

from nilearn import surface

texture = surface.vol_to_surf(stat_img, fsaverage.pial_right)

Plot the result#

You can visualize the texture on the surface using the function plot_surf_stat_map which uses matplotlib as the default plotting engine:

from nilearn import plotting

fig = plotting.plot_surf_stat_map(
    fsaverage.infl_right, texture, hemi='right',
    title='Surface right hemisphere', colorbar=True,
    threshold=1., bg_map=fsaverage.sulc_right
)
fig.show()
Surface right hemisphere

Interactive plotting with Plotly#

If you have a recent version of Nilearn (>=0.8.2), and if you have plotly installed, you can easily configure plot_surf_stat_map to use plotly instead of matplotlib:

engine = 'plotly'
# If plotly is not installed, use matplotlib
try:
    import plotly.graph_objects as go  # noqa: F401
except ImportError:
    engine = 'matplotlib'

print(f"Using plotting engine {engine}.")

fig = plotting.plot_surf_stat_map(
    fsaverage.infl_right, texture, hemi='right',
    title='Surface right hemisphere', colorbar=True,
    threshold=1., bg_map=fsaverage.sulc_right,
    engine=engine  # Specify the plotting engine here
)
fig.show()  # Display the figure as with matplotlib figures
Using plotting engine plotly.
/home/alexis/miniconda3/envs/nilearn/lib/python3.10/site-packages/nilearn/plotting/js_plotting_utils.py:81: UserWarning: vmin cannot be chosen when cmap is symmetric
  warnings.warn('vmin cannot be chosen when cmap is symmetric')


When using matplolib as the plotting engine, a standard matplotlib.figure.Figure is returned. With plotly as the plotting engine, a custom PlotlySurfaceFigure is returned which provides a similar API to the Figure. For example, you can save a static version of the figure to file (this option requires to have kaleido installed):

# Save the figure as we would do with a matplotlib figure
# Uncomment the following line to save the previous figure to file
# fig.savefig("right_hemisphere.png")

Plot 3D image for comparison#

plotting.plot_glass_brain(stat_img, display_mode='r', plot_abs=False,
                          title='Glass brain', threshold=2.)

plotting.plot_stat_map(stat_img, display_mode='x', threshold=1.,
                       cut_coords=range(0, 51, 10), title='Slices')
  • plot 3d map to surface projection
  • plot 3d map to surface projection
<nilearn.plotting.displays._slicers.XSlicer object at 0x7ff8f2c5eaa0>

Use an atlas and choose regions to outline#

import numpy as np

destrieux_atlas = datasets.fetch_atlas_surf_destrieux()
parcellation = destrieux_atlas['map_right']

# these are the regions we want to outline
regions_dict = {b'G_postcentral': 'Postcentral gyrus',
                b'G_precentral': 'Precentral gyrus'}

# get indices in atlas for these labels
regions_indices = [np.where(np.array(destrieux_atlas['labels']) == region)[0][0]
                   for region in regions_dict]

labels = list(regions_dict.values())

Display outlines of the regions of interest on top of a statistical map#

figure = plotting.plot_surf_stat_map(fsaverage.infl_right, texture, hemi='right',
                                     title='Surface right hemisphere',
                                     colorbar=True, threshold=1.,
                                     bg_map=fsaverage.sulc_right)

plotting.plot_surf_contours(fsaverage.infl_right, parcellation, labels=labels,
                            levels=regions_indices, figure=figure, legend=True,
                            colors=['g', 'k'])
plotting.show()
Surface right hemisphere

Plot with higher-resolution mesh#

fetch_surf_fsaverage takes a mesh argument which specifies whether to fetch the low-resolution fsaverage5 mesh, or the high-resolution fsaverage mesh. Using mesh="fsaverage" will result in more memory usage and computation time, but finer visualizations.

big_fsaverage = datasets.fetch_surf_fsaverage('fsaverage')
big_texture = surface.vol_to_surf(stat_img, big_fsaverage.pial_right)

plotting.plot_surf_stat_map(big_fsaverage.infl_right,
                            big_texture, hemi='right', colorbar=True,
                            title='Surface right hemisphere: fine mesh',
                            threshold=1., bg_map=big_fsaverage.sulc_right)
Surface right hemisphere: fine mesh
<Figure size 470x400 with 2 Axes>

Plot multiple views of the 3D volume on a surface#

plot_img_on_surf takes a statistical map and projects it onto a surface. It supports multiple choices of orientations, and can plot either one or both hemispheres. If no surf_mesh is given, plot_img_on_surf projects the images onto FreeSurfer's fsaverage5.

plotting.plot_img_on_surf(stat_img,
                          views=['lateral', 'medial'],
                          hemispheres=['left', 'right'],
                          colorbar=True)
plotting.show()
plot 3d map to surface projection

3D visualization in a web browser#

An alternative to nilearn.plotting.plot_surf_stat_map is to use nilearn.plotting.view_surf or nilearn.plotting.view_img_on_surf that give more interactive visualizations in a web browser. See 3D Plots of statistical maps or atlases on the cortical surface for more details.

view = plotting.view_surf(fsaverage.infl_right, texture, threshold='90%',
                          bg_map=fsaverage.sulc_right)

# In a Jupyter notebook, if ``view`` is the output of a cell, it will
# be displayed below the cell
view


# uncomment this to open the plot in a web browser:
# view.open_in_browser()

We don’t need to do the projection ourselves, we can use view_img_on_surf:

view = plotting.view_img_on_surf(stat_img, threshold='90%')
# view.open_in_browser()

view


Impact of plot parameters on visualization#

You can specify arguments to be passed on to the function nilearn.surface.vol_to_surf using vol_to_surf_kwargs. This allows fine-grained control of how the input 3D image is resampled and interpolated - for example if you are viewing a volumetric atlas, you would want to avoid averaging the labels between neighboring regions. Using nearest-neighbor interpolation with zero radius will achieve this.

destrieux = datasets.fetch_atlas_destrieux_2009(legacy_format=False)

view = plotting.view_img_on_surf(
    destrieux.maps,
    surf_mesh="fsaverage",
    vol_to_surf_kwargs={"n_samples": 1, "radius": 0.0, "interpolation": "nearest"},
    symmetric_cmap=False,
)

# view.open_in_browser()
view


Total running time of the script: ( 0 minutes 26.850 seconds)

Estimated memory usage: 547 MB

Gallery generated by Sphinx-Gallery