Skip to content
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

Harmonize position of sensors in layout for MEGIN systems #12342

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

mscheltienne
Copy link
Member

@mscheltienne mscheltienne commented Jan 9, 2024

The position of the grads is inconsistent in the Vectorview layouts, sometimes with the sensor MEG XXX2 on top, and sometimes with the sensor MEG XXX3 on top. A similar variability can be observed on the DACQ acquisition software of a MEGIN Triux Neo, although it's not the same triplets of channels than are affected thus yielding a difference between MEGIN's software and MNE.

I don't believe the order MEG XXX2 or MEG XXX3 on top is relevant as both sensor occupy the same location. This PR harmonize the position by placing MEG XXX3 on top in Vectorview-all and Vectorview-grad. The large diff comes from the difference in space/tabulation; standardize with the writer and arbitrary in the original file.

from mne.channels import read_layout

read_layout("Vectorview-all").plot()

On main:

layout

On this PR:

layout

@mscheltienne
Copy link
Member Author

Please close if there was a reason for the up/down position of the sensor MEG XXX2/MEG XXX3 that I was unaware of.

@larsoner
Copy link
Member

larsoner commented Jan 9, 2024

I suspect this layout comes from the original Neuromag software. In my interactions with the MEGIN/Neuromag folks I have never seen them make a decision seemingly at random, there is always some reason -- so I wonder what the reason is here. One possibility for example is that the one on top is whichever sensor has the direction of sensitivity oriented toward the front of the helmet, or the top of the helmet, or something else. It would be good to understand this reasoning before making this change.

Moreover, regardless of whether there is a reason (or not) I'd more be in favor of some different naming scheme used for the sensor-number-based layout proposed here and the one we have in main. People have presumably used sensor layouts for years (e.g., in Xfit) and it would be nice not to suddenly change things on them, or if we do (since we are more lenient on this for viz), at least offer a layout option that gives them the old behavior back.

@larsoner
Copy link
Member

larsoner commented Jan 9, 2024

A similar variability can be observed on the DACQ acquisition software of a MEGIN Triux Neo, although it's not the same triplets of channels than are affected thus yielding a difference between MEGIN's software and MNE.

Just saw this part on reread -- I wonder if this changed over the years somehow...

@larsoner
Copy link
Member

larsoner commented Jan 9, 2024

Please close if there was a reason for the up/down position of the sensor MEG XXX2/MEG XXX3 that I was unaware of.

I'll ask some folks who might know and see if I can get some info

@mscheltienne
Copy link
Member Author

I assume it was arbitrary as I assumed the files were coming from old Neuromag systems and did not match the one on our DACQ.. I can ask MEGIN directly, it would be nice to know if there is a reason or not.

I'd more be in favor of some different naming scheme used for the sensor-number-based layout proposed here and the one we have in main. People have presumably used sensor layouts for years (e.g., in Xfit) and it would be nice not to suddenly change things on them, or if we do (since we are more lenient on this for viz), at least offer a layout option that gives them the old behavior back.

IMO, adding one more layout option can be confusing. If there is only one, it eliminates the question "Which one should I take?" or "What's the difference?" and makes the API more consistent. Personally, I would consider this a bugfix.

@mscheltienne
Copy link
Member Author

OK, I'll let you ask then ;)

@mscheltienne mscheltienne marked this pull request as draft January 9, 2024 15:35
@larsoner
Copy link
Member

Okay got some info. Quoting @staulu who did some invistigation and inquiries (thanks!):

  1. In the old 122-channel Neuromag system, all gradiometer directions (latitude/longitude) were consistent. I think this means that in that system you could infer the direction of the gradient from the last digit of the channel number (2 or 3).
  2. In the 306-channel system, every other row of sensor elements had to be turned by 90 degrees because of lack of physical space.
  3. Therefore, one cannot infer the direction of the gradient from that number (2 or 3) directly, but the Xplotter layout is consistent in the sense that the upper and lower gradiometers of each sensor element always correspond to the latitude and longitude gradients, respectively.

So I wrote a little script to check this:

Ori code check
import numpy as np
import mne

raw = mne.io.read_raw_fif(mne.datasets.sample.data_path() / 'MEG/sample/sample_audvis_raw.fif')
raw.pick("grad")
lout = mne.channels.read_layout("Vectorview-all")
ys = lout.pos[:, 1]
mask_top = np.zeros(len(raw.ch_names), bool)
for ci, (ch_name_1, ch_name_2) in enumerate(zip(raw.ch_names[::2], raw.ch_names[1::2])):
    assert ch_name_1[:-1] == ch_name_2[:-1]
    assert set([ch_name_1[-1], ch_name_2[-1]]) == {"2", "3"}
    idx_1 = np.where(np.array(lout.names) == ch_name_1)[0]
    assert len(idx_1) == 1
    idx_2 = np.where(np.array(lout.names) == ch_name_2)[0]
    assert len(idx_2) == 1
    if ys[idx_1[0]] > ys[idx_2[0]]:
        this_mask_top = [True, False]
    else:
        this_mask_top = [False, True]
    mask_top[2 * ci:2 * ci + 2] = this_mask_top
mask_bottom = ~mask_top
# spot check against plot from gh-12342
assert "MEG 0112" in np.array(raw.ch_names)[mask_top]
assert "MEG 2033" in np.array(raw.ch_names)[mask_top]

pos = np.array([ch["loc"][:3] for ch in raw.info["chs"]])  # in MEG device coord frame
ori = np.array([ch["loc"][3:6] for ch in raw.info["chs"]])

renderer_kwargs = dict(bgcolor="w")
renderer = mne.viz.backends.renderer.create_3d_figure(
    size=(800, 800), scene=False, bgcolor="w",
)
mne.viz.plot_alignment(raw.info, meg="sensors", coord_frame="meg", fig=renderer.scene())
renderer.quiver3d(*pos[mask_top].T, *ori[mask_top].T, "r", scale=0.015, mode="arrow")
renderer.quiver3d(*pos[mask_bottom].T, *ori[mask_bottom].T, "orange", scale=0.015, mode="arrow")
mne.viz.set_3d_view(renderer.figure, azimuth=0, elevation=90, distance=0.55)

Which seems to confirm that the top sensors in the topo plot (red) are oriented along lines of latitude (horizontal sensitivity along the equator), whereas the bottom sensors in the top plot (orange) are oriented along lines of longitude (vertical sensitivity along the equator):

ori

And this also matches the hardware manual:

image

So @mscheltienne I suggest we make a documentation update somewhere describing this, probably in read_layout?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants