Repository: nilearn/nilearn

Issue:
Move _mix_colormaps() util function
This is a follow-up issue from #3173.

`plotting.html_surface._mix_colormaps()` should be moved to `plotting._utils` or `plotting.cm` in order to be more easily accessed from the rest of the codebase.


Discussion:
I would like to solve this issue. can you specify path of this files
Hi @deepgohil!
#3173 hasn't been merged yet, so you can't work on this yet.
I can assign you to this issue and ping you when the previous PR gets merged if you want!
Hi @deepgohil would you still like to work on this? It's now possible

Resolving patch:
diff --git a/nilearn/plotting/cm.py b/nilearn/plotting/cm.py
--- a/nilearn/plotting/cm.py
+++ b/nilearn/plotting/cm.py
@@ -7,6 +7,44 @@
 # Custom colormaps for two-tailed symmetric statistics
 
 
+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
+
+
 def _rotate_cmap(cmap, swap_order=("green", "red", "blue")):
     """Swap the colors of a colormap."""
     orig_cdict = cmap._segmentdata.copy()
diff --git a/nilearn/plotting/html_surface.py b/nilearn/plotting/html_surface.py
--- a/nilearn/plotting/html_surface.py
+++ b/nilearn/plotting/html_surface.py
@@ -26,44 +26,6 @@ class SurfaceView(HTMLDocument):  # noqa: D101
     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
-
-
 def _get_vertexcolor(surf_map, cmap, norm,
                      absolute_threshold=None, bg_map=None,
                      bg_on_data=None, darkness=None):
@@ -105,7 +67,7 @@ def _get_vertexcolor(surf_map, cmap, norm,
         # so that background map becomes visible
         surf_colors[~under_threshold, 3] = 0.7
 
-    vertex_colors = _mix_colormaps(surf_colors, bg_colors)
+    vertex_colors = cm._mix_colormaps(surf_colors, bg_colors)
 
     return to_color_strings(vertex_colors)
 
diff --git a/nilearn/plotting/surf_plotting.py b/nilearn/plotting/surf_plotting.py
--- a/nilearn/plotting/surf_plotting.py
+++ b/nilearn/plotting/surf_plotting.py
@@ -16,8 +16,8 @@
 
 from nilearn import image, surface
 from nilearn._utils import check_niimg_3d, fill_doc
-from nilearn.plotting.cm import cold_hot
-from nilearn.plotting.html_surface import _get_vertexcolor, _mix_colormaps
+from nilearn.plotting.cm import _mix_colormaps, cold_hot
+from nilearn.plotting.html_surface import _get_vertexcolor
 from nilearn.plotting.img_plotting import _get_colorbar_and_data_ranges
 from nilearn.plotting.js_plotting_utils import colorscale
 from nilearn.surface import load_surf_data, load_surf_mesh, vol_to_surf


Test patch:
diff --git a/nilearn/plotting/tests/test_cm.py b/nilearn/plotting/tests/test_cm.py
--- a/nilearn/plotting/tests/test_cm.py
+++ b/nilearn/plotting/tests/test_cm.py
@@ -2,8 +2,10 @@
 # vi: set ft=python sts=4 ts=4 sw=4 et:
 """Smoke testing the cm module."""
 import matplotlib.pyplot as plt
+import numpy as np
+import pytest
 
-from nilearn.plotting.cm import dim_cmap, replace_inside
+from nilearn.plotting.cm import _mix_colormaps, dim_cmap, replace_inside
 
 
 def test_dim_cmap():
@@ -22,3 +24,48 @@ def test_replace_inside():
 
 def test_cm_preload():
     plt.imshow([list(range(10))], cmap="cold_hot")
+
+
+def test_mix_colormaps():
+    n = 100
+
+    # Mixin map's shape should be equal to that of
+    # the foreground and background maps
+    foreground_map = np.random.rand(n, 4)
+    background_map = np.random.rand(n, 4)
+    mix_map = _mix_colormaps(foreground_map, background_map)
+    assert mix_map.shape == (n, 4)
+    # Transparency of mixin map should be higher
+    # than that of both the background and the foreground maps
+    assert np.all(mix_map[:, 3] >= foreground_map[:, 3])
+    assert np.all(mix_map[:, 3] >= background_map[:, 3])
+
+    # If foreground and background maps' shapes are different,
+    # an Exception should be raised
+    background_map = np.random.rand(n - 1, 4)
+    with pytest.raises(Exception):
+        _mix_colormaps(foreground_map, background_map)
+
+    # If foreground map is transparent,
+    # mixin should be equal to background map
+    foreground_map = np.random.rand(n, 4)
+    background_map = np.random.rand(n, 4)
+    foreground_map[:, 3] = 0
+    mix_map = _mix_colormaps(foreground_map, background_map)
+    assert np.allclose(mix_map, background_map)
+
+    # If background map is transparent,
+    # mixin should be equal to foreground map
+    foreground_map = np.random.rand(n, 4)
+    background_map = np.random.rand(n, 4)
+    background_map[:, 3] = 0
+    mix_map = _mix_colormaps(foreground_map, background_map)
+    assert np.allclose(mix_map, foreground_map)
+
+    # If foreground and background maps are equal,
+    # RBG values of the mixin map should be equal
+    # to that of the foreground and background maps
+    foreground_map = np.random.rand(n, 4)
+    background_map = foreground_map
+    mix_map = _mix_colormaps(foreground_map, background_map)
+    assert np.allclose(mix_map[:, :3], foreground_map[:, :3])
diff --git a/nilearn/plotting/tests/test_html_surface.py b/nilearn/plotting/tests/test_html_surface.py
--- a/nilearn/plotting/tests/test_html_surface.py
+++ b/nilearn/plotting/tests/test_html_surface.py
@@ -224,48 +224,3 @@ def test_view_img_on_surf():
                                              "radius": 0.,
                                              "interpolation": "nearest"})
     check_html(html)
-
-
-def test_mix_colormaps():
-    n = 100
-
-    # Mixin map's shape should be equal to that of
-    # the foreground and background maps
-    foreground_map = np.random.rand(n, 4)
-    background_map = np.random.rand(n, 4)
-    mix_map = html_surface._mix_colormaps(foreground_map, background_map)
-    assert mix_map.shape == (n, 4)
-    # Transparency of mixin map should be higher
-    # than that of both the background and the foreground maps
-    assert np.all(mix_map[:, 3] >= foreground_map[:, 3])
-    assert np.all(mix_map[:, 3] >= background_map[:, 3])
-
-    # If foreground and background maps' shapes are different,
-    # an Exception should be raised
-    background_map = np.random.rand(n - 1, 4)
-    with pytest.raises(Exception):
-        html_surface._mix_colormaps(foreground_map, background_map)
-
-    # If foreground map is transparent,
-    # mixin should be equal to background map
-    foreground_map = np.random.rand(n, 4)
-    background_map = np.random.rand(n, 4)
-    foreground_map[:, 3] = 0
-    mix_map = html_surface._mix_colormaps(foreground_map, background_map)
-    assert np.allclose(mix_map, background_map)
-
-    # If background map is transparent,
-    # mixin should be equal to foreground map
-    foreground_map = np.random.rand(n, 4)
-    background_map = np.random.rand(n, 4)
-    background_map[:, 3] = 0
-    mix_map = html_surface._mix_colormaps(foreground_map, background_map)
-    assert np.allclose(mix_map, foreground_map)
-
-    # If foreground and background maps are equal,
-    # RBG values of the mixin map should be equal
-    # to that of the foreground and background maps
-    foreground_map = np.random.rand(n, 4)
-    background_map = foreground_map
-    mix_map = html_surface._mix_colormaps(foreground_map, background_map)
-    assert np.allclose(mix_map[:, :3], foreground_map[:, :3])