Ejemplo n.º 1
0
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
Ejemplo n.º 3
0
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'])
Ejemplo n.º 4
0
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)
Ejemplo n.º 7
0
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')
Ejemplo n.º 8
0
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