Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ Enhancements

- Add :func:`mne.viz.Brain.add_volume_labels` to plot subcortical surfaces and other regions of interest (:gh:`9540` by `Alex Rockhill`_ and `Eric Larson`_)

- Add custom cortex curvature colors in :class:`mne.viz.Brain` via the ``cortex`` argument (:gh:`9750` by `Eric Larson`_)

- Add :meth:`mne.channels.DigMontage.apply_trans` to apply a transform directly to a montage (:gh:`9601` by `Alex Rockhill`_)

- :meth:`mne.preprocessing.ICA.fit` now emits a warning if any of the ``start``, ``stop``, ``reject``, and ``flat`` parameters are passed when performing ICA on `~mne.Epochs`. These parameters only have an effect on `~mne.io.Raw` data and were previously silently ignored in the case of `~mne.Epochs` (:gh:`9605` by `Richard Höchenberger`_)
Expand Down
9 changes: 9 additions & 0 deletions examples/visualization/eeg_on_scalp.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@
coord_frame='head', subjects_dir=subjects_dir)
# Set viewing angle
set_3d_view(figure=fig, azimuth=135, elevation=80)

# %%
# A similar effect can be achieved using :class:`mne.viz.Brain`:

brain = mne.viz.Brain(
'sample', 'both', 'pial', 'frontal', background='w',
subjects_dir=subjects_dir)
brain.add_head()
brain.add_sensors(raw.info, trans, meg=False, eeg=('original', 'projected'))
3 changes: 2 additions & 1 deletion mne/tests/test_cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os.path as op
import itertools as itt
import sys

from numpy.testing import (assert_array_almost_equal, assert_array_equal,
assert_equal, assert_allclose)
Expand Down Expand Up @@ -87,7 +88,7 @@ def test_compute_whitener(proj, pca):
W3, _, C3 = compute_whitener(cov3, raw.info, pca=pca, return_colorer=True,
picks=None, verbose='error')
# this tol is not great, but Windows needs it
rtol = 3e-5 if pca in (True, 'white') and proj is True else 1e-11
rtol = 3e-5 if sys.platform.startswith('win') else 1e-11
assert_allclose(W, W2, rtol=rtol)
assert_allclose(C, C2, rtol=rtol)
n_channels = len(raw.ch_names) - len(raw.info['bads'])
Expand Down
2 changes: 1 addition & 1 deletion mne/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
_on_missing, _check_on_missing, int_like, _safe_input,
_check_all_same_channel_names, path_like, _ensure_events,
_check_eeglabio_installed, _check_dict_keys,
_check_edflib_installed)
_check_edflib_installed, _to_rgb)
from .config import (set_config, get_config, get_config_path, set_cache_dir,
set_memmap_min_size, get_subjects_dir, _get_stim_channel,
sys_info, _get_extra_data_path, _get_root_dir,
Expand Down
12 changes: 12 additions & 0 deletions mne/utils/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,3 +826,15 @@ def _ensure_events(events):
raise ValueError(
f'events must be of shape (N, 3), got {events.shape}')
return events


def _to_rgb(*args, name='color', alpha=False):
from matplotlib.colors import colorConverter
func = colorConverter.to_rgba if alpha else colorConverter.to_rgb
try:
return func(*args)
except ValueError:
args = args[0] if len(args) == 1 else args
raise ValueError(
f'Invalid RGB{"A" if alpha else ""} argument(s) for {name}: '
f'{repr(args)}') from None
27 changes: 13 additions & 14 deletions mne/viz/_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from ..utils import (get_subjects_dir, logger, _check_subject, verbose, warn,
has_nibabel, check_version, fill_doc, _pl, get_config,
_ensure_int, _validate_type, _check_option, deprecated,
CONNECTIVITY_DEPRECATION_MSG)
CONNECTIVITY_DEPRECATION_MSG, _to_rgb)
from .utils import (mne_analyze_colormap, _get_color_list,
plt_show, tight_layout, figure_nobar, _check_time_unit)
from ..bem import ConductorModel, _bem_find_surface, _ensure_bem_surfaces
Expand Down Expand Up @@ -1030,6 +1030,7 @@ def _plot_sensors(renderer, info, to_cf_t, picks, meg, eeg, fnirs,

actors = dict(meg=list(), ref_meg=list(), eeg=list(), fnirs=list(),
ecog=list(), seeg=list(), dbs=list())
scalar = 1 if units == 'm' else 1e3
for ch_name, ch_coord in ch_pos.items():
ch_type = channel_type(info, info.ch_names.index(ch_name))
# for default picking
Expand All @@ -1041,7 +1042,6 @@ def _plot_sensors(renderer, info, to_cf_t, picks, meg, eeg, fnirs,
plot_sensors = (ch_type != 'fnirs' or 'channels' in fnirs) and \
(ch_type != 'eeg' or 'original' in eeg)
color = defaults[ch_type + '_color']
scalar = 1 if units == 'm' else 1e3
# plot sensors
if isinstance(ch_coord, tuple): # is meg, plot coil
verts, triangles = ch_coord
Expand Down Expand Up @@ -1071,7 +1071,8 @@ def _plot_sensors(renderer, info, to_cf_t, picks, meg, eeg, fnirs,
'pairs' in fnirs:
actor, _ = renderer.tube( # array of origin and dest points
origin=sources[ch_name][np.newaxis] * scalar,
destination=detectors[ch_name][np.newaxis] * scalar)
destination=detectors[ch_name][np.newaxis] * scalar,
radius=0.001 * scalar)
actors[ch_type].append(actor)

# add projected eeg
Expand All @@ -1080,17 +1081,19 @@ def _plot_sensors(renderer, info, to_cf_t, picks, meg, eeg, fnirs,
logger.info('Projecting sensors to the head surface')
eeg_loc = np.array([
ch_pos[info.ch_names[idx]] for idx in eeg_indices])
eeg_loc = apply_trans(to_cf_t['head'], eeg_loc)
eegp_loc, eegp_nn = _project_onto_surface(
eeg_loc, head_surf, project_rrs=True,
return_nn=True)[2:4]
del eeg_loc
eegp_loc *= scalar
scale = defaults['eegp_scale'] * scalar
actor, _ = renderer.quiver3d(
x=eegp_loc[:, 0], y=eegp_loc[:, 1], z=eegp_loc[:, 2],
u=eegp_nn[:, 0], v=eegp_nn[:, 1], w=eegp_nn[:, 2],
color=defaults['eegp_color'], mode='cylinder',
scale=defaults['eegp_scale'], opacity=0.6,
scale=scale, opacity=0.6,
glyph_height=defaults['eegp_height'],
glyph_center=(0., -defaults['eegp_height'], 0),
glyph_center=(0., -defaults['eegp_height'] / 2., 0),
glyph_resolution=20,
backface_culling=True)
actors['eeg'].append(actor)
Expand Down Expand Up @@ -2650,7 +2653,6 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
The triangular mesh surface.
"""
import matplotlib.pyplot as plt
from matplotlib.colors import ColorConverter
# Update the backend
from .backends.renderer import _get_renderer

Expand Down Expand Up @@ -2713,8 +2715,6 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
for stc in stcs]
unique_vertnos = np.unique(np.concatenate(vertnos).ravel())

color_converter = ColorConverter()

renderer = _get_renderer(bgcolor=bgcolor, size=(600, 600), name=fig_name)
surface = renderer.mesh(x=points[:, 0], y=points[:, 1],
z=points[:, 2], triangles=use_faces,
Expand Down Expand Up @@ -2756,7 +2756,7 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
x, y, z = points[v]
nx, ny, nz = normals[v]
renderer.quiver3d(x=x, y=y, z=z, u=nx, v=ny, w=nz,
color=color_converter.to_rgb(c),
color=_to_rgb(c),
mode=mode, scale=scale_factor)

for k in ind:
Expand Down Expand Up @@ -3169,8 +3169,6 @@ def _plot_dipole(ax, data, vox, idx, dipole, gridx, gridy, ori, coord_frame,
show_all, pos, color, highlight_color, title):
"""Plot dipoles."""
import matplotlib.pyplot as plt
from matplotlib.colors import ColorConverter
color_converter = ColorConverter()
xidx, yidx, zidx = np.round(vox[idx]).astype(int)
xslice = data[xidx]
yslice = data[:, yidx]
Expand All @@ -3179,8 +3177,9 @@ def _plot_dipole(ax, data, vox, idx, dipole, gridx, gridy, ori, coord_frame,
ori = ori[idx]
if color is None:
color = 'y' if show_all else 'r'
color = np.array(color_converter.to_rgba(color))
highlight_color = np.array(color_converter.to_rgba(highlight_color))
color = np.array(_to_rgb(color, alpha=True))
highlight_color = np.array(_to_rgb(
highlight_color, name='highlight_color', alpha=True))
if show_all:
colors = np.repeat(color[np.newaxis], len(vox), axis=0)
colors[idx] = highlight_color
Expand Down
Loading