write_raw_bids( raw, bids_path, events_data=events_data, event_id=event_id, overwrite=True, ) # MRI scan t1_fname = op.join(subjects_dir, f"sub-{subject}", "mri", "T1.mgz") # transformation matrix trans = mne.read_trans(op.join(bids_root, f"sub-{subject}-trans.fif")) t1w_bids_path = BIDSPath(subject=subject, root=bids_root, suffix="T1w") landmarks = get_anat_landmarks( t1_fname, info=raw.info, trans=trans, fs_subject=f"sub-{subject}", fs_subjects_dir=subjects_dir, ) t1w_bids_path = write_anat( image=t1_fname, bids_path=t1w_bids_path, landmarks=landmarks, verbose=True, ) anat_dir = t1w_bids_path.directory # ERM erm_fname = op.join(bids_root, "sub-%s_erm_raw.fif" % subject) erm = mne.io.read_raw_fif(erm_fname, allow_maxshield="yes") erm.info["line_freq"] = 60 er_date = erm.info["meas_date"].strftime("%Y%m%d")
def test_update_anat_landmarks(tmp_path): """Test updating the anatomical landmarks of an MRI scan.""" data_path = Path(testing.data_path()) raw_path = data_path / 'MEG' / 'sample' / 'sample_audvis_trunc_raw.fif' trans_path = Path(str(raw_path).replace('_raw.fif', '-trans.fif')) t1_path = data_path / 'subjects' / 'sample' / 'mri' / 'T1.mgz' fs_subject = 'sample' fs_subjects_dir = data_path / 'subjects' bids_root = tmp_path bids_path_mri = BIDSPath(subject=subject_id, session=session_id, acquisition=acq, root=bids_root, datatype='anat', suffix='T1w') # First, write the MRI scan to BIDS, including the anatomical landmarks info = mne.io.read_info(raw_path) trans = mne.read_trans(trans_path) landmarks = get_anat_landmarks(image=t1_path, info=info, trans=trans, fs_subject=fs_subject, fs_subjects_dir=fs_subjects_dir) bids_path_mri = write_anat(image=t1_path, bids_path=bids_path_mri, landmarks=landmarks, deface=False) bids_path_mri_json = bids_path_mri.copy().update(extension='.json') # Modify the landmarks # Move the nasion a bit landmarks_new = landmarks.copy() landmarks_new.dig[1]['r'] *= 0.9 update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new) with bids_path_mri_json.fpath.open(encoding='utf-8') as f: mri_json = json.load(f) assert np.allclose(landmarks_new.dig[1]['r'], mri_json['AnatomicalLandmarkCoordinates']['NAS']) # Remove JSON sidecar; updating the anatomical landmarks should re-create # the file unless `on_missing` is `'raise'` bids_path_mri_json.fpath.unlink() with pytest.raises(KeyError, match='No AnatomicalLandmarkCoordinates section found'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new) update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new, on_missing='ignore') with pytest.raises(KeyError, match='landmark not found'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new, kind='ses-1') update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new, kind='ses-1', on_missing='ignore') mri_json = json.loads(bids_path_mri_json.fpath.read_text(encoding='utf-8')) assert 'NAS' in mri_json['AnatomicalLandmarkCoordinates'] assert 'NAS_ses-1' in mri_json['AnatomicalLandmarkCoordinates'] assert np.allclose(landmarks_new.dig[1]['r'], mri_json['AnatomicalLandmarkCoordinates']['NAS']) # Check without extension provided bids_path_mri_no_ext = bids_path_mri.copy().update(extension=None) update_anat_landmarks(bids_path=bids_path_mri_no_ext, landmarks=landmarks_new) # Check without datatytpe provided bids_path_mri_no_datatype = bids_path_mri.copy().update(datatype=None) update_anat_landmarks(bids_path=bids_path_mri_no_datatype, landmarks=landmarks) # Check handling of invalid input bids_path_invalid = bids_path_mri.copy().update(datatype='meg') with pytest.raises(ValueError, match='Can only operate on "anat"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(suffix=None) with pytest.raises(ValueError, match='lease specify the "suffix"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(suffix='meg') with pytest.raises(ValueError, match='Can only operate on "T1w" and "FLASH"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(subject='invalid') with pytest.raises(ValueError, match='Could not find an MRI scan'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) # Unsupported coordinate frame landmarks_invalid = landmarks.copy() for digpoint in landmarks_invalid.dig: digpoint['coord_frame'] = FIFF.FIFFV_MNE_COORD_RAS with pytest.raises(ValueError, match='must be specified in MRI voxel'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_invalid) # Missing cardinal point landmarks_invalid = landmarks.copy() del landmarks_invalid.dig[0] with pytest.raises(ValueError, match='did not contain all required cardinal points'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_invalid) # Test with path-like landmarks fiducials_path = (data_path / 'subjects' / 'sample' / 'bem' / 'sample-fiducials.fif') update_anat_landmarks(bids_path=bids_path_mri, landmarks=fiducials_path, fs_subject='sample', fs_subjects_dir=data_path / 'subjects') expected_coords_in_voxels = np.array([ [68.38202, 45.24057, 43.439808], # noqa: E241 [42.27006, 30.758774, 74.09837], # noqa: E202, E241 [17.044853, 46.586075, 42.618504] ]) mri_json = json.loads(bids_path_mri_json.fpath.read_text(encoding='utf-8')) for landmark, expected_coords in zip(('LPA', 'NAS', 'RPA'), expected_coords_in_voxels): assert np.allclose(mri_json['AnatomicalLandmarkCoordinates'][landmark], expected_coords)
def test_update_anat_landmarks(tmpdir): """Test updating the anatomical landmarks of an MRI scan.""" data_path = Path(testing.data_path()) raw_path = data_path / 'MEG' / 'sample' / 'sample_audvis_trunc_raw.fif' trans_path = Path(str(raw_path).replace('_raw.fif', '-trans.fif')) t1_path = data_path / 'subjects' / 'sample' / 'mri' / 'T1.mgz' fs_subject = 'sample' fs_subjects_dir = data_path / 'subjects' bids_root = Path(tmpdir) bids_path_mri = BIDSPath(subject=subject_id, session=session_id, acquisition=acq, root=bids_root, datatype='anat', suffix='T1w') # First, write the MRI scan to BIDS, including the anatomical landmarks info = mne.io.read_info(raw_path) trans = mne.read_trans(trans_path) landmarks = get_anat_landmarks( image=t1_path, info=info, trans=trans, fs_subject=fs_subject, fs_subjects_dir=fs_subjects_dir ) bids_path_mri = write_anat(image=t1_path, bids_path=bids_path_mri, landmarks=landmarks, deface=False) bids_path_mri_json = bids_path_mri.copy().update(extension='.json') # Modify the landmarks # Move the nasion a bit landmarks_new = landmarks.copy() landmarks_new.dig[1]['r'] *= 0.9 update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new) with bids_path_mri_json.fpath.open(encoding='utf-8') as f: mri_json = json.load(f) assert np.allclose( landmarks_new.dig[1]['r'], mri_json['AnatomicalLandmarkCoordinates']['NAS'] ) # Remove JSON sidecar; updating the anatomical landmarks should re-create # the file bids_path_mri_json.fpath.unlink() update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_new) with bids_path_mri_json.fpath.open(encoding='utf-8') as f: mri_json = json.load(f) assert np.allclose( landmarks_new.dig[1]['r'], mri_json['AnatomicalLandmarkCoordinates']['NAS'] ) # Check without extension provided bids_path_mri_no_ext = bids_path_mri.copy().update(extension=None) update_anat_landmarks(bids_path=bids_path_mri_no_ext, landmarks=landmarks_new) # Check without datatytpe provided bids_path_mri_no_datatype = bids_path_mri.copy().update(datatype=None) update_anat_landmarks(bids_path=bids_path_mri_no_datatype, landmarks=landmarks) # Check handling of invalid input bids_path_invalid = bids_path_mri.copy().update(datatype='meg') with pytest.raises(ValueError, match='Can only operate on "anat"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(suffix=None) with pytest.raises(ValueError, match='lease specify the "suffix"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(suffix='meg') with pytest.raises(ValueError, match='Can only operate on "T1w" and "FLASH"'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) bids_path_invalid = bids_path_mri.copy().update(subject='invalid') with pytest.raises(ValueError, match='Could not find an MRI scan'): update_anat_landmarks(bids_path=bids_path_invalid, landmarks=landmarks) # Unsupported coordinate frame landmarks_invalid = landmarks.copy() for digpoint in landmarks_invalid.dig: digpoint['coord_frame'] = FIFF.FIFFV_MNE_COORD_RAS with pytest.raises(ValueError, match='must be specified in MRI voxel'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_invalid) # Missing cardinal point landmarks_invalid = landmarks.copy() del landmarks_invalid.dig[0] with pytest.raises(ValueError, match='did not contain all required cardinal points'): update_anat_landmarks(bids_path=bids_path_mri, landmarks=landmarks_invalid)
bids_path = BIDSPath(subject=subject_id, task=task, root=bids_root) # plot T1 to show that it is ACPC-aligned # note that the origin is centered on the anterior commissure (AC) # with the y-axis passing through the posterior commissure (PC) T1_fname = op.join(subjects_dir, 'sample_seeg', 'mri', 'T1.mgz') fig = plot_anat(T1_fname, cut_coords=(0, 0, 0)) fig.axes['x'].ax.annotate('AC', (2., -2.), (30., -40.), color='w', arrowprops=dict(facecolor='w', alpha=0.5)) fig.axes['x'].ax.annotate('PC', (-31., -2.), (-80., -40.), color='w', arrowprops=dict(facecolor='w', alpha=0.5)) # write ACPC-aligned T1 landmarks = get_anat_landmarks(T1_fname, raw.info, trans, 'sample_seeg', subjects_dir) T1_bids_path = write_anat(T1_fname, bids_path, deface=True, landmarks=landmarks) # write `raw` to BIDS and anonymize it (converts to BrainVision format) # # we need to pass the `montage` argument for coordinate frames other than # "head" which is what MNE uses internally in the `raw` object # # `acpc_aligned=True` affirms that our MRI is aligned to ACPC # if this is not true, convert to `fsaverage` (see below)! write_raw_bids(raw, bids_path, anonymize=dict(daysback=40000),
# consist of the coordinates of three anatomical landmarks (LPA, Nasion and # RPA (=left and right preauricular points) expressed in voxel coordinates # w.r.t. the T1 image. # First create the BIDSPath object. t1w_bids_path = BIDSPath(subject=sub, session=ses, root=output_path, suffix='T1w') # use ``trans`` to transform landmarks from the ``raw`` file to # the voxel space of the image landmarks = get_anat_landmarks( t1_fname, # path to the MRI scan info=raw.info, # the MEG data file info from the same subject as the MRI trans=trans, # our transformation matrix fs_subject='sample', # FreeSurfer subject fs_subjects_dir=fs_subjects_dir, # FreeSurfer subjects directory ) # We use the write_anat function t1w_bids_path = write_anat( image=t1_fname, # path to the MRI scan bids_path=t1w_bids_path, landmarks=landmarks, # the landmarks in MRI voxel space verbose=True # this will print out the sidecar file ) anat_dir = t1w_bids_path.directory # %% # Let's have another look at our BIDS directory