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

Conversation

contsili
Copy link

@contsili contsili commented Apr 7, 2025

Reference issue (if any)

Fixes #9609 .

What does this implement/fix?

  • Store the canonical CTF and Neuromag sensor definitions in a txt file
  • Extent interpolate_to() to MEG sensors to accommodate axial to planar gradiometer transformation (and back)
  • Since interpolate_to() works similarly to interpolate_bads() I suggest we create two new functions in interpolation.py: _interpolate_to_meeg that uses the 'MNE' interpolation method

Additional information

The channel positions and orientations are adopted from fieldtrip: https://github.com/fieldtrip/fieldtrip/blob/master/template/gradiometer

I am not sure if for the interpolation we need the coil positions and orientations. For clarity: a gradiometer is ONE channel but has TWO coils. A magnetometer is ONE channel and has ONE coil.

As an expectation I have that I will plot ERPs in the ctf and neuromag format. Also I want to create topoplots like: https://www.fieldtriptoolbox.org/assets/img/tutorial/eventrelatedaveraging/figure8.png

contsili added 2 commits April 7, 2025 17:26
This is based on the channel positions and orientations provided by fieldtrip: https://github.com/fieldtrip/fieldtrip/blob/master/template/gradiometer/ctf275.mat
This is based on the channel positions and orientations provided by fieldtrip: https://github.com/fieldtrip/fieldtrip/blob/master/template/gradiometer/neuromag306.mat
Copy link

welcome bot commented Apr 7, 2025

Hello! 👋 Thanks for opening your first pull request here! ❤️ We will try to get back to you soon. 🚴

@contsili contsili marked this pull request as draft April 7, 2025 15:57
@larsoner
Copy link
Member

larsoner commented Apr 9, 2025

Looks like you're making some progress, let me know when you'd like some feedback!

@contsili
Copy link
Author

contsili commented Apr 9, 2025

Hi @larsoner! After a while, I finally found some time :)

I think this is a good moment for some feedback.

Issues / Questions I encountered:
1. Handling ch_type when interpolating from CTF → Neuromag

My initial understanding was that when we interpolate from CTF to Neuromag, the ch_type should already be known.

This is because, CTF systems have reference sensors that should not be interpolated and Neuromag systems have gradiometers and magnetometers, so we need to know the type of each channel.

That's why I added the ch_type parameter in the .txt montage files — to explicitly specify this.

2. Scope of interpolation: only gradiometers?
In relation to (1), I assumed we would only interpolate gradiometers (i.e., axial ↔ planar), and not magnetometers.

Based on this, I thought I would input in _map_eeg_and_meg_channels would use info_from and info_to objects that contain only the gradiometer channels. This was another reason for adding the ch_type parameter in the montage.

3. Limitations of make_dig_montage
All the montages are created via make_dig_montage.

From the documentation of make_dig_montage:

 .. note::
            For custom montages without fiducials, this parameter must be set
            to ``'head'``.

I understood that for my setup (no fiducials) I should use a custom montage.

However: running montage = make_dig_montage(ch_pos=ch_pos, coord_frame="head") does not parse the ch_type or ori and my channels are read as generic EEG labels, e.g., standard_montage.ch_names[0] = 'EEG #1' etc.

4. Custom _meg() function and related problems
Because of (3), I created a custom_meg()function to parse the .txt files.

But now I run into new problems. When I run interpolate_to() :

_validate_type(sensors, DigMontage, "sensors") leads to the error: AttributeError: 'CustomMontage' object has no attribute 'get_positions'
ch_pos = sensors.get_positions().get("ch_pos", {}) leads to the error: sensors must be an instance of DigMontage, got <class 'mne.channels._standard_montage_utils._meg.<locals>.CustomMontage'> instead

5. I added _meg() inside _standard_montage_utils but am I allowed to change such private function ?

6. I am trying to follow how interpolate_to() code style and structure is already built.

However, this forces us to diverge a bit from the standard interpolate_bads recipe

One idea I had would be to encapsulate some logic in a new function interpolate_to_meeg() inside interpolation.py. Just as interpolate_bads_meeg() works inside interpolation.py. But if we do that then we need to do the same thing for the EEG part of interpolate_to()

@contsili contsili marked this pull request as ready for review April 9, 2025 15:48
@contsili contsili requested a review from drammock as a code owner April 9, 2025 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Axial to planar gradiometer transformation
2 participants