def test_ch_loc(): """Test raw kit loc.""" raw_py = read_raw_kit(sqd_path, mrk_path, elp_txt_path, hsp_txt_path, stim='<') raw_bin = read_raw_fif(op.join(data_dir, 'test_bin_raw.fif')) ch_py = np.array([ch['loc'] for ch in raw_py._raw_extras[0]['channels'][:160]]) # ch locs stored as m, not mm ch_py[:, :3] *= 1e3 ch_sns = read_sns(op.join(data_dir, 'sns.txt')) assert_array_almost_equal(ch_py, ch_sns, 2) assert_array_almost_equal(raw_py.info['dev_head_t']['trans'], raw_bin.info['dev_head_t']['trans'], 4) for py_ch, bin_ch in zip(raw_py.info['chs'], raw_bin.info['chs']): if bin_ch['ch_name'].startswith('MEG'): # the stored ch locs have more precision than the sns.txt assert_array_almost_equal(py_ch['loc'], bin_ch['loc'], decimal=2) # test when more than one marker file provided mrks = [mrk_path, mrk2_path, mrk3_path] read_raw_kit(sqd_path, mrks, elp_txt_path, hsp_txt_path, preload=False) # this dataset does not have the equivalent set of points :( raw_bin.info['dig'] = raw_bin.info['dig'][:8] raw_py.info['dig'] = raw_py.info['dig'][:8] assert_dig_allclose(raw_py.info, raw_bin.info)
def test_ch_loc(): """Test raw kit loc.""" raw_py = read_raw_kit(sqd_path, mrk_path, elp_txt_path, hsp_txt_path, stim='<') raw_bin = read_raw_fif(op.join(data_dir, 'test_bin_raw.fif')) ch_py = np.array([ch['loc'] for ch in raw_py._raw_extras[0]['channels'][:160]]) # ch locs stored as m, not mm ch_py[:, :3] *= 1e3 ch_sns = read_sns(op.join(data_dir, 'sns.txt')) assert_array_almost_equal(ch_py, ch_sns, 2) assert_array_almost_equal(raw_py.info['dev_head_t']['trans'], raw_bin.info['dev_head_t']['trans'], 4) for py_ch, bin_ch in zip(raw_py.info['chs'], raw_bin.info['chs']): if bin_ch['ch_name'].startswith('MEG'): # the stored ch locs have more precision than the sns.txt assert_array_almost_equal(py_ch['loc'], bin_ch['loc'], decimal=2) # test when more than one marker file provided mrks = [mrk_path, mrk2_path, mrk3_path] read_raw_kit(sqd_path, mrks, elp_txt_path, hsp_txt_path, preload=False) # this dataset does not have the equivalent set of points :( raw_bin.info['dig'] = raw_bin.info['dig'][:8] raw_py.info['dig'] = raw_py.info['dig'][:8] assert_dig_allclose(raw_py.info, raw_bin.info)
def test_egi_dig_montage(): """Test EGI MFF XML dig montage support.""" dig_montage = read_dig_montage(egi=egi_dig_montage_fname, unit='m') # # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'egi_test.fif') _check_roundtrip(dig_montage, fname_temp) # Test coordinate transform dig_montage.transform_to_head() # nasion assert_almost_equal(dig_montage.nasion[0], 0) assert_almost_equal(dig_montage.nasion[2], 0) # lpa and rpa assert_allclose(dig_montage.lpa[1:], 0, atol=1e-16) assert_allclose(dig_montage.rpa[1:], 0, atol=1e-16) # Test accuracy and embedding within raw object raw_egi = read_raw_egi(egi_raw_fname, channel_naming='EEG %03d') raw_egi.set_montage(dig_montage) test_raw_egi = read_raw_fif(egi_fif_fname) assert_equal(len(raw_egi.ch_names), len(test_raw_egi.ch_names)) for ch_raw, ch_test_raw in zip(raw_egi.info['chs'], test_raw_egi.info['chs']): assert_equal(ch_raw['ch_name'], ch_test_raw['ch_name']) assert_equal(ch_raw['coord_frame'], FIFF.FIFFV_COORD_HEAD) assert_allclose(ch_raw['loc'], ch_test_raw['loc'], atol=1e-7) assert_dig_allclose(raw_egi.info, test_raw_egi.info)
def test_egi_dig_montage(): """Test EGI MFF XML dig montage support.""" dig_montage = read_dig_montage(egi=egi_dig_montage_fname, unit='m') # # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'egi_test.fif') _check_roundtrip(dig_montage, fname_temp) # Test coordinate transform dig_montage.transform_to_head() # nasion assert_almost_equal(dig_montage.nasion[0], 0) assert_almost_equal(dig_montage.nasion[2], 0) # lpa and rpa assert_allclose(dig_montage.lpa[1:], 0, atol=1e-16) assert_allclose(dig_montage.rpa[1:], 0, atol=1e-16) # Test accuracy and embedding within raw object raw_egi = read_raw_egi(egi_raw_fname, channel_naming='EEG %03d') raw_egi.set_montage(dig_montage) test_raw_egi = read_raw_fif(egi_fif_fname) assert_equal(len(raw_egi.ch_names), len(test_raw_egi.ch_names)) for ch_raw, ch_test_raw in zip(raw_egi.info['chs'], test_raw_egi.info['chs']): assert_equal(ch_raw['ch_name'], ch_test_raw['ch_name']) assert_equal(ch_raw['coord_frame'], FIFF.FIFFV_COORD_HEAD) assert_allclose(ch_raw['loc'], ch_test_raw['loc'], atol=1e-7) assert_dig_allclose(raw_egi.info, test_raw_egi.info)
def test_raw(): """Test bti conversion to Raw object.""" for pdf, config, hs, exported in zip(pdf_fnames, config_fnames, hs_fnames, exported_fnames): # rx = 2 if 'linux' in pdf else 0 pytest.raises(ValueError, read_raw_bti, pdf, 'eggs', preload=False) pytest.raises(ValueError, read_raw_bti, pdf, config, 'spam', preload=False) if op.exists(tmp_raw_fname): os.remove(tmp_raw_fname) ex = read_raw_fif(exported, preload=True) ra = read_raw_bti(pdf, config, hs, preload=False) assert ('RawBTi' in repr(ra)) assert_equal(ex.ch_names[:NCH], ra.ch_names[:NCH]) assert_array_almost_equal(ex.info['dev_head_t']['trans'], ra.info['dev_head_t']['trans'], 7) assert len(ex.info['dig']) in (3563, 5154) assert_dig_allclose(ex.info, ra.info, limit=100) coil1, coil2 = [ np.concatenate([d['loc'].flatten() for d in r_.info['chs'][:NCH]]) for r_ in (ra, ex) ] assert_array_almost_equal(coil1, coil2, 7) loc1, loc2 = [ np.concatenate([d['loc'].flatten() for d in r_.info['chs'][:NCH]]) for r_ in (ra, ex) ] assert_allclose(loc1, loc2) assert_allclose(ra[:NCH][0], ex[:NCH][0]) assert_array_equal([c['range'] for c in ra.info['chs'][:NCH]], [c['range'] for c in ex.info['chs'][:NCH]]) assert_array_equal([c['cal'] for c in ra.info['chs'][:NCH]], [c['cal'] for c in ex.info['chs'][:NCH]]) assert_array_equal(ra._cals[:NCH], ex._cals[:NCH]) # check our transforms for key in ('dev_head_t', 'dev_ctf_t', 'ctf_head_t'): if ex.info[key] is None: pass else: assert (ra.info[key] is not None) for ent in ('to', 'from', 'trans'): assert_allclose(ex.info[key][ent], ra.info[key][ent]) ra.save(tmp_raw_fname) re = read_raw_fif(tmp_raw_fname) print(re) for key in ('dev_head_t', 'dev_ctf_t', 'ctf_head_t'): assert (isinstance(re.info[key], dict)) this_t = re.info[key]['trans'] assert_equal(this_t.shape, (4, 4)) # check that matrix by is not identity assert (not np.allclose(this_t, np.eye(4))) os.remove(tmp_raw_fname)
def test_bvct_dig_montage_old_api(): # XXX: to remove in 0.20 """Test BrainVision CapTrak XML dig montage support.""" with pytest.warns(RuntimeWarning, match='Using "m" as unit for BVCT file'): read_dig_montage(bvct=bvct_dig_montage_fname, unit='m') with pytest.deprecated_call(): dig_montage = read_dig_montage(bvct=bvct_dig_montage_fname) # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'bvct_test.fif') _check_roundtrip(dig_montage, fname_temp) with pytest.deprecated_call(): # nasion assert_almost_equal(dig_montage.nasion[0], 0) assert_almost_equal(dig_montage.nasion[2], 0) # lpa and rpa assert_allclose(dig_montage.lpa[1:], 0, atol=1e-16) assert_allclose(dig_montage.rpa[1:], 0, atol=1e-16) # Test accuracy and embedding within raw object raw_bv = read_raw_brainvision(bv_raw_fname) with pytest.warns(RuntimeWarning, match='Did not set.*channel pos'): raw_bv.set_montage(dig_montage) test_raw_bv = read_raw_fif(bv_fif_fname) assert_equal(len(raw_bv.ch_names), len(test_raw_bv.ch_names)) for ch_raw, ch_test_raw in zip(raw_bv.info['chs'], test_raw_bv.info['chs']): assert_equal(ch_raw['ch_name'], ch_test_raw['ch_name']) assert_equal(ch_raw['coord_frame'], FIFF.FIFFV_COORD_HEAD) assert_allclose(ch_raw['loc'], ch_test_raw['loc'], atol=1e-7) assert_dig_allclose(raw_bv.info, test_raw_bv.info)
def test_fif_dig_montage(): """Test FIF dig montage support.""" dig_montage = read_dig_fif(fif_dig_montage_fname) # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'test.fif') _check_roundtrip(dig_montage, fname_temp) # Make a BrainVision file like the one the user would have had raw_bv = read_raw_brainvision(bv_fname, preload=True) raw_bv_2 = raw_bv.copy() mapping = dict() for ii, ch_name in enumerate(raw_bv.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 1,) raw_bv.rename_channels(mapping) for ii, ch_name in enumerate(raw_bv_2.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 33,) raw_bv_2.rename_channels(mapping) raw_bv.add_channels([raw_bv_2]) for ch in raw_bv.info['chs']: ch['kind'] = FIFF.FIFFV_EEG_CH # Set the montage raw_bv.set_montage(dig_montage) # Check the result evoked = read_evokeds(evoked_fname)[0] # check info[chs] matches assert_equal(len(raw_bv.ch_names), len(evoked.ch_names) - 1) for ch_py, ch_c in zip(raw_bv.info['chs'], evoked.info['chs'][:-1]): assert_equal(ch_py['ch_name'], ch_c['ch_name'].replace('EEG ', 'EEG')) # C actually says it's unknown, but it's not (?): # assert_equal(ch_py['coord_frame'], ch_c['coord_frame']) assert_equal(ch_py['coord_frame'], FIFF.FIFFV_COORD_HEAD) c_loc = ch_c['loc'].copy() c_loc[c_loc == 0] = np.nan assert_allclose(ch_py['loc'], c_loc, atol=1e-7) # check info[dig] assert_dig_allclose(raw_bv.info, evoked.info) # Roundtrip of non-FIF start montage = make_dig_montage(hsp=read_polhemus_fastscan(hsp), hpi=read_mrk(hpi)) elp_points = read_polhemus_fastscan(elp) ch_pos = {"EEG%03d" % (k + 1): pos for k, pos in enumerate(elp_points[8:])} montage += make_dig_montage(nasion=elp_points[0], lpa=elp_points[1], rpa=elp_points[2], ch_pos=ch_pos) pytest.raises(RuntimeError, montage.save, fname_temp) # must be head coord montage = transform_to_head(montage) _check_roundtrip(montage, fname_temp)
def test_raw(): """Test bti conversion to Raw object.""" for pdf, config, hs, exported in zip(pdf_fnames, config_fnames, hs_fnames, exported_fnames): # rx = 2 if 'linux' in pdf else 0 pytest.raises(ValueError, read_raw_bti, pdf, 'eggs', preload=False) pytest.raises(ValueError, read_raw_bti, pdf, config, 'spam', preload=False) if op.exists(tmp_raw_fname): os.remove(tmp_raw_fname) ex = read_raw_fif(exported, preload=True) ra = read_raw_bti(pdf, config, hs, preload=False) assert ('RawBTi' in repr(ra)) assert_equal(ex.ch_names[:NCH], ra.ch_names[:NCH]) assert_array_almost_equal(ex.info['dev_head_t']['trans'], ra.info['dev_head_t']['trans'], 7) assert len(ex.info['dig']) in (3563, 5154) assert_dig_allclose(ex.info, ra.info, limit=100) coil1, coil2 = [np.concatenate([d['loc'].flatten() for d in r_.info['chs'][:NCH]]) for r_ in (ra, ex)] assert_array_almost_equal(coil1, coil2, 7) loc1, loc2 = [np.concatenate([d['loc'].flatten() for d in r_.info['chs'][:NCH]]) for r_ in (ra, ex)] assert_allclose(loc1, loc2) assert_allclose(ra[:NCH][0], ex[:NCH][0]) assert_array_equal([c['range'] for c in ra.info['chs'][:NCH]], [c['range'] for c in ex.info['chs'][:NCH]]) assert_array_equal([c['cal'] for c in ra.info['chs'][:NCH]], [c['cal'] for c in ex.info['chs'][:NCH]]) assert_array_equal(ra._cals[:NCH], ex._cals[:NCH]) # check our transforms for key in ('dev_head_t', 'dev_ctf_t', 'ctf_head_t'): if ex.info[key] is None: pass else: assert (ra.info[key] is not None) for ent in ('to', 'from', 'trans'): assert_allclose(ex.info[key][ent], ra.info[key][ent]) ra.save(tmp_raw_fname) re = read_raw_fif(tmp_raw_fname) print(re) for key in ('dev_head_t', 'dev_ctf_t', 'ctf_head_t'): assert (isinstance(re.info[key], dict)) this_t = re.info[key]['trans'] assert_equal(this_t.shape, (4, 4)) # check that matrix by is not identity assert (not np.allclose(this_t, np.eye(4))) os.remove(tmp_raw_fname)
def test_fif_dig_montage(): """Test FIF dig montage support.""" with pytest.deprecated_call(): dig_montage = read_dig_montage(fif=fif_dig_montage_fname) # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'test.fif') _check_roundtrip(dig_montage, fname_temp) # Make a BrainVision file like the one the user would have had raw_bv = read_raw_brainvision(bv_fname, preload=True) raw_bv_2 = raw_bv.copy() mapping = dict() for ii, ch_name in enumerate(raw_bv.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 1, ) raw_bv.rename_channels(mapping) for ii, ch_name in enumerate(raw_bv_2.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 33, ) raw_bv_2.rename_channels(mapping) raw_bv.add_channels([raw_bv_2]) for ii in range(2): # Set the montage raw_bv.set_montage(dig_montage) # Check the result evoked = read_evokeds(evoked_fname)[0] assert_equal(len(raw_bv.ch_names), len(evoked.ch_names) - 1) for ch_py, ch_c in zip(raw_bv.info['chs'], evoked.info['chs'][:-1]): assert_equal(ch_py['ch_name'], ch_c['ch_name'].replace('EEG ', 'EEG')) # C actually says it's unknown, but it's not (?): # assert_equal(ch_py['coord_frame'], ch_c['coord_frame']) assert_equal(ch_py['coord_frame'], FIFF.FIFFV_COORD_HEAD) c_loc = ch_c['loc'].copy() c_loc[c_loc == 0] = np.nan assert_allclose(ch_py['loc'], c_loc, atol=1e-7) assert_dig_allclose(raw_bv.info, evoked.info) # Roundtrip of non-FIF start names = ['nasion', 'lpa', 'rpa', '1', '2', '3', '4', '5'] montage = read_dig_montage(hsp, hpi, elp, names, transform=False) pytest.raises(RuntimeError, montage.save, fname_temp) # must be head coord montage = read_dig_montage(hsp, hpi, elp, names) _check_roundtrip(montage, fname_temp) # Test old way matches new way with pytest.deprecated_call(): dig_montage = read_dig_montage(fif=fif_dig_montage_fname) dig_montage_fif = read_dig_fif(fif_dig_montage_fname) assert dig_montage.dig == dig_montage_fif.dig assert object_diff(dig_montage.ch_names, dig_montage_fif.ch_names) == ''
def test_fif_dig_montage(): """Test FIF dig montage support.""" dig_montage = read_dig_montage(fif=fif_dig_montage_fname) # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'test.fif') _check_roundtrip(dig_montage, fname_temp) # Make a BrainVision file like the one the user would have had raw_bv = read_raw_brainvision(bv_fname, preload=True) raw_bv_2 = raw_bv.copy() mapping = dict() for ii, ch_name in enumerate(raw_bv.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 1,) raw_bv.rename_channels(mapping) for ii, ch_name in enumerate(raw_bv_2.ch_names): mapping[ch_name] = 'EEG%03d' % (ii + 33,) raw_bv_2.rename_channels(mapping) raw_bv.add_channels([raw_bv_2]) for ii in range(2): if ii == 1: dig_montage.transform_to_head() # should have no meaningful effect # Set the montage raw_bv.set_montage(dig_montage) # Check the result evoked = read_evokeds(evoked_fname)[0] assert_equal(len(raw_bv.ch_names), len(evoked.ch_names) - 1) for ch_py, ch_c in zip(raw_bv.info['chs'], evoked.info['chs'][:-1]): assert_equal(ch_py['ch_name'], ch_c['ch_name'].replace('EEG ', 'EEG')) # C actually says it's unknown, but it's not (?): # assert_equal(ch_py['coord_frame'], ch_c['coord_frame']) assert_equal(ch_py['coord_frame'], FIFF.FIFFV_COORD_HEAD) c_loc = ch_c['loc'].copy() c_loc[c_loc == 0] = np.nan assert_allclose(ch_py['loc'], c_loc, atol=1e-7) assert_dig_allclose(raw_bv.info, evoked.info) # Roundtrip of non-FIF start names = ['nasion', 'lpa', 'rpa', '1', '2', '3', '4', '5'] montage = read_dig_montage(hsp, hpi, elp, names, transform=False) pytest.raises(RuntimeError, montage.save, fname_temp) # must be head coord montage = read_dig_montage(hsp, hpi, elp, names) _check_roundtrip(montage, fname_temp)
def test_egi_dig_montage(): """Test EGI MFF XML dig montage support.""" dig_montage = read_dig_egi(egi_dig_montage_fname) fid, coord = _get_fid_coords(dig_montage.dig) assert coord == FIFF.FIFFV_COORD_UNKNOWN assert_allclose( actual=np.array([fid[key] for key in ['nasion', 'lpa', 'rpa']]), desired=[ [0., 10.564, -2.051], # noqa [-8.592, 0.498, -4.128], # noqa [8.592, 0.498, -4.128] ], # noqa ) # Test accuracy and embedding within raw object raw_egi = read_raw_egi(egi_raw_fname, channel_naming='EEG %03d') raw_egi.set_montage(dig_montage) test_raw_egi = read_raw_fif(egi_fif_fname) assert_equal(len(raw_egi.ch_names), len(test_raw_egi.ch_names)) for ch_raw, ch_test_raw in zip(raw_egi.info['chs'], test_raw_egi.info['chs']): assert_equal(ch_raw['ch_name'], ch_test_raw['ch_name']) assert_equal(ch_raw['coord_frame'], FIFF.FIFFV_COORD_HEAD) assert_allclose(ch_raw['loc'], ch_test_raw['loc'], atol=1e-7) assert_dig_allclose(raw_egi.info, test_raw_egi.info) dig_montage_in_head = transform_to_head(dig_montage.copy()) fid, coord = _get_fid_coords(dig_montage_in_head.dig) assert coord == FIFF.FIFFV_COORD_HEAD assert_allclose( actual=np.array([fid[key] for key in ['nasion', 'lpa', 'rpa']]), desired=[[0., 10.278, 0.], [-8.592, 0., 0.], [8.592, 0., 0.]], atol=1e-4, ) # test round-trip IO temp_dir = _TempDir() fname_temp = op.join(temp_dir, 'egi_test.fif') _check_roundtrip(dig_montage_in_head, fname_temp) # XXX: write forces head
def test_read_dig_captrack(tmpdir): """Test reading a captrack montage file.""" EXPECTED_CH_NAMES = [ 'AF3', 'AF4', 'AF7', 'AF8', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'CP1', 'CP2', 'CP3', 'CP4', 'CP5', 'CP6', 'CPz', 'Cz', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'FC1', 'FC2', 'FC3', 'FC4', 'FC5', 'FC6', 'FT10', 'FT7', 'FT8', 'FT9', 'Fp1', 'Fp2', 'Fz', 'O1', 'O2', 'Oz', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'PO10', 'PO3', 'PO4', 'PO7', 'PO8', 'PO9', 'POz', 'Pz', 'T7', 'T8', 'TP10', 'TP7', 'TP8', 'TP9' ] montage = read_dig_captrack( fname=op.join(data_path, 'montage', 'captrak_coords.bvct') ) assert montage.ch_names == EXPECTED_CH_NAMES assert montage.__repr__() == ( '<DigMontage | ' '0 extras (headshape), 0 HPIs, 3 fiducials, 64 channels>' ) montage = transform_to_head(montage) # transform_to_head has to be tested _check_roundtrip(montage=montage, fname=str(tmpdir.join('bvct_test.fif'))) with pytest.deprecated_call(): assert_allclose( actual=np.array([montage.nasion, montage.lpa, montage.rpa]), desired=[[0, 0.11309, 0], [-0.09189, 0, 0], [0.09240, 0, 0]], atol=1e-5, ) # I think that comparing dig should be enough. cc: @sappelhoff raw_bv = read_raw_brainvision(bv_raw_fname) with pytest.warns(RuntimeWarning, match='Did not set 3 channel pos'): raw_bv.set_montage(montage) test_raw_bv = read_raw_fif(bv_fif_fname) assert_dig_allclose(raw_bv.info, test_raw_bv.info)
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_ieeg_coords_reading(bids_path, tmpdir): """Test reading iEEG coordinates from BIDS files.""" data_path = op.join(testing.data_path(), 'EDF') raw_fname = op.join(data_path, 'test_reduced.edf') bids_fname = bids_path.copy().update(datatype='ieeg', suffix='ieeg', extension='.edf', root=tmpdir) raw = _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 = ['mni_tal'] for coord_frame in coordinate_frames: # XXX: mne-bids doesn't support multiple electrodes.tsv files sh.rmtree(tmpdir) montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame=coord_frame) raw.set_montage(montage) write_raw_bids(raw, bids_fname, 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_path=bids_fname, 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(tmpdir) write_raw_bids(raw, bids_fname, overwrite=True, verbose=False) # obtain the sensor positions and assert ch_coords are same raw_test = read_raw_bids(bids_path=bids_fname, 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} bids_fname.update(root=tmpdir) coordsystem_fname = _find_matching_sidecar(bids_fname, suffix='coordsystem', extension='.json') electrodes_fname = _find_matching_sidecar(bids_fname, suffix='electrodes', extension='.tsv') 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_path=bids_fname, 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_path=bids_fname, 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 = ['Other'] 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="Defaulting coordinate " "frame to unknown"): raw_test = read_raw_bids(bids_path=bids_fname, verbose=False) assert raw_test.info['dig'] is not None # check that standard template identifiers that are unsupported in # mne-python coordinate frames, still get read in, but produce a warning coordinate_frames = [ 'individual', 'fsnative', 'scanner', 'ICBM452AirSpace', 'NIHPD' ] 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=f"iEEG Coordinate frame {coord_frame} " f"is not a readable BIDS keyword "): raw_test = read_raw_bids(bids_path=bids_fname, verbose=False) assert raw_test.info['dig'] is not None # ACPC should be read in as RAS for iEEG _update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', 'ACPC') raw_test = read_raw_bids(bids_path=bids_fname, verbose=False) coord_frame_int = MNE_STR_TO_FRAME['mri'] for digpoint in raw_test.info['dig']: assert digpoint['coord_frame'] == coord_frame_int # if we delete the coordsystem.json file, an error will be raised os.remove(coordsystem_fname) with pytest.raises(RuntimeError, match='BIDS mandates that ' 'the coordsystem.json'): raw = read_raw_bids(bids_path=bids_fname, verbose=False) # test error message if electrodes is not a subset of Raw bids_path.update(root=tmpdir) write_raw_bids(raw, bids_path, 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) # popping off channels should not result in an error # however, a warning will be raised through mne-python with pytest.warns(RuntimeWarning, match='DigMontage is ' 'only a subset of info'): read_raw_bids(bids_path=bids_fname, verbose=False) # make sure montage is set if there are coordinates w/ 'n/a' raw.info['bads'] = [] write_raw_bids(raw, bids_path, 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_path=bids_fname, 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']