diff --git a/doc/changes/dev/14462.bugfix.rst b/doc/changes/dev/14462.bugfix.rst new file mode 100644 index 00000000000..1781cb34e5a --- /dev/null +++ b/doc/changes/dev/14462.bugfix.rst @@ -0,0 +1 @@ +Use the smooth head surface packaged with MNE-Python as ``fsaverage-head.fif`` in :func:`mne.datasets.fetch_fsaverage` (and fix it for a ``subjects_dir`` given as a :class:`str` that does not exist yet), and prefer ``-head.fif`` over ``outer_skin.surf`` as the head surface in :func:`mne.viz.plot_alignment` and the coregistration GUI, by `Christian Brodbeck`_. diff --git a/mne/_freesurfer.py b/mne/_freesurfer.py index dc94f09daf3..49d65157217 100644 --- a/mne/_freesurfer.py +++ b/mne/_freesurfer.py @@ -838,10 +838,10 @@ def _get_head_surface(surf, subject, subjects_dir, bem=None, verbose=None): ] else: try_fnames = [ + op.join(subject_dir, "bem", f"{subject}-head.fif"), op.join(subject_dir, "bem", "outer_skin.surf"), op.join(subject_dir, "bem", "flash", "outer_skin.surf"), op.join(subject_dir, "bem", f"{subject}-head-sparse.fif"), - op.join(subject_dir, "bem", f"{subject}-head.fif"), ] for fname in try_fnames: if op.exists(fname): diff --git a/mne/coreg.py b/mne/coreg.py index 8363fb5fea8..f73b197901c 100644 --- a/mne/coreg.py +++ b/mne/coreg.py @@ -91,9 +91,9 @@ fid_fname_general = os.path.join(bem_dirname, "{head}-fiducials.fif") src_fname = os.path.join(bem_dirname, "{subject}-{spacing}-src.fif") _head_fnames = ( + head_bem_fname, os.path.join(bem_dirname, "outer_skin.surf"), head_sparse_fname, - head_bem_fname, ) _high_res_head_fnames = ( os.path.join(bem_dirname, "{subject}-head-dense.fif"), diff --git a/mne/datasets/_fsaverage/base.py b/mne/datasets/_fsaverage/base.py index 1bbecb3851d..e1485902035 100644 --- a/mne/datasets/_fsaverage/base.py +++ b/mne/datasets/_fsaverage/base.py @@ -50,6 +50,14 @@ def fetch_fsaverage(subjects_dir=None, *, verbose=None): the zip file is downloaded and files are updated. No files will be overwritten. + The head surface ``bem/fsaverage-head.fif`` is the smooth head surface + packaged with MNE-Python (the one :func:`mne.create_default_subject` + installs) rather than the watershed outer skin surface. To update an + existing copy of fsaverage, delete the file and call this function again. + + .. versionchanged:: 1.14 + The smooth head surface is used for ``bem/fsaverage-head.fif``. + .. versionadded:: 0.18 """ # Code used to create the BEM (other files taken from MNE-sample-data): @@ -70,21 +78,24 @@ def fetch_fsaverage(subjects_dir=None, *, verbose=None): # with open('fsaverage.txt', 'w') as fid: # fid.write('\n'.join(names)) # + # In fsaverage-1.1, bem/fsaverage-head.fif in both zip files was replaced + # with mne/data/fsaverage/fsaverage-head.fif. + # subjects_dir = _set_montage_coreg_path(subjects_dir) - subjects_dir = subjects_dir.expanduser().absolute() + subjects_dir = Path(subjects_dir).expanduser().absolute() fs_dir = subjects_dir / "fsaverage" fs_dir.mkdir(parents=True, exist_ok=True) _manifest_check_download( manifest_path=FSAVERAGE_MANIFEST_PATH / "root.txt", destination=subjects_dir, - url="https://github.com/mne-tools/mne-data/releases/download/fsaverage-1.0/fsaverage-root.zip", - hash_="5133fe92b7b8f03ae19219d5f46e4177", + url="https://github.com/mne-tools/mne-data/releases/download/fsaverage-1.1/fsaverage-root.zip", + hash_="edad902f15196bdd0c98521e657f41b6", ) _manifest_check_download( manifest_path=FSAVERAGE_MANIFEST_PATH / "bem.txt", destination=subjects_dir / "fsaverage", - url="https://github.com/mne-tools/mne-data/releases/download/fsaverage-1.0/fsaverage-bem.zip", - hash_="b31509cdcf7908af6a83dc5ee8f49fb1", + url="https://github.com/mne-tools/mne-data/releases/download/fsaverage-1.1/fsaverage-bem.zip", + hash_="cdf3de397b02ca7f055a6d62dbfac1ff", ) return fs_dir diff --git a/mne/datasets/tests/test_datasets.py b/mne/datasets/tests/test_datasets.py index 3be699d17e5..bdfe0e7755a 100644 --- a/mne/datasets/tests/test_datasets.py +++ b/mne/datasets/tests/test_datasets.py @@ -88,6 +88,9 @@ def noop(*args, **kwargs): monkeypatch.setattr(mne.datasets._fsaverage.base, "_manifest_check_download", noop) sd_2 = datasets.fetch_fsaverage() assert sd / "fsaverage" == sd_2 + # a str for a not-yet-existing subjects_dir + sd_3 = datasets.fetch_fsaverage(str(tmp_path / "new_sd")) + assert sd_3 == tmp_path / "new_sd" / "fsaverage" def test_erp_core_fetch_file(tmp_path, monkeypatch): diff --git a/mne/tests/test_coreg.py b/mne/tests/test_coreg.py index 6dbc5bd7ba3..6e712c4c325 100644 --- a/mne/tests/test_coreg.py +++ b/mne/tests/test_coreg.py @@ -21,6 +21,7 @@ from mne.channels import DigMontage from mne.coreg import ( Coregistration, + _find_head_bem, _is_mri_subject, coregister_fiducials, create_default_subject, @@ -443,6 +444,8 @@ def test_coregistration(scale_mode, ref_scale, grow_hair, fiducials, fid_match): coreg = Coregistration( info, subject=subject, subjects_dir=subjects_dir, fiducials=fiducials ) + # the subject's own head file is preferred over the watershed outer_skin.surf + assert _find_head_bem(subject, subjects_dir).endswith("sample-head.fif") assert np.allclose(coreg._last_parameters, coreg._parameters) assert len(coreg.fiducials.dig) == 3 for dig_point in coreg.fiducials.dig: diff --git a/mne/viz/tests/test_3d.py b/mne/viz/tests/test_3d.py index df767f83897..af9d96c8449 100644 --- a/mne/viz/tests/test_3d.py +++ b/mne/viz/tests/test_3d.py @@ -795,6 +795,8 @@ def test_plot_alignment_eeg(renderer, evoked): assert "ecog: 1" in log assert "seeg: 1" in log assert "eeg: 10" in log + # the subject's own head file is preferred over the watershed outer_skin.surf + assert "Using sample-head.fif for head surface." in log # check number of actors # 4 surfs (both hemis, skin, skull), 1 ECoG, 1 sEEG, # 5 orig EEG + 1 projected EEG