mne-python
mne-python copied to clipboard
Default standard_1020 montage contains realistic, not template positions
Describe the bug
On the current master (i.e., after #7066) frontal EEG channels are drawn outside of the head in topoplots.
Steps to reproduce
The simplest variant is probably
mne.viz.plot_montage(mne.channels.make_standard_montage('standard_1020'))
Naturally it also occurs in plot_topomap
and is particularly suboptimal for outline='head'
and extrapolate='head'
.
Expected results
I would expect the head circumference to intersect the positions of the outer AF, F, FT, ... PO, O electrodes. This would match previous behavior and common plots (e.g., the 10-05 paper http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.116.7379&rep=rep1&type=pdf or https://github.com/sappelhoff/eeg_positions).
Actual results
Hence, the head seems to be shifted vertically (T9 and T10 are also not vertically centered with respect to the ears).
Additionally, the comment in the extrapolation snippet https://mne.tools/dev/auto_examples/visualization/plot_evoked_topomap.html suggests that changing the sphere diameter has an influence, but it didn't result in any changes for me.
for @larsoner
This is because we plot in head coordinates now, and when we change to head coords it shifts the positions. In theory if you shift the sphere
center to the opposite of this shift, it should fix the problem.
It looks like T9/T10/ are not the same as LPA/RPA. And looking at the file:
https://github.com/mne-tools/mne-python/blob/master/mne/channels/data/montages/standard_1020.elc
The LPA is at -86.0761 -19.9897 -47.9860 and T9 is at -85.8941 -15.8287 -48.2830. This contradicts what @sappelhoff's site says:
The left preauricular point = (-1, 0, 0) ... coincides with T9 The right preauricular point = (1, 0, 0) ... coincides with T10
So this does not agree with our standard_1020.elc
. Not sure if our elc
is wrong, or what.
Additionally, the comment in the extrapolation snippet
Yes this is wrong, it should say that changing the head center will influence things.
So the values in standard_1020.elc
seem to be identical to the "realistic" ones in Robert Oostenveld's blog (https://robertoostenveld.nl/electrode/) :
I have also made a computation of all the electrode locations on a reallistical head surface, based on the distances along the (triangulated) surface of the head. The head surface used was constructed from the canonical MRI that is included in the SPM2 package, and locations are expressed in MNI coordinates.
The coordinates can be found at: https://robertoostenveld.nl/electrodes/realistic_1005.txt
For the y coordinates, the absolute values of the maximum (0.0845
, Nz) and minimum (-0.113
, Iz) differ which might cause the problem if some normalization/centering is performed in the code.
There are also spherical versions
The first version (“sphere1”) assumes Fpz, Oz, T7 and T9 to be on the poles of the sphere. The location of Nz, Iz, LPA and RPA is subsequently determined according to the following: Oz is at the equator, Cz is at the north-pole. The difference between them is 90 degrees or pi/2 along the surface of the unit sphere. The distance between each of Cz-CPz-Pz-POz-Oz is equal and is 22.5 degree (note that Pz is at 45 degrees from the north-pole). The distance between Iz and Oz is equal to the other distances, therefore Iz is at 22.5 below the equator. Using this, the coordinates of Iz can be computed using the sin() and cos() of 22.5 degrees. The other “fiducial” points have similar coordinates. The Z value for the whole fiducial points contour is -sin(22.5)=-0.3827.In the second version (“sphere2”) I have tried to mimic the electrode locations that are used in BESA as close as possible. Compared to the “sphere1” version described above, the electrodes are all shifted to slightly lower locations.
For a spherical head description, LPA and T9 are identical and the absolute values of the y coordinate are the same for Nz and Iz: https://robertoostenveld.nl/electrodes/plotting_1005.txt and https://robertoostenveld.nl/electrodes/sphere_1005.txt
I would say that when using a standard EEG layout, people are fine with a spherical head model and this is especially the case when having topomaps with a circular head shape. Although I haven't tried it, a simple solution might be to use the spherical version for the standard_10xx
layouts.
how would you implement this and expose this to users?
I only see this now, but let me add some explanation (no solution, only information):
@robertoostenveld's computations versus my own computations are slightly different in their assumptions.
- Robert says the equator goes through Fpz and Oz (and T7 and T9 ... I think the T9 is actually a typo and meant to be T8)
The first version (“sphere1”) assumes Fpz, Oz, T7 and T9 to be on the poles of the sphere.
- I say the equator goes through Nz and Iz (and T9 and T10, which I say coincide with LPA/RPA for the sake of the model coordinate system, which is defined by Nasion, Inion, and the preauricular points)
so "my" equator is one contour line below that of Robert's computation, hence the positions do not match up absolutely (only in terms of relative distance).
I am of the impression that the choice through which electrodes the equator of the spherical head model passes is rather arbitrary. My choice was inspired by the Brain Products cap, where on the outermost circle, you would find the Nz, Iz, T9 and T10 electrodes.
correct, T9 should be T8. I have corrected it on https://robertoostenveld.nl/electrode/
My motivation for the circles is based on https://pdfs.semanticscholar.org/53a7/cf6bf8568c660240c080125e55836d507098.pdf figure 6 and 7 and Jasper 1958 (could not find a PDF so quickly).
The contour through Fpz/T7/Oz/T8 is one that is reasonably easy to follow due to the regularity of the head. If you go further down, you get the nose, eye sockets, and ears in the way. So estimating 10% (or 20%) distances along the lower (Nz/Iz) contour is hard, and not for the Fpz/Oz contour. In https://github.com/fieldtrip/fieldtrip/blob/master/ft_electrodeplacement.m with method=1020 I am using this contour following procedure to place electrodes on a scalp surface, following the contours that follow from the plane intersections with the triangulated surface.
But this is all for source modeling, whereas the original question is about visualization of 3D data on a 2D plane: I don't think spheres reflect the head very well, even though we also use them for http://www.fieldtriptoolbox.org/template/layout/. I like the "helmet" layouts (see under CTF, Neuromag and 4D) more than the sphere ones. I think it would be good (for the wider EEG field) to also use better 2D layouts than "spheres with a triangle for the nose".
Wrt the schematic EEG layout with a circle, I would get rid of the ears. They are not at the right location relative to the electrodes.
PS the 1020 algorithm that is also in ft_electrodeplacement was actually used to create the electrode positions I am sharing on my blog, even though at that time FT did not exist yet. I think I used it both for the spherical model and for the realistic model.
I think the problem is not that some locations are now outside the head (e.g. AF9, AF10, ...), but that too few locations are outside the head as compared to the previous behavior. Previously, Fz was pretty close to the head circumference (close to the nose), whereas now it is way closer to the center (see before and after plots in #7430).
I don't know which configuration is more correct, but the old behavior definitely looked much better.
@cbrnr I agree, I will take a look at this. Your problem in #7430 may be related to #7341.
What I also find weird is that the center of the head indicated by channel scatter does not match the center of head circle.
If I move the sphere center to have Cz
closer to the head circle center the channel scatter has a straight horizontal line at FCz
level.
mne.viz.plot_montage(mntg, sphere=(0, 0.016, 0, 0.1))
After checking: this issue comes from channel positions, see the first panel on the left (scatter size indicates channel distance in the ignored dimension): the channels positions seem to be digitized/realistic, not schematic/spherical. This is what @hekolk mentioned earlier. I think the schematic positions would be better if we project to the sphere anyway.
Shouldn't channels T9 - T7 - C5 - C3 - C1 - Cz - ... form a horizontal line (according to the cartoon posted by @sappelhoff)?
I think they should, take a look at the previous comment by @hekolk.
I'm still not sure what the problem is. Do we already know? What's the plan to fix it? I'd really like this to be fixed for 0.20 so I'm adding this to the milestone.
@cbrnr The 10-20 channel positions 'standard_1020'
shipped with mne are the "realistic channel positions" (https://robertoostenveld.nl/electrodes/realistic_1005.txt) but there are also less realistic spherical coordinates (https://robertoostenveld.nl/electrodes/plotting_1005.txt and https://robertoostenveld.nl/electrodes/sphere_1005.txt). The realistic positions are better for coregistering with MRI and source localization (if one does not have digitized channel positions) but for plotting the plotting/spherical positions should be better.
@agramfort @larsoner
I think we should default make_standard_montage('standard_1020')
to spherical 10-20 coords and allow for make_standard_montage('realistic_1020')
(or 'standard_1020_realistic'
) for the current 10-20 default.
Fixing the usage of sphere
in topomap code does help a little (PR soon, more details there) but introduces other issues (that could be avoided but will require some decision).
👍 for changing standard_1020
to spherical coords and renaming the existing realistic ones to realistic_1020
. Which of the two spherical would you use? Also, which function reads these kinds of files?
no objection as long as it's well documented
I think we should default make_standard_montage('standard_1020') to spherical 10-20 coords and allow for make_standard_montage('realistic_1020') (or 'standard_1020_realistic') for the current 10-20 default.
Usually we want to deprecate things if the existing option is mostly wrong and we want to give people time to adapt gracefully.
Our current behavior can be summarized as: good source loc, bad sphere head plots. Making the proposed deprecation would lead to: bad source loc, good sphere head plots. This is not a 100% clear gain to me, so I'm not sure if deprecation is justified. I'd lean toward making a standard_1020_sphere
and documenting the differences rather than force a "fix" on people that will break otherwise reasonable/working code.
However, I am no a "template EEG" expert, so if having the 10-20 electrode locations land where people expect in topomaps -- and it has come up a few times obviously -- is substantially more important than having good source loc by default, and important/common enough to justify breaking existing code for people, I can live with it. But if we do so we should probably just get rid of standard_1020
altogether and have standard_1020_sphere
and standard_1020_realistic
as the new options to force people to be explicit about their choice.
Also, which function reads these kinds of files?
https://mne.tools/dev/generated/mne.channels.make_standard_montage.html
I really liked the old topomaps much better. In my EEG-related work I never had real electrode locations, but there are use cases where real locations improve e.g. source localization. Just creating a nice looking topomap serves a different purpose, so I think it would be good to have both options. As long as we plot to a spherical topomap (is there an option to use a realistic head, especially in 2D?) I think realistic locations are not appropriate and I'd prefer the theoretical locations.
How can I read non-built-in montage files with this function?
As long as we plot to a spherical topomap (is there an option to use a realistic head, especially in 2D?) I think realistic locations are not appropriate and I'd prefer the theoretical locations.
The central idea that the big refactoring took care of was that we have one code path for all electrode locations (digitized or from a template / "theoretical") to go to the flattened 2D plot for topomaps. I'd prefer not to compromise this or the better-for-source localization (i.e., switch to theoretical rather than realistic locations) to make the 2D flattening projection look a bit better. So I think we just have differences in the weights we assign these things...
How can I read non-built-in montage files with this function?
I am not an expert here (or in the file formats), but the Notes
seems to list a bunch of function names. Did you look there and/or in our docs to see if what you want to do is covered?
The central idea that the big refactoring took care of was that we have one code path for all electrode locations (digitized or from a template / "theoretical") to go to the flattened 2D plot for topomaps. I'd prefer not to compromise this or the better-for-source localization (i.e., switch to theoretical rather than realistic locations) to make the 2D flattening projection look a bit better. So I think we just have differences in the weights we assign these things...
Having one code path is a good thing, but the new behavior is clearly a regression for the use case of plotting template locations on a 2D sphere. If you think this can be solved by tweaking some weights this is fine with me (as long as the result looks like it used to). Is this what #7455 solves?
I am not an expert here (or in the file formats), but the Notes seems to list a bunch of function names. Did you look there and/or in our docs to see if what you want to do is covered?
Me neither, but I saw the function names and thought mne.channels.read_custom_montage
would do the job. It does read the file, but I can't plot the montage because ValueError: Some fiducial points are missing (got dict_keys(['nasion', 'lpa', 'rpa']))
. So maybe this is not the correct reader function.
@cbrnr
I really liked the old topomaps much better.
I think you are referring to two (or more) issues here actually. The channel positions for 1020 were like this (meaning: realistic) before topomap plotting changes IIRC, so they are not responsible for your preferrence of the old topomaps. What you experience in #7430 is thus more related to the new topo plotting: (1) the channels are not stretched to the whole head as previously irrespective of their actual position (this is a definitive improvement), (2) the sphere
argument did not work exactly as expected (#7455, #7341) and as I suggest in #7455 could have a better default.
It would be best to disccuss topomap (2) in #7455 and focus on standard_1020 realistic vs spherical here.
@larsoner
I'd lean toward making a standard_1020_sphere and documenting the differences rather than force a "fix" on people that will break otherwise reasonable/working code.
I'm fine with this too. Although I think that 2d topomap plotting is much more popular in EEG world than source localization using template chan locs. There seems to be only one example in mne docs on how to do source localization with channel and brain template so it might be worth stressing the usage of standard_1020_realistic
there (probably most users trying to do that use this example as a starting point). Other channel positions like biosemi
for example are spherical by default - so it would be worthwhile to add such a warning to the docs anyway (by default mne coreg tries to rescale the head model to match the channel positions, while with spherical EEG chanlocs we would actually prefer to do the opposite). Informing the users about standard_1020_spherical
might be more difficult ( a warning when reading standard_1020
in the next two mne versions? that might be effective but also irritating :) ). But I don't have a strong opinion here, I just slightly prefer (+0.25
) a deprecation (with standard_1020
transitioning from standard_1020_realistic
to standard_1020_spherical
).
if we do so we should probably just get rid of standard_1020 altogether and have
standard_1020_sphere
andstandard_1020_realistic
as the new options to force people to be explicit about their choice.
:+1: for this suggestion. It's disruptive, but IMO that's better than silently having old code make plots look different enough to cause people to get annoyed.
@drammock @larsoner Oh, I might have missed this part - I also like this idea.
FWIW, the biosemi64
montage contains 10-20 labels and seems to have template locations (as opposed to realistic ones):
This looks OK. Here's what this montage looks with 0.19.2:
IMO the new plot looks better (in terms of where I'd expect template locations to be). So whatever will be changed regarding the suboptimal visualization of realistic montages (e.g. #7455), please make sure that template montages are not affected.
@cbrnr All topomap/montage plotting will be affected in a way (but not the montages themselves), because plotting with respect to a sphere has to work the same way irrespective of used montage. But don't worry, if channel positions don't look good we may consider changing the default sphere radius. But most of all - you will be able to control the exact channel placement by changing sphere center and radius, to adjust channel placement to your liking.
That's part of the problem. I'd expect a template montage to look good by default when plotted on a sphere. I don't want to tweak parameters until the locations are right.
Based on the discussions, this issue seems sufficiently complex that I think we should move the milestone to 0.21. Otherwise I fear we'll end up delaying release for weeks or months as we sort everything out.
shall we plan a quick live discussion on hangout ?
Moving the milestone to 0.21 under the assumption that #7455 is good enough for release and/or we can backport any fix to 0.20.1
Has this been superseded by #7472?
If not, I'd like to chime in and say:
👍 for changing
standard_1020
to spherical coords and renaming the existing realistic ones torealistic_1020
.
Yes please!!
I was also just running into this issue:
m = mne.channels.make_standard_montage('standard_1005')
print(m.dig[m.ch_names.index('Cz')])
gives:
<DigPoint | EEG #39 : (-78.6, -13.5, 28.6) mm : unknown frame>
which is really not what it should be…
@hoechenberger That is because len(mntg.ch_names) < len(mntg.dig)
, first three dig points are fiducials. But I agree this is inconvenient - maybe fiducials should be at the end, not front of the list?