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
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']
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
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']