def test_line_freq_estimation(): """Test estimating line frequency.""" bids_root = _TempDir() # read in USA dataset, so it should find 50 Hz raw = mne.io.read_raw_fif(raw_fname) kind = "meg" # assert that we get the same line frequency set bids_fname = bids_basename.copy().update(suffix=f'{kind}.fif') # find sidecar JSON fname write_raw_bids(raw, bids_basename, bids_root, overwrite=True) sidecar_fname = _find_matching_sidecar(bids_fname, bids_root, '{}.json'.format(kind), allow_fail=True) # 1. when nothing is set, default to use PSD estimation -> should be 60 # for `sample` dataset raw.info['line_freq'] = None write_raw_bids(raw, bids_basename, bids_root, overwrite=True) _update_sidecar(sidecar_fname, "PowerLineFrequency", "n/a") with pytest.warns(RuntimeWarning, match="No line frequency found"): raw = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, kind=kind) assert raw.info['line_freq'] == 60 # test that `somato` dataset finds 50 Hz (EU dataset) somato_raw = mne.io.read_raw_fif(somato_raw_fname) somato_raw.info['line_freq'] = None write_raw_bids(somato_raw, bids_basename, bids_root, overwrite=True) sidecar_fname = _find_matching_sidecar(bids_fname, bids_root, '{}.json'.format(kind), allow_fail=True) _update_sidecar(sidecar_fname, "PowerLineFrequency", "n/a") with pytest.warns(RuntimeWarning, match="No line frequency found"): somato_raw = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, kind=kind) assert somato_raw.info['line_freq'] == 50 # assert that line_freq should be None when # all picks are not meg/eeg/ecog/seeg somato_raw.info['line_freq'] = None somato_raw.set_channel_types({ somato_raw.ch_names[i]: 'bio' for i in range(len(somato_raw.ch_names)) }) somato_raw = _handle_info_reading(sidecar_fname, somato_raw, verbose=True) assert somato_raw.info['line_freq'] is None
def _update_electrodes_json(electrodes_json_fpath, **kwargs): electrodes_json_fpath = Path(electrodes_json_fpath) if not electrodes_json_fpath.exists(): electrodes_json_fpath.parent.mkdir(parents=True, exist_ok=True) with open(electrodes_json_fpath, "w") as fout: sidecar_json = json.dump(kwargs, fout) else: for key, val in kwargs.items(): _update_sidecar(electrodes_json_fpath, key, val) with open(electrodes_json_fpath, "r") as fin: sidecar_json = json.load(fin) return sidecar_json
def test_handle_info_reading(): """Test reading information from a BIDS sidecar.json file.""" bids_root = _TempDir() # read in USA dataset, so it should find 50 Hz raw = _read_raw_fif(raw_fname) # write copy of raw with line freq of 60 # bids basename and fname bids_path = BIDSPath(subject='01', session='01', task='audiovisual', run='01', root=bids_root) suffix = "meg" bids_fname = bids_path.copy().update(suffix=suffix, extension='.fif') write_raw_bids(raw, bids_path, overwrite=True) # find sidecar JSON fname bids_fname.update(datatype=suffix) sidecar_fname = _find_matching_sidecar(bids_fname, suffix=suffix, extension='.json') # assert that we get the same line frequency set raw = read_raw_bids(bids_path=bids_path) assert raw.info['line_freq'] == 60 # 2. if line frequency is not set in raw file, then ValueError raw.info['line_freq'] = None with pytest.raises(ValueError, match="PowerLineFrequency .* required"): write_raw_bids(raw, bids_path, overwrite=True) # make a copy of the sidecar in "derivatives/" # to check that we make sure we always get the right sidecar # in addition, it should not break the sidecar reading # in `read_raw_bids` deriv_dir = op.join(bids_root, "derivatives") sidecar_copy = op.join(deriv_dir, op.basename(sidecar_fname)) os.mkdir(deriv_dir) with open(sidecar_fname, "r") as fin: sidecar_json = json.load(fin) sidecar_json["PowerLineFrequency"] = 45 _write_json(sidecar_copy, sidecar_json) raw = read_raw_bids(bids_path=bids_path) assert raw.info['line_freq'] == 60 # 3. assert that we get an error when sidecar json doesn't match _update_sidecar(sidecar_fname, "PowerLineFrequency", 55) with pytest.raises(ValueError, match="Line frequency in sidecar json"): raw = read_raw_bids(bids_path=bids_path) assert raw.info['line_freq'] == 55
def test_handle_info_reading(): """Test reading information from a BIDS sidecar.json file.""" bids_root = _TempDir() # read in USA dataset, so it should find 50 Hz raw = mne.io.read_raw_fif(raw_fname) raw.info['line_freq'] = 60 # write copy of raw with line freq of 60 # bids basename and fname bids_basename = make_bids_basename(subject='01', session='01', task='audiovisual', run='01') kind = "meg" bids_fname = bids_basename + '_{}.fif'.format(kind) write_raw_bids(raw, bids_basename, bids_root, overwrite=True) # find sidecar JSON fname sidecar_fname = _find_matching_sidecar(bids_fname, bids_root, '{}.json'.format(kind), allow_fail=True) # assert that we get the same line frequency set raw = mne_bids.read_raw_bids(bids_fname, bids_root) assert raw.info['line_freq'] == 60 # 2. if line frequency is not set in raw file, then default to sidecar raw.info['line_freq'] = None write_raw_bids(raw, bids_basename, bids_root, overwrite=True) _update_sidecar(sidecar_fname, "PowerLineFrequency", 55) raw = mne_bids.read_raw_bids(bids_fname, bids_root) assert raw.info['line_freq'] == 55 # make a copy of the sidecar in "derivatives/" # to check that we make sure we always get the right sidecar # in addition, it should not break the sidecar reading # in `read_raw_bids` deriv_dir = op.join(bids_root, "derivatives") sidecar_copy = op.join(deriv_dir, op.basename(sidecar_fname)) os.mkdir(deriv_dir) with open(sidecar_fname, "r") as fin: sidecar_json = json.load(fin) sidecar_json["PowerLineFrequency"] = 45 _write_json(sidecar_copy, sidecar_json) raw = mne_bids.read_raw_bids(bids_fname, bids_root) assert raw.info['line_freq'] == 55 # 3. if line frequency is set in raw file, but not sidecar raw.info['line_freq'] = 60 write_raw_bids(raw, bids_basename, bids_root, overwrite=True) _update_sidecar(sidecar_fname, "PowerLineFrequency", "n/a") raw = mne_bids.read_raw_bids(bids_fname, bids_root) assert raw.info['line_freq'] == 60 # 4. assert that we get an error when sidecar json doesn't match _update_sidecar(sidecar_fname, "PowerLineFrequency", 55) with pytest.raises(ValueError, match="Line frequency in sidecar json"): raw = mne_bids.read_raw_bids(bids_fname, bids_root)
def test_handle_ieeg_coords_reading(bids_basename): """Test reading iEEG coordinates from BIDS files.""" bids_root = _TempDir() data_path = op.join(testing.data_path(), 'EDF') raw_fname = op.join(data_path, 'test_reduced.edf') bids_fname = bids_basename.copy().update(suffix='ieeg.edf') raw = mne.io.read_raw_edf(raw_fname) # ensure we are writing 'ecog'/'ieeg' data raw.set_channel_types({ch: 'ecog' for ch in raw.ch_names}) # coordinate frames in mne-python should all map correctly # set a `random` montage ch_names = raw.ch_names elec_locs = np.random.random((len(ch_names), 3)).astype(float) ch_pos = dict(zip(ch_names, elec_locs)) coordinate_frames = ['mri', 'ras'] for coord_frame in coordinate_frames: # XXX: mne-bids doesn't support multiple electrodes.tsv files sh.rmtree(bids_root) montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame=coord_frame) raw.set_montage(montage) write_raw_bids(raw, bids_basename, bids_root, overwrite=True, verbose=False) # read in raw file w/ updated coordinate frame # and make sure all digpoints are correct coordinate frames raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) coord_frame_int = MNE_STR_TO_FRAME[coord_frame] for digpoint in raw_test.info['dig']: assert digpoint['coord_frame'] == coord_frame_int # start w/ new bids root sh.rmtree(bids_root) write_raw_bids(raw, bids_basename, bids_root, overwrite=True, verbose=False) # obtain the sensor positions and assert ch_coords are same raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) orig_locs = raw.info['dig'][1] test_locs = raw_test.info['dig'][1] assert orig_locs == test_locs assert not object_diff(raw.info['chs'], raw_test.info['chs']) # read in the data and assert montage is the same # regardless of 'm', 'cm', 'mm', or 'pixel' scalings = {'m': 1, 'cm': 100, 'mm': 1000} coordsystem_fname = _find_matching_sidecar(bids_fname, bids_root, suffix='coordsystem.json', allow_fail=True) electrodes_fname = _find_matching_sidecar(bids_fname, bids_root, "electrodes.tsv", allow_fail=True) orig_electrodes_dict = _from_tsv(electrodes_fname, [str, float, float, float, str]) # not BIDS specified should not be read coord_unit = 'km' scaling = 0.001 _update_sidecar(coordsystem_fname, 'iEEGCoordinateUnits', coord_unit) electrodes_dict = _from_tsv(electrodes_fname, [str, float, float, float, str]) for axis in ['x', 'y', 'z']: electrodes_dict[axis] = \ np.multiply(orig_electrodes_dict[axis], scaling) _to_tsv(electrodes_dict, electrodes_fname) with pytest.warns(RuntimeWarning, match='Coordinate unit is not ' 'an accepted BIDS unit'): raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) # correct BIDS units should scale to meters properly for coord_unit, scaling in scalings.items(): # update coordinate SI units _update_sidecar(coordsystem_fname, 'iEEGCoordinateUnits', coord_unit) electrodes_dict = _from_tsv(electrodes_fname, [str, float, float, float, str]) for axis in ['x', 'y', 'z']: electrodes_dict[axis] = \ np.multiply(orig_electrodes_dict[axis], scaling) _to_tsv(electrodes_dict, electrodes_fname) # read in raw file w/ updated montage raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) # obtain the sensor positions and make sure they're the same assert_dig_allclose(raw.info, raw_test.info) # XXX: Improve by changing names to 'unknown' coordframe (needs mne PR) # check that coordinate systems other coordinate systems should be named # in the file and not the CoordinateSystem, which is reserved for keywords coordinate_frames = ['lia', 'ria', 'lip', 'rip', 'las'] for coord_frame in coordinate_frames: # update coordinate units _update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', coord_frame) # read in raw file w/ updated coordinate frame # and make sure all digpoints are MRI coordinate frame with pytest.warns(RuntimeWarning, match="iEEG Coordinate frame is " "not accepted BIDS keyword"): raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) assert raw_test.info['dig'] is None # ACPC should be read in as RAS for iEEG _update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', 'acpc') raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) coord_frame_int = MNE_STR_TO_FRAME['ras'] for digpoint in raw_test.info['dig']: assert digpoint['coord_frame'] == coord_frame_int # test error message if electrodes don't match write_raw_bids(raw, bids_basename, bids_root, overwrite=True) electrodes_dict = _from_tsv(electrodes_fname) # pop off 5 channels for key in electrodes_dict.keys(): for i in range(5): electrodes_dict[key].pop() _to_tsv(electrodes_dict, electrodes_fname) with pytest.raises(RuntimeError, match='Channels do not correspond'): raw_test = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) # make sure montage is set if there are coordinates w/ 'n/a' raw.info['bads'] = [] write_raw_bids(raw, bids_basename, bids_root, overwrite=True, verbose=False) electrodes_dict = _from_tsv(electrodes_fname) for axis in ['x', 'y', 'z']: electrodes_dict[axis][0] = 'n/a' electrodes_dict[axis][3] = 'n/a' _to_tsv(electrodes_dict, electrodes_fname) # test if montage is correctly set via mne-bids # electrode coordinates should be nan # when coordinate is 'n/a' nan_chs = [electrodes_dict['name'][i] for i in [0, 3]] with pytest.warns(RuntimeWarning, match='There are channels ' 'without locations'): raw = read_raw_bids(bids_basename=bids_basename, bids_root=bids_root, verbose=False) for idx, ch in enumerate(raw.info['chs']): if ch['ch_name'] in nan_chs: assert all(np.isnan(ch['loc'][:3])) else: assert not any(np.isnan(ch['loc'][:3])) assert ch['ch_name'] not in raw.info['bads']
def test_handle_eeg_coords_reading(): """Test reading iEEG coordinates from BIDS files.""" bids_root = _TempDir() data_path = op.join(testing.data_path(), 'EDF') raw_fname = op.join(data_path, 'test_reduced.edf') raw = mne.io.read_raw_edf(raw_fname) # ensure we are writing 'eeg' data raw.set_channel_types({ch: 'eeg' for ch in raw.ch_names}) # set a `random` montage ch_names = raw.ch_names elec_locs = np.random.random((len(ch_names), 3)).astype(float) ch_pos = dict(zip(ch_names, elec_locs)) # # create montage in 'unknown' coordinate frame # # and assert coordsystem/electrodes sidecar tsv don't exist montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame="unknown") raw.set_montage(montage) with pytest.warns(RuntimeWarning, match="Skipping EEG electrodes.tsv"): write_raw_bids(raw, bids_basename, bids_root, overwrite=True) coordsystem_fname = _find_matching_sidecar(bids_basename, bids_root, suffix='coordsystem.json', allow_fail=True) electrodes_fname = _find_matching_sidecar(bids_basename, bids_root, suffix="electrodes.tsv", allow_fail=True) assert coordsystem_fname is None assert electrodes_fname is None # create montage in head frame and set should result in # warning if landmarks not set montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame="head") raw.set_montage(montage) with pytest.warns(RuntimeWarning, match='Setting montage not possible ' 'if anatomical landmarks'): write_raw_bids(raw, bids_basename, bids_root, overwrite=True) montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame="head", nasion=[1, 0, 0], lpa=[0, 1, 0], rpa=[0, 0, 1]) raw.set_montage(montage) write_raw_bids(raw, bids_basename, bids_root, overwrite=True) # obtain the sensor positions and assert ch_coords are same raw_test = read_raw_bids(bids_basename, bids_root, verbose=True) assert not object_diff(raw.info['chs'], raw_test.info['chs']) # modify coordinate frame to not-captrak coordsystem_fname = _find_matching_sidecar(bids_basename, bids_root, suffix='coordsystem.json', allow_fail=True) _update_sidecar(coordsystem_fname, 'EEGCoordinateSystem', 'besa') with pytest.warns(RuntimeWarning, match='EEG Coordinate frame is not ' 'accepted BIDS keyword'): raw_test = read_raw_bids(bids_basename, bids_root) assert raw_test.info['dig'] is None