Skip to content

Axial to planar gradiometer transformation #13196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/preprocessing/interpolate_to.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@
print(__doc__)
ylim = (-10, 10)

# %%
# Load the MEG data
data_path = sample.data_path()
fif_file_path = data_path / "MEG" / "sample" / "sample_audvis_raw.fif"
raw_meg = mne.io.read_raw_fif(fif_file_path)

raw_meg.pick("meg")

# %%
# Define the target montage
standard_montage_meg = make_standard_montage("ctf275")

# %%
# Use interpolate_to to project MEG data to the standard montage
raw_meg_interpolated_mne = raw_meg.copy().interpolate_to(
standard_montage_meg, method="MNE"
)

# %%
# Load EEG data
data_path = sample.data_path()
Expand Down
48 changes: 48 additions & 0 deletions mne/channels/_standard_montage_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,52 @@ def _mgh_or_standard(basename, head_size, coord_frame="unknown"):
)


def _meg(basename, head_size):
fname = op.join(MONTAGE_PATH, basename)

# Create a simple class instance instead of a list
class CustomMontage:
pass

montage = CustomMontage()

# Read the file
with open(fname) as f:
# Skip header line
header = f.readline().strip().split()

ch_names = []
ch_types = []
pos = []
ori = []

for line in f:
parts = line.strip().split()
ch_names.append(parts[0])
ch_types.append(parts[1])
pos.append([float(parts[2]), float(parts[3]), float(parts[4])])
ori.append([float(parts[5]), float(parts[6]), float(parts[7])])

pos = np.array(pos)

montage.ch_names = ch_names

# # Create a dictionary mapping channel names to positions
# montage.ch_pos = dict(zip(ch_names, pos))
montage.ch_pos = pos
# # TODO - make_dig_montage():
# For custom montages without fiducials, this parameter must be set
# to ``'head'``. ->
# "kind": FIFF.FIFFV_POINT_EEG, ->
# dig names each chennel as EEG #1 ...

# These aren't standard DigMontage attributes but can be useful
montage.ch_types = ch_types
montage.ori = ori

return montage


standard_montage_look_up_table = {
"EGI_256": _egi_256,
"easycap-M1": partial(_easycap, basename="easycap-M1.txt"),
Expand Down Expand Up @@ -165,6 +211,8 @@ def _mgh_or_standard(basename, head_size, coord_frame="unknown"):
"brainproducts-RNP-BA-128": partial(
_easycap, basename="brainproducts-RNP-BA-128.txt"
),
"ctf275": partial(_meg, basename="ctf275.txt"),
"neuromag306": partial(_meg, basename="neuromag306.txt"),
}


Expand Down
25 changes: 23 additions & 2 deletions mne/channels/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ def interpolate_bads(

return self

def interpolate_to(self, sensors, origin="auto", method="spline", reg=0.0):
def interpolate_to(self, sensors, origin="auto", method=None, reg=0.0):
"""Interpolate EEG data onto a new montage.

.. warning::
Expand Down Expand Up @@ -1003,10 +1003,31 @@ def interpolate_to(self, sensors, origin="auto", method="spline", reg=0.0):
from .montage import DigMontage

# Check that the method option is valid.
_check_option("method", method, ["spline", "MNE"])
_validate_type(sensors, DigMontage, "sensors")
# TODO: Handle the error: sensors must be an instance of DigMontage, got <class 'mne.channels._standard_montage_utils._meg.<locals>.CustomMontage'> instead.

method = _handle_default("interpolation_method", method)

# Filter method to only include 'eeg' and 'meg'
supported_ch_types = ["eeg", "meg"]
keys_to_delete = [key for key in method if key not in supported_ch_types]
for key in keys_to_delete:
del method[key]

# Force MEG to always use MNE method,
# otherwise when method = "spline", the _handle_default function
# forces all channel types to use that method
# TODO: Check if there is a better way to handle this
if "meg" in method:
method["meg"] = "MNE"
valids = {"eeg": ("spline", "MNE"), "meg": ("MNE")}
for key in method:
_check_option("method[key]", key, tuple(valids))
_check_option(f"method['{key}']", method[key], valids[key])
logger.info("Setting channel interpolation method to %s.", method)

# Get target positions from the montage
# TODO: handle the error: AttributeError: 'CustomMontage' object has no attribute 'get_positions'
ch_pos = sensors.get_positions().get("ch_pos", {})
target_ch_names = list(ch_pos.keys())
if not target_ch_names:
Expand Down
Loading
Loading