def test_sensors_inside_bem(): """Test that sensors inside the BEM are problematic.""" rr = _get_ico_surface(1)['rr'] rr /= np.linalg.norm(rr, axis=1, keepdims=True) rr *= 0.1 assert len(rr) == 42 info = create_info(len(rr), 1000., 'mag') info['dev_head_t'] = Transform('meg', 'head', np.eye(4)) for ii, ch in enumerate(info['chs']): ch['loc'][:] = np.concatenate((rr[ii], np.eye(3).ravel())) trans = Transform('head', 'mri', np.eye(4)) trans['trans'][2, 3] = 0.03 sphere_noshell = make_sphere_model((0., 0., 0.), None) sphere = make_sphere_model((0., 0., 0.), 1.01) with pytest.raises(RuntimeError, match='.* 15 MEG.*inside the scalp.*'): make_forward_solution(info, trans, fname_src, fname_bem) make_forward_solution(info, trans, fname_src, fname_bem_meg) # okay make_forward_solution(info, trans, fname_src, sphere_noshell) # okay with pytest.raises(RuntimeError, match='.* 42 MEG.*outermost sphere sh.*'): make_forward_solution(info, trans, fname_src, sphere) sphere = make_sphere_model((0., 0., 2.0), 1.01) # weird, but okay make_forward_solution(info, trans, fname_src, sphere) for ch in info['chs']: ch['loc'][:3] *= 0.1 with pytest.raises(RuntimeError, match='.* 42 MEG.*the inner skull.*'): make_forward_solution(info, trans, fname_src, fname_bem_meg)
def gen_ras_to_head_trans(head_to_mri_t, t1_img): # RAS -> VOXEL ras_to_vox_t = Transform(fro='ras', to='mri_voxel', trans=np.linalg.inv(t1_img.header.get_vox2ras())) # VOXEL -> MRI vox_to_mri_t = Transform(fro='mri_voxel', to='mri', trans=t1_img.header.get_vox2ras_tkr()) # MRI -> HEAD mri_to_head_t = invert_transform(head_to_mri_t) # Now we have generated all the required transformations # to go from RAS to MNE Head coordinates. Let's combine # the transforms into a single transform. This requires # two calls to `combine_transforms()`. # RAS -> MRI ras_to_mri_t = combine_transforms(ras_to_vox_t, vox_to_mri_t, fro='ras', to='mri') # RAS -> HEAD ras_to_head_t = combine_transforms(ras_to_mri_t, mri_to_head_t, fro='ras', to='head') return ras_to_head_t
def make_static_trans(subj): from mne.transforms import Transform fro = 4 to = 5 trans_mat = np.array([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]]) trans = Transform(fro, to, trans_mat) fname_trans = op.join(study_path, 'source_stim', subj, 'source_files', 'orig', '%s_static-trans.fif' % subj) trans.save(fname_trans)
def _create_eeg_el(ch, t=None): """Create an electrode definition, transform coords if necessary.""" if ch['kind'] != FIFF.FIFFV_EEG_CH: raise RuntimeError('%s is not an EEG channel. Cannot create an ' 'electrode definition.' % ch['ch_name']) if t is None: t = Transform('head', 'head') # identity, no change if t.from_str != 'head': raise RuntimeError('Inappropriate coordinate transformation') r0ex = _loc_to_eeg_loc(ch['loc']) if r0ex.shape[1] == 1: # no reference w = np.array([1.]) else: # has reference w = np.array([1., -1.]) # Optional coordinate transformation r0ex = apply_trans(t['trans'], r0ex.T) # The electrode location cosmag = r0ex.copy() _normalize_vectors(cosmag) res = dict(chname=ch['ch_name'], coil_class=FIFF.FWD_COILC_EEG, w=w, accuracy=_accuracy_dict['normal'], type=ch['coil_type'], coord_frame=t['to'], rmag=r0ex, cosmag=cosmag) return res
def map_ch_coords_to_mne(inst): """Transform sensors to MNE coordinates .. note:: operates in place .. warning:: For several reasons we do not use the MNE coordinates for the inverse modeling. This however won't always play nicely with visualization. Parameters ---------- inst : MNE data containers Raw, Epochs, Evoked. """ bti_dev_t = Transform('ctf_meg', 'meg', _get_bti_dev_t()) dev_ctf_t = inst.info['dev_ctf_t'] for ch in inst.info['chs']: loc = ch['loc'][:] if loc is not None: logger.debug('converting %s' % ch['ch_name']) t = _loc_to_coil_trans(loc) t = _convert_coil_trans(t, dev_ctf_t, bti_dev_t) loc = _coil_trans_to_loc(t) ch['loc'] = loc
def test_use_coil_def(tmp_path): """Test use_coil_def.""" info = create_info(1, 1000., 'mag') info['chs'][0]['coil_type'] = 9999 info['chs'][0]['loc'][:] = [0, 0, 0.02, 1, 0, 0, 0, 1, 0, 0, 0, 1] sphere = make_sphere_model((0., 0., 0.), 0.01) src = setup_volume_source_space(pos=5, sphere=sphere) trans = Transform('head', 'mri', None) with pytest.raises(RuntimeError, match='coil definition not found'): make_forward_solution(info, trans, src, sphere) coil_fname = tmp_path / 'coil_def.dat' with open(coil_fname, 'w') as fid: fid.write("""# custom cube coil def 1 9999 2 8 3e-03 0.000e+00 "Test" 0.1250 -0.750e-03 -0.750e-03 -0.750e-03 0.000 0.000""") with pytest.raises(RuntimeError, match='Could not interpret'): with use_coil_def(coil_fname): make_forward_solution(info, trans, src, sphere) with open(coil_fname, 'w') as fid: fid.write("""# custom cube coil def 1 9999 2 8 3e-03 0.000e+00 "Test" 0.1250 -0.750e-03 -0.750e-03 -0.750e-03 0.000 0.000 1.000 0.1250 -0.750e-03 0.750e-03 -0.750e-03 0.000 0.000 1.000 0.1250 0.750e-03 -0.750e-03 -0.750e-03 0.000 0.000 1.000 0.1250 0.750e-03 0.750e-03 -0.750e-03 0.000 0.000 1.000 0.1250 -0.750e-03 -0.750e-03 0.750e-03 0.000 0.000 1.000 0.1250 -0.750e-03 0.750e-03 0.750e-03 0.000 0.000 1.000 0.1250 0.750e-03 -0.750e-03 0.750e-03 0.000 0.000 1.000 0.1250 0.750e-03 0.750e-03 0.750e-03 0.000 0.000 1.000""") with use_coil_def(coil_fname): make_forward_solution(info, trans, src, sphere)
def get_aligned_artifacts(info=None, trans=None, subject=None, subjects_dir=None, coord_frame='mri', head_surf=None): head_mri_t, _ = _get_trans(trans, 'head', 'mri') dev_head_t, _ = _get_trans(info['dev_head_t'], 'meg', 'head') head_trans = head_mri_t mri_trans = Transform('mri', 'mri') mri_fiducials = mne.coreg.get_mni_fiducials(subject, subjects_dir) fid_loc = _fiducial_coords(mri_fiducials, FIFF.FIFFV_COORD_MRI) fid_loc = apply_trans(mri_trans, fid_loc) fid_loc = pd.DataFrame(fid_loc, index=[fid["ident"]._name.split("_")[-1] for fid in mri_fiducials], columns=["x", "y", "z"]) if head_surf is None: subject_dir = Path(get_subjects_dir(subjects_dir, raise_error=True)) / subject fname = subject_dir / 'bem' / 'sample-head.fif' head_surf = read_bem_surfaces(fname)[0] head_surf = transform_surface_to(head_surf, coord_frame, [mri_trans, head_trans], copy=True) eeg_picks = mne.pick_types(info, meg=False, eeg=True, ref_meg=False) eeg_loc = np.array([info['chs'][k]['loc'][:3] for k in eeg_picks]) eeg_loc = apply_trans(head_trans, eeg_loc) eegp_loc = _project_onto_surface(eeg_loc, head_surf, project_rrs=True, return_nn=True)[2] eegp_loc = pd.DataFrame(eegp_loc, index=[ch["ch_name"] for ch in info['chs']], columns=["x", "y", "z"]) return eegp_loc, fid_loc, head_surf
def test_repr(): """Test Info repr.""" info = create_info(1, 1000, 'eeg') assert '7 non-empty values' in repr(info) t = Transform(1, 2, np.ones((4, 4))) info['dev_head_t'] = t assert 'dev_head_t: MEG device -> isotrak transform' in repr(info)
def test_transforms(): """Test transformations.""" bti_trans = (0.0, 0.02, 0.11) bti_dev_t = Transform('ctf_meg', 'meg', _get_bti_dev_t(0.0, bti_trans)) for pdf, config, hs, in zip(pdf_fnames, config_fnames, hs_fnames): raw = read_raw_bti(pdf, config, hs, preload=False) dev_ctf_t = raw.info['dev_ctf_t'] dev_head_t_old = raw.info['dev_head_t'] ctf_head_t = raw.info['ctf_head_t'] # 1) get BTI->Neuromag bti_dev_t = Transform('ctf_meg', 'meg', _get_bti_dev_t(0.0, bti_trans)) # 2) get Neuromag->BTI head t = combine_transforms(invert_transform(bti_dev_t), dev_ctf_t, 'meg', 'ctf_head') # 3) get Neuromag->head dev_head_t_new = combine_transforms(t, ctf_head_t, 'meg', 'head') assert_array_equal(dev_head_t_new['trans'], dev_head_t_old['trans'])
def _prepare_trans(info, trans, coord_frame='head'): head_mri_t = _ensure_trans(trans, 'head', 'mri') dev_head_t = info['dev_head_t'] del trans # Figure out our transformations if coord_frame == 'meg': head_trans = invert_transform(dev_head_t) meg_trans = Transform('meg', 'meg') mri_trans = invert_transform( combine_transforms(dev_head_t, head_mri_t, 'meg', 'mri')) elif coord_frame == 'mri': head_trans = head_mri_t meg_trans = combine_transforms(dev_head_t, head_mri_t, 'meg', 'mri') mri_trans = Transform('mri', 'mri') else: # coord_frame == 'head' head_trans = Transform('head', 'head') meg_trans = info['dev_head_t'] mri_trans = invert_transform(head_mri_t) return head_trans, meg_trans, mri_trans
def gen_ras_to_head_trans(head_to_mri_t, t1_img): mri_to_head_t = invert_transform(head_to_mri_t) # RAS <> VOXEL ras_to_vox_t = Transform(fro='ras', to='mri_voxel', trans=np.linalg.inv(t1_img.header.get_vox2ras())) # trans=t1_img.header.get_ras2vox()) vox_to_mri_t = Transform(fro='mri_voxel', to='mri', trans=t1_img.header.get_vox2ras_tkr()) # RAS <> MRI ras_to_mri_t = combine_transforms(ras_to_vox_t, vox_to_mri_t, fro='ras', to='mri') ras_to_head_t = combine_transforms(ras_to_mri_t, mri_to_head_t, fro='ras', to='head') return ras_to_head_t
def test_nan_trans(): """Test unlikely case that the device to head transform is empty.""" for ii, pdf_fname in enumerate(pdf_fnames): bti_info = _read_bti_header(pdf_fname, config_fnames[ii], sort_by_ch_name=True) dev_ctf_t = Transform('ctf_meg', 'ctf_head', _correct_trans(bti_info['bti_transform'][0])) # reading params convert = True rotation_x = 0. translation = (0.0, 0.02, 0.11) bti_dev_t = _get_bti_dev_t(rotation_x, translation) bti_dev_t = Transform('ctf_meg', 'meg', bti_dev_t) ecg_ch = 'E31' eog_ch = ('E63', 'E64') # read parts of info to get trans bti_ch_names = list() for ch in bti_info['chs']: ch_name = ch['name'] if not ch_name.startswith('A'): ch_name = ch.get('chan_label', ch_name) bti_ch_names.append(ch_name) neuromag_ch_names = _rename_channels(bti_ch_names, ecg_ch=ecg_ch, eog_ch=eog_ch) ch_mapping = zip(bti_ch_names, neuromag_ch_names) # add some nan in some locations! dev_ctf_t['trans'][:, 3] = np.nan _check_nan_dev_head_t(dev_ctf_t) for idx, (chan_4d, chan_neuromag) in enumerate(ch_mapping): loc = bti_info['chs'][idx]['loc'] if loc is not None: if convert: t = _loc_to_coil_trans(bti_info['chs'][idx]['loc']) t = _convert_coil_trans(t, dev_ctf_t, bti_dev_t)
def transform_sensors_to_mne(inst): """ Transform sensors to MNE coordinates For several reasons we do not use the MNE coordinates for the inverse modeling. This however won't always play nicely with visualization. """ bti_dev_t = Transform('ctf_meg', 'meg', _get_bti_dev_t()) dev_ctf_t = inst.info['dev_ctf_t'] for ch in inst.info['chs']: loc = ch['loc'][:] if loc is not None: print('converting %s' % ch['ch_name']) t = _loc_to_coil_trans(loc) t = _convert_coil_trans(t, dev_ctf_t, bti_dev_t) loc = _coil_trans_to_loc(t) ch['loc'] = loc
def test_info_bad(): """Test our info sanity checkers.""" info = create_info(2, 1000., 'eeg') info['description'] = 'foo' info['experimenter'] = 'bar' info['line_freq'] = 50. info['bads'] = info['ch_names'][:1] info['temp'] = ('whatever', 1.) # After 0.24 these should be pytest.raises calls check, klass = pytest.raises, RuntimeError with check(klass, match=r"info\['temp'\]"): info['bad_key'] = 1. for (key, match) in ([('sfreq', r'inst\.resample'), ('chs', r'inst\.add_channels')]): with check(klass, match=match): info[key] = info[key] with pytest.raises(ValueError, match='between meg<->head'): info['dev_head_t'] = Transform('mri', 'head', np.eye(4))
def test_snapshot_brain_montage(renderer): """Test snapshot brain montage.""" info = read_info(evoked_fname) fig = plot_alignment( info, trans=Transform('head', 'mri'), subject='sample', subjects_dir=subjects_dir) xyz = np.vstack([ich['loc'][:3] for ich in info['chs']]) ch_names = [ich['ch_name'] for ich in info['chs']] xyz_dict = dict(zip(ch_names, xyz)) xyz_dict[info['chs'][0]['ch_name']] = [1, 2] # Set one ch to only 2 vals # Make sure wrong types are checked pytest.raises(TypeError, snapshot_brain_montage, fig, xyz) # All chs must have 3 position values pytest.raises(ValueError, snapshot_brain_montage, fig, xyz_dict) # Make sure we raise error if the figure has no scene pytest.raises(ValueError, snapshot_brain_montage, None, info)
def test_coregister_fiducials(): """Test coreg.coregister_fiducials()""" # prepare head and MRI fiducials trans = Transform('head', 'mri', rotation(.4, .1, 0).dot(translation(.1, -.1, .1))) coords_orig = np.array([[-0.08061612, -0.02908875, -0.04131077], [0.00146763, 0.08506715, -0.03483611], [0.08436285, -0.02850276, -0.04127743]]) coords_trans = apply_trans(trans, coords_orig) def make_dig(coords, cf): return ({'coord_frame': cf, 'ident': 1, 'kind': 1, 'r': coords[0]}, {'coord_frame': cf, 'ident': 2, 'kind': 1, 'r': coords[1]}, {'coord_frame': cf, 'ident': 3, 'kind': 1, 'r': coords[2]}) mri_fiducials = make_dig(coords_trans, FIFF.FIFFV_COORD_MRI) info = {'dig': make_dig(coords_orig, FIFF.FIFFV_COORD_HEAD)} # test coregister_fiducials() trans_est = coregister_fiducials(info, mri_fiducials) assert_equal(trans_est.from_str, trans.from_str) assert_equal(trans_est.to_str, trans.to_str) assert_array_almost_equal(trans_est['trans'], trans['trans'])
def test_plot_alignment_basic(tmpdir, renderer, mixed_fwd_cov_evoked): """Test plotting of -trans.fif files and MEG sensor layouts.""" # generate fiducials file for testing tempdir = str(tmpdir) fiducials_path = op.join(tempdir, 'fiducials.fif') fid = [{ 'coord_frame': 5, 'ident': 1, 'kind': 1, 'r': [-0.08061612, -0.02908875, -0.04131077] }, { 'coord_frame': 5, 'ident': 2, 'kind': 1, 'r': [0.00146763, 0.08506715, -0.03483611] }, { 'coord_frame': 5, 'ident': 3, 'kind': 1, 'r': [0.08436285, -0.02850276, -0.04127743] }] write_dig(fiducials_path, fid, 5) evoked = read_evokeds(evoked_fname)[0] info = evoked.info sample_src = read_source_spaces(src_fname) pytest.raises(TypeError, plot_alignment, 'foo', trans_fname, subject='sample', subjects_dir=subjects_dir) pytest.raises(OSError, plot_alignment, info, trans_fname, subject='sample', subjects_dir=subjects_dir, src='foo') pytest.raises(ValueError, plot_alignment, info, trans_fname, subject='fsaverage', subjects_dir=subjects_dir, src=sample_src) sample_src.plot(subjects_dir=subjects_dir, head=True, skull=True, brain='white') # mixed source space mixed_src = mixed_fwd_cov_evoked[0]['src'] assert mixed_src.kind == 'mixed' plot_alignment(info, meg=['helmet', 'sensors'], dig=True, coord_frame='head', trans=Path(trans_fname), subject='sample', mri_fiducials=fiducials_path, subjects_dir=subjects_dir, src=mixed_src) renderer.backend._close_all() # no-head version renderer.backend._close_all() # trans required with pytest.raises(ValueError, match='transformation matrix is required'): plot_alignment(info, trans=None, src=src_fname) with pytest.raises(ValueError, match='transformation matrix is required'): plot_alignment(info, trans=None, mri_fiducials=True) with pytest.raises(ValueError, match='transformation matrix is required'): plot_alignment(info, trans=None, surfaces=['brain']) # all coord frames plot_alignment(info) # works: surfaces='auto' default for coord_frame in ('meg', 'head', 'mri'): fig = plot_alignment(info, meg=['helmet', 'sensors'], dig=True, coord_frame=coord_frame, trans=Path(trans_fname), subject='sample', mri_fiducials=fiducials_path, subjects_dir=subjects_dir, src=src_fname) renderer.backend._close_all() # EEG only with strange options evoked_eeg_ecog_seeg = evoked.copy().pick_types(meg=False, eeg=True) evoked_eeg_ecog_seeg.info['projs'] = [] # "remove" avg proj evoked_eeg_ecog_seeg.set_channel_types({ 'EEG 001': 'ecog', 'EEG 002': 'seeg' }) with catch_logging() as log: plot_alignment(evoked_eeg_ecog_seeg.info, subject='sample', trans=trans_fname, subjects_dir=subjects_dir, surfaces=['white', 'outer_skin', 'outer_skull'], meg=['helmet', 'sensors'], eeg=['original', 'projected'], ecog=True, seeg=True, verbose=True) log = log.getvalue() assert 'ecog: 1' in log assert 'seeg: 1' in log renderer.backend._close_all() sphere = make_sphere_model(info=info, r0='auto', head_radius='auto') bem_sol = read_bem_solution( op.join(subjects_dir, 'sample', 'bem', 'sample-1280-1280-1280-bem-sol.fif')) bem_surfs = read_bem_surfaces( op.join(subjects_dir, 'sample', 'bem', 'sample-1280-1280-1280-bem.fif')) sample_src[0]['coord_frame'] = 4 # hack for coverage plot_alignment( info, trans_fname, subject='sample', eeg='projected', meg='helmet', bem=sphere, dig=True, surfaces=['brain', 'inner_skull', 'outer_skull', 'outer_skin']) plot_alignment(info, subject='sample', meg='helmet', subjects_dir=subjects_dir, eeg='projected', bem=sphere, surfaces=['head', 'brain'], src=sample_src) # no trans okay, no mri surfaces plot_alignment(info, bem=sphere, surfaces=['brain']) with pytest.raises(ValueError, match='A head surface is required'): plot_alignment(info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, eeg='projected', surfaces=[]) with pytest.raises(RuntimeError, match='No brain surface found'): plot_alignment(info, trans=trans_fname, subject='foo', subjects_dir=subjects_dir, surfaces=['brain']) assert all(surf['coord_frame'] == FIFF.FIFFV_COORD_MRI for surf in bem_sol['surfs']) plot_alignment(info, trans_fname, subject='sample', meg=[], subjects_dir=subjects_dir, bem=bem_sol, eeg=True, surfaces=['head', 'inflated', 'outer_skull', 'inner_skull']) assert all(surf['coord_frame'] == FIFF.FIFFV_COORD_MRI for surf in bem_sol['surfs']) plot_alignment(info, trans_fname, subject='sample', meg=True, subjects_dir=subjects_dir, surfaces=['head', 'inner_skull'], bem=bem_surfs) # single-layer BEM can still plot head surface assert bem_surfs[-1]['id'] == FIFF.FIFFV_BEM_SURF_ID_BRAIN bem_sol_homog = read_bem_solution( op.join(subjects_dir, 'sample', 'bem', 'sample-1280-bem-sol.fif')) for use_bem in (bem_surfs[-1:], bem_sol_homog): with catch_logging() as log: plot_alignment(info, trans_fname, subject='sample', meg=True, subjects_dir=subjects_dir, surfaces=['head', 'inner_skull'], bem=use_bem, verbose=True) log = log.getvalue() assert 'not find the surface for head in the provided BEM model' in log # sphere model sphere = make_sphere_model('auto', 'auto', info) src = setup_volume_source_space(sphere=sphere) plot_alignment( info, trans=Transform('head', 'mri'), eeg='projected', meg='helmet', bem=sphere, src=src, dig=True, surfaces=['brain', 'inner_skull', 'outer_skull', 'outer_skin']) sphere = make_sphere_model('auto', None, info) # one layer # if you ask for a brain surface with a 1-layer sphere model it's an error with pytest.raises(RuntimeError, match='Sphere model does not have'): fig = plot_alignment(trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=['brain'], bem=sphere) # but you can ask for a specific brain surface, and # no info is permitted fig = plot_alignment(trans=trans_fname, subject='sample', meg=False, coord_frame='mri', subjects_dir=subjects_dir, surfaces=['white'], bem=sphere, show_axes=True) renderer.backend._close_all() if renderer._get_3d_backend() == 'mayavi': import mayavi # noqa: F401 analysis:ignore assert isinstance(fig, mayavi.core.scene.Scene) # 3D coil with no defined draw (ConvexHull) info_cube = pick_info(info, np.arange(6)) info['dig'] = None info_cube['chs'][0]['coil_type'] = 9999 info_cube['chs'][1]['coil_type'] = 9998 with pytest.raises(RuntimeError, match='coil definition not found'): plot_alignment(info_cube, meg='sensors', surfaces=()) coil_def_fname = op.join(tempdir, 'temp') with open(coil_def_fname, 'w') as fid: fid.write(coil_3d) # make sure our other OPMs can be plotted, too for ii, kind in enumerate( ('QUSPIN_ZFOPM_MAG', 'QUSPIN_ZFOPM_MAG2', 'FIELDLINE_OPM_MAG_GEN1', 'KERNEL_OPM_MAG_GEN1'), 2): info_cube['chs'][ii]['coil_type'] = getattr(FIFF, f'FIFFV_COIL_{kind}') with use_coil_def(coil_def_fname): with catch_logging() as log: plot_alignment(info_cube, meg='sensors', surfaces=(), dig=True, verbose='debug') log = log.getvalue() assert 'planar geometry' in log # one layer bem with skull surfaces: with pytest.raises(RuntimeError, match='Sphere model does not.*boundary'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=['brain', 'head', 'inner_skull'], bem=sphere) # wrong eeg value: with pytest.raises(ValueError, match='Invalid value for the .eeg'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, eeg='foo') # wrong meg value: with pytest.raises(ValueError, match='Invalid value for the .meg'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, meg='bar') # multiple brain surfaces: with pytest.raises(ValueError, match='Only one brain surface can be plot'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=['white', 'pial']) with pytest.raises(TypeError, match='surfaces.*must be'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=[1]) with pytest.raises(ValueError, match='Unknown surface type'): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=['foo']) with pytest.raises(TypeError, match="must be an instance of "): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=dict(brain='super clear')) with pytest.raises(ValueError, match="must be between 0 and 1"): plot_alignment(info=info, trans=trans_fname, subject='sample', subjects_dir=subjects_dir, surfaces=dict(brain=42)) fwd_fname = op.join(data_dir, 'MEG', 'sample', 'sample_audvis_trunc-meg-eeg-oct-4-fwd.fif') fwd = read_forward_solution(fwd_fname) plot_alignment(subject='sample', subjects_dir=subjects_dir, trans=trans_fname, fwd=fwd, surfaces='white', coord_frame='head') fwd = convert_forward_solution(fwd, force_fixed=True) plot_alignment(subject='sample', subjects_dir=subjects_dir, trans=trans_fname, fwd=fwd, surfaces='white', coord_frame='head') fwd['coord_frame'] = FIFF.FIFFV_COORD_MRI # check required to get to MRI with pytest.raises(ValueError, match='transformation matrix is required'): plot_alignment(info, trans=None, fwd=fwd) # surfaces as dict plot_alignment(subject='sample', coord_frame='head', trans=trans_fname, subjects_dir=subjects_dir, surfaces={ 'white': 0.4, 'outer_skull': 0.6, 'head': None })
def make_mne_anatomy(subject, subjects_dir, recordings_path=None, hcp_path=op.curdir, outputs=('label', 'mri', 'surf')): """Extract relevant anatomy and create MNE friendly directory layout The function will create the following outputs by default: $subjects_dir/$subject/bem/inner_skull.surf $subjects_dir/$subject/label/* $subjects_dir/$subject/mri/* $subjects_dir/$subject/surf/* $recordings_path/$subject/$subject-head_mri-trans.fif These can then be set as $SUBJECTS_DIR and as MEG directory, consistent with MNE examples. Parameters ---------- subject : str The subject name. subjects_dir : str The path corresponding to MNE/freesurfer SUBJECTS_DIR (to be created) hcp_path : str The path where the HCP files can be found. outputs : {'label', 'mri', 'stats', 'surf', 'touch'} The outputs of the freesrufer pipeline shipped by HCP. Defaults to ('mri', 'surf'), the minimum needed to extract MNE-friendly anatomy files and data. """ if hcp_path == op.curdir: hcp_path = op.realpath(hcp_path) if not op.isabs(subjects_dir): subjects_dir = op.realpath(subjects_dir) this_subjects_dir = op.join(subjects_dir, subject) if not op.isabs(recordings_path): recordings_path = op.realpath(recordings_path) this_recordings_path = op.join(recordings_path, subject) if not op.exists(this_recordings_path): os.makedirs(this_recordings_path) for output in outputs: if not op.exists(op.join(this_subjects_dir, output)): os.makedirs(op.join(this_subjects_dir, output)) if output == 'mri': for suboutput in ['orig', 'transforms']: if not op.exists(op.join(this_subjects_dir, output, suboutput)): os.makedirs(op.join(this_subjects_dir, output, suboutput)) files = get_file_paths(subject=subject, data_type='freesurfer', output=output, hcp_path=hcp_path) for source in files: match = [match for match in re.finditer(subject, source)][-1] split_path = source[:match.span()[1] + 1] target = op.join(this_subjects_dir, source.split(split_path)[-1]) if (not op.isfile(target) and not op.islink(target) and op.exists(source)): # don't link if it's not there. if sys.platform != 'win32': os.symlink(source, target) else: shutil.copyfile(source, target) logger.info('reading extended structural processing ...') # Step 1 ################################################################# # transform head models to expected coordinate system # make hcp trans transforms_fname = get_file_paths(subject=subject, data_type='meg_anatomy', output='transforms', hcp_path=hcp_path) transforms_fname = [ k for k in transforms_fname if k.endswith('transform.txt') ][0] hcp_trans = _read_trans_hcp(fname=transforms_fname, convert_to_meter=False) # get RAS freesurfer trans c_ras_trans_fname = get_file_paths(subject=subject, data_type='freesurfer', output='mri', hcp_path=hcp_path) c_ras_trans_fname = [ k for k in c_ras_trans_fname if k.endswith('c_ras.mat') ][0] logger.info('reading RAS freesurfer transform') # ceci n'est pas un .mat file ... with open(op.join(subjects_dir, c_ras_trans_fname)) as fid: ras_trans = np.array([r.split() for r in fid.read().split('\n') if r], dtype=np.float64) logger.info('Combining RAS transform and coregistration') ras_trans_m = linalg.inv(ras_trans) # and the inversion logger.info('extracting head model') head_model_fname = get_file_paths(subject=subject, data_type='meg_anatomy', output='head_model', hcp_path=hcp_path)[0] pnts, faces = _get_head_model(head_model_fname=head_model_fname) logger.info('coregistring head model to MNE-HCP coordinates') pnts = apply_trans(ras_trans_m.dot(hcp_trans['bti2spm']), pnts) tri_fname = op.join(this_subjects_dir, 'bem', 'inner_skull.surf') if not op.exists(op.dirname(tri_fname)): os.makedirs(op.dirname(tri_fname)) write_surface(tri_fname, pnts, faces) # Step 2 ################################################################# # write corresponding device to MRI transform logger.info('extracting coregistration') # now convert to everything meter too here ras_trans_m[:3, 3] *= 1e-3 bti2spm = hcp_trans['bti2spm'] bti2spm[:3, 3] *= 1e-3 head_mri_t = Transform( # we're lying here for a good purpose 'head', 'mri', np.dot(ras_trans_m, bti2spm)) # it should be 'ctf_head' write_trans( op.join(this_recordings_path, '%s-head_mri-trans.fif' % subject), head_mri_t)
def select_vertices_in_sensor_range(inst, dist, info=None, picks=None, trans=None, indices=False, verbose=None): """Find vertices within given distance to a sensor. Parameters ---------- inst : instance of Forward | instance of SourceSpaces The object to select vertices from. dist : float The minimum distance between a vertex and the nearest sensor. All vertices for which the distance to the nearest sensor exceeds this limit are discarded. info : instance of Info | None The info structure that contains information about the channels. Only needs to be specified if the object to select vertices from does is an instance of SourceSpaces. picks : array-like of int | None Indices of sensors to include in the search for the nearest sensor. If ``None``, the default, only MEG channels are used. trans : str | instance of Transform | None Either the full path to the head<->MRI transform ``*-trans.fif`` file produced during coregistration, or the Transformation itself. If trans is None, an identity matrix is assumed. Only needed when ``inst`` is a source space in MRI coordinates. indices: False | True If ``True``, return vertex indices instead of vertex numbers. Defaults to ``False``. verbose : bool | str | int | None If not None, override default verbose level (see :func:`mne.verbose` and :ref:`Logging documentation <tut_logging>` for more). Returns ------- vertices : pair of lists | list of int Either a list of vertex numbers for the left and right hemisphere (if ``indices==False``) or a single list with vertex indices. See Also -------- restrict_forward_to_vertices : restrict Forward to the given vertices restrict_src_to_vertices : restrict SourceSpaces to the given vertices """ if isinstance(inst, Forward): info = inst['info'] src = inst['src'] elif isinstance(inst, SourceSpaces): src = inst if info is None: raise ValueError('You need to specify an Info object with ' 'information about the channels.') # Load the head<->MRI transform if necessary if src[0]['coord_frame'] == FIFF.FIFFV_COORD_MRI: if trans is None: raise ValueError('Source space is in MRI coordinates, but no ' 'head<->MRI transform was given. Please specify ' 'the full path to the appropriate *-trans.fif ' 'file as the "trans" parameter.') if isinstance(trans, string_types): trans = read_trans(trans, return_all=True) for trans in trans: # we got at least 1 try: trans = _ensure_trans(trans, 'head', 'mri') except Exception as exp: pass else: break else: raise exp src_trans = invert_transform(_ensure_trans(trans, 'head', 'mri')) print('Transform!') else: src_trans = Transform('head', 'head') # Identity transform dev_to_head = _ensure_trans(info['dev_head_t'], 'meg', 'head') if picks is None: picks = pick_types(info, meg=True) if len(picks) > 0: logger.info('Using MEG channels') else: logger.info('Using EEG channels') picks = pick_types(info, eeg=True) src_pos = np.vstack([ apply_trans(src_trans, s['rr'][s['inuse'].astype(np.bool)]) for s in src ]) sensor_pos = [] for ch in picks: # MEG channels are in device coordinates, translate them to head if channel_type(info, ch) in ['mag', 'grad']: sensor_pos.append( apply_trans(dev_to_head, info['chs'][ch]['loc'][:3])) else: sensor_pos.append(info['chs'][ch]['loc'][:3]) sensor_pos = np.array(sensor_pos) # Find vertices that are within range of a sensor. We use a KD-tree for # speed. logger.info('Finding vertices within sensor range...') tree = cKDTree(sensor_pos) distances, _ = tree.query(src_pos, distance_upper_bound=dist) # Vertices out of range are flagged as np.inf src_sel = np.isfinite(distances) logger.info('[done]') if indices: return np.flatnonzero(src_sel) else: n_lh_verts = src[0]['nuse'] lh_sel, rh_sel = src_sel[:n_lh_verts], src_sel[n_lh_verts:] vert_lh = src[0]['vertno'][lh_sel] vert_rh = src[1]['vertno'][rh_sel] return [vert_lh, vert_rh]
def get_sensor_pos_from_fwd(inst, info=None, picks=None, trans=None): from mne import SourceSpaces, Forward from mne.io.constants import FIFF from six import string_types from mne.transforms import read_trans, _ensure_trans, invert_transform, Transform, apply_trans from mne.io.pick import channel_type, pick_types if isinstance(inst, Forward): info = inst['info'] src = inst['src'] elif isinstance(inst, SourceSpaces): src = inst if info is None: raise ValueError('You need to specify an Info object with ' 'information about the channels.') # Load the head<->MRI transform if necessary if src[0]['coord_frame'] == FIFF.FIFFV_COORD_MRI: if trans is None: raise ValueError('Source space is in MRI coordinates, but no ' 'head<->MRI transform was given. Please specify ' 'the full path to the appropriate *-trans.fif ' 'file as the "trans" parameter.') if isinstance(trans, string_types): trans = read_trans(trans, return_all=True) for trans in trans: # we got at least 1 try: trans = _ensure_trans(trans, 'head', 'mri') except Exception as exp: pass else: break else: raise exp src_trans = invert_transform(_ensure_trans(trans, 'head', 'mri')) print('Transform!') else: src_trans = Transform('head', 'head') # Identity transform dev_to_head = _ensure_trans(info['dev_head_t'], 'meg', 'head') if picks is None: picks = pick_types(info, meg=True) if len(picks) > 0: print('Using MEG channels') else: print('Using EEG channels') picks = pick_types(info, eeg=True) sensor_pos = [] for ch in picks: # MEG channels are in device coordinates, translate them to head if channel_type(info, ch) in ['mag', 'grad']: sensor_pos.append( apply_trans(dev_to_head, info['chs'][ch]['loc'][:3])) else: sensor_pos.append(info['chs'][ch]['loc'][:3]) sensor_pos = np.array(sensor_pos) return sensor_pos
if len(sys.argv) != 3: print("usage: {} subject $ds".format(sys.argv[0])) sys.exit(1) subject = sys.argv[1] dsname = sys.argv[2] try: FShome = os.environ['FREESURFER_HOME'] except KeyError: print("You must set the FREESURFER_HOME environment variable!") sys.exit(1) try: Subjdir = os.environ['SUBJECTS_DIR'] except KeyError: Subjdir = op.join(FShome, "subjects") print("Note: Using the default SUBJECTS_DIR:", Subjdir) name = op.join(Subjdir, subject, "bem", "{}-fiducials.fif".format(subject)) fids = read_fiducials(name) fidc = _fiducial_coords(fids[0]) raw = read_raw_ctf(dsname, clean_names = True, preload = False) fidd = _fiducial_coords(raw.info['dig']) xform = fit_matched_points(fidd, fidc, weights = [1, 10, 1]) t = Transform(FIFF.FIFFV_COORD_HEAD, FIFF.FIFFV_COORD_MRI, xform) name = op.join(Subjdir, subject, "bem", "{}-trans.fif".format(subject)) write_trans(name, t)
def test_scale_mri_xfm(tmp_path, few_surfaces, subjects_dir_tmp_few): """Test scale_mri transforms and MRI scaling.""" # scale fsaverage tempdir = str(subjects_dir_tmp_few) sample_dir = subjects_dir_tmp_few / 'sample' subject_to = 'flachkopf' spacing = 'oct2' for subject_from in ('fsaverage', 'sample'): if subject_from == 'fsaverage': scale = 1. # single dim else: scale = [0.9, 2, .8] # separate src_from_fname = op.join(tempdir, subject_from, 'bem', '%s-%s-src.fif' % (subject_from, spacing)) src_from = mne.setup_source_space(subject_from, spacing, subjects_dir=tempdir, add_dist=False) write_source_spaces(src_from_fname, src_from) vertices_from = np.concatenate([s['vertno'] for s in src_from]) assert len(vertices_from) == 36 hemis = ([0] * len(src_from[0]['vertno']) + [1] * len(src_from[0]['vertno'])) mni_from = mne.vertex_to_mni(vertices_from, hemis, subject_from, subjects_dir=tempdir) if subject_from == 'fsaverage': # identity transform source_rr = np.concatenate( [s['rr'][s['vertno']] for s in src_from]) * 1e3 assert_allclose(mni_from, source_rr) if subject_from == 'fsaverage': overwrite = skip_fiducials = False else: with pytest.raises(IOError, match='No fiducials file'): scale_mri(subject_from, subject_to, scale, subjects_dir=tempdir) skip_fiducials = True with pytest.raises(IOError, match='already exists'): scale_mri(subject_from, subject_to, scale, subjects_dir=tempdir, skip_fiducials=skip_fiducials) overwrite = True if subject_from == 'sample': # support for not needing all surf files os.remove(op.join(sample_dir, 'surf', 'lh.curv')) scale_mri(subject_from, subject_to, scale, subjects_dir=tempdir, verbose='debug', overwrite=overwrite, skip_fiducials=skip_fiducials) if subject_from == 'fsaverage': assert _is_mri_subject(subject_to, tempdir), "Scaling failed" src_to_fname = op.join(tempdir, subject_to, 'bem', '%s-%s-src.fif' % (subject_to, spacing)) assert op.exists(src_to_fname), "Source space was not scaled" # Check MRI scaling fname_mri = op.join(tempdir, subject_to, 'mri', 'T1.mgz') assert op.exists(fname_mri), "MRI was not scaled" # Check MNI transform src = mne.read_source_spaces(src_to_fname) vertices = np.concatenate([s['vertno'] for s in src]) assert_array_equal(vertices, vertices_from) mni = mne.vertex_to_mni(vertices, hemis, subject_to, subjects_dir=tempdir) assert_allclose(mni, mni_from, atol=1e-3) # 0.001 mm # Check head_to_mni (the `trans` here does not really matter) trans = rotation(0.001, 0.002, 0.003) @ translation(0.01, 0.02, 0.03) trans = Transform('head', 'mri', trans) pos_head_from = np.random.RandomState(0).randn(4, 3) pos_mni_from = mne.head_to_mni(pos_head_from, subject_from, trans, tempdir) pos_mri_from = apply_trans(trans, pos_head_from) pos_mri = pos_mri_from * scale pos_head = apply_trans(invert_transform(trans), pos_mri) pos_mni = mne.head_to_mni(pos_head, subject_to, trans, tempdir) assert_allclose(pos_mni, pos_mni_from, atol=1e-3)