Skip to content

Commit 8f83332

Browse files
committed
BUG: Fix bug with coord frame reading (#13129)
1 parent 64e01e5 commit 8f83332

File tree

5 files changed

+87
-24
lines changed

5 files changed

+87
-24
lines changed

mne/_fiff/_digitization.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ def _read_dig_fif(fid, meas_info, *, return_ch_names=False):
186186
dig.extend(tag.data)
187187
elif kind == FIFF.FIFF_MNE_COORD_FRAME:
188188
tag = read_tag(fid, pos)
189-
coord_frame = _coord_frame_named.get(int(tag.data.item()))
189+
coord_frame = int(tag.data.item())
190+
coord_frame = _coord_frame_named.get(coord_frame, coord_frame)
190191
elif kind == FIFF.FIFF_MNE_CH_NAME_LIST:
191192
tag = read_tag(fid, pos)
192193
ch_names = _safe_name_list(tag.data, "read", "ch_names")

mne/_fiff/constants.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,11 @@
281281
FIFF.FIFFV_COORD_HPI,
282282
FIFF.FIFFV_COORD_HEAD,
283283
FIFF.FIFFV_COORD_MRI,
284-
FIFF.FIFFV_COORD_MRI_SLICE,
285-
FIFF.FIFFV_COORD_MRI_DISPLAY,
286-
FIFF.FIFFV_COORD_DICOM_DEVICE,
287-
FIFF.FIFFV_COORD_IMAGING_DEVICE,
284+
# We never use these but could add at some point
285+
# FIFF.FIFFV_COORD_MRI_SLICE,
286+
# FIFF.FIFFV_COORD_MRI_DISPLAY,
287+
# FIFF.FIFFV_COORD_DICOM_DEVICE,
288+
# FIFF.FIFFV_COORD_IMAGING_DEVICE,
288289
)
289290
}
290291
#
@@ -817,6 +818,17 @@
817818
#
818819
FIFF.FIFFV_MNE_COORD_4D_HEAD = FIFF.FIFFV_MNE_COORD_CTF_HEAD
819820
FIFF.FIFFV_MNE_COORD_KIT_HEAD = FIFF.FIFFV_MNE_COORD_CTF_HEAD
821+
_coord_frame_named.update({
822+
key: key
823+
for key in (
824+
FIFF.FIFFV_MNE_COORD_CTF_DEVICE,
825+
FIFF.FIFFV_MNE_COORD_MRI_VOXEL,
826+
FIFF.FIFFV_MNE_COORD_RAS,
827+
FIFF.FIFFV_MNE_COORD_MNI_TAL,
828+
FIFF.FIFFV_MNE_COORD_FS_TAL,
829+
FIFF.FIFFV_MNE_COORD_KIT_HEAD,
830+
)
831+
})
820832

821833
#
822834
# FWD Types

mne/_fiff/tests/test_constants.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
_dig_kind_named,
2424
)
2525
from mne.forward._make_forward import _read_coil_defs
26+
from mne.transforms import _frame_to_str, _verbose_frames
2627
from mne.utils import requires_good_network
2728

2829
# https://github.com/mne-tools/fiff-constants/commits/master
@@ -405,7 +406,14 @@ def test_constants(tmp_path):
405406
"^FIFFV_.*_CH$",
406407
(FIFF.FIFFV_DIPOLE_WAVE, FIFF.FIFFV_GOODNESS_FIT),
407408
),
408-
(_coord_frame_named, "FIFFV_COORD_", ()),
409+
pytest.param(
410+
_coord_frame_named,
411+
"FIFFV_(MNE_)?COORD_",
412+
(),
413+
marks=pytest.mark.xfail(
414+
reason="Intentional mismatch tested by test_coord_frame_consistency",
415+
),
416+
),
409417
(_ch_unit_named, "FIFF_UNIT_", ()),
410418
(_ch_unit_mul_named, "FIFF_UNITM_", ()),
411419
(_ch_coil_type_named, "FIFFV_COIL_", ()),
@@ -419,3 +427,29 @@ def test_dict_completion(dict_, match, extras):
419427
got.add(e)
420428
want = set(dict_)
421429
assert got == want, match
430+
431+
432+
def test_coord_frame_consistency():
433+
"""Test consistency between coord frame mappings."""
434+
all_frames = set(
435+
key for key in dir(FIFF) if key.startswith(("FIFFV_COORD_", "FIFFV_MNE_COORD"))
436+
)
437+
# ... but there are some frames that we never work in so let's cull those for now
438+
ignore_frames = set(
439+
f"FIFFV_COORD_{name}"
440+
for name in """
441+
MRI_SLICE MRI_DISPLAY DICOM_DEVICE IMAGING_DEVICE
442+
""".strip().split()
443+
)
444+
ignore_frames |= set(
445+
f"FIFFV_MNE_COORD_{name}"
446+
for name in """
447+
DIGITIZER TUFTS_EEG FS_TAL_GTZ FS_TAL_LTZ
448+
""".strip().split()
449+
)
450+
assert ignore_frames.issubset(all_frames)
451+
all_frames -= ignore_frames
452+
all_ints = set(FIFF[key] for key in all_frames)
453+
assert set(_frame_to_str) == all_ints
454+
assert set(_verbose_frames) == all_ints
455+
assert set(_coord_frame_named) == all_ints

mne/channels/tests/test_montage.py

+26-13
Original file line numberDiff line numberDiff line change
@@ -151,22 +151,35 @@ def test_dig_montage_trans(tmp_path):
151151
assert str(position1) == str(position2) # exactly equal
152152

153153

154-
def test_fiducials():
154+
@pytest.mark.parametrize("fname", (fif_fname, ctf_fif_fname))
155+
def test_fiducials(tmp_path, fname):
155156
"""Test handling of fiducials."""
156157
# Eventually the code used here should be unified with montage.py, but for
157158
# now it uses code in odd places
158-
for fname in (fif_fname, ctf_fif_fname):
159-
fids, coord_frame = read_fiducials(fname)
160-
points = _fiducial_coords(fids, coord_frame)
161-
assert points.shape == (3, 3)
162-
# Fids
163-
assert_allclose(points[:, 2], 0.0, atol=1e-6)
164-
assert_allclose(points[::2, 1], 0.0, atol=1e-6)
165-
assert points[2, 0] > 0 # RPA
166-
assert points[0, 0] < 0 # LPA
167-
# Nasion
168-
assert_allclose(points[1, 0], 0.0, atol=1e-6)
169-
assert points[1, 1] > 0
159+
fids, coord_frame = read_fiducials(fname)
160+
assert coord_frame == FIFF.FIFFV_COORD_HEAD
161+
points = _fiducial_coords(fids, coord_frame)
162+
assert points.shape == (3, 3)
163+
# Fids
164+
assert_allclose(points[:, 2], 0.0, atol=1e-6)
165+
assert_allclose(points[::2, 1], 0.0, atol=1e-6)
166+
assert points[2, 0] > 0 # RPA
167+
assert points[0, 0] < 0 # LPA
168+
# Nasion
169+
assert_allclose(points[1, 0], 0.0, atol=1e-6)
170+
assert points[1, 1] > 0
171+
fname_out = tmp_path / "test-dig.fif"
172+
make_dig_montage(
173+
lpa=fids[0]["r"], nasion=fids[1]["r"], rpa=fids[2]["r"], coord_frame="mri_voxel"
174+
).save(fname_out, overwrite=True)
175+
fids_2, coord_frame_2 = read_fiducials(fname_out)
176+
assert coord_frame_2 == FIFF.FIFFV_MNE_COORD_MRI_VOXEL
177+
assert_allclose(
178+
[fid["r"] for fid in fids[:3]],
179+
[fid["r"] for fid in fids_2],
180+
rtol=1e-6,
181+
)
182+
assert coord_frame_2 is not None
170183

171184

172185
def test_documented():

mne/transforms.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242

4343

4444
_str_to_frame = dict(
45+
isotrak=FIFF.FIFFV_COORD_ISOTRAK,
4546
meg=FIFF.FIFFV_COORD_DEVICE,
4647
mri=FIFF.FIFFV_COORD_MRI,
4748
mri_voxel=FIFF.FIFFV_MNE_COORD_MRI_VOXEL,
4849
head=FIFF.FIFFV_COORD_HEAD,
50+
hpi=FIFF.FIFFV_COORD_HPI,
4951
mni_tal=FIFF.FIFFV_MNE_COORD_MNI_TAL,
5052
ras=FIFF.FIFFV_MNE_COORD_RAS,
5153
fs_tal=FIFF.FIFFV_MNE_COORD_FS_TAL,
@@ -63,15 +65,16 @@
6365
FIFF.FIFFV_COORD_HEAD: "head",
6466
FIFF.FIFFV_COORD_MRI: "MRI (surface RAS)",
6567
FIFF.FIFFV_MNE_COORD_MRI_VOXEL: "MRI voxel",
66-
FIFF.FIFFV_COORD_MRI_SLICE: "MRI slice",
67-
FIFF.FIFFV_COORD_MRI_DISPLAY: "MRI display",
6868
FIFF.FIFFV_MNE_COORD_CTF_DEVICE: "CTF MEG device",
6969
FIFF.FIFFV_MNE_COORD_CTF_HEAD: "CTF/4D/KIT head",
7070
FIFF.FIFFV_MNE_COORD_RAS: "RAS (non-zero origin)",
7171
FIFF.FIFFV_MNE_COORD_MNI_TAL: "MNI Talairach",
72-
FIFF.FIFFV_MNE_COORD_FS_TAL_GTZ: "Talairach (MNI z > 0)",
73-
FIFF.FIFFV_MNE_COORD_FS_TAL_LTZ: "Talairach (MNI z < 0)",
74-
-1: "unknown",
72+
FIFF.FIFFV_MNE_COORD_FS_TAL: "FS Talairach",
73+
# We don't use these, but keep them in case we ever want to add them.
74+
# FIFF.FIFFV_COORD_MRI_SLICE: "MRI slice",
75+
# FIFF.FIFFV_COORD_MRI_DISPLAY: "MRI display",
76+
# FIFF.FIFFV_MNE_COORD_FS_TAL_GTZ: "Talairach (MNI z > 0)",
77+
# FIFF.FIFFV_MNE_COORD_FS_TAL_LTZ: "Talairach (MNI z < 0)",
7578
}
7679

7780

0 commit comments

Comments
 (0)