def test_dig_pixels(tmp_path): """Test dig stored correctly for the Pixels coordinate frame.""" bids_root = tmp_path / 'bids1' # test coordinates in pixels bids_path = _bids_path.copy().update(root=bids_root, datatype='ieeg', space='Pixels') os.makedirs(op.join(bids_root, 'sub-01', 'ses-01', bids_path.datatype), exist_ok=True) raw_test = raw.copy() raw_test.pick_types(eeg=True) raw_test.del_proj() raw_test.set_channel_types({ch: 'ecog' for ch in raw_test.ch_names}) mnt = raw_test.get_montage() # fake transform to pixel coordinates mnt.apply_trans(mne.transforms.Transform('head', 'unknown')) _write_dig_bids(bids_path, raw_test, mnt) 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') with pytest.warns(RuntimeWarning, match='not an MNE-Python coordinate frame'): _read_dig_bids(electrodes_path, coordsystem_path, bids_path.datatype, raw_test) mnt2 = raw_test.get_montage() assert mnt2.get_positions()['coord_frame'] == 'unknown' assert_almost_equal( np.array(list(mnt.get_positions()['ch_pos'].values())), np.array(list(mnt2.get_positions()['ch_pos'].values())))
def read_raw_bids(bids_path, extra_params=None, verbose=True): """Read BIDS compatible data. Will attempt to read associated events.tsv and channels.tsv files to populate the returned raw object with raw.annotations and raw.info['bads']. Parameters ---------- bids_path : mne_bids.BIDSPath The file to read. The :class:`mne_bids.BIDSPath` instance passed here **must** have the ``.root`` attribute set. The ``.datatype`` attribute **may** be set. If ``.datatype`` is not set and only one data type (e.g., only EEG or MEG data) is present in the dataset, it will be selected automatically. extra_params : None | dict Extra parameters to be passed to MNE read_raw_* functions. If a dict, for example: ``extra_params=dict(allow_maxshield=True)``. Note that the ``exclude`` parameter, which is supported by some MNE-Python readers, is not supported; instead, you need to subset your channels **after** reading. verbose : bool The verbosity level. Returns ------- raw : mne.io.Raw The data as MNE-Python Raw object. Raises ------ RuntimeError If multiple recording data types are present in the dataset, but ``datatype=None``. RuntimeError If more than one data files exist for the specified recording. RuntimeError If no data file in a supported format can be located. ValueError If the specified ``datatype`` cannot be found in the dataset. """ if not isinstance(bids_path, BIDSPath): raise RuntimeError('"bids_path" must be a BIDSPath object. Please ' 'instantiate using mne_bids.BIDSPath().') bids_path = bids_path.copy() sub = bids_path.subject ses = bids_path.session bids_root = bids_path.root datatype = bids_path.datatype suffix = bids_path.suffix # check root available if bids_root is None: raise ValueError('The root of the "bids_path" must be set. ' 'Please use `bids_path.update(root="<root>")` ' 'to set the root of the BIDS folder to read.') # infer the datatype and suffix if they are not present in the BIDSPath if datatype is None: datatype = _infer_datatype(root=bids_root, sub=sub, ses=ses) bids_path.update(datatype=datatype) if suffix is None: bids_path.update(suffix=datatype) data_dir = bids_path.directory bids_fname = bids_path.fpath.name if op.splitext(bids_fname)[1] == '.pdf': bids_raw_folder = op.join(data_dir, f'{bids_path.basename}') bids_fpath = glob.glob(op.join(bids_raw_folder, 'c,rf*'))[0] config = op.join(bids_raw_folder, 'config') else: bids_fpath = op.join(data_dir, bids_fname) config = None if extra_params is None: extra_params = dict() elif 'exclude' in extra_params: del extra_params['exclude'] logger.info('"exclude" parameter is not supported by read_raw_bids') raw = _read_raw(bids_fpath, electrode=None, hsp=None, hpi=None, config=config, verbose=None, **extra_params) # Try to find an associated events.tsv to get information about the # events in the recorded data events_fname = _find_matching_sidecar(bids_path, suffix='events', extension='.tsv', on_error='warn') if events_fname is not None: raw = _handle_events_reading(events_fname, raw) # Try to find an associated channels.tsv to get information about the # status and type of present channels channels_fname = _find_matching_sidecar(bids_path, suffix='channels', extension='.tsv', on_error='warn') if channels_fname is not None: raw = _handle_channels_reading(channels_fname, raw) # Try to find an associated electrodes.tsv and coordsystem.json # to get information about the status and type of present channels on_error = 'warn' if suffix == 'ieeg' else 'ignore' electrodes_fname = _find_matching_sidecar(bids_path, suffix='electrodes', extension='.tsv', on_error=on_error) coordsystem_fname = _find_matching_sidecar(bids_path, suffix='coordsystem', extension='.json', on_error=on_error) if electrodes_fname is not None: if coordsystem_fname is None: raise RuntimeError(f"BIDS mandates that the coordsystem.json " f"should exist if electrodes.tsv does. " f"Please create coordsystem.json for" f"{bids_path.basename}") if datatype in ['meg', 'eeg', 'ieeg']: raw = _read_dig_bids(electrodes_fname, coordsystem_fname, raw, datatype, verbose) # Try to find an associated sidecar .json to get information about the # recording snapshot sidecar_fname = _find_matching_sidecar(bids_path, suffix=datatype, extension='.json', on_error='warn') if sidecar_fname is not None: raw = _handle_info_reading(sidecar_fname, raw, verbose=verbose) # read in associated scans filename scans_fname = BIDSPath(subject=bids_path.subject, session=bids_path.session, suffix='scans', extension='.tsv', root=bids_path.root).fpath if scans_fname.exists(): raw = _handle_scans_reading(scans_fname, raw, bids_path, verbose=verbose) # read in associated subject info from participants.tsv participants_tsv_fpath = op.join(bids_root, 'participants.tsv') subject = f"sub-{bids_path.subject}" if op.exists(participants_tsv_fpath): raw = _handle_participants_reading(participants_tsv_fpath, raw, subject, verbose=verbose) else: warn("Participants file not found for {}... Not reading " "in any particpants.tsv data.".format(bids_fname)) return raw
def read_raw_bids(bids_basename, bids_root, kind=None, extra_params=None, verbose=True): """Read BIDS compatible data. Will attempt to read associated events.tsv and channels.tsv files to populate the returned raw object with raw.annotations and raw.info['bads']. Parameters ---------- bids_basename : str | BIDSPath The base filename of the BIDS compatible files. Typically, this can be generated using :func:`mne_bids.make_bids_basename`. bids_root : str | pathlib.Path Path to root of the BIDS folder kind : str | None The kind of recording to read. If ``None`` and only one kind (e.g., only EEG or only MEG data) is present in the dataset, it will be selected automatically. extra_params : None | dict Extra parameters to be passed to MNE read_raw_* functions. If a dict, for example: ``extra_params=dict(allow_maxshield=True)``. verbose : bool The verbosity level. Returns ------- raw : instance of Raw The data as MNE-Python Raw object. Raises ------ RuntimeError If multiple recording kinds are present in the dataset, but ``kind=None``. RuntimeError If more than one data files exist for the specified recording. RuntimeError If no data file in a supported format can be located. ValueError If the specified ``kind`` cannot be found in the dataset. """ # convert to BIDS Path if isinstance(bids_basename, str): params = _parse_bids_filename(bids_basename, verbose) bids_basename = BIDSPath(subject=params.get('sub'), session=params.get('ses'), recording=params.get('rec'), acquisition=params.get('acq'), processing=params.get('proc'), space=params.get('space'), run=params.get('run'), task=params.get('task')) sub = bids_basename.subject ses = bids_basename.session acq = bids_basename.acquisition if kind is None: kind = _infer_kind(bids_basename=bids_basename, bids_root=bids_root, sub=sub, ses=ses) data_dir = make_bids_folders(subject=sub, session=ses, kind=kind, make_dir=False) bids_fname = bids_basename.get_bids_fname(kind=kind, bids_root=bids_root) if op.splitext(bids_fname)[1] == '.pdf': bids_raw_folder = op.join(bids_root, data_dir, f'{bids_basename}_{kind}') bids_fpath = glob.glob(op.join(bids_raw_folder, 'c,rf*'))[0] config = op.join(bids_raw_folder, 'config') else: bids_fpath = op.join(bids_root, data_dir, bids_fname) config = None if extra_params is None: extra_params = dict() raw = _read_raw(bids_fpath, electrode=None, hsp=None, hpi=None, config=config, verbose=None, **extra_params) # Try to find an associated events.tsv to get information about the # events in the recorded data events_fname = _find_matching_sidecar(bids_fname, bids_root, 'events.tsv', allow_fail=True) if events_fname is not None: raw = _handle_events_reading(events_fname, raw) # Try to find an associated channels.tsv to get information about the # status and type of present channels channels_fname = _find_matching_sidecar(bids_fname, bids_root, 'channels.tsv', allow_fail=True) if channels_fname is not None: raw = _handle_channels_reading(channels_fname, bids_fname, raw) # Try to find an associated electrodes.tsv and coordsystem.json # to get information about the status and type of present channels search_modifier = f'acq-{acq}' if acq else '' elec_suffix = f'{search_modifier}*_electrodes.tsv' coord_suffix = f'{search_modifier}*_coordsystem.json' electrodes_fname = _find_matching_sidecar(bids_fname, bids_root, suffix=elec_suffix, allow_fail=True) coordsystem_fname = _find_matching_sidecar(bids_fname, bids_root, suffix=coord_suffix, allow_fail=True) if electrodes_fname is not None: if coordsystem_fname is None: raise RuntimeError("BIDS mandates that the coordsystem.json " "should exist if electrodes.tsv does. " "Please create coordsystem.json for" "{}".format(bids_basename)) if kind in ['meg', 'eeg', 'ieeg']: raw = _read_dig_bids(electrodes_fname, coordsystem_fname, raw, kind, verbose) # Try to find an associated sidecar.json to get information about the # recording snapshot sidecar_fname = _find_matching_sidecar(bids_fname, bids_root, '{}.json'.format(kind), allow_fail=True) if sidecar_fname is not None: raw = _handle_info_reading(sidecar_fname, raw, verbose=verbose) # read in associated subject info from participants.tsv participants_tsv_fpath = op.join(bids_root, 'participants.tsv') subject = f"sub-{bids_basename.subject}" if op.exists(participants_tsv_fpath): raw = _handle_participants_reading(participants_tsv_fpath, raw, subject, verbose=verbose) else: warn("Participants file not found for {}... Not reading " "in any particpants.tsv data.".format(bids_fname)) 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_raw_bids(bids_path, extra_params=None, verbose=None): """Read BIDS compatible data. Will attempt to read associated events.tsv and channels.tsv files to populate the returned raw object with raw.annotations and raw.info['bads']. Parameters ---------- bids_path : BIDSPath The file to read. The :class:`mne_bids.BIDSPath` instance passed here **must** have the ``.root`` attribute set. The ``.datatype`` attribute **may** be set. If ``.datatype`` is not set and only one data type (e.g., only EEG or MEG data) is present in the dataset, it will be selected automatically. .. note:: If ``bids_path`` points to a symbolic link of a ``.fif`` file without a ``split`` entity, the link will be resolved before reading. extra_params : None | dict Extra parameters to be passed to MNE read_raw_* functions. Note that the ``exclude`` parameter, which is supported by some MNE-Python readers, is not supported; instead, you need to subset your channels **after** reading. %(verbose)s Returns ------- raw : mne.io.Raw The data as MNE-Python Raw object. Raises ------ RuntimeError If multiple recording data types are present in the dataset, but ``datatype=None``. RuntimeError If more than one data files exist for the specified recording. RuntimeError If no data file in a supported format can be located. ValueError If the specified ``datatype`` cannot be found in the dataset. """ if not isinstance(bids_path, BIDSPath): raise RuntimeError('"bids_path" must be a BIDSPath object. Please ' 'instantiate using mne_bids.BIDSPath().') bids_path = bids_path.copy() sub = bids_path.subject ses = bids_path.session bids_root = bids_path.root datatype = bids_path.datatype suffix = bids_path.suffix # check root available if bids_root is None: raise ValueError('The root of the "bids_path" must be set. ' 'Please use `bids_path.update(root="<root>")` ' 'to set the root of the BIDS folder to read.') # infer the datatype and suffix if they are not present in the BIDSPath if datatype is None: datatype = _infer_datatype(root=bids_root, sub=sub, ses=ses) bids_path.update(datatype=datatype) if suffix is None: bids_path.update(suffix=datatype) if bids_path.fpath.suffix == '.pdf': bids_raw_folder = bids_path.directory / f'{bids_path.basename}' raw_path = list(bids_raw_folder.glob('c,rf*'))[0] config_path = bids_raw_folder / 'config' else: raw_path = bids_path.fpath # Resolve for FIFF files if (raw_path.suffix == '.fif' and bids_path.split is None and raw_path.is_symlink()): target_path = raw_path.resolve() logger.info(f'Resolving symbolic link: ' f'{raw_path} -> {target_path}') raw_path = target_path config_path = None # Special-handle EDF filenames: we accept upper- and lower-case extensions if raw_path.suffix.lower() == '.edf': for extension in ('.edf', '.EDF'): candidate_path = raw_path.with_suffix(extension) if candidate_path.exists(): raw_path = candidate_path break if not raw_path.exists(): raise FileNotFoundError(f'File does not exist: {raw_path}') if config_path is not None and not config_path.exists(): raise FileNotFoundError(f'config directory not found: {config_path}') if extra_params is None: extra_params = dict() elif 'exclude' in extra_params: del extra_params['exclude'] logger.info('"exclude" parameter is not supported by read_raw_bids') if raw_path.suffix == '.fif' and 'allow_maxshield' not in extra_params: extra_params['allow_maxshield'] = True raw = _read_raw(raw_path, electrode=None, hsp=None, hpi=None, config_path=config_path, **extra_params) # Try to find an associated events.tsv to get information about the # events in the recorded data events_fname = _find_matching_sidecar(bids_path, suffix='events', extension='.tsv', on_error='warn') if events_fname is not None: raw = _handle_events_reading(events_fname, raw) # Try to find an associated channels.tsv to get information about the # status and type of present channels channels_fname = _find_matching_sidecar(bids_path, suffix='channels', extension='.tsv', on_error='warn') if channels_fname is not None: raw = _handle_channels_reading(channels_fname, raw) # Try to find an associated electrodes.tsv and coordsystem.json # to get information about the status and type of present channels on_error = 'warn' if suffix == 'ieeg' else 'ignore' electrodes_fname = _find_matching_sidecar(bids_path, suffix='electrodes', extension='.tsv', on_error=on_error) coordsystem_fname = _find_matching_sidecar(bids_path, suffix='coordsystem', extension='.json', on_error=on_error) if electrodes_fname is not None: if coordsystem_fname is None: raise RuntimeError(f"BIDS mandates that the coordsystem.json " f"should exist if electrodes.tsv does. " f"Please create coordsystem.json for" f"{bids_path.basename}") if datatype in ['meg', 'eeg', 'ieeg']: _read_dig_bids(electrodes_fname, coordsystem_fname, raw=raw, datatype=datatype) # Try to find an associated sidecar .json to get information about the # recording snapshot sidecar_fname = _find_matching_sidecar(bids_path, suffix=datatype, extension='.json', on_error='warn') if sidecar_fname is not None: raw = _handle_info_reading(sidecar_fname, raw) # read in associated scans filename scans_fname = BIDSPath(subject=bids_path.subject, session=bids_path.session, suffix='scans', extension='.tsv', root=bids_path.root).fpath if scans_fname.exists(): raw = _handle_scans_reading(scans_fname, raw, bids_path) # read in associated subject info from participants.tsv participants_tsv_path = bids_root / 'participants.tsv' subject = f"sub-{bids_path.subject}" if op.exists(participants_tsv_path): raw = _handle_participants_reading( participants_fname=participants_tsv_path, raw=raw, subject=subject) else: warn(f"participants.tsv file not found for {raw_path}") assert raw.annotations.orig_time == raw.info['meas_date'] return raw