Skip to content

Instantly share code, notes, and snippets.

@wmvanvliet
Created April 15, 2025 10:10
Show Gist options
  • Save wmvanvliet/bdc0e7679bbc0519ee9b367c37541065 to your computer and use it in GitHub Desktop.
Save wmvanvliet/bdc0e7679bbc0519ee9b367c37541065 to your computer and use it in GitHub Desktop.
Virtually move head position inside the MEG helmet using MNE-Python
"""Virtually move around head position."""
import os
from copy import deepcopy
from subprocess import run
import mne
import numpy as np
from move_head import move_head
path = mne.datasets.sample.data_path()
ev_orig = mne.read_evokeds(
path / "MEG/sample/sample_audvis-ave.fif", condition="Left Auditory"
)
ev_orig.apply_baseline()
ev_orig.comment = "original"
# Make an Info with the head in the default position. This is only relevant to the
# example. Presumably you already have some other data with the desired head position
# you want to use.
info_dest = deepcopy(ev_orig.info)
info_dest["dev_head_t"]["trans"] = np.array(
[
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0.04],
[0, 0, 0, 1],
]
)
# Move head position using MNE-Python
ev_moved = ev_orig.copy()
move_head(ev_moved, info_dest)
ev_moved.comment = "moved"
# Move head position using the maxfilter program
if os.path.exists("/tmp/maxfilter_ave.fif"):
os.unlink("/tmp/maxfilter_ave.fif")
run(
[
"maxfilter",
"-f",
str(path / "MEG/sample/sample_audvis-ave.fif"),
"-trans",
"default",
"-o",
"/tmp/maxfilter_ave.fif",
"-force",
"-v",
]
)
ev_maxfilter = mne.read_evokeds("/tmp/maxfilter_ave.fif", condition="Left Auditory")
ev_maxfilter.apply_baseline()
ev_maxfilter.comment = "maxfilter"
# Plot comparison
mne.viz.plot_evoked_topo([ev_orig, ev_moved, ev_maxfilter])
fig_orig = mne.viz.plot_alignment(
ev_orig.info,
trans=path / "MEG/sample/sample_audvis_raw-trans.fif",
subject="sample",
subjects_dir=path / "subjects",
)
fig_orig.plotter.add_text("original")
fig_moved = mne.viz.plot_alignment(
ev_moved.info,
trans=path / "MEG/sample/sample_audvis_raw-trans.fif",
subject="sample",
subjects_dir=path / "subjects",
)
fig_moved.plotter.add_text("moved")
fig_maxfilter = mne.viz.plot_alignment(
ev_maxfilter.info,
trans=path / "MEG/sample/sample_audvis_raw-trans.fif",
subject="sample",
subjects_dir=path / "subjects",
)
fig_maxfilter.plotter.add_text("maxfilter")
from mne._fiff.meas_info import Info
from mne._fiff.pick import pick_types, pick_info
from mne.channels.interpolation import _do_interp_dots
from mne.epochs import BaseEpochs
from mne.evoked import Evoked
from mne.forward import _map_meg_or_eeg_channels
from mne.io import BaseRaw
from mne.utils.check import _validate_type
def move_head(inst, dest):
"""Virtually move head position to a new destination.
After this operation, the data will have the sensor layout and head position of the
destination data.
Parameters
----------
inst : Raw | Epochs | Evoked
The data to virtually moved. Will be modified in-place.
dest : Raw | Epochs | Evoked | Info
Some data with the desired head position.
Notes
-----
As a side effect, all bad channels will be interpolated.
"""
_validate_type(dest, (BaseRaw, BaseEpochs, Evoked, Info), "dest")
if isinstance(dest, Info):
info_to = dest
else:
info_to = dest.info
if "dev_head_t" not in info_to:
raise ValueError(
"Could find an MEG->head transformation in the supplied `dest`."
)
picks_to = pick_types(info_to, meg=True, exclude=[])
info_to = pick_info(info_to, picks_to)
_validate_type(inst, (BaseRaw, BaseEpochs, Evoked), "inst")
info_from = inst.info
if "dev_head_t" not in info_from:
raise ValueError(
"Could find an MEG->head transformation in the supplied `inst`."
)
picks_from = pick_types(info_from, exclude="bads", meg=True)
info_from = pick_info(info_from, picks_from)
mapping = _map_meg_or_eeg_channels(
info_from, info_to, mode="accurate", origin="auto"
)
_do_interp_dots(inst, mapping, picks_from, picks_to)
inst.info["dev_head_t"] = info_to["dev_head_t"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment