Exemple #1
0
def _read_dig_bids(electrodes_fpath, coordsystem_fpath,
                   raw, datatype, verbose):
    """Read MNE-Python formatted DigMontage from BIDS files.

    Handles coordinatesystem.json and electrodes.tsv reading
    to DigMontage.

    Parameters
    ----------
    electrodes_fpath : str
        Filepath of the electrodes.tsv to read.
    coordsystem_fpath : str
        Filepath of the coordsystem.json to read.
    raw : instance of Raw
        The data as MNE-Python Raw object.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    verbose : bool
        Set verbose output to true or false.

    Returns
    -------
    raw : instance of Raw
        The data as MNE-Python Raw object.
    """
    # get the space entity
    params = get_entities_from_fname(electrodes_fpath)
    space = params['space']
    if space is None:
        space = ''
    space = space.lower()

    # read in coordinate information
    coord_frame, coord_unit = _handle_coordsystem_reading(coordsystem_fpath,
                                                          datatype, verbose)

    if datatype == 'meg':
        if coord_frame not in BIDS_MEG_COORDINATE_FRAMES:
            warn("MEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        elif coord_frame == 'other':
            warn("Coordinate frame of MEG data can't be determined "
                 "when 'other'. The currently accepted keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)
    elif datatype == 'ieeg':
        if coord_frame not in BIDS_IEEG_COORDINATE_FRAMES:
            warn("iEEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_IEEG_COORDINATE_FRAMES))
            coord_frame = None
        elif coord_frame == 'pixels':
            warn("Coordinate frame of iEEG data in pixels does not "
                 "get read in by mne-python. Skipping reading of "
                 "electrodes.tsv ...")
            coord_frame = None
        elif coord_frame == 'acpc':
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)
        elif coord_frame == 'other':
            # XXX: We allow 'other' coordinate frames, but must be mne-python
            if space not in BIDS_TO_MNE_FRAMES:
                # default coordinate frames to available ones in mne-python
                # noqa: see https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html
                warn("Defaulting coordinate frame to unknown "
                     "from coordinate system input {}".format(coord_frame))
            coord_frame = BIDS_TO_MNE_FRAMES.get(space, None)
    elif datatype == 'eeg':
        # only accept captrak
        if coord_frame not in BIDS_EEG_COORDINATE_FRAMES:
            warn("EEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_IEEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)

    # check coordinate units
    if coord_unit not in BIDS_COORDINATE_UNITS:
        warn("Coordinate unit is not an accepted BIDS unit for {}. "
             "Please specify to be one of {}. Skipping electrodes.tsv "
             "reading..."
             .format(electrodes_fpath, BIDS_COORDINATE_UNITS))
        coord_frame = None

    # only set montage if coordinate frame was properly parsed
    if coord_frame is not None:
        # read in electrode coordinates and attach to raw
        raw = _handle_electrodes_reading(electrodes_fpath, coord_frame,
                                         coord_unit, raw, verbose)

    return raw
Exemple #2
0
def test_dig_template(tmp_path):
    """Test that eeg and ieeg dig are stored properly."""
    bids_root = tmp_path / 'bids1'
    for datatype in ('eeg', 'ieeg'):
        (bids_root / 'sub-01' / 'ses-01' / datatype).mkdir(parents=True)

    raw_test = raw.copy().pick_types(eeg=True)

    for datatype in ('eeg', 'ieeg'):
        bids_path = _bids_path.copy().update(root=bids_root, datatype=datatype)
        for coord_frame in BIDS_STANDARD_TEMPLATE_COORDINATE_SYSTEMS:
            bids_path.update(space=coord_frame)
            mnt = montage.copy()
            pos = mnt.get_positions()
            mne_coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)
            if mne_coord_frame is None:
                mnt.apply_trans(mne.transforms.Transform('head', 'unknown'))
            else:
                mnt.apply_trans(
                    mne.transforms.Transform('head', mne_coord_frame))
            _write_dig_bids(bids_path, raw_test, mnt, acpc_aligned=True)
            electrodes_path = bids_path.copy().update(task=None,
                                                      run=None,
                                                      suffix='electrodes',
                                                      extension='.tsv')
            coordsystem_path = bids_path.copy().update(task=None,
                                                       run=None,
                                                       suffix='coordsystem',
                                                       extension='.json')
            if mne_coord_frame is None:
                with pytest.warns(RuntimeWarning,
                                  match='not an MNE-Python coordinate frame'):
                    _read_dig_bids(electrodes_path, coordsystem_path, datatype,
                                   raw_test)
            else:
                if coord_frame == 'MNI305':  # saved to fsaverage, same
                    electrodes_path.update(space='fsaverage')
                    coordsystem_path.update(space='fsaverage')
                _read_dig_bids(electrodes_path, coordsystem_path, datatype,
                               raw_test)
            mnt2 = raw_test.get_montage()
            pos2 = mnt2.get_positions()
            np.testing.assert_array_almost_equal(
                np.array(list(pos['ch_pos'].values())),
                np.array(list(pos2['ch_pos'].values())))
            if mne_coord_frame is None:
                assert pos2['coord_frame'] == 'unknown'
            else:
                assert pos2['coord_frame'] == mne_coord_frame

    # test MEG
    raw_test = raw.copy()
    for coord_frame in BIDS_STANDARD_TEMPLATE_COORDINATE_SYSTEMS:
        bids_path = _bids_path.copy().update(root=bids_root,
                                             datatype='meg',
                                             space=coord_frame)
        write_raw_bids(raw_test, bids_path)
        raw_test2 = read_raw_bids(bids_path)
        for ch, ch2 in zip(raw.info['chs'], raw_test2.info['chs']):
            np.testing.assert_array_equal(ch['loc'], ch2['loc'])
            assert ch['coord_frame'] == ch2['coord_frame']
Exemple #3
0
def _read_dig_bids(electrodes_fpath, coordsystem_fpath, datatype, raw,
                   verbose):
    """Read MNE-Python formatted DigMontage from BIDS files.

    Handles coordinatesystem.json and electrodes.tsv reading
    to DigMontage.

    Parameters
    ----------
    electrodes_fpath : str
        Filepath of the electrodes.tsv to read.
    coordsystem_fpath : str
        Filepath of the coordsystem.json to read.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    raw : mne.io.Raw
        The raw data as MNE-Python ``Raw`` object. Will set montage
        read in via ``raw.set_montage(montage)``.
    verbose : bool
        Set verbose output to true or false.

    Returns
    -------
    montage : mne.channels.DigMontage
        The coordinate data as MNE-Python DigMontage object.
    """
    # read in coordinate information
    bids_coord_frame, bids_coord_unit = _handle_coordsystem_reading(
        coordsystem_fpath, datatype, verbose)

    if datatype == 'meg':
        if bids_coord_frame not in BIDS_MEG_COORDINATE_FRAMES:
            warn("MEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        elif bids_coord_frame == 'Other':
            warn("Coordinate frame of MEG data can't be determined "
                 "when 'other'. The currently accepted keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
    elif datatype == 'ieeg':
        # iEEG datatype for BIDS only supports
        # acpc, pixels and then standard templates
        # iEEG datatype for mne-python only supports
        # mni_tal == fsaverage == MNI305
        if bids_coord_frame == 'Pixels':
            warn("Coordinate frame of iEEG data in pixels does not "
                 "get read in by mne-python. Skipping reading of "
                 "electrodes.tsv ...")
            coord_frame = None
        elif bids_coord_frame == 'ACPC':
            coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
        elif bids_coord_frame == 'Other':
            # default coordinate frames to available ones in mne-python
            # noqa: see https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html
            warn(f"Defaulting coordinate frame to unknown "
                 f"from coordinate system input {bids_coord_frame}")
            coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)

            # XXX: if the coordinate frame is not recognized, then
            # coordinates are stored in a system that we cannot
            # recognize yet.
            if coord_frame is None:
                warn(f"iEEG Coordinate frame {bids_coord_frame} is not a "
                     f"readable BIDS keyword by mne-bids yet. The allowed "
                     f"keywords are: {BIDS_IEEG_COORDINATE_FRAMES}")
                coord_frame = 'unknown'
    elif datatype == 'eeg':
        # only accept captrak
        if bids_coord_frame not in BIDS_EEG_COORDINATE_FRAMES:
            warn("EEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_IEEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)

    # check coordinate units
    if bids_coord_unit not in BIDS_COORDINATE_UNITS:
        warn(f"Coordinate unit is not an accepted BIDS unit for "
             f"{electrodes_fpath}. Please specify to be one of "
             f"{BIDS_COORDINATE_UNITS}. Skipping electrodes.tsv reading...")
        coord_frame = None

    # montage is interpretable only if coordinate frame was properly parsed
    if coord_frame is not None:
        # read in electrode coordinates as a DigMontage object
        montage = _handle_electrodes_reading(electrodes_fpath, coord_frame,
                                             bids_coord_unit, verbose)
    else:
        montage = None

    if montage is not None:
        # determine if there are problematic channels
        ch_pos = montage._get_ch_pos()
        nan_chs = []
        for ch_name, ch_coord in ch_pos.items():
            if any(np.isnan(ch_coord)) and ch_name not in raw.info['bads']:
                nan_chs.append(ch_name)
        if len(nan_chs) > 0:
            warn(f"There are channels without locations "
                 f"(n/a) that are not marked as bad: {nan_chs}")

    # add montage to Raw object
    raw.set_montage(montage, on_missing='warn', verbose=verbose)

    return montage
Exemple #4
0
def _read_dig_bids(electrodes_fpath, coordsystem_fpath, datatype, raw):
    """Read MNE-Python formatted DigMontage from BIDS files.

    Handles coordinatesystem.json and electrodes.tsv reading
    to DigMontage.

    Parameters
    ----------
    electrodes_fpath : str
        Filepath of the electrodes.tsv to read.
    coordsystem_fpath : str
        Filepath of the coordsystem.json to read.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    raw : mne.io.Raw
        The raw data as MNE-Python ``Raw`` object. The montage
        will be set in place.
    """
    bids_coord_frame, bids_coord_unit = _handle_coordsystem_reading(
        coordsystem_fpath, datatype)

    if bids_coord_frame not in ALLOWED_SPACES[datatype]:
        warn(f'"{bids_coord_frame}" is not a BIDS-acceptable coordinate frame '
             f'for {datatype.upper()}. The supported coordinate frames are: '
             '{}'.format(ALLOWED_SPACES[datatype]))
        coord_frame = None
    elif bids_coord_frame in BIDS_TO_MNE_FRAMES:
        coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
    else:
        warn(f"{bids_coord_frame} is not an MNE-Python coordinate frame "
             f"for {datatype.upper()} data and so will be set to 'unknown'")
        coord_frame = 'unknown'

    # check coordinate units
    if bids_coord_unit not in BIDS_COORDINATE_UNITS:
        warn(f"Coordinate unit is not an accepted BIDS unit for "
             f"{electrodes_fpath}. Please specify to be one of "
             f"{BIDS_COORDINATE_UNITS}. Skipping electrodes.tsv reading...")
        coord_frame = None

    # montage is interpretable only if coordinate frame was properly parsed
    if coord_frame is not None:
        # read in electrode coordinates as a DigMontage object
        montage = _handle_electrodes_reading(electrodes_fpath, coord_frame,
                                             bids_coord_unit)
    else:
        montage = None

    if montage is not None:
        # determine if there are problematic channels
        ch_pos = montage._get_ch_pos()
        nan_chs = []
        for ch_name, ch_coord in ch_pos.items():
            if any(np.isnan(ch_coord)) and ch_name not in raw.info['bads']:
                nan_chs.append(ch_name)
        if len(nan_chs) > 0:
            warn(f"There are channels without locations "
                 f"(n/a) that are not marked as bad: {nan_chs}")

    # add montage to Raw object
    # XXX: Starting with mne 0.24, this will raise a RuntimeWarning
    #      if channel types are included outside of
    #      (EEG/sEEG/ECoG/DBS/fNIRS). Probably needs a fix in the future.
    with warnings.catch_warnings():
        warnings.filterwarnings(action='ignore',
                                category=RuntimeWarning,
                                message='.*nasion not found',
                                module='mne')
        raw.set_montage(montage, on_missing='warn')

    # put back in unknown for unknown coordinate frame
    if coord_frame == 'unknown':
        for ch in raw.info['chs']:
            ch['coord_frame'] = MNE_STR_TO_FRAME['unknown']
        for d in raw.info['dig']:
            d['coord_frame'] = MNE_STR_TO_FRAME['unknown']