mne-tools / mne-python

MNE: Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python
https://mne.tools
BSD 3-Clause "New" or "Revised" License
2.66k stars 1.31k forks source link

Montage in MRI space converted to head when saving to epoch.fif #10946

Open AlexLepauvre opened 2 years ago

AlexLepauvre commented 2 years ago

Describe the bug

Montage in MRI space gets transformed to head space when saving to an epochs .fif file. The issue seems to be due to the saving and not the loading, but not 100% clear. See here

Steps to reproduce

import mne
import numpy as np
from numpy.random import normal
import tempfile
import os

# Create random montage:
montage = mne.channels.make_dig_montage(ch_pos={"ch_1": np.array([1, 2, 3]), "ch_2": np.array([4, 5, 6]),
                                                "ch_3": np.array([7, 8, 9])}, coord_frame="mri")

# Create random data:
info = mne.create_info(ch_names=['ch_1', 'ch_2', 'ch_3'],
                       ch_types=['ecog'] * 3,
                       sfreq=100)
data = np.array([[normal(loc=0, scale=2, size=100), normal(loc=0, scale=2, size=100),
                  normal(loc=0, scale=2, size=100)],
                 [normal(loc=0, scale=2, size=100), normal(loc=0, scale=2, size=100),
                  normal(loc=0, scale=2, size=100)]])
epochs = mne.EpochsArray(data, info, tmin=-0.2, verbose="ERROR")
epochs.set_montage(montage, verbose="ERROR")

# Printing the epochs montage info:
print("Coordinate frames before saving:")
print(epochs.get_montage().dig[0]["coord_frame"])

# Creating a temporary directory
temp_dir = tempfile.mkdtemp()
# Generate a file name
fname = temp_dir + os.sep + "-epo.fif"
epochs.save(fname)

loaded_epochs = mne.read_epochs(fname, verbose="ERROR")
print("Coordinate frames after saving:")
print(loaded_epochs.get_montage().dig[0]["coord_frame"])

Expected results

Coordinate frames before saving: 5 (FIFFV_COORD_MRI) 5 (FIFFV_COORD_MRI)

Actual results

Coordinate frames before saving: 5 (FIFFV_COORD_MRI) 4 (FIFFV_COORD_HEAD)

Additional information

Platform: Windows-10-10.0.19044-SP0 Python: 3.9.7 | packaged by conda-forge | (default, Sep 29 2021, 19:15:42) [MSC v.1916 64 bit (AMD64)] Executable: C:\Users\alexander.lepauvre\Anaconda3\envs\mne_24\python.exe CPU: Intel64 Family 6 Model 142 Stepping 10, GenuineIntel: 8 cores Memory: 15.9 GB mne: 0.24.0 numpy: 1.21.4 {blas=NO_ATLAS_INFO, lapack=lapack} scipy: 1.7.2 matplotlib: 3.4.3 {backend=Qt5Agg} sklearn: 1.0.1 numba: 0.53.1 nibabel: 3.2.1 nilearn: 0.8.1 dipy: 1.4.1 cupy: Not found pandas: 1.3.4 mayavi: 4.7.2 pyvista: 0.32.1 {OpenGL 4.5.0 - Build 26.20.100.6860 via Intel(R) UHD Graphics 620} pyvistaqt: 0.5.0 ipyvtklink: Not found vtk: 9.0.3 PyQt5: 5.12.3 ipympl: Not found mne_qt_browser: Not found

agramfort commented 2 years ago

ok I looked a bit into it. The problem happens on write (not on read). Basically when we write dig points on epochs.save we just ignore the coord frame. We call write_dig_points without a coord_frame parameter. So on read it defaults to head...

I think it's something we can tackle during our sprint late August unless @larsoner is eager to fix it before.

AlexLepauvre commented 2 years ago

Thanks @agramfort. In the mean time, is there any way I change the coordinate frame of the epochs object manually to override this issue? I am thinking something like:

for ch in montage.dig:
   montage.dig[ch] = "5 (FIFFV_COORD_MRI)"

Kind regards,

Alex

agramfort commented 2 years ago

you can hack it like this:

from mne.io.constants import FIFF for d in loaded_epochs.info["dig"]: d['coord_frame'] = FIFF.FIFFV_COORD_MRI

Message ID: @.***>

wmvanvliet commented 2 years ago

Converting to head space is actually intended behavior:

MNE-Python prefers to have EEG channel locations in head space, always. So .apply_montage converts the montage to head space (if you get rid of the verbose='ERROR' you can see the warning message saying there are no fiducials so an identity transform is assumed). We probably want to keep this. If we allow electrodes to be in all kinds of coordinate spaces, we have to verify the coordinate space every time we use channel locations.

What is the reason for wanting to store digitized channel locations in MRI space? It may be simpler not to try and fight MNE-Python on this.

wmvanvliet commented 2 years ago

Ahh, this is ECOG data, I see.

larsoner commented 2 years ago

Reopening as this issue will exist until make a fix in MNE-BIDS