def get_atlas_mapping(atlas): """Get an atlas mapping. Parameters ---------- atlas : str The atlas name. Can be "LBPA40" or "IXI". Returns ------- mapping : dict The mapping from string name to atlas integer. """ from mne.utils import _check_option _check_option('kind', atlas, ('LBPA40', 'IXI')) out = globals()[atlas].split('\n') out = {line.split('\t')[1]: int(line.split('\t')[0]) for line in out} return out
def __init__(self, n_components=4, reg=None, log=None, cov_est="concat", transform_into='average_power', norm_trace=False, cov_method_params=None, rank=None): """Init of CSP.""" # Init default CSP if not isinstance(n_components, int): raise ValueError('n_components must be an integer.') self.n_components = n_components self.rank = rank self.reg = reg # Init default cov_est if not (cov_est == "concat" or cov_est == "epoch"): raise ValueError("unknown covariance estimation method") self.cov_est = cov_est # Init default transform_into _check_option('transform_into', transform_into, ['average_power', 'csp_space']) self.transform_into = transform_into # Init default log if transform_into == 'average_power': if log is not None and not isinstance(log, bool): raise ValueError('log must be a boolean if transform_into == ' '"average_power".') else: if log is not None: raise ValueError('log must be a None if transform_into == ' '"csp_space".') self.log = log if not isinstance(norm_trace, bool): raise ValueError('norm_trace must be a bool.') self.norm_trace = norm_trace self.cov_method_params = cov_method_params
def test_check_option(): """Test checking the value of a parameter against a list of options.""" allowed_values = ['valid', 'good', 'ok'] # Value is allowed assert _check_option('option', 'valid', allowed_values) assert _check_option('option', 'good', allowed_values) assert _check_option('option', 'ok', allowed_values) assert _check_option('option', 'valid', ['valid']) # Check error message for invalid value msg = ("Invalid value for the 'option' parameter. Allowed values are " "'valid', 'good' and 'ok', but got 'bad' instead.") with pytest.raises(ValueError, match=msg): assert _check_option('option', 'bad', allowed_values) # Special error message if only one value is allowed msg = ("Invalid value for the 'option' parameter. The only allowed value " "is 'valid', but got 'bad' instead.") with pytest.raises(ValueError, match=msg): assert _check_option('option', 'bad', ['valid'])
def _apply_inverse_cov(cov, info, nave, inverse_operator, lambda2=1. / 9., method="dSPM", pick_ori=None, prepared=False, label=None, method_params=None, return_residual=False, verbose=None, log=True): """Apply inverse operator to evoked data HACKED """ from mne.minimum_norm.inverse import _check_reference from mne.minimum_norm.inverse import _check_ori from mne.minimum_norm.inverse import _check_ch_names from mne.minimum_norm.inverse import _check_or_prepare from mne.minimum_norm.inverse import _check_ori from mne.minimum_norm.inverse import _pick_channels_inverse_operator from mne.minimum_norm.inverse import _assemble_kernel from mne.minimum_norm.inverse import _subject_from_inverse from mne.minimum_norm.inverse import _get_src_type from mne.minimum_norm.inverse import combine_xyz from mne.minimum_norm.inverse import _make_stc from mne.utils import _check_option from mne.utils import logger from mne.io.constants import FIFF from collections import namedtuple INVERSE_METHODS = ['MNE', 'dSPM', 'sLORETA', 'eLORETA'] fake_evoked = namedtuple('fake', 'info')(info=info) _check_reference(fake_evoked, inverse_operator['info']['ch_names']) _check_option('method', method, INVERSE_METHODS) if method == 'eLORETA' and return_residual: raise ValueError('eLORETA does not currently support return_residual') _check_ori(pick_ori, inverse_operator['source_ori']) # # Set up the inverse according to the parameters # _check_ch_names(inverse_operator, info) inv = _check_or_prepare(inverse_operator, nave, lambda2, method, method_params, prepared) # # Pick the correct channels from the data # sel = _pick_channels_inverse_operator(cov['names'], inv) logger.info('Applying inverse operator to cov...') logger.info(' Picked %d channels from the data' % len(sel)) logger.info(' Computing inverse...') K, noise_norm, vertno, source_nn = _assemble_kernel( inv, label, method, pick_ori) # apply imaging kernel sol = np.einsum('ij,ij->i', K, (cov.data[sel] @ K.T).T)[:, None] is_free_ori = (inverse_operator['source_ori'] == FIFF.FIFFV_MNE_FREE_ORI and pick_ori != 'normal') if is_free_ori and pick_ori != 'vector': logger.info(' Combining the current components...') sol = combine_xyz(sol) if noise_norm is not None: logger.info(' %s...' % (method, )) if is_free_ori and pick_ori == 'vector': noise_norm = noise_norm.repeat(3, axis=0) sol *= noise_norm tstep = 1.0 / info['sfreq'] tmin = 0.0 subject = _subject_from_inverse(inverse_operator) src_type = _get_src_type(inverse_operator['src'], vertno) if log: sol = np.log10(sol, out=sol) stc = _make_stc(sol, vertno, tmin=tmin, tstep=tstep, subject=subject, vector=(pick_ori == 'vector'), source_nn=source_nn, src_type=src_type) logger.info('[done]') return stc
def run(): """Run command.""" from mne.commands.utils import get_optparser, _add_verbose_flag parser = get_optparser(__file__) parser.add_option("-s", "--subject", dest="subject", help="Subject name (required)", default=None) parser.add_option("-d", "--subjects-dir", dest="subjects_dir", help="Subjects directory", default=None) parser.add_option("-o", "--overwrite", dest="overwrite", help="Write over existing files", action="store_true") parser.add_option("-v", "--volume", dest="volume", help="Defaults to T1", default='T1') parser.add_option("-a", "--atlas", dest="atlas", help="Specify the --atlas option for mri_watershed", default=False, action="store_true") parser.add_option("-g", "--gcaatlas", dest="gcaatlas", help="Specify the --brain_atlas option for " "mri_watershed", default=False, action="store_true") parser.add_option("-p", "--preflood", dest="preflood", help="Change the preflood height", default=None) parser.add_option("--copy", dest="copy", help="Use copies instead of symlinks for surfaces", action="store_true") parser.add_option("-t", "--T1", dest="T1", help="Whether or not to pass the -T1 flag " "(can be true, false, 0, or 1). " "By default it takes the same value as gcaatlas.", default=None) parser.add_option("-b", "--brainmask", dest="brainmask", help="The filename for the brainmask output file " "relative to the " "$SUBJECTS_DIR/$SUBJECT/bem/watershed/ directory.", default="ws") _add_verbose_flag(parser) options, args = parser.parse_args() if options.subject is None: parser.print_help() sys.exit(1) subject = options.subject subjects_dir = options.subjects_dir overwrite = options.overwrite volume = options.volume atlas = options.atlas gcaatlas = options.gcaatlas preflood = options.preflood copy = options.copy brainmask = options.brainmask T1 = options.T1 if T1 is not None: T1 = T1.lower() _check_option("--T1", T1, ('true', 'false', '0', '1')) T1 = T1 in ('true', '1') verbose = options.verbose make_watershed_bem(subject=subject, subjects_dir=subjects_dir, overwrite=overwrite, volume=volume, atlas=atlas, gcaatlas=gcaatlas, preflood=preflood, copy=copy, T1=T1, brainmask=brainmask, verbose=verbose)
def template_to_head(info, space, coord_frame='auto', unit='auto', verbose=None): """Transform a BIDS standard template montage to the head coordinate frame. Parameters ---------- %(info_not_none)s The info is modified in place. space : str The name of the BIDS standard template. See https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html#standard-template-identifiers for a list of acceptable spaces. coord_frame : 'mri' | 'mri_voxel' | 'ras' BIDS template coordinate systems do not specify a coordinate frame, so this must be determined by inspecting the documentation for the dataset or the ``electrodes.tsv`` file. If ``'auto'``, the coordinate frame is assumed to be ``'mri_voxel'`` if the coordinates are strictly positive, and ``'ras'`` (``"scanner RAS"``) otherwise. .. warning:: ``scanner RAS`` and ``surface RAS`` coordinates frames are similar so be very careful not to assume a BIDS dataset's coordinates are in one when they are actually in the other. The only way to tell for template coordinate systems, currently, is if it is specified in the dataset documentation. unit : 'm' | 'mm' | 'auto' The unit that was used in the coordinate system specification. If ``'auto'``, ``'m'`` will be inferred if the montage spans less than ``-1`` to ``1``, and ``'mm'`` otherwise. If the ``coord_frame`` is ``'mri_voxel'``, ``unit`` will be ignored. %(verbose)s Returns ------- %(info_not_none)s The modified ``Info`` object. trans : mne.transforms.Transform The data transformation matrix from ``'head'`` to ``'mri'`` coordinates. """ _validate_type(info, mne.io.Info) _check_option('space', space, BIDS_STANDARD_TEMPLATE_COORDINATE_SYSTEMS) _check_option('coord_frame', coord_frame, ('auto', 'mri', 'mri_voxel', 'ras')) _check_option('unit', unit, ('auto', 'm', 'mm')) # XXX: change to after 0.11 release # montage = info.get_montage() montage = _get_montage(info) if montage is None: raise RuntimeError('No montage found in the `raw` object') montage.remove_fiducials() # we will add fiducials so remove any pos = montage.get_positions() if pos['coord_frame'] not in ('mni_tal', 'unknown'): raise RuntimeError( "Montage coordinate frame '{}' not expected for a template " "montage, should be 'unknown' or 'mni_tal'".format( pos['coord_frame'])) locs = np.array(list(pos['ch_pos'].values())) locs = locs[~np.any(np.isnan(locs), axis=1)] # only channels with loc if locs.size == 0: raise RuntimeError('No channel locations found in the montage') if unit == 'auto': unit = 'm' if abs(locs - locs.mean(axis=0)).max() < 1 else 'mm' if coord_frame == 'auto': coord_frame = 'mri_voxel' if locs.min() >= 0 else 'ras' # transform montage to head data_dir = files('mne_bids.data') # set to the right coordinate frame as specified by the user for d in montage.dig: # ensure same coordinate frame d['coord_frame'] = MNE_STR_TO_FRAME[coord_frame] # do the transforms, first ras -> vox if needed if montage.get_positions()['coord_frame'] == 'ras': ras_vox_trans = mne.read_trans(data_dir / f'space-{space}_ras-vox_trans.fif') if unit == 'm': # must be in mm here for d in montage.dig: d['r'] *= 1000 montage.apply_trans(ras_vox_trans) if montage.get_positions()['coord_frame'] == 'mri_voxel': vox_mri_trans = mne.read_trans(data_dir / f'space-{space}_vox-mri_trans.fif') montage.apply_trans(vox_mri_trans) assert montage.get_positions()['coord_frame'] == 'mri' if not (unit == 'm' and coord_frame == 'mri'): # if so, already in m for d in montage.dig: d['r'] /= 1000 # mm -> m # now add fiducials (in mri coordinates) fids = mne.io.read_fiducials(data_dir / f'space-{space}_fiducials.fif')[0] montage.dig = fids + montage.dig # add fiducials for fid in fids: # ensure also in mri fid['coord_frame'] = MNE_STR_TO_FRAME['mri'] info.set_montage(montage) # transform to head # finally return montage return info, mne.read_trans(data_dir / f'space-{space}_trans.fif')
def plot_3d_montage(info, view_map, *, src_det_names='auto', ch_names='numbered', subject='fsaverage', trans='fsaverage', surface='pial', subjects_dir=None, verbose=None): """ Plot a 3D sensor montage. Parameters ---------- info : instance of Info Measurement info. view_map : dict Dict of view (key) to channel-pair-numbers (value) to use when plotting. Note that, because these get plotted as 1-based channel *numbers*, the values should be 1-based rather than 0-based. The keys are of the form: ``'{side}-{view}'`` For views like ``'left-lat'`` or ``'right-frontal'`` where the side matters. ``'{view}'`` For views like ``'caudal'`` that are along the midline. See :meth:`mne.viz.Brain.show_view` for ``view`` options, and the Examples section below for usage examples. src_det_names : None | dict | str Source and detector names to use. "auto" (default) will see if the channel locations correspond to standard 10-20 locations and will use those if they do (otherwise will act like None). None will use S1, S2, ..., D1, D2, ..., etc. Can also be an explicit dict mapping, for example:: src_det_names=dict(S1='Fz', D1='FCz', ...) ch_names : str | dict | None If ``'numbered'`` (default), use ``['1', '2', ...]`` for the channel names, or ``None`` to use ``['S1_D2', 'S2_D1', ...]``. Can also be a dict to provide a mapping from the ``'S1_D2'``-style names (keys) to other names, e.g., ``defaultdict(lambda: '')`` will prevent showing the names altogether. .. versionadded:: 0.3 subject : str The subject. trans : str | Transform The subjects head<->MRI transform. surface : str The FreeSurfer surface name (e.g., 'pial', 'white'). subjects_dir : str The subjects directory. %(verbose)s Returns ------- figure : matplotlib.figure.Figure The matplotlib figimage. Examples -------- For a Hitachi system with two sets of 12 source-detector arrangements, one on each side of the head, showing 1-12 on the left and 13-24 on the right can be accomplished using the following ``view_map``:: >>> view_map = { ... 'left-lat': np.arange(1, 13), ... 'right-lat': np.arange(13, 25), ... } NIRx typically involves more complicated arrangements. See :ref:`the 3D tutorial <tut-fnirs-vis-brain-plot-3d-montage>` for an advanced example that incorporates the ``'caudal'`` view as well. """ # noqa: E501 import matplotlib.pyplot as plt from scipy.spatial.distance import cdist _validate_type(info, Info, 'info') _validate_type(view_map, dict, 'views') _validate_type(src_det_names, (None, dict, str), 'src_det_names') _validate_type(ch_names, (dict, str, None), 'ch_names') info = pick_info(info, pick_types(info, fnirs=True, exclude=())[::2]) if isinstance(ch_names, str): _check_option('ch_names', ch_names, ('numbered', ), extra='when str') ch_names = { name.split()[0]: str(ni) for ni, name in enumerate(info['ch_names'], 1) } info['bads'] = [] if isinstance(src_det_names, str): _check_option('src_det_names', src_det_names, ('auto', ), extra='when str') # Decide if we can map to 10-20 locations names, pos = zip( *transform_to_head(make_standard_montage( 'standard_1020')).get_positions()['ch_pos'].items()) pos = np.array(pos, float) locs = dict() bad = False for ch in info['chs']: name = ch['ch_name'] s_name, d_name = name.split()[0].split('_') for name, loc in [(s_name, ch['loc'][3:6]), (d_name, ch['loc'][6:9])]: if name in locs: continue # see if it's close enough idx = np.where(cdist(loc[np.newaxis], pos)[0] < 1e-3)[0] if len(idx) < 1: bad = True break # Some are duplicated (e.g., T7+T3) but we can rely on the # first one being the canonical one locs[name] = names[idx[0]] if bad: break if bad: src_det_names = None logger.info('Could not automatically map source/detector names to ' '10-20 locations.') else: src_det_names = locs logger.info('Source-detector names automatically mapped to 10-20 ' 'locations') head_mri_t = _get_trans(trans, 'head', 'mri')[0] del trans views = list() for key, num in view_map.items(): _validate_type(key, str, f'view_map key {repr(key)}') _validate_type(num, np.ndarray, f'view_map[{repr(key)}]') if '-' in key: hemi, v = key.split('-', maxsplit=1) hemi = dict(left='lh', right='rh')[hemi] views.append((hemi, v, num)) else: views.append(('lh', key, num)) del view_map size = (400 * len(views), 400) brain = Brain(subject, 'both', surface, views=['lat'] * len(views), size=size, background='w', units='m', view_layout='horizontal', subjects_dir=subjects_dir) with _safe_brain_close(brain): brain.add_head(dense=False, alpha=0.1) brain.add_sensors(info, trans=head_mri_t, fnirs=['channels', 'pairs', 'sources', 'detectors']) add_text_kwargs = dict() if 'render' in _get_args(brain.plotter.add_text): add_text_kwargs['render'] = False for col, view in enumerate(views): plotted = set() brain.show_view(view[1], hemi=view[0], focalpoint=(0, -0.02, 0.02), distance=0.4, row=0, col=col) brain.plotter.subplot(0, col) vp = brain.plotter.renderer for ci in view[2]: # figure out what we need to plot this_ch = info['chs'][ci - 1] ch_name = this_ch['ch_name'].split()[0] s_name, d_name = ch_name.split('_') needed = [ (ch_names, 'ch_names', ch_name, this_ch['loc'][:3], 12, 'Centered'), (src_det_names, 'src_det_names', s_name, this_ch['loc'][3:6], 8, 'Bottom'), (src_det_names, 'src_det_names', d_name, this_ch['loc'][6:9], 8, 'Bottom'), ] for lookup, lname, name, ch_pos, font_size, va in needed: if name in plotted: continue plotted.add(name) orig_name = name if lookup is not None: name = lookup[name] _validate_type(name, str, f'{lname}[{repr(orig_name)}]') ch_pos = apply_trans(head_mri_t, ch_pos) vp.SetWorldPoint(np.r_[ch_pos, 1.]) vp.WorldToDisplay() ch_pos = (np.array(vp.GetDisplayPoint()[:2]) - np.array(vp.GetOrigin())) actor = brain.plotter.add_text(name, ch_pos, font_size=font_size, color=(0., 0., 0.), **add_text_kwargs) prop = actor.GetTextProperty() getattr(prop, f'SetVerticalJustificationTo{va}')() prop.SetJustificationToCentered() actor.SetTextProperty(prop) prop.SetBold(True) img = brain.screenshot() return plt.figimage(img, resize=True).figure