mne-tools / mne-python

MNE: Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python
https://mne.tools
BSD 3-Clause "New" or "Revised" License
2.7k stars 1.31k forks source link

VIZ: plot_topomap outlines 'head' and 'skirt' have the same output #7397

Closed fraimondo closed 1 year ago

fraimondo commented 4 years ago

Describe the bug

Calling plot_topomap with outlines parameter seat to 'head' or 'skirt' produce the same output.

Steps to reproduce

import os
import mne
from matplotlib import pyplot as plt

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.crop(tmax=60).load_data()
raw.pick_types(meg='mag')

fig, axes = plt.subplots(1, 2)
mne.viz.plot_topomap(raw._data[:, 0], pos=raw.info, outlines='skirt',  
                     axes=axes[0])
axes[0].set_title('skirt')
mne.viz.plot_topomap(raw._data[:, 0], pos=raw.info, outlines='head',
                     axes=axes[1])
axes[1].set_title('head')

Expected results

I expected that by setting outlines='head', all the sensors to be inside the head.

Actual results

Screenshot 2020-03-06 at 10 42 21

Additional information

Maybe this is related to this "bug" (if it's the case), or it's because of the new sphere parameter, but this started to happen after I updated to MNE 0.20.dev0.

I also use another code to plot this EGI topographies (with custom outlines, based on the sensors positions): Screenshot 2020-03-06 at 10 44 04

But after updating MNE to the current master (and the one from two weeks ago), the topographies look like this: Screenshot 2020-03-06 at 10 43 52

fraimondo commented 4 years ago

It kind of also breaks the extrapolate parameter. For example, using either 'head' or 'local' results in something like this (left), in contrast with the value 'box' (right) Screenshot 2020-03-06 at 11 40 44

larsoner commented 4 years ago

We intentionally do not force MEG sensors inside the head (it does not make physical sense to do so). So that at least seems like a documentation issue.

The extrapolation looks like a bug

fraimondo commented 4 years ago

We intentionally do not force MEG sensors inside the head (it does not make physical sense to do so). So that at least seems like a documentation issue.

Make sense, however the documentation is not straightforward. The default behaviour for MEG sensors is then 'skirt'.

The extrapolation looks like a bug

Indeed everything started because I want to get rid of the frontal interpolation effect (red circle in the image). I have some other topos where this effect is even worse.

Screenshot 2020-03-06 at 11 40 44

I'll dig into the extrapolate issue then.

larsoner commented 4 years ago

@mmagnuski recently worked a bit on the extrapolation modes (make sure you are on latest master @fraimondo ). Any ideas to guide @fraimondo in finding the extrapolate problem?

mmagnuski commented 4 years ago

@fraimondo The topos on the left look weird indeed. I will take a look at that but a reproducible example would be very helpful. You could for example paste the x, y, z pos of the channels and the corresponding topo values somewhere (gist for example).

I must also say that I don't like very much the way head extrapolation works now - it will result in artifacts in many cases. I was thinking to [change it to]/[add another option] circle where the extrapolation points are placed on a circle with some specified distance to the channels. Because this is related to this issue we could discuss it here. My idea is to:

mmagnuski commented 4 years ago

BTW your EGI topographies look better now - but it seems to be rather related to the correct channel placement (or ordering) than extrapolation.

edit: ah, these are different values, it seems your issue is with the shape of the channel positions. The EGI channel positions are not exactly spherical (they are eliptical) and now the channels are placed on a sphere so that's why you get a different shape.

fraimondo commented 4 years ago

@fraimondo The topos on the left look weird indeed. I will take a look at that but a reproducible example would be very helpful. You could for example paste the x, y, z pos of the channels and the corresponding topo values somewhere (gist for example).

Here's a gist to create this figure: Screenshot 2020-03-10 at 09 24 23

Indeed my issue is this: Screenshot 2020-03-10 at 09 11 16

The interpolation in the front sensors creates the sensation that there's something there, which is false. Thus, I want to use the "local" extrapolate approach.

I must also say that I don't like very much the way head extrapolation works now - it will result in artifacts in many cases. I was thinking to [change it to]/[add another option] circle where the extrapolation points are placed on a circle with some specified distance to the channels. Because this is related to this issue we could discuss it here. My idea is to:

  • first add a warning when using extrapolate='head' with channels outside of head radius
  • add extrapolate='circle' that works like the head option but the extrapolation points are not always placed on the head radius - the extrapolation circle radius is adapted so that all extrapolation points are outside the channels.

That could work.

BTW your EGI topographies look better now - but it seems to be rather related to the correct channel placement (or ordering) than extrapolation.

edit: ah, these are different values, it seems your issue is with the shape of the channel positions. The EGI channel positions are not exactly spherical (they are eliptical) and now the channels are placed on a sphere so that's why you get a different shape.

To be honest, what annoys me the most is that the colorbar is not aligned to the bottom of the topo. I personally think that the new placement/topo is more representative of the reality. However, it's quite difficult to manipulate the plot with the subplots_adjust method from pyplot.

mmagnuski commented 4 years ago

@fraimondo for the interpolation problem use border='mean' - I added it recently for exactly this reason.

fraimondo commented 4 years ago

@mmagnuski I tried with border='mean' but it does not solve the issue.

I just updated the gist.

Screenshot 2020-03-10 at 10 44 40

mmagnuski commented 4 years ago

Thanks, I've run your gist and I see the same as you. extrapolate='box' is what gives you the funny values at the front in the first two cases - in this extrapolation scheme only four extrapolation points are created and they are quite far away from all the channels. In such case border='mean' and border=0 do not help much

but it does not solve the issue.

extrapolate='local' with border='mean' (third axis) looks good for me. Is there something in that topography that you would not expect? The issue, I thought, were the strange yellow colors in one of your early posts, which is no longer present.

fraimondo commented 4 years ago

I made an even better gist that creates this figures:

Screenshot 2020-03-10 at 11 49 36

Screenshot 2020-03-10 at 11 50 06

The first figure is a positive value in the range [0.85, 0.89]. This creates an artefact when the border is not 'mean'. The second figure is a contrast, so the vmin, vmax parameters are symmetric around 0.

In both cases I'm happy with extrapolate='local' and border='mean', so I can finish my paper. However, it's tricky that the same parameters have different behaviours depending on the value ranges and colormap.

mmagnuski commented 4 years ago

In both cases I'm happy with extrapolate='local' and border='mean', so I can finish my paper. However, it's tricky that the same parameters have different behaviours depending on the value ranges and colormap.

This is expected, unfortunatelly, although it is not so much related to the colormap but the scale of the values. The default border=0 (which will change to 'mean' pretty soon) causes the extrapolation points to have a value of 0. If all your data points are far away from zero (as in your example: between about 0.86 and 0.9) the presence of these extrapolation zeros may cause interpolation artifacts (at least with the current interpolation algorithm used in plot_topomap, scipy.interpolate.CloughTocher2DInterpolator). This is one of the reasons why border='mean' will be made the default (see also the left axis of the second image in this PR).

fraimondo commented 4 years ago

Ok. This make a lot of sense! In the EGI topos that I posted before, I do not plot the face electrodes values. So what I do is to replace them with vmin for sequential values/colormaps or 0 in the case of a divergent colormap centered at 0.

My issue is now clarified and solved.

larsoner commented 4 years ago

Thanks for figuring this out @fraimondo @mmagnuski

hoechenberger commented 4 years ago

I just ran into the same issue – I believe this is not fixed. The docstring of viz.plot_evoked_topomap() states:

outlines‘head’ | ‘skirt’ | dict | None
The outlines to be drawn. If ‘head’, the default head scheme will be drawn. If ‘skirt’ the head 
scheme will be drawn, but sensors are allowed to be plotted outside of the head circle. If dict, 
each key refers to a tuple of x and y positions, the values in ‘mask_pos’ will serve as image mask. 
Alternatively, a matplotlib patch object can be passed for advanced masking options, either 
directly or as a function that returns patches (required for multi-axis plots). If None, nothing will 
be drawn. Defaults to ‘head’.

These are some plots I got for a bunch of sensors, first figure is with outlines='skirt', second with outlines='head':

outlines_skirt outlines_head

As you can see, they look identical. Now @larsoner mentioned that for MEG, outlines='head' doesn't make any sense; but then the docs should be updated and potentially the function should throw an error. For EEG, however, both 'skirt' and 'head' should work, which is currently not the case.

mmagnuski commented 4 years ago

@hoechenberger This is expected, the channels are now projected to a sphere, with channels approximately at head circumference placed at topo circle radius. The same projection is done in the case of outlines 'head' and 'skirt' - so the topomaps are identical. Head and skirt modes are an artifact of the previous topomap system when channel positions were stretched to cover the whole head or the whole head + skirt. Currently, once #7455 is decided upon and finished you would be able to control the "skirt" with sphere=radius.

hoechenberger commented 4 years ago

Thanks @mmagnuski. But then this is still an obvious documentation bug – or is it just me?? (wouldn't be the first time!!!)

mmagnuski commented 4 years ago

No, you are right, the documentation should be clarified. But the issue might be a bit broader: @larsoner, @agramfort - maybe we should just deprecate the 'head' vs 'skirt' outlines? Currently they would give the same topomaps, so they should be either removed or their behavior should be changed to affect topomap layout. This second option would duplicate the change in layout obtained when changing sphere radius with sphere argument, so I'd favor the first one.

larsoner commented 4 years ago

head and skirt can still give different results when all electrodes are within the head circle, no?

mmagnuski commented 1 year ago

This issue is no longer relevant (we don't have skirt option anymore).