Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Surface plots #2477

Merged
merged 58 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
0201b22
Add plot_surf_montage
dangom Jan 20, 2019
78bec87
Fix float division by zero in Python 2 in plot_surf_montage
dangom Jan 21, 2019
ea0dd3f
Rename plot_surf_montage to plot_img_on_surf
dangom Jan 21, 2019
55fb7a7
Move _check_mesh from html_surface into check_mesh in surface.
dangom Jan 21, 2019
4d87ae9
Plot_img_on_surf now uses check_mesh
dangom Jan 21, 2019
d334693
Improve handling of keyword args in plot_img_on_surf
dangom Jan 21, 2019
867a01e
Factor out plot_img_on_surf's colorbar code into a helper function
dangom Jan 21, 2019
983745b
Improve docs related to surf_mesh
dangom Jan 21, 2019
f31bd3c
Move cmap to a keyword argument in plot_img_on_surf
dangom Jan 21, 2019
f32e614
Fix plot_img_on_surf surf_mesh default. From None to fsaverage5
dangom Jan 21, 2019
1ce4aad
Add more tests.
dangom Jan 21, 2019
e4e7a9e
Update examples/01_plotting/plot_img_on_surf.py
jeromedockes Jan 22, 2019
1388051
Update examples/01_plotting/plot_img_on_surf.py
jeromedockes Jan 22, 2019
31a7991
Move plot_img_on_surf example to plot_3d_map_to_surface_projection
dangom Jan 23, 2019
648fa94
Remove unused import in surf_plotting.py
dangom Jan 23, 2019
7bdd9c1
Make 'cold_hot' the default cmap in plot_img_on_surf
dangom Jan 23, 2019
2dc314f
Use safeguard check_niimg_3d to plot_img_on_surf
dangom Jan 23, 2019
35e30c3
Merged latest master into @dangom's/surf_montage.
ZviBaratz May 21, 2020
66c2a47
Fixed surface module's tests.
ZviBaratz May 21, 2020
14fac1a
Fixed the test failure. It was caused by a line I missed in the merge.
ZviBaratz May 21, 2020
e2dd639
Merged upstream and added examples artifacts to gitignore.
ZviBaratz May 29, 2020
1a50359
Added plot_img_on_surf() to reference.rst and made tiny styling fixes…
ZviBaratz May 29, 2020
fc0aaa1
Added a What's New entry for plot_img_on_surf().
ZviBaratz May 29, 2020
63129da
Added some missing tests for plot_img_on_surf().
ZviBaratz May 29, 2020
05d2510
Changed relative imports to absolute imports in surf_plotting.py.
ZviBaratz May 29, 2020
a1895fc
Merge branch 'master' into surface_plots
ZviBaratz Jun 1, 2020
a62f0fc
Merged master and edited plot_img_on_surf() docstring to fix RST inde…
ZviBaratz Jun 1, 2020
5eaad19
Another round with Sphinx.
ZviBaratz Jun 1, 2020
d4cb770
Updated test surface fetch method and resolved linting issues.
ZviBaratz Jun 1, 2020
74c016e
Fixed to conform with the default flake8 max-line-length configuration.
ZviBaratz Jun 1, 2020
d4c1a88
Sphinx U+1F3F3
ZviBaratz Jun 1, 2020
c0c4f58
Added .vscode to gitignore and organized a bit.
ZviBaratz Jun 2, 2020
56de47c
Changed test_plot_img_on_surf_output_file() in attempt to solve build…
ZviBaratz Jun 2, 2020
6db1144
Merge branch 'master' into surface_plots
ZviBaratz Jun 6, 2020
e3e993b
Fixed horizontal squeeze by adding an aspect_ratio parameter to plot_…
ZviBaratz Jun 7, 2020
7f55423
Fixed bad :func: directive in plot_img_on_surf() example.
ZviBaratz Jun 7, 2020
962e13f
Update examples/01_plotting/plot_3d_map_to_surface_projection.py
ZviBaratz Jun 9, 2020
ebb7e6f
Removed length check from `_check_hemisphere()`
ZviBaratz Jun 9, 2020
e95c882
Proposed Orientation Enum for surface plotting configuration.
ZviBaratz Jun 9, 2020
c150fdf
Updated example to comply with changing the display_mode parameter to…
ZviBaratz Jun 9, 2020
d218a82
Reverted Enum creation and updated plot_img_on_surf's hemispheres par…
ZviBaratz Jun 9, 2020
bb118d7
Reverted changes to gitignore.
ZviBaratz Jun 10, 2020
942fa0f
Removed obj domain referencing in docstrings.
ZviBaratz Jun 10, 2020
3034054
Removed f-strings to maintain python 3.5 compatibility.
ZviBaratz Jun 10, 2020
1c617f2
Fixed missed f-strings.
ZviBaratz Jun 10, 2020
f6ccb9f
Fixed error message strings in _check_views() and _check_hemispheres().
ZviBaratz Jun 10, 2020
03a2e78
Reverted image module docstring fixes.
ZviBaratz Jun 25, 2020
9ede345
Made check_mesh() private.
ZviBaratz Jun 25, 2020
b357672
Reverted img_plotting module docstring fixes.
ZviBaratz Jun 25, 2020
da9d243
Removed list conversion in _check_hemispehres() and _check_views() an…
ZviBaratz Jun 25, 2020
44646f4
Made 'fsaverage5' plot_img_on_surf()'s default surf_mesh value and up…
ZviBaratz Jun 25, 2020
ac23a61
Added a try-except block to remove plot_img_on_surf()'s output file a…
ZviBaratz Jun 25, 2020
3c84edc
Changed test_plot_img_on_surf_output_file() to use pytest's tmp_path …
ZviBaratz Jun 25, 2020
5f5d3e8
Fixed plot_img_on_surf()'s aspect ratio parameter to default to 1.4 a…
ZviBaratz Jun 25, 2020
9f8fbc6
Removed symmetric_cbar parameter from plot_img_on_surf().
ZviBaratz Jun 25, 2020
52794d6
Removed symmetric_cbar parameter from _colorbar_from_array().
ZviBaratz Jun 25, 2020
d7daa31
Reverted changes to the image.py module.
ZviBaratz Jun 30, 2020
6feb6ba
Merged changes from master.
ZviBaratz Jun 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion examples/01_plotting/plot_3d_map_to_surface_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,18 @@
threshold=1., bg_map=big_fsaverage.sulc_right)


plotting.show()
##############################################################################
# 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 both or any
# one hemisphere. If no surf_mesh is given, plot_img_on_surf projects the
# images onto Freesurfer's fsaverage5.
plotting.plot_img_on_surf(stat_img, display_mode='lateral+medial',
hemisphere='left+right', colorbar=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still have the problem that the generated image looks distorted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry it's taking so long.
I tried to run:

python3 -m sphinx -D sphinx_gallery_conf.filename_pattern=plot_3d_map_to_surface_projection.py -b html -d _build/doctrees . _build/html

but when I navigate to example 9.2.14.7 I'm seeing an older title (Plotting EPI image with function plot_epi), so I figured I need to make html-strict but that keeps raising:

WARNING: /home/flavus/Projects/nilearn/examples/07_advanced/plot_surface_bids_analysis.py failed to execute correctly: Traceback (most recent call last):                                            
  File "/home/flavus/Projects/nilearn/venv/lib/python3.8/site-packages/sphinx_gallery/gen_rst.py", line 460, in _memory_usage
    out = func()
  File "/home/flavus/Projects/nilearn/venv/lib/python3.8/site-packages/sphinx_gallery/gen_rst.py", line 442, in __call__
    exec(self.code, self.fake_main.__dict__)
  File "/home/flavus/Projects/nilearn/examples/07_advanced/plot_surface_bids_analysis.py", line 54, in <module>
    first_level_models_from_bids(
  File "/home/flavus/Projects/nilearn/nilearn/stats/first_level_model/first_level_model.py", line 818, in first_level_models_from_bids
    raise ValueError('derivatives folder does not exist in given dataset')
ValueError: derivatives folder does not exist in given dataset

I haven't had much time to try to understand what exactly is happening here. Hopefully I'll have some time to figure it out Monday/Tuesday.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment is actually on 9.2.15.7; you can see the image here: https://5819-1235740-gh.circle-artifacts.com/0/doc/_build/html/auto_examples/01_plotting/plot_3d_map_to_surface_projection.html#plot-multiple-views-of-the-3d-volume-on-a-surface

The title seems correct, there ! It's just the images that look distorted. Let me know if we can help.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops - I was using Gael's link from above.
Thank you!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


plotting.show()

##############################################################################
# 3D visualization in a web browser
Expand Down
5 changes: 3 additions & 2 deletions nilearn/plotting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def _set_mpl_backend():
from .html_surface import view_surf, view_img_on_surf
from .html_stat_map import view_img
from .html_connectome import view_connectome, view_markers
from .surf_plotting import plot_surf, plot_surf_stat_map, plot_surf_roi
from .surf_plotting import (plot_surf, plot_surf_stat_map, plot_surf_roi,
plot_img_on_surf)

__all__ = ['cm', 'plot_img', 'plot_anat', 'plot_epi',
'plot_roi', 'plot_stat_map', 'plot_glass_brain',
Expand All @@ -58,5 +59,5 @@ def _set_mpl_backend():
'show', 'plot_matrix', 'view_surf', 'view_img_on_surf',
'view_img', 'view_connectome', 'view_markers',
'find_parcellation_cut_coords', 'find_probabilistic_atlas_cut_coords',
'plot_surf', 'plot_surf_stat_map', 'plot_surf_roi'
'plot_surf', 'plot_surf_stat_map', 'plot_surf_roi', 'plot_img_on_surf'
]
3 changes: 2 additions & 1 deletion nilearn/plotting/html_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from matplotlib import cm as mpl_cm

from .._utils.niimg_conversions import check_niimg_3d
from .. import surface
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
from .. import datasets, surface
from nilearn.reporting import HTMLDocument
from . import cm
Expand Down Expand Up @@ -91,7 +92,7 @@ def full_brain_info(volume_img, mesh='fsaverage5', threshold=None,

"""
info = {}
mesh = _check_mesh(mesh)
mesh = surface.check_mesh(mesh)
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
surface_maps = {
h: surface.vol_to_surf(volume_img, mesh['pial_{}'.format(h)],
**vol_to_surf_kwargs)
Expand Down
261 changes: 260 additions & 1 deletion nilearn/plotting/surf_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from matplotlib.cm import ScalarMappable, get_cmap
from matplotlib.colors import Normalize, LinearSegmentedColormap

from ..surface import load_surf_data, load_surf_mesh
from ..surface import load_surf_data, load_surf_mesh, vol_to_surf, check_mesh
from .._utils import check_niimg_3d
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nilearn project is switching to absolute imports for all new & amended imports.
(We don't modify existing & unrelated to PR relative imports as that adds noise to the review process).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

from .img_plotting import _get_colorbar_and_data_ranges, _crop_colorbar


Expand Down Expand Up @@ -422,6 +423,264 @@ def plot_surf_stat_map(surf_mesh, stat_map, bg_map=None,
return display


def _check_display_mode(display_mode):
"""Checks whether the display_mode string passed to plot_img_on_surf
is meaningful.

display_mode: str
Any combination of 'lateral', 'medial', 'dorsal', 'ventral',
'anterior', 'posterior', united by a '+'. Ex.: 'dorsal+lateral'
"""

available_modes = {'lateral', 'medial',
'dorsal', 'ventral',
'anterior', 'posterior'}

desired_modes = display_mode.split('+')

wrong_modes = set(desired_modes).difference(available_modes)
if wrong_modes:
msg = 'display_mode given: {}, available modes: {}'
msg = msg.format(wrong_modes, available_modes)
raise ValueError(msg)

return desired_modes


def _check_hemisphere(hemisphere):
"""Checks whether the hemisphere in plot_img_on_surf is correct.

display_mode: str
Any combination of 'left', 'right'
"""
desired_hemispheres = hemisphere.split('+')
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
wrong_hemisphere = set(desired_hemispheres).difference({'left', 'right'})
if wrong_hemisphere:
raise ValueError('hemisphere only accepts left and right.')
if len(desired_hemispheres) > 2:
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError('Currently only supports plotting 2 hemispheres.')

return desired_hemispheres


def _colorbar_from_array(array, vmax,
threshold, symmetric_cbar,
kwargs,
cmap='cold_hot'):
""" Generate a custom colorbar for an array.

Internal function used by plot_img_on_surf

array: Any 3D array

vmax : float, optional (default=None)
upper bound for plotting of stat_map values.

threshold : float, optional (default=None)
If None is given, the colorbar is not thresholded.
If a number is given, it is used to threshold the colorbar.
Absolute values lower than threshold are shown in gray.

symmetric_cbar : bool or 'auto', optional, default 'auto'
Specifies whether the colorbar should range from -vmax to vmax
or from vmin to vmax. Setting to 'auto' will select the latter
if the range of the whole image is either positive or negative.
Note: The colormap will always range from -vmax to vmax.

kwargs: extra arguments passed to _get_colorbar_and_data_ranges

cmap: str, optional (default='cold_hot')
the name of a matplotlib or nilearn colormap.
"""

cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use cbar_vmin and cbar_vmax for symmetric_cbar to have an effect (or not expose a symmetric_cbar parameter)

Copy link
Contributor Author

@ZviBaratz ZviBaratz Jun 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand exactly what you mean, please see the updated code and elaborate in case any changes are still required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that as we are not taking it into account, we should probably remove the symmetric_cbar parameter (as I saw you have already done for plot_img_on_surf, thanks).
My comment on cbar_vmin is that if we wanted to actually have a symmetric_cbar argument (we might add it in a future PR), we could get the necessary information from cbar_vmin and cbar_vmax

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry but I'm still not sure I understand - symmetric_cbar is elsewhere used in _colorbar_from_array() and plot_surf_stat_map(). Would you like for me to remove it in both? They both call _get_colorbar_and_data_ranges(), which is imported from the img_plotting module and requires this argument. If any one of the two calling functions omits the symmetric_cbar parameter we should probably also give it a default of "auto".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when _get_colorbar_and_data_ranges is called with symmetric_cbar=False or "auto" resolves to False, it takes that into account by returning appropriate values for cbar_vmin and cbar_vmax . at the moment _colorbar_from_array ignores the values of cbar_vmin and cbar_vmax, so the value of symmetric_cbar has no effect, _colorbar_from_array always behaves as if symmetric_cbar=True. So my suggestion if (at least for now) to remove symmetric_cbar as a parameter from _colorbar_from_array's signature, as it has no effect anyway, and always call _get_colorbar_and_data_ranges(... , symmetric_cbar=True). A future PR can add this parameter and handle it corrrectly, but the new functionality is still useful without it.

regarding the call to plot_surf_stat_map, the value of symmetric_cbar doesn't matter because we call it with cbar=False: it does not add a colorbar here, instead plot_img_on_surf adds a colorbar for the whole figure separately.

so to summarize I suggest: no symmetric_cbar parameter for plot_img_on_surf nor _colorbar_from_array, and _colorbar_from_array calls _get_colorbar_and_data_ranges with symmetric_cbar=True

Copy link
Contributor Author

@ZviBaratz ZviBaratz Jun 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to pass symmetric_cbar=True as a positional argument (avoiding changing anything in the img_plotting module) but otherwise I hope the updated code reflects what you said.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that is what I meant, thank you for bearing with me

array, vmax, symmetric_cbar, kwargs)

norm = Normalize(vmin=vmin, vmax=vmax)
cmaplist = [cmap(i) for i in range(cmap.N)]

if threshold is None:
threshold = 0.

# set colors to grey for absolute values < threshold
istart = int(norm(-threshold, clip=True) * (cmap.N - 1))
istop = int(norm(threshold, clip=True) * (cmap.N - 1))
for i in range(istart, istop):
cmaplist[i] = (0.5, 0.5, 0.5, 1.)
our_cmap = LinearSegmentedColormap.from_list('Custom cmap',
cmaplist, cmap.N)
sm = plt.cm.ScalarMappable(cmap=our_cmap,
norm=plt.Normalize(vmin=vmin, vmax=vmax))
# fake up the array of the scalar mappable.
sm._A = []

return sm


def plot_img_on_surf(stat_map, surf_mesh=None, mask_img=None,
hemisphere='left+right',
inflate=False, display_mode='lateral+medial',
output_file=None, title=None, colorbar=True,
vmax=None, threshold=None, symmetric_cbar='auto',
cmap='cold_hot', **kwargs):
"""Convenience function to plot multiple views of plot_surf_stat_map
in a single figure. It projects stat_map into meshes and plots views of
left and right hemispheres. The display_mode argument defines the views
that are shown. This function returns the fig, axes elements from
matplotlib unless kwargs sets and output_file, in which case nothing
is returned.

stat_map : str or Niimg-like object, 3d.
See http://nilearn.github.io/manipulating_images/input_output.html

surf_mesh: str, dictionary, or None, optional (default=None)
If str, either one of the two:
'fsaverage5': the low-resolution fsaverage5 mesh (10242 nodes)
'fsaverage': the high-resolution fsaverage mesh (163842 nodes)
If dictionary, a dictionary with keys:
['infl_left', 'infl_right',
'pial_left', 'pial_right',
'sulc_left', 'sulc_right'],
where values are surface mesh geometries as accepted
by plot_surf_stat_map.
If None, plot_img_on_surf will use Freesurfer's 'fsaverage5'.

mask_img : Niimg-like object or None, optional (default=None)
The mask is passed to vol_to_surf.
Samples falling out of this mask or out of the image are ignored
during projection of the volume to the surface.
If ``None``, don't apply any mask.

inflate: bool, optional (default=False)
If True, display images in inflated brain.
If False, display images in pial surface.

display_mode: str, optional (default='lateral+medial')
A string containing all views to display, separated by '+'.
Available views: {'lateral', 'medial',
'dorsal', 'ventral',
'anterior', 'posterior'}
The montage will contain as many rows as views specified by
display mode. Order is preserved, and left and right hemispheres
are shown on the left and right sides of the figure.

output_file: str, optional (default=None)
The name of an image file to export plot to. Valid extensions
are .png, .pdf, .svg. If output_file is not None, the plot
is saved to a file, and the display is closed.
Plot_img_to_surf then returns None.

title: str, optional (default=None)
Place a title on the upper center of the figure.

colorbar : bool, optional (default=True)
If True, a symmetric colorbar of the statistical map is displayed.

vmax : float, optional (default=None)
upper bound for plotting of stat_map values.

threshold : float, optional (default=None)
If None is given, the image is not thresholded.
If a number is given, it is used to threshold the image,
values below the threshold (in absolute value) are plotted
as transparent.

symmetric_cbar : bool or 'auto', optional, default 'auto'
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
Specifies whether the colorbar should range from -vmax to vmax
or from vmin to vmax. Setting to 'auto' will select the latter
if the range of the whole image is either positive or negative.
Note: The colormap will always range from -vmax to vmax.

cmap: str, optional (default='cold_hot')
the name of a matplotlib or nilearn colormap.

kwargs: keyword arguments passed to plot_surf_stat_map

See Also
--------
nilearn.datasets.fetch_surf_fsaverage : For surface data object to be
used as the default background map for this plotting function.

nilearn.surface.vol_to_surf : For info on the generation of surfaces.

nilearn.plotting.plot_surf_stat_map : For info on kwargs options
accepted by plot_img_on_surf.
"""
for arg in ('figure', 'axes'):
if arg in kwargs:
raise ValueError(('plot_img_on_surf does not'
' accept %s as an argument' % arg))

if surf_mesh is None:
ZviBaratz marked this conversation as resolved.
Show resolved Hide resolved
surf_mesh = 'fsaverage5'

stat_map = check_niimg_3d(stat_map, dtype='auto')
modes = _check_display_mode(display_mode)
hemis = _check_hemisphere(hemisphere)
surf_mesh = check_mesh(surf_mesh)

surf = {
'left':
surf_mesh['infl_left'] if inflate else surf_mesh['pial_left'],
'right':
surf_mesh['infl_right'] if inflate else surf_mesh['pial_right']
}

texture = {
'left': vol_to_surf(stat_map, surf_mesh['pial_left'],
mask_img=mask_img),
'right': vol_to_surf(stat_map, surf_mesh['pial_right'],
mask_img=mask_img)
}

fig, axes = plt.subplots(nrows=len(modes), ncols=len(hemis),
figsize=plt.figaspect(len(modes) / 2.),
subplot_kw={'projection': '3d'})

axes = np.atleast_2d(axes)

if len(hemis) == 1:
axes = axes.T

for index_mode, mode in enumerate(modes):
for index_hemi, hemi in enumerate(hemis):
bg_map = surf_mesh['sulc_%s' % hemi]
plot_surf_stat_map(surf[hemi], texture[hemi],
view=mode, hemi=hemi,
bg_map=bg_map,
axes=axes[index_mode, index_hemi],
colorbar=False, # Colorbar created externally.
vmax=vmax,
threshold=threshold,
symmetric_cbar=symmetric_cbar,
cmap=cmap,
**kwargs)

for ax in axes.flatten():
# We increase this value to better position the camera of the
# 3D projection plot. The default value makes meshes look too small.
ax.dist = 6

if colorbar:
sm = _colorbar_from_array(stat_map.get_data(),
vmax, threshold, symmetric_cbar, kwargs,
get_cmap(cmap))

cbar_ax = fig.add_subplot(32, 1, 32)
fig.colorbar(sm, cax=cbar_ax, orientation='horizontal')

fig.subplots_adjust(wspace=-0.02, hspace=0.0)

if title is not None:
fig.suptitle(title)

if output_file is not None:
fig.savefig(output_file)
plt.close(fig)
else:
return fig, axes


def plot_surf_roi(surf_mesh, roi_map, bg_map=None,
hemi='left', view='lateral', threshold=1e-14,
alpha='auto', vmin=None, vmax=None, cmap='gist_ncar',
Expand Down