Skip to content

Instantly share code, notes, and snippets.

@wmvanvliet
Created July 25, 2017 13:22
Show Gist options
  • Save wmvanvliet/93f1cb4f8463371077687e16aacb1b25 to your computer and use it in GitHub Desktop.
Save wmvanvliet/93f1cb4f8463371077687e16aacb1b25 to your computer and use it in GitHub Desktop.
One function to rule them all..
def plot_source_estimates(stc, subject=None, surface=None, hemi='lh',
colormap='auto', time_label='auto',
smoothing_steps=10, transparent=None,
brain_alpha=None, alpha=None, vector_alpha=1.,
scale_factor=None, time_viewer=False,
subjects_dir=None, figure=None, views='lat',
colorbar=True, clim='auto', cortex="classic",
size=800, background="black", foreground="white",
initial_time=None, time_unit='s', backend='auto',
spacing='oct6'):
"""Plot SourceEstimates with PySurfer or matplotlib.
Note: PySurfer currently needs the SUBJECTS_DIR environment variable,
which will automatically be set by this function. Plotting multiple
SourceEstimates with different values for subjects_dir will cause
PySurfer to use the wrong FreeSurfer surfaces when using methods of
the returned Brain object. It is therefore recommended to set the
SUBJECTS_DIR environment variable or always use the same value for
subjects_dir (within the same Python session).
By default this function uses Mayavi to plot the source estimates. If
Mayavi is not installed, the plotting is done with matplotlib (much slower,
decimated source space by default).
Parameters
----------
stc : SourceEstimate | VectorSourceEstimate
The source estimate to plot.
subject : str | None
The subject name corresponding to FreeSurfer environment
variable SUBJECT. If None stc.subject will be used. If that
is None, the environment will be used.
surface : str | None
The type of surface (inflated, white etc.). By default, the inflated
surface is chosen for plotting SurfaceSourceEstimates. For
VectorSourceEstimates, the only valid option is the white matter
surface.
hemi : str, 'lh' | 'rh' | 'split' | 'both'
The hemisphere to display. Defaults to 'lh'.
colormap : str | np.ndarray of float, shape(n_colors, 3 | 4)
Name of colormap to use or a custom look up table. If array, must
be (n x 3) or (n x 4) array for with RGB or RGBA values between
0 and 255. If 'auto', either 'hot' or 'mne' will be chosen
based on whether 'lims' or 'pos_lims' are specified in `clim`.
time_label : str | callable | None
Format of the time label (a format string, a function that maps
floating point time values to strings, or None for no label). The
default is ``time=%0.2f ms``.
smoothing_steps : int
The amount of smoothing. Defaults to 10.
transparent : bool | None
If True, use a linear transparency between fmin and fmid.
None will choose automatically based on colormap type. Has no effect
with mpl backend.
brain_alpha : float | None
Alpha value to apply globally to the surface meshes. Defaults to 1.
when plotting SourceEstimates and 0.4 when plotting
VectorSourceEstimates.
alpha : float | None
Alpha value to apply globally to the overlay. Defaults to
``brain_alpha``. Has no effect with mpl backend.
vector_alpha : float
Alpha value to apply globally to the vector glyphs. Defaults to 1.
scale_factor : float | None
Scaling factor for vector glyphs. By default, an attempt is made to
automatically determine a sane value.
time_viewer : bool
Display time viewer GUI. Defaults to False.
subjects_dir : str
The path to the freesurfer subjects reconstructions.
By default this value is taken from the Freesurfer environment variable
SUBJECTS_DIR.
figure : instance of mayavi.core.scene.Scene | instance of matplotlib.figure.Figure | list | int | None
If None, a new figure will be created. If multiple views or a
split view is requested, this must be a list of the appropriate
length. If int is provided it will be used to identify the Mayavi
figure by it's id or create a new figure with the given id. If an
instance of matplotlib figure, mpl backend is used for plotting.
views : str | list
View to use. See surfer.Brain(). Supported views: ['lat', 'med', 'fos',
'cau', 'dor' 'ven', 'fro', 'par']. Using multiple views is not
supported for mpl backend. Defaults to a lateral ('lat') view.
colorbar : bool
If True, display colorbar on scene. Not available on mpl backend.
Defaults to True.
clim : str | dict
Colorbar properties specification. If 'auto', set clim automatically
based on data percentiles. If dict, should contain:
``kind`` : str
Flag to specify type of limits. 'value' or 'percent'.
``lims`` : list | np.ndarray | tuple of float, 3 elements
Note: Only use this if 'colormap' is not 'mne'.
Left, middle, and right bound for colormap.
``pos_lims`` : list | np.ndarray | tuple of float, 3 elements
Note: Only use this if 'colormap' is 'mne'.
Left, middle, and right bound for colormap. Positive values
will be mirrored directly across zero during colormap
construction to obtain negative control points.
cortex : str or tuple
Specifies how binarized curvature values are rendered.
Either the name of a preset PySurfer cortex colorscheme (one of
'classic', 'bone', 'low_contrast', or 'high_contrast'), or the name of
mayavi colormap, or a tuple with values (colormap, min, max, reverse)
to fully specify the curvature colors. Has no effect with mpl backend.
Defaults to 'classic'.
size : float or pair of floats
The size of the window, in pixels. can be one number to specify
a square window, or the (width, height) of a rectangular window.
Has no effect with mpl backend.
background : matplotlib color
Color of the background of the display window. Defaults to "black".
foreground : matplotlib color
Color of the foreground of the display window. Has no effect with mpl
backend. Defaults to "white".
initial_time : float | None
The time to display on the plot initially. ``None`` to display the
first time sample (default).
time_unit : 's' | 'ms'
Whether time is represented in seconds ("s", default) or
milliseconds ("ms").
backend : 'auto' | 'mayavi' | 'matplotlib'
Which backend to use. If ``'auto'`` (default), tries to plot with
mayavi, but resorts to matplotlib if mayavi is not available.
.. versionadded:: 0.15.0
spacing : str
The spacing to use for the source space. Can be ``'ico#'`` for a
recursively subdivided icosahedron, ``'oct#'`` for a recursively
subdivided octahedron, or ``'all'`` for all points. In general, you can
speed up the plotting by selecting a sparser source space. Has no
effect with mayavi backend. Defaults to 'oct6'.
.. versionadded:: 0.15.0
Returns
-------
figure : surfer.viz.Brain | matplotlib.figure.Figure
An instance of surfer.viz.Brain from PySurfer or matplotlib figure.
""" # noqa: E501
# import here to avoid circular import problem
from ..source_estimate import (_BaseSourceEstimate, SourceEstimate,
VectorSourceEstimate)
if not isinstance(stc, _BaseSourceEstimate):
raise ValueError('stc has to be a surface or vector source estimate')
subjects_dir = get_subjects_dir(subjects_dir=subjects_dir,
raise_error=True)
subject = _check_subject(stc.subject, subject, True)
if backend not in ['auto', 'matplotlib', 'mayavi']:
raise ValueError("backend must be 'auto', 'mayavi' or 'matplotlib'. "
"Got %s." % backend)
plot_mpl = backend == 'matplotlib'
if not plot_mpl:
try:
import mayavi
except ImportError:
if backend == 'auto' and isinstance(stc, SourceEstimate):
warn('Mayavi not found. Resorting to matplotlib 3d.')
plot_mpl = True
else: # 'mayavi'
raise
if plot_mpl:
if isinstance(stc, VectorSourceEstimate):
raise NotImplementedError(
'Plotting of VectorSourceEstimates using matplotlib is not '
'supported.'
)
if surface is None:
surface = 'inflated'
return _plot_mpl_stc(stc, subject=subject, surface=surface, hemi=hemi,
colormap=colormap, time_label=time_label,
smoothing_steps=smoothing_steps,
subjects_dir=subjects_dir, views=views, clim=clim,
figure=figure, initial_time=initial_time,
time_unit=time_unit, background=background,
spacing=spacing, time_viewer=time_viewer)
import surfer
from surfer import Brain, TimeViewer
surfer_version = LooseVersion(surfer.__version__)
v06 = LooseVersion('0.6')
v07 = LooseVersion('0.7')
v08 = LooseVersion('0.8')
brain_kwargs = dict()
data_kwargs = dict()
if initial_time is not None and surfer_version > v06:
data_kwargs['initial_time'] = initial_time
initial_time = None # don't set it twice
time_label, times = _handle_time(time_label, time_unit, stc.times)
if hemi not in ['lh', 'rh', 'split', 'both']:
raise ValueError('hemi has to be either "lh", "rh", "split", '
'or "both"')
# check `figure` parameter (This will be performed by PySurfer > 0.6)
if figure is not None:
if isinstance(figure, int):
# use figure with specified id
size_ = size if isinstance(size, (tuple, list)) else (size, size)
figure = [mayavi.mlab.figure(figure, size=size_)]
elif not isinstance(figure, (list, tuple)):
figure = [figure]
if not all(isinstance(f, mayavi.core.scene.Scene) for f in figure):
raise TypeError('figure must be a mayavi scene or list of scenes')
if isinstance(stc, SourceEstimate):
if surfer_version < v06:
raise ImportError("This function requires PySurfer 0.6 (you are "
"running version %s). You can update PySurfer "
"using:\n\n $ pip install -U pysurfer" %
surfer.__version__)
# convert control points to locations in colormap
scale_pts, colormap = _limits_to_control_points(clim, stc.data,
colormap)
if surface is None:
surface = 'inflated'
if brain_alpha is None:
brain_alpha = 1.
elif isinstance(stc, VectorSourceEstimate):
if surfer_version < v08:
raise ImportError("To plot vector source estimates, this function "
"requires PySurfer 0.8 (you are running version "
"%s). You can update PySurfer using:\n\n"
" $ pip install -U pysurfer" %
surfer.__version__)
# convert control points to locations in colormap
scale_pts, colormap = _limits_to_control_points(
clim, stc.magnitude().data, colormap
)
if surface is None:
surface = 'white'
elif surface != 'white':
raise ValueError("For plotting VectorSourceEstimates, the surface "
"parameter must be set to 'white'.")
data_kwargs['scale_factor'] = scale_factor
data_kwargs['vector_alpha'] = vector_alpha
if brain_alpha is None:
brain_alpha = 0.4
else:
raise ValueError('stc has to be a surface or vector source estimate')
if surfer_version > v07:
brain_kwargs['alpha'] = brain_alpha
if alpha is None:
alpha = brain_alpha
if hemi in ['both', 'split']:
hemis = ['lh', 'rh']
else:
hemis = [hemi]
title = subject if len(hemis) > 1 else '%s - %s' % (subject, hemis[0])
with warnings.catch_warnings(record=True): # traits warnings
brain = Brain(subject, hemi=hemi, surf=surface, curv=True,
title=title, cortex=cortex, size=size,
background=background, foreground=foreground,
figure=figure, subjects_dir=subjects_dir,
views=views, **brain_kwargs)
_toggle_mlab_render(brain._figures, False)
for hemi in hemis:
hemi_idx = 0 if hemi == 'lh' else 1
if hemi_idx == 0:
data = stc.data[:len(stc.vertices[0])]
else:
data = stc.data[len(stc.vertices[0]):]
vertices = stc.vertices[hemi_idx]
if len(data) > 0:
with warnings.catch_warnings(record=True): # traits warnings
brain.add_data(data, colormap=colormap, vertices=vertices,
smoothing_steps=smoothing_steps, time=times,
time_label=time_label, alpha=alpha, hemi=hemi,
colorbar=colorbar, **data_kwargs)
# scale colormap and set time (index) to display
brain.scale_data_colormap(fmin=scale_pts[0], fmid=scale_pts[1],
fmax=scale_pts[2], transparent=transparent)
if initial_time is not None:
brain.set_time(initial_time)
_toggle_mlab_render(brain._figures, True)
if time_viewer:
TimeViewer(brain)
return brain
@wmvanvliet
Copy link
Author

In my opinion, this version of the function is way too complicated. Too many branches, nooks and crannies, too many parameters that only work in certain cases (SourceEstimate vs. VectorSourceEstimate, PySurfer v0.6 vs PySurfer v0.8, matplotlib backend vs PySurfer, etc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment