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

Allow not scaling background maps for surface plots #3173

Merged
merged 96 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
58d848c
Enable not scaling background map for matplotlib
alexisthual Mar 7, 2022
7fd68a7
Missing part in previous commit
alexisthual Mar 7, 2022
0f2d741
Enable bg_on_data and scale_bg_map for plotly
alexisthual Mar 7, 2022
1d96f3f
Update documentation with scale_bg_map
alexisthual Mar 7, 2022
cf5ce0d
Correct spelling mistake
alexisthual Mar 7, 2022
aa82381
Address comments on default values for low-level functions
alexisthual Mar 8, 2022
017586b
Fix threshold = None for plotly
alexisthual Mar 8, 2022
f5171fe
Add surf scale_bg_map to plot_surf_stat_map
alexisthual Apr 5, 2022
37158f0
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Aug 27, 2022
d385305
Fix tests (pep8 + plot_surf_stat_map description)
alexisthual Aug 27, 2022
3850d14
Fix typo causing tests to fail
alexisthual Sep 27, 2022
af7f2f4
Add scale_bg_map to plot_surf_roi + reorder arguments properly
alexisthual Sep 27, 2022
98f490d
Add scale_bg_map and bg_on_data to view_surf and view_img_on_surf
alexisthual Sep 27, 2022
aa67d93
Use scale_bg_map=False in main surface plotting example
alexisthual Sep 27, 2022
d5adb50
Update nilearn/plotting/surf_plotting.py
alexisthual Sep 28, 2022
0e55580
Update nilearn/plotting/surf_plotting.py
alexisthual Sep 28, 2022
0bfaeb0
Update nilearn/plotting/surf_plotting.py
alexisthual Sep 28, 2022
c23345d
Add underscore to one_mesh_info and full_brain_info methods
alexisthual Sep 28, 2022
afacb72
Test different values of scale_bg_map for _get_vertexcolor
alexisthual Sep 28, 2022
112373c
Merge branch 'feature/not_scaled_bg_map' of github.com:alexisthual/ni…
alexisthual Sep 28, 2022
f2b08cb
Fix pep8
alexisthual Sep 28, 2022
a38473a
Address Jerome's & Nicolas' comments
alexisthual Oct 18, 2022
f8002bb
Fix pep8
alexisthual Oct 18, 2022
a4238e1
Fix incorrect calculus in curv sign
alexisthual Oct 23, 2022
72bec43
Rename scale_bg_map to bg_map_rescaled
alexisthual Nov 1, 2022
1e175f5
Proper color mixin function to merge foreground and background maps
alexisthual Nov 1, 2022
1468bd3
Better defaults for view_img_to_surf
alexisthual Nov 1, 2022
ca24eb5
Better default for darkness parameter throughout codebase
alexisthual Nov 1, 2022
619ea2c
Bugfix color mixin when bg_on_data=False
alexisthual Nov 1, 2022
2e73ae7
Correct color mixin for matplotlib
alexisthual Nov 3, 2022
3c6b128
Update nilearn/plotting/tests/test_html_surface.py
alexisthual Nov 5, 2022
08c1477
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Nov 5, 2022
8326a52
Specify default value for bg_map_rescale in view_img_on_surf
alexisthual Nov 5, 2022
dabc284
Specify default value for bg_map_rescale in view_surf
alexisthual Nov 5, 2022
bb64104
Raise error in _mix_colormaps if colormaps have different shapes
alexisthual Nov 5, 2022
ccfadb9
Test plotting.html_surface._mix_colormaps()
alexisthual Nov 5, 2022
9907b8a
Docstring in numpy-style
alexisthual Nov 7, 2022
816b9a5
Factorize division by alpha channel
alexisthual Nov 7, 2022
56bff37
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Nov 28, 2022
1e6e7cc
Curv sign as default bg map when inflate for plot_img_on_surf
alexisthual Nov 28, 2022
bb7b074
Fix spelling mistake
alexisthual Nov 28, 2022
a626420
Add whatsnew entry
alexisthual Nov 28, 2022
e0f39f0
Update nilearn/plotting/html_surface.py
alexisthual Nov 30, 2022
f49440e
[full doc] clearer docstring for bg_map in new methods
alexisthual Dec 5, 2022
bc7e65c
Merge branch 'feature/not_scaled_bg_map' of github.com:alexisthual/ni…
alexisthual Dec 5, 2022
bc69d49
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Dec 5, 2022
01374e8
[full doc]
alexisthual Dec 5, 2022
efb5b23
Update nilearn/_utils/docs.py
alexisthual Dec 6, 2022
125f114
Update nilearn/plotting/html_surface.py
alexisthual Dec 12, 2022
ab5ce75
Factorise logic in _mix_colormaps
alexisthual Dec 12, 2022
ae6c8f5
Bugfix modifying input argument in full_brain_info
alexisthual Dec 12, 2022
371feae
Clearer docstrings for the color mixin util function
alexisthual Dec 12, 2022
b1f0459
Merge branch 'feature/not_scaled_bg_map' of github.com:alexisthual/ni…
alexisthual Dec 12, 2022
ce60b21
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Dec 12, 2022
fcad3b6
Bugfix color mixing function
alexisthual Dec 13, 2022
3740329
Bugfix missing ifelse case in full_brain_info
alexisthual Dec 13, 2022
1a6c194
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Dec 13, 2022
0967313
[full doc]
alexisthual Dec 13, 2022
9b05243
Change default value for bg_map_rescale to "auto"
alexisthual Dec 13, 2022
9128f26
Modify example to use simpler background maps
alexisthual Dec 13, 2022
c99dfcc
Fix dimming effect by copying background surface
alexisthual Dec 19, 2022
5e2288d
Prevent modifying arguments values in _get_vertexcolor
alexisthual Dec 20, 2022
a8c7a34
Update comments in tests for _get_vertexcolor
alexisthual Dec 20, 2022
8159cfe
Use similar logic for _get_vertexcolor and _compute_facecolros_matplo…
alexisthual Dec 20, 2022
ebb9317
Add tests for _compute_facecolors_matplotlib
alexisthual Dec 20, 2022
9891f56
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Jan 2, 2023
589b68b
Fix incorrect use of darkness in _compute_facecolors_matplotlib
alexisthual Jan 3, 2023
8516e91
Clean comments and formulation
alexisthual Jan 3, 2023
b847a83
Explicitly name kwargs when calling _get_vertexcolor()
alexisthual Jan 3, 2023
4c27c08
Correct docstring for bg_map for view_img_on_surf
alexisthual Jan 3, 2023
b0ef759
Clearer handling lf alpha for matplotlib facecolors
alexisthual Jan 3, 2023
0a6ead3
Use correct value for alpha when testing _compute_facecolors_matplotlib
alexisthual Jan 3, 2023
08a99bb
Increase default darkness from .5 to .7
alexisthual Jan 10, 2023
33670b2
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Feb 4, 2023
f8a87f0
Fix pep8 missing line
alexisthual Feb 4, 2023
f02d357
Take 2 background maps for view_img_on_surf
alexisthual Feb 4, 2023
131eab4
Rename argument in view_img_on_surf
alexisthual Feb 4, 2023
5efc41c
Enable customizing bgmap for plot_img_on_surf
alexisthual Feb 4, 2023
600b233
Add docstring for background related args for plot_img_on_surf
alexisthual Feb 5, 2023
02adeca
Bugfix wrong index for background maps
alexisthual Feb 5, 2023
b35dda3
Add tests for plot_img_on_surf
alexisthual Feb 5, 2023
8cac2af
Change versionadded for plotting functions
alexisthual Feb 5, 2023
95abf59
Remove useless print in plotting tests
alexisthual Feb 5, 2023
af798ed
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Feb 11, 2023
caf98f1
Add deprecation warnings for scaled background maps
alexisthual Feb 11, 2023
7414de6
Remove mentions to Surface data object
alexisthual Feb 11, 2023
fbac198
Use correct warning category
alexisthual Feb 11, 2023
23ec6eb
Update examples/01_plotting/plot_3d_map_to_surface_projection.py
alexisthual Feb 28, 2023
32ddb23
Merge branch 'main' of https://github.com/nilearn/nilearn into featur…
alexisthual Mar 1, 2023
94dace5
Remove background_map warning ; remove bg_map_rescale param ; edit bg…
alexisthual Mar 1, 2023
d6f95a1
Fix typo
alexisthual Mar 1, 2023
de6a760
Remove useless files generated by example 04
alexisthual Mar 1, 2023
9522cef
Remove bg_maps argument
alexisthual Mar 1, 2023
a40ee06
[full doc]
alexisthual Mar 1, 2023
7157971
Move enhancement description to correct section
alexisthual Mar 8, 2023
0d7862b
Merge branch 'main' into feature/not_scaled_bg_map
ymzayek Mar 8, 2023
File filter

Filter by extension

Filter by extension

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

- Surface plotting methods no longer automatically rescale background maps, which, among other things, allows to use curvature sign as a background map (:gh:`3173` by `Alexis Thual`_).

Changes
-------
- The behavior of :func:`~nilearn.datasets.fetch_atlas_craddock_2012`, :func:`~nilearn.datasets.fetch_atlas_smith_2009` and :func:`~nilearn.datasets.fetch_atlas_basc_multiscale_2015` is updated with their new parameters to return one map along with a deprecation cycle (:gh:`3353` by `Ahmad Chamma`_).
Expand Down
24 changes: 17 additions & 7 deletions examples/01_plotting/plot_3d_map_to_surface_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,23 @@
fsaverage = datasets.fetch_surf_fsaverage()

##############################################################################
# Sample the 3D data around each node of the mesh
# -----------------------------------------------
# Use mesh curvature to display useful anatomical information
# on inflated meshes
#
# Here, we load the curvature map of the hemisphere under study,
# and define a surface map whose value for a given vertex is
# 1 if the curvature is positive, -1 if the curvature is negative.

import numpy as np
from nilearn import surface

curv_right = surface.load_surf_data(fsaverage.curv_right)
curv_right_sign = np.sign(curv_right)
alexisthual marked this conversation as resolved.
Show resolved Hide resolved

##############################################################################
# Sample the 3D data around each node of the mesh
# -----------------------------------------------

texture = surface.vol_to_surf(stat_img, fsaverage.pial_right)

##############################################################################
Expand All @@ -40,14 +52,14 @@
#
# You can visualize the texture on the surface using the function
# :func:`~nilearn.plotting.plot_surf_stat_map` which uses ``matplotlib``
# as the default plotting engine:
# as the default plotting engine.

from nilearn import plotting

fig = plotting.plot_surf_stat_map(
fsaverage.infl_right, texture, hemi='right',
title='Surface right hemisphere', colorbar=True,
threshold=1., bg_map=fsaverage.sulc_right
threshold=1., bg_map=curv_right_sign,
)
fig.show()

Expand All @@ -72,7 +84,7 @@
fig = plotting.plot_surf_stat_map(
fsaverage.infl_right, texture, hemi='right',
title='Surface right hemisphere', colorbar=True,
threshold=1., bg_map=fsaverage.sulc_right,
threshold=1., bg_map=curv_right_sign, bg_on_data=True,
engine=engine # Specify the plotting engine here
)
fig.show() # Display the figure as with matplotlib figures
Expand Down Expand Up @@ -104,8 +116,6 @@
# Use an atlas and choose regions to outline
# ------------------------------------------

import numpy as np

destrieux_atlas = datasets.fetch_atlas_surf_destrieux()
parcellation = destrieux_atlas['map_right']

Expand Down
8 changes: 5 additions & 3 deletions nilearn/_utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,12 @@ def custom_function(vertices):
# bg_on_data
docdict['bg_on_data'] = """
bg_on_data : :obj:`bool`, optional
If ``True``, and a ``bg_map`` is specified,
If ``True`` and a ``bg_map`` is specified,
the ``surf_data`` data is multiplied by the background
image, so that e.g. sulcal depth is visible beneath
the ``surf_data``.
image, so that e.g. sulcal depth is jointly visible with ``surf_data``.
Otherwise, the background image will only be visible where there
is no surface data (either because ``surf_data`` contains ``nan``\s
or because is was thresholded).

.. note::
This non-uniformly changes the surf_data values according
Expand Down
129 changes: 109 additions & 20 deletions nilearn/plotting/html_surface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import collections.abc
import json
import warnings

import numpy as np
import matplotlib as mpl
Expand All @@ -20,26 +21,86 @@ class SurfaceView(HTMLDocument):
pass


def _mix_colormaps(fg, bg):
"""Mixes foreground and background arrays of RGBA colors.

Parameters
----------
fg : numpy.ndarray
Array of shape (n, 4), foreground RGBA colors
represented as floats in [0, 1]
bg : numpy.ndarray
Array of shape (n, 4), background RGBA colors
represented as floats in [0, 1]

Returns
-------
mix : numpy.ndarray
Array of shape (n, 4), mixed colors
represented as floats in [0, 1]
"""
# Adapted from https://stackoverflow.com/questions/726549/algorithm-for-additive-color-mixing-for-rgb-values/727339#727339 # noqa: E501
if fg.shape != bg.shape:
raise ValueError(
"Trying to mix colormaps with different shapes: "
f"{fg.shape}, {bg.shape}"
)

mix = np.empty_like(fg)

mix[:, 3] = 1 - (1 - fg[:, 3]) * (1 - bg[:, 3])

for color_index in range(0, 3):
mix[:, color_index] = (
fg[:, color_index] * fg[:, 3]
+ bg[:, color_index] * bg[:, 3] * (1 - fg[:, 3])
) / mix[:, 3]

return mix
bthirion marked this conversation as resolved.
Show resolved Hide resolved


def _get_vertexcolor(surf_map, cmap, norm,
absolute_threshold=None, bg_map=None):
vertexcolor = cmap(norm(surf_map).data)
if absolute_threshold is None:
return to_color_strings(vertexcolor)
absolute_threshold=None, bg_map=None,
bg_on_data=None, darkness=None):
if bg_map is None:
bg_map = np.ones(len(surf_map)) * .5
bg_data = np.ones(len(surf_map)) * .5
bg_vmin, bg_vmax = 0, 1
else:
bg_map = surface.load_surf_data(bg_map)
bg_vmin, bg_vmax = np.min(bg_map), np.max(bg_map)
bg_norm = mpl.colors.Normalize(vmin=bg_vmin, vmax=bg_vmax)
bg_color = plt.get_cmap('Greys')(bg_norm(bg_map))
vertexcolor[np.abs(surf_map) < absolute_threshold] = bg_color[
np.abs(surf_map) < absolute_threshold]
return to_color_strings(vertexcolor)
bg_data = np.copy(surface.load_surf_data(bg_map))

# scale background map if need be
bg_vmin, bg_vmax = np.min(bg_data), np.max(bg_data)
if (bg_vmin < 0 or bg_vmax > 1):
bg_norm = mpl.colors.Normalize(vmin=bg_vmin, vmax=bg_vmax)
bg_data = bg_norm(bg_data)

if darkness is not None:
bg_data *= darkness
alexisthual marked this conversation as resolved.
Show resolved Hide resolved

bg_colors = plt.get_cmap('Greys')(bg_data)

# select vertices which are filtered out by the threshold
alexisthual marked this conversation as resolved.
Show resolved Hide resolved
if absolute_threshold is None:
under_threshold = np.zeros_like(surf_map, dtype=bool)
else:
under_threshold = np.abs(surf_map) < absolute_threshold

surf_colors = cmap(norm(surf_map).data)
# set transparency of voxels under threshold to 0
surf_colors[under_threshold, 3] = 0
if bg_on_data:
# if need be, set transparency of voxels above threshold to 0.7
# so that background map becomes visible
surf_colors[~under_threshold, 3] = 0.7
bthirion marked this conversation as resolved.
Show resolved Hide resolved

vertex_colors = _mix_colormaps(surf_colors, bg_colors)

return to_color_strings(vertex_colors)


def one_mesh_info(surf_map, surf_mesh, threshold=None, cmap=cm.cold_hot,
black_bg=False, bg_map=None, symmetric_cmap=True,
bg_on_data=False, darkness=.7,
vmax=None, vmin=None):
"""Prepare info for plotting one surface map on a single mesh.

Expand All @@ -55,7 +116,9 @@ def one_mesh_info(surf_map, surf_mesh, threshold=None, cmap=cm.cold_hot,
info['inflated_left'] = mesh_to_plotly(surf_mesh)
info['vertexcolor_left'] = _get_vertexcolor(
surf_map, colors['cmap'], colors['norm'],
colors['abs_threshold'], bg_map)
absolute_threshold=colors['abs_threshold'], bg_map=bg_map,
bg_on_data=bg_on_data, darkness=darkness,
)
info["cmin"], info["cmax"] = float(colors['vmin']), float(colors['vmax'])
info['black_bg'] = black_bg
info['full_brain_mesh'] = False
Expand All @@ -80,6 +143,7 @@ def _check_mesh(mesh):

def full_brain_info(volume_img, mesh='fsaverage5', threshold=None,
cmap=cm.cold_hot, black_bg=False, symmetric_cmap=True,
bg_on_data=False, darkness=.7,
vmax=None, vmin=None, vol_to_surf_kwargs={}):
"""Project 3D map on cortex; prepare info to plot both hemispheres.

Expand All @@ -101,15 +165,19 @@ def full_brain_info(volume_img, mesh='fsaverage5', threshold=None,
symmetric_cmap=symmetric_cmap, vmax=vmax, vmin=vmin)

for hemi, surf_map in surface_maps.items():
bg_map = surface.load_surf_data(mesh['sulc_{}'.format(hemi)])
curv_map = surface.load_surf_data(mesh["curv_{}".format(hemi)])
bg_map = np.sign(curv_map)

info['pial_{}'.format(hemi)] = mesh_to_plotly(
mesh['pial_{}'.format(hemi)])
info['inflated_{}'.format(hemi)] = mesh_to_plotly(
mesh['infl_{}'.format(hemi)])

info['vertexcolor_{}'.format(hemi)] = _get_vertexcolor(
surf_map, colors['cmap'], colors['norm'],
colors['abs_threshold'], bg_map)
absolute_threshold=colors['abs_threshold'], bg_map=bg_map,
bg_on_data=bg_on_data, darkness=darkness,
)
info["cmin"], info["cmax"] = float(colors['vmin']), float(colors['vmax'])
info['black_bg'] = black_bg
info['full_brain_mesh'] = True
Expand All @@ -130,6 +198,7 @@ def _fill_html_template(info, embed_js=True):
def view_img_on_surf(stat_map_img, surf_mesh='fsaverage5',
threshold=None, cmap=cm.cold_hot,
black_bg=False, vmax=None, vmin=None, symmetric_cmap=True,
bg_on_data=False, darkness=.7,
colorbar=True, colorbar_height=.5, colorbar_fontsize=25,
title=None, title_fontsize=25, vol_to_surf_kwargs={}):
"""Insert a surface plot of a statistical map into an HTML page.
Expand Down Expand Up @@ -164,6 +233,12 @@ def view_img_on_surf(stat_map_img, surf_mesh='fsaverage5',
If True, image is plotted on a black background. Otherwise on a
white background. Default=False.

%(bg_on_data)s
Default=False.

%(darkness)s
Default=1.

vmax : float or None, optional
upper bound for the colorbar. if None, use the absolute max of the
brain map.
Expand Down Expand Up @@ -221,7 +296,9 @@ def view_img_on_surf(stat_map_img, surf_mesh='fsaverage5',
info = full_brain_info(
volume_img=stat_map_img, mesh=surf_mesh, threshold=threshold,
cmap=cmap, black_bg=black_bg, vmax=vmax, vmin=vmin,
symmetric_cmap=symmetric_cmap, vol_to_surf_kwargs=vol_to_surf_kwargs)
bg_on_data=bg_on_data, darkness=darkness,
symmetric_cmap=symmetric_cmap, vol_to_surf_kwargs=vol_to_surf_kwargs
)
info['colorbar'] = colorbar
info['cbar_height'] = colorbar_height
info['cbar_fontsize'] = colorbar_fontsize
Expand All @@ -232,8 +309,9 @@ def view_img_on_surf(stat_map_img, surf_mesh='fsaverage5',

def view_surf(surf_mesh, surf_map=None, bg_map=None, threshold=None,
cmap=cm.cold_hot, black_bg=False, vmax=None, vmin=None,
symmetric_cmap=True, colorbar=True, colorbar_height=.5,
colorbar_fontsize=25, title=None, title_fontsize=25):
bg_on_data=False, darkness=.7, symmetric_cmap=True,
colorbar=True, colorbar_height=.5, colorbar_fontsize=25,
title=None, title_fontsize=25):
"""Insert a surface plot of a surface map into an HTML page.

Parameters
Expand All @@ -252,10 +330,20 @@ def view_surf(surf_mesh, surf_map=None, bg_map=None, threshold=None,
.thickness, .area, .curv, .sulc, .annot, .label) or
a Numpy array

bg_map : Surface data, optional
bg_map : str or numpy.ndarray, optional
Background image to be plotted on the mesh underneath the
surf_data in greyscale, most likely a sulcal depth map for
realistic shading.
If the map contains values outside [0, 1], it will be
rescaled such that all values are in [0, 1]. Otherwise,
it will not be modified.
Default=None.

%(bg_on_data)s
Default=False.

%(darkness)s
Default=1.

threshold : str, number or None, optional
If None, no thresholding.
Expand Down Expand Up @@ -329,6 +417,7 @@ def view_surf(surf_mesh, surf_map=None, bg_map=None, threshold=None,
info = one_mesh_info(
surf_map=surf_map, surf_mesh=surf_mesh, threshold=threshold,
cmap=cmap, black_bg=black_bg, bg_map=bg_map,
bg_on_data=bg_on_data, darkness=darkness,
symmetric_cmap=symmetric_cmap, vmax=vmax, vmin=vmin)
info['colorbar'] = colorbar
info['cbar_height'] = colorbar_height
Expand Down