mne-tools / mne-tools/mne-python

Ability to use discrete colormaps in `mne.viz.plot_source_estimates` without color interpolation

Open
#13,190 6 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

ENH
Dominant language
Python
Stars
3.5k
Forks
1.6k
Avg merge
1d 6h
Merged PRs (30d)
100

Description

Describe the new feature or enhancement

We are using mne to plot single-source points with a discrete colormap to show "which model fits best here" results. We have found that when we supply discrete colormaps, the plotted results end up with a little "halo" of false colour around the edges of of the vertices which should be coloured:

Image

I believe the reason for this is that instead of referencing into the colormap directly to get the colors to plot, mne creates a LUT from 256 sample colours and then the renderer interpolates between them.

I acknowledge that in most cases, where continuous values are plotted using a continuous colormap, interpolated LUTs are a great solution, but I believe they are preventing us from controlling our colors precisely.

I had two questions:

  1. Is there a specific reason, other than performance, why this approach is required?
  2. If we worked on a PR which allowed a specific Colormap object to be queried to plot all colours (thereby allowing us to precisely control color output), would you consider it?
Describe your proposed implementation

My initial approach for the PR would be to create an object which behaves like the LUT externally, but which internally queries the supplied Colormap directly.

Describe possible alternatives

The only other option I can think of would be to increase the number of samples in the LUT to greater than 256. However this doesn't seem like it solves the issue "once and for all", unless the above proposed solution would be insufficiently performant.

Additional context

More concretely, in our specific example we have created a forced-discrete colormap like this:

class DiscreteListedColormap(ListedColormap):
    """Like ListedColormap, but without interpolation between values."""
    def __init__(self, colors: list, name = 'from_list', N = None, scale01: bool = False):
        """
        Args:
            scale01 (bool): True if the values will be supplied to the colormap in the range [0, 1] instead of the range
                [0, N-1].
        """
        self.scale01: bool = scale01
        super().__init__(colors=colors, name=name, N=N)

    def __call__(self, X, *args, **kwargs):
        if self.scale01:
            # Values are supplied between 0 and 1, so map them up to their corresponding index (or close to it)
            X *= self.N
        rounded = np.round(X).astype(int)
        return super().__call__(X=rounded, *args, **kwargs)

But then by sampling this colormap 256 times and interpolating the results when plotting, we lose our ability to control which precise values correspond to which precise colours.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at mne.viz.plot_source_estimates and trace how the supplied colormap becomes the 256-sample LUT and is passed to the renderer. Compare discrete and continuous colormap behavior, then define and validate a performant path that preserves exact discrete colors without haloing.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data-visualization
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.