Skip to content

Commit

Permalink
ENH: Add initial arguments to vol viz
Browse files Browse the repository at this point in the history
  • Loading branch information
larsoner committed Sep 13, 2019
1 parent 9585e93 commit b65a045
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 154 deletions.
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Changelog

- Add function to check the type of a FIF file using :func:`mne.what` and `mne what <gen_mne_what>` by `Eric Larson`_

- Add support for specifying the initial time and/or position and providing a :class:`mne.SourceMorph` instead of :class:`mne.SourceSpaces` in :func:`mne.viz.plot_volume_source_estimates` by `Eric Larson`_

- Speed up morph map generation in :func:`mne.read_morph_map` by ~5-10x by using :func:`numba.jit` by `Eric Larson`_

- Speed up :func:`mne.setup_volume_source_space`, especially when ``volume_label is not None`` by `Eric Larson`_
Expand Down
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,11 @@ def reset_warnings(gallery_conf, fname):
# nibabel
'Nifti1Image': 'nibabel.nifti1.Nifti1Image',
'Nifti2Image': 'nibabel.nifti2.Nifti2Image',
'SpatialImage': '~nibabel.spatialimages.SpatialImage',
# MNE
'Label': 'mne.Label', 'Forward': 'mne.Forward', 'Evoked': 'mne.Evoked',
'Info': 'mne.Info', 'SourceSpaces': 'mne.SourceSpaces',
'SourceMorph': 'mne.SourceMorph',
'Epochs': 'mne.Epochs', 'Layout': 'mne.channels.Layout',
'EvokedArray': 'mne.EvokedArray', 'BiHemiLabel': 'mne.BiHemiLabel',
'AverageTFR': 'mne.time_frequency.AverageTFR',
Expand Down
26 changes: 19 additions & 7 deletions examples/inverse/plot_lcmv_beamformer_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Compute LCMV inverse solution in volume source space
====================================================
Compute LCMV beamformer on an auditory evoked dataset in a volume source space.
Compute LCMV beamformer on an auditory evoked dataset in a volume source space,
and show activation on ``fsaverage``.
"""
# Author: Alexandre Gramfort <alexandre.gramfort@inria.fr>
#
Expand All @@ -22,7 +23,7 @@

data_path = sample.data_path()
subjects_dir = data_path + '/subjects'
raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
raw_fname = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
event_fname = data_path + '/MEG/sample/sample_audvis_raw-eve.fif'
fname_fwd = data_path + '/MEG/sample/sample_audvis-meg-vol-7-fwd.fif'

Expand All @@ -35,7 +36,7 @@
# Setup for reading the raw data
raw = mne.io.read_raw_fif(raw_fname, preload=True)
raw.info['bads'] = ['MEG 2443', 'EEG 053'] # 2 bads channels
events = mne.read_events(event_fname)
events = mne.find_events(raw)

# Pick the channels of interest
raw.pick(['meg', 'eog'])
Expand Down Expand Up @@ -89,9 +90,20 @@
clim=clim)

###############################################################################
# We can also visualize the activity on a "glass brain" (shown here with
# absolute values):
# Now let's:
# - visualize the activity on a "glass brain",
# - show absolute values, and
# - morph data to ``'fsaverage'`` instead of `sample`

# XXX The morph is still wrong. And even without morph it's wrong because it's
# not in MNI space.

# morph = mne.compute_source_morph(
# forward['src'], 'sample', 'fsaverage', subjects_dir=subjects_dir,
# verbose='debug')
clim = dict(kind='value', lims=[0.3, 0.6, 0.9])
abs(stc).plot(src=forward['src'], subject='sample', subjects_dir=subjects_dir,
mode='glass_brain', clim=clim)
abs(stc).crop(0.08, 0.12).plot(
# src=morph,
src=forward['src'],
subject='sample', subjects_dir=subjects_dir,
mode='glass_brain', clim=clim)
3 changes: 2 additions & 1 deletion mne/morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .source_estimate import (VolSourceEstimate, SourceEstimate,
VolVectorSourceEstimate, VectorSourceEstimate,
_get_ico_tris)
from .source_space import SourceSpaces
from .source_space import SourceSpaces, _ensure_src
from .surface import read_morph_map, mesh_edges, read_surface, _compute_nearest
from .utils import (logger, verbose, check_version, get_subjects_dir,
warn as warn_, deprecated, fill_doc, _check_option,
Expand Down Expand Up @@ -119,6 +119,7 @@ def compute_source_morph(src, subject_from=None, subject_to='fsaverage',
kind = 'surface'
subject_from = _check_subject_from(subject_from, src.subject)
else:
src = _ensure_src(src)
src_data, kind = _get_src_data(src)
subject_from = _check_subject_from(subject_from, src)
if not isinstance(subject_to, str):
Expand Down
13 changes: 8 additions & 5 deletions mne/source_estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ def to_original_src(self, src_orig, subject_orig=None,
"""
if self.subject is None:
raise ValueError('stc.subject must be set')
src_orig = _ensure_src(src_orig, kind='surf')
src_orig = _ensure_src(src_orig, kind='surface')
subject_orig = _ensure_src_subject(src_orig, subject_orig)
data_idx, vertices = _get_morph_src_reordering(
self.vertices, src_orig, subject_orig, self.subject, subjects_dir)
Expand Down Expand Up @@ -1709,12 +1709,15 @@ def _vertices_list(self):
@copy_function_doc_to_method_doc(plot_volume_source_estimates)
def plot(self, src, subject=None, subjects_dir=None, mode='stat_map',
bg_img=None, colorbar=True, colormap='auto', clim='auto',
transparent='auto', show=True, verbose=None):
transparent='auto', show=True, initial_time=None,
initial_pos=None, verbose=None):
data = self.magnitude() if self._data_ndim == 3 else self
return plot_volume_source_estimates(
data, src=src, subject=subject, subjects_dir=subjects_dir,
mode=mode, bg_img=bg_img, colorbar=colorbar, colormap=colormap,
clim=clim, transparent=transparent, show=show, verbose=verbose)
clim=clim, transparent=transparent, show=show,
initial_time=initial_time, initial_pos=initial_pos,
verbose=verbose)

def save_as_volume(self, fname, src, dest='mri', mri_resolution=False,
format='nifti1'):
Expand Down Expand Up @@ -2151,7 +2154,7 @@ def plot_surface(self, src, subject=None, surface='inflated', hemi='lh',
A instance of `surfer.Brain` from PySurfer.
"""
# extract surface source spaces
surf = _ensure_src(src, kind='surf')
surf = _ensure_src(src, kind='surface')

# extract surface source estimate
data = self.data[:surf[0]['nuse'] + surf[1]['nuse']]
Expand Down Expand Up @@ -2450,7 +2453,7 @@ def spatial_inter_hemi_connectivity(src, dist, verbose=None):
using geodesic distances.
"""
from scipy.spatial.distance import cdist
src = _ensure_src(src, kind='surf')
src = _ensure_src(src, kind='surface')
conn = cdist(src[0]['rr'][src[0]['vertno']],
src[1]['rr'][src[1]['vertno']])
conn = sparse.csr_matrix(conn <= dist, dtype=int)
Expand Down
15 changes: 6 additions & 9 deletions mne/source_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -2326,23 +2326,20 @@ def _adjust_patch_info(s, verbose=None):


@verbose
def _ensure_src(src, kind=None, verbose=None):
def _ensure_src(src, kind=None, extra='', verbose=None):
"""Ensure we have a source space."""
msg = 'src must be a string or instance of SourceSpaces%s' % (extra,)
if _check_path_like(src):
src = str(src)
if not op.isfile(src):
raise IOError('Source space file "%s" not found' % src)
logger.info('Reading %s...' % src)
src = read_source_spaces(src, verbose=False)
if not isinstance(src, SourceSpaces):
raise ValueError('src must be a string or instance of SourceSpaces')
if kind is not None:
if kind == 'surf':
surf = [s for s in src if s['type'] == 'surf']
if len(surf) != 2 or len(src) != 2:
raise ValueError('Source space must contain exactly two '
'surfaces.')
src = surf
raise ValueError('%s, got %s (type %s)' % (msg, src, type(src)))
if kind is not None and src.kind != kind:
raise ValueError('Source space must be %s type, got '
'%s' % (kind, src.kind))
return src


Expand Down
7 changes: 4 additions & 3 deletions mne/tests/test_morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,14 @@ def test_volume_source_morph():
stc_vol = read_source_estimate(fname_vol, 'sample')

# check for invalid input type
with pytest.raises(TypeError, match='src must be an instance of'):
with pytest.raises(ValueError, match='src must be a string or instance'):
compute_source_morph(src=42)

# check for raising an error if neither
# inverse_operator_vol['src'][0]['subject_his_id'] nor subject_from is set,
# but attempting to perform a volume morph
src = inverse_operator_vol['src']
src[0]['subject_his_id'] = None
assert src[0]['subject_his_id'] is None # already None on disk (old!)

with pytest.raises(ValueError, match='subject_from could not be inferred'):
compute_source_morph(src=src, subjects_dir=subjects_dir)
Expand All @@ -259,7 +259,8 @@ def test_volume_source_morph():
zooms = 20
kwargs = dict(zooms=zooms, niter_sdr=(1,), niter_affine=(1,))
source_morph_vol = compute_source_morph(
subjects_dir=subjects_dir, src=inverse_operator_vol['src'], **kwargs)
subjects_dir=subjects_dir, src=fname_inv_vol, subject_from='sample',
**kwargs)
shape = (13,) * 3 # for the given zooms

assert source_morph_vol.subject_from == 'sample'
Expand Down
Loading

0 comments on commit b65a045

Please sign in to comment.