Skip to content

Commit

Permalink
[ENH, MRG] Simplify reading dig (#979)
Browse files Browse the repository at this point in the history
* Simplify reading dig [skip ci]

* update whatsnew

* Update doc/whats_new.rst

Co-authored-by: Adam Li <adam2392@gmail.com>

Co-authored-by: Richard Höchenberger <richard.hoechenberger@gmail.com>
Co-authored-by: Adam Li <adam2392@gmail.com>
  • Loading branch information
3 people committed Mar 7, 2022
1 parent 7343e20 commit 33cbe67
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 89 deletions.
2 changes: 2 additions & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Enhancements

- :func:`mne_bids.update_anat_landmarks` can now directly work with fiducials saved from the MNE-Python coregistration GUI or :func:`mne.io.write_fiducials`, by Richard Höchenberger`_ (:gh:`977`)

- All non-MNE-Python BIDS coordinate frames are now set to ``'unknown'`` on reading, by `Alex Rockhill`_ (:gh:`979`)

API and behavior changes
^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
41 changes: 19 additions & 22 deletions mne_bids/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,27 @@
'UNCInfant2V23',
]

coordsys_meg = ['CTF', 'ElektaNeuromag', '4DBti', 'KitYokogawa', 'ChietiItab']
coordsys_eeg = ['CapTrak']
coordsys_ieeg = ['Pixels', 'ACPC']
# accepted BIDS formats, which may be subject to change
# depending on the specification
BIDS_IEEG_COORDINATE_FRAMES = ['ACPC', 'Pixels']
BIDS_MEG_COORDINATE_FRAMES = ['CTF', 'ElektaNeuromag',
'4DBti', 'KitYokogawa',
'ChietiItab']
BIDS_EEG_COORDINATE_FRAMES = ['CapTrak']

# accepted coordinate SI units
BIDS_COORDINATE_UNITS = ['m', 'cm', 'mm']
coordsys_wildcard = ['Other']
coordsys_shared = (coordsys_standard_template +
coordsys_standard_template_deprecated +
coordsys_wildcard)
BIDS_SHARED_COORDINATE_FRAMES = (coordsys_standard_template +
coordsys_standard_template_deprecated +
coordsys_wildcard)

ALLOWED_SPACES = dict()
ALLOWED_SPACES['meg'] = coordsys_shared + coordsys_meg + coordsys_eeg
ALLOWED_SPACES['eeg'] = coordsys_shared + coordsys_meg + coordsys_eeg
ALLOWED_SPACES['ieeg'] = coordsys_shared + coordsys_ieeg
ALLOWED_SPACES['meg'] = ALLOWED_SPACES['eeg'] = \
BIDS_SHARED_COORDINATE_FRAMES + BIDS_MEG_COORDINATE_FRAMES + \
BIDS_EEG_COORDINATE_FRAMES
ALLOWED_SPACES['ieeg'] = \
BIDS_SHARED_COORDINATE_FRAMES + BIDS_IEEG_COORDINATE_FRAMES
ALLOWED_SPACES['anat'] = None
ALLOWED_SPACES['beh'] = None

Expand All @@ -235,17 +244,6 @@
'extension': 'label'
}

# accepted BIDS formats, which may be subject to change
# depending on the specification
BIDS_IEEG_COORDINATE_FRAMES = ['ACPC', 'Pixels', 'Other']
BIDS_MEG_COORDINATE_FRAMES = ['CTF', 'ElektaNeuromag',
'4DBti', 'KitYokogawa',
'ChietiItab', 'Other']
BIDS_EEG_COORDINATE_FRAMES = ['CapTrak']

# accepted coordinate SI units
BIDS_COORDINATE_UNITS = ['m', 'cm', 'mm']

# mapping from supported BIDs coordinate frames -> MNE
BIDS_TO_MNE_FRAMES = {
'CTF': 'ctf_head',
Expand All @@ -256,8 +254,7 @@
'CapTrak': 'head',
'ACPC': 'mri', # assumes T1 is ACPC-aligned, if not the coordinates are lost # noqa
'fsaverage': 'mni_tal', # XXX: note fsaverage and MNI305 are the same # noqa
'MNI305': 'mni_tal',
'Other': 'unknown'
'MNI305': 'mni_tal'
}

# mapping from supported MNE coordinate frames -> BIDS
Expand Down
66 changes: 12 additions & 54 deletions mne_bids/dig.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
from mne.utils import logger, warn
from mne.io.pick import _picks_to_idx

from mne_bids.config import (BIDS_IEEG_COORDINATE_FRAMES,
BIDS_MEG_COORDINATE_FRAMES,
BIDS_EEG_COORDINATE_FRAMES,
from mne_bids.config import (ALLOWED_SPACES,
BIDS_COORDINATE_UNITS,
MNE_TO_BIDS_FRAMES, BIDS_TO_MNE_FRAMES,
MNE_FRAME_TO_STR, BIDS_COORD_FRAME_DESCRIPTIONS)
Expand Down Expand Up @@ -535,57 +533,17 @@ def _read_dig_bids(electrodes_fpath, coordsystem_fpath,
bids_coord_frame, bids_coord_unit = _handle_coordsystem_reading(
coordsystem_fpath, datatype)

if datatype == 'meg':
if bids_coord_frame not in BIDS_MEG_COORDINATE_FRAMES:
warn(f'MEG coordinate frame "{bids_coord_frame}" is not '
f'supported. The supported coordinate frames are: '
f'{BIDS_MEG_COORDINATE_FRAMES}')
coord_frame = None
elif bids_coord_frame == 'Other':
warn("Coordinate frame of MEG data can't be determined "
"when 'other'. The currently accepted keywords are: "
"{}".format(BIDS_MEG_COORDINATE_FRAMES))
coord_frame = None
else:
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
elif datatype == 'ieeg':
# iEEG datatype for BIDS only supports
# acpc, pixels and then standard templates
# iEEG datatype for mne-python only supports
# mni_tal == fsaverage == MNI305
if bids_coord_frame == 'Pixels':
warn("Coordinate frame of iEEG data in pixels is not "
"recognized by mne-python, the coordinate frame "
"of the montage will be set to 'unknown'")
coord_frame = 'unknown'
elif bids_coord_frame == 'ACPC':
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
elif bids_coord_frame == 'Other':
# default coordinate frames to available ones in mne-python
# noqa: see https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html
warn(f"Defaulting coordinate frame to unknown "
f"from coordinate system input {bids_coord_frame}")
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
else:
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)

# XXX: if the coordinate frame is not recognized, then
# coordinates are stored in a system that we cannot
# recognize yet.
if coord_frame is None:
warn(f"iEEG Coordinate frame {bids_coord_frame} is not a "
f"readable BIDS keyword by mne-bids yet. The allowed "
f"keywords are: {BIDS_IEEG_COORDINATE_FRAMES}")
coord_frame = 'unknown'
elif datatype == 'eeg':
# only accept captrak
if bids_coord_frame not in BIDS_EEG_COORDINATE_FRAMES:
warn("EEG Coordinate frame is not accepted "
"BIDS keyword. The allowed keywords are: "
"{}".format(BIDS_IEEG_COORDINATE_FRAMES))
coord_frame = None
else:
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
if bids_coord_frame not in ALLOWED_SPACES[datatype]:
warn(f'"{bids_coord_frame}" is not a BIDS-acceptable coordinate frame '
f'for {datatype.upper()}. The supported coordinate frames are: '
'{}'.format(ALLOWED_SPACES[datatype]))
coord_frame = None
elif bids_coord_frame in BIDS_TO_MNE_FRAMES:
coord_frame = BIDS_TO_MNE_FRAMES.get(bids_coord_frame, None)
else:
warn(f"{bids_coord_frame} is not an MNE-Python coordinate frame "
f"for {datatype.upper()} data and so will be set to 'unknown'")
coord_frame = 'unknown'

# check coordinate units
if bids_coord_unit not in BIDS_COORDINATE_UNITS:
Expand Down
25 changes: 13 additions & 12 deletions mne_bids/tests/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from mne.datasets import testing, somato

from mne_bids import BIDSPath
from mne_bids.config import MNE_STR_TO_FRAME
from mne_bids.config import (MNE_STR_TO_FRAME, BIDS_SHARED_COORDINATE_FRAMES,
BIDS_TO_MNE_FRAMES)
from mne_bids.read import (read_raw_bids,
_read_raw, get_head_mri_trans,
_handle_events_reading)
Expand Down Expand Up @@ -733,8 +734,8 @@ def test_handle_eeg_coords_reading(tmp_path):
suffix='coordsystem',
extension='.json')
_update_sidecar(coordsystem_fname, 'EEGCoordinateSystem', 'besa')
with pytest.warns(RuntimeWarning, match='EEG Coordinate frame is not '
'accepted BIDS keyword'):
with pytest.warns(RuntimeWarning, match='is not a BIDS-acceptable '
'coordinate frame for EEG'):
raw_test = read_raw_bids(bids_path)
assert raw_test.info['dig'] is None

Expand Down Expand Up @@ -842,25 +843,25 @@ def test_handle_ieeg_coords_reading(bids_path, tmp_path):
_update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', coord_frame)
# read in raw file w/ updated coordinate frame
# and make sure all digpoints are MRI coordinate frame
with pytest.warns(RuntimeWarning, match="Defaulting coordinate "
"frame to unknown"):
with pytest.warns(RuntimeWarning, match="not an MNE-Python "
"coordinate frame"):
raw_test = read_raw_bids(bids_path=bids_fname, verbose=False)
assert raw_test.info['dig'] is not None

# check that standard template identifiers that are unsupported in
# mne-python coordinate frames, still get read in, but produce a warning
coordinate_frames = ['individual', 'fsnative', 'scanner',
'ICBM452AirSpace', 'NIHPD']
for coord_frame in coordinate_frames:
for coord_frame in BIDS_SHARED_COORDINATE_FRAMES:
# update coordinate units
_update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', coord_frame)
# read in raw file w/ updated coordinate frame
# and make sure all digpoints are MRI coordinate frame
with pytest.warns(
RuntimeWarning, match=f"iEEG Coordinate frame {coord_frame} "
f"is not a readable BIDS keyword "):
if coord_frame in BIDS_TO_MNE_FRAMES:
raw_test = read_raw_bids(bids_path=bids_fname, verbose=False)
assert raw_test.info['dig'] is not None
else:
with pytest.warns(RuntimeWarning, match="not an MNE-Python "
"coordinate frame"):
raw_test = read_raw_bids(bids_path=bids_fname, verbose=False)
assert raw_test.info['dig'] is not None

# ACPC should be read in as RAS for iEEG
_update_sidecar(coordsystem_fname, 'iEEGCoordinateSystem', 'ACPC')
Expand Down
2 changes: 1 addition & 1 deletion mne_bids/tests/test_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -3642,7 +3642,7 @@ def test_write_dig(tmpdir):
coordsystem_path = bids_path.copy().update(
task=None, run=None, suffix='coordsystem', extension='.json')
with pytest.warns(RuntimeWarning,
match='recognized by mne-python'):
match='not an MNE-Python coordinate frame'):
_read_dig_bids(electrodes_path, coordsystem_path,
bids_path.datatype, raw)
montage2 = raw.get_montage()
Expand Down

0 comments on commit 33cbe67

Please sign in to comment.