ENH: Need tools for fixing mesh topology
See original GitHub issueDescribe the bug
Someone else’s high-resolution head was used for the selected subject.
lh.seghead in FreeView looks like:

But the high-resolution head rendered in mne coreg gui looks like:
(Notice the differences of the top-left corner of the goggle, the ears, and the nose bridge.)
I can confirm that it is from other subject’s head-dense.fif (lh.seghead) via FreeView.

The high-resolution head also did not align to the head. No rotations were made, the head looks like:

FreeView showed that lh.seghead and outer_skin.surf were aligned.
From the above images, it looks like someone else’s dense head was used. The specified subject is subject-346, the error messages indicated that subject-102 dense head was used instead?
subject-102 is the first subject in the subject dropdown dialog.
It seems to me that mne coreg gui renders the first subject and then update with the selected subject. Maybe the bug result from that process?
mne coreg -s subject-346
Exception occurred in traits notification handler for object: <mne.gui._file_traits.SurfaceSource object at 0x7f3db86bcb30>, trait: file, old value: subjects//subject-102/bem/subject-102-head-dense.fif, new value: subjects//subject-346/bem/subject-346-head-dense.fif
Traceback (most recent call last):
File "/home/ntubabylab/anaconda3/envs/mne/lib/python3.8/site-packages/traits/trait_notifiers.py", line 522, in _dispatch_change_event
self.dispatch(handler, *args)
File "/home/ntubabylab/anaconda3/envs/mne/lib/python3.8/site-packages/traits/trait_notifiers.py", line 484, in dispatch
handler(*args)
File "/home/ntubabylab/anaconda3/envs/mne/lib/python3.8/site-packages/mne/gui/_file_traits.py", line 175, in read_file
bem = read_bem_surfaces(self.file, verbose=False)[0]
File "<decorator-gen-98>", line 22, in read_bem_surfaces
File "/home/ntubabylab/anaconda3/envs/mne/lib/python3.8/site-packages/mne/bem.py", line 1270, in read_bem_surfaces
_check_complete_surface(this)
File "/home/ntubabylab/anaconda3/envs/mne/lib/python3.8/site-packages/mne/bem.py", line 270, in _check_complete_surface
raise RuntimeError(msg)
RuntimeError: Surface outer skin has topological defects: 13 / 383510 vertices have fewer than three neighboring triangles [44385, 44387, 44389, 45198, 46031, 46033, 46861, 46862, 47708, 48546, 49428, 50338, 53081]
Steps to reproduce
I can’t provide a MWE because it only happened to this subject.
Expected results
subject-346’s lh.seghead
Actual results
subject-102(?)'s lh.seghead
Additional information
The above images in this post will be removed in the future.
Platform: Linux-5.8.0-40-generic-x86_64-with-glibc2.10
Python: 3.8.5 (default, Sep 4 2020, 07:30:14) [GCC 7.3.0]
Executable: /home/ntubabylab/anaconda3/envs/mne/bin/python
CPU: x86_64: 12 cores
Memory: 62.8 GB
mne: 0.22.0
numpy: 1.19.2 {blas=mkl_rt, lapack=mkl_rt}
scipy: 1.5.2
matplotlib: 3.3.2 {backend=Qt5Agg}
sklearn: 0.23.2
numba: 0.51.2
nibabel: 3.2.0
nilearn: Not found
dipy: Not found
cupy: 8.1.0
pandas: 1.1.3
mayavi: 4.7.2
pyvista: 0.26.1 {pyvistaqt=0.2.0, OpenGL 4.5.0 NVIDIA 450.102.04 via GeForce GTX 1660/PCIe/SSE2}
vtk: 9.0.1
PyQt5: 5.15.1
The high-resolution head was created with mne make_scalp_surfaces.
#!/usr/bin/env python
import os
import subprocess
from mne.parallel import parallel_func
from config import (N_JOBS, subjects_to_run, exclude_subjects, mri_dir,
subjects_dir)
def run_command(command, log_file):
with open(log_file, "wb") as f:
proc = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in proc.stdout:
f.write(line)
if proc.wait() != 0:
raise RuntimeError("command failed")
def process_subject_head(subject):
subject_mri_dir = os.path.join(mri_dir, subject)
run_command([
"mne", "make_scalp_surfaces", "-s", subject, "-d", subjects_dir,
"--force", "--overwrite", "--verbose"
], os.path.join(subject_mri_dir, f"{subject}_make_scalp_surfaces.txt"))
print(f"Created high-resolution head surfaces for {subject}")
process_subject_head('subject-346')
Logs in mne make_scalp_surfaces
1. Creating a dense scalp tessellation with mkheadsurf...
Running subprocess: mkheadsurf -subjid subject-346 -srcvol T1.mgz
INFO: log file is /mnt/sdb1/MEG_Prosody/subjects/subject-346/scripts/mkheadsurf.log
--------------------------------
Tue 02 Feb 2021 10:22:20 AM CST
/mnt/sdb1/MEG_Prosody
mri_seghead --invol /mnt/sdb1/MEG_Prosody/subjects/subject-346/mri/T1.mgz --outvol /mnt/sdb1/MEG_Prosody/subjects/subject-346/mri/seghead.mgz --fill 1 --thresh1 20 --thresh2 20 --nhitsmin 2
--------------------------------
input volume: /mnt/sdb1/MEG_Prosody/subjects/subject-346/mri/T1.mgz
output volume: /mnt/sdb1/MEG_Prosody/subjects/subject-346/mri/seghead.mgz
threshold1: 20
threshold2: 20
nhitsmin: 2
fill value: 1
Loading input volume
Filling Columns
Filling Rows
Filling Slices
Merging and Inverting
Growing
Counting
N Head Voxels = 2763508
N Back Voxels = 14013708
Avg. Back Intensity = 1.099117
Max. Back Intensity = 235.000000
Writing output
Done
--------------------------------
Tue 02 Feb 2021 10:22:22 AM CST
/mnt/sdb1/MEG_Prosody
mri_tessellate /mnt/sdb1/MEG_Prosody/subjects/subject-346/mri/seghead.mgz 1 /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead
--------------------------------
7.1.1
7.1.1
slice 40: 4930 vertices, 5067 faces
slice 50: 13321 vertices, 13533 faces
slice 60: 23339 vertices, 23642 faces
slice 70: 32759 vertices, 33068 faces
slice 80: 41073 vertices, 41395 faces
slice 90: 49432 vertices, 49789 faces
slice 100: 58965 vertices, 59458 faces
slice 110: 68826 vertices, 69328 faces
slice 120: 77977 vertices, 78498 faces
slice 130: 86895 vertices, 87425 faces
slice 140: 95446 vertices, 96008 faces
slice 150: 104054 vertices, 104633 faces
slice 160: 112518 vertices, 113104 faces
slice 170: 122539 vertices, 123275 faces
slice 180: 135046 vertices, 136220 faces
slice 190: 149553 vertices, 150907 faces
slice 200: 164959 vertices, 166490 faces
slice 210: 181548 vertices, 183291 faces
slice 220: 189480 vertices, 191117 faces
slice 230: 190186 vertices, 191755 faces
slice 240: 190186 vertices, 191755 faces
slice 250: 190186 vertices, 191755 faces
using the conformed surface RAS to save vertex points...
writing /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead
using vox2ras matrix:
-1.00000 0.00000 0.00000 128.00000;
0.00000 0.00000 1.00000 -128.00000;
0.00000 -1.00000 0.00000 128.00000;
0.00000 0.00000 0.00000 1.00000;
--------------------------------
Tue 02 Feb 2021 10:22:24 AM CST
/mnt/sdb1/MEG_Prosody
mris_smooth -n 10 -b area.seghead -c curv.seghead /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead
--------------------------------
smoothing for 10 iterations
smoothing surface tessellation for 10 iterations...
smoothing complete - recomputing first and second fundamental forms...
writing smoothed curvature to /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.curv.seghead
writing smoothed area to /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.area.seghead
--------------------------------
Tue 02 Feb 2021 10:22:27 AM CST
/mnt/sdb1/MEG_Prosody
mris_inflate -n 10 -sulc sulc.seghead /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead.inflated
--------------------------------
niterations = 10
sulc name = sulc.seghead
Reading /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead
avg radius = 87.9 mm, total surface area = 117737 mm^2
step 000: RMS=0.148 (target=0.015)
step 005: RMS=0.114 (target=0.015)
step 010: RMS=0.103 (target=0.015)
step 015: RMS=0.097 (target=0.015)
step 020: RMS=0.092 (target=0.015)
step 025: RMS=0.088 (target=0.015)
step 030: RMS=0.085 (target=0.015)
step 035: RMS=0.082 (target=0.015)
step 040: RMS=0.081 (target=0.015)
step 045: RMS=0.080 (target=0.015)
step 050: RMS=0.079 (target=0.015)
step 055: RMS=0.080 (target=0.015)
step 060: RMS=0.080 (target=0.015) writing inflated surface to /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.seghead.inflated
writing sulcal depths to /mnt/sdb1/MEG_Prosody/subjects/subject-346/surf/lh.sulc.seghead
inflation complete.
inflation took 0.3 minutes
mris_inflate utimesec 16.246170
mris_inflate stimesec 0.531678
mris_inflate ru_maxrss 299472
mris_inflate ru_ixrss 0
mris_inflate ru_idrss 0
mris_inflate ru_isrss 0
mris_inflate ru_minflt 743476
mris_inflate ru_majflt 0
mris_inflate ru_nswap 0
mris_inflate ru_inblock 0
mris_inflate ru_oublock 10448
mris_inflate ru_msgsnd 0
mris_inflate ru_msgrcv 0
mris_inflate ru_nsignals 0
mris_inflate ru_nvcsw 0
mris_inflate ru_nivcsw 1765
Started at: Tue 02 Feb 2021 10:22:20 AM CST
Ended at: Tue 02 Feb 2021 10:22:43 AM CST
mkheadsurf done
2. Creating /mnt/sdb1/MEG_Prosody/subjects/subject-346/bem/subject-346-head-dense.fif ...
outer skin CM is 10.24 5.95 -47.79 mm
Surfaces passed the basic topology checks.
3. Creating medium tessellation...
3.1 Decimating the dense tessellation...
3.2 Creating /mnt/sdb1/MEG_Prosody/subjects/subject-346/bem/subject-346-head-medium.fif
outer skin CM is 18.27 27.64 -63.59 mm
Surfaces passed the basic topology checks.
4. Creating sparse tessellation...
4.1 Decimating the dense tessellation...
4.2 Creating /mnt/sdb1/MEG_Prosody/subjects/subject-346/bem/subject-346-head-sparse.fif
outer skin CM is 17.12 24.57 -62.60 mm
Surfaces passed the basic topology checks.
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin has topological defects: 13 / 383510 vertices have fewer than three neighboring triangles [44385, 44387, 44389, 45198, 46031, 46033, 46861, 46862, 47708, 48546, 49428, 50338, 53081]
Consider using --force as an additional input parameter.
sys.exit(main())
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin is not complete (sum of solid angles yielded 0.999793, should be 1.)
sys.exit(main())
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin has topological defects: 53 / 30000 vertices have fewer than three neighboring triangles [1659, 1663, 1690, 1732, 1734, 1767, 1812, 1813, 1815, 1849, 1850, 1887, 2012, 2127, 2212, 2272, 2507, 3476, 5047, 5435, 6169, 6296, 6297, 6532, 6540, 6587, 6976, 7295, 7855, 8106, 8199, 8528, 8533, 8538, 8727, 8795, 8953, 9039, 10498, 10546, 11537, 11560, 11755, 12293, 12587, 13594, 13779, 14204, 14207, 14300, 14313, 14463, 14532]
Consider using --force as an additional input parameter.
sys.exit(main())
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin is not complete (sum of solid angles yielded 0.999567, should be 1.)
sys.exit(main())
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin has topological defects: 17 / 2500 vertices have fewer than three neighboring triangles [176, 180, 184, 189, 271, 272, 677, 775, 991, 1060, 1061, 1107, 1128, 1160, 1161, 1162, 1190]
Consider using --force as an additional input parameter.
sys.exit(main())
/home/ntubabylab/anaconda3/envs/mne/bin/mne:8: RuntimeWarning: Surface outer skin is not complete (sum of solid angles yielded 1.00046, should be 1.)
sys.exit(main())
Issue Analytics
- State:
- Created 3 years ago
- Comments:14 (13 by maintainers)

Top Related StackOverflow Question
Thank you very much for providing the code. I had the same problem and it worked for me too! I would really appreciate to see this function
mne.surface.fix_topology(rr, tris)implemented in mne python@yh-luo I’ll reopen until we get a variant of this code in MNE-Python. I’ve also needed to use it so that’s at least three people, and these topology questions come up a lot so I think it’s worth pursuing proper inclusion as a function.
@christian-oreilly since you did all the heavy lifting on this, would you be up for opening a EDIT: rough PR to add a
mne.surface.fix_topology(rr, tris)function? Then I’m happy to hack away at the code to add tests, etc. to get it working, but the first commit will already get you code credit for the changes when we squash+merge, which I think is helpful since you did the heavy lifting!I think in the end we probably also want a
write_head_bemfunction since the code to do this currently requires poking around in private attributes. Then the fix could have maybe just been for you @yh-luo :