matplotlib 将Alpha添加到现有色彩Map表

ecfsfe2w  于 2023-10-24  发布在  其他
关注(0)|答案(3)|浏览(194)

我想叠加几个hexbin图,但使用内置的色彩Map表,只有最后一个是可见的。我不想重新构建色彩Map表。如何在事先不知道色彩Map表的内部结构的情况下将线性alpha添加到色彩Map表?

t1rydlwq

t1rydlwq1#

我不太确定这是否符合“不知道色彩Map表的内部结构”的要求,但也许像这样的东西可以为现有的色彩Map表添加线性alpha?

import numpy as np
import matplotlib.pylab as pl
from matplotlib.colors import ListedColormap

# Random data
data1 = np.random.random((4,4))

# Choose colormap
cmap = pl.cm.RdBu

# Get the colormap colors
my_cmap = cmap(np.arange(cmap.N))

# Set alpha
my_cmap[:,-1] = np.linspace(0, 1, cmap.N)

# Create new colormap
my_cmap = ListedColormap(my_cmap)

pl.figure()
pl.subplot(121)
pl.pcolormesh(data1, cmap=pl.cm.RdBu)
pl.colorbar()

pl.subplot(122)
pl.pcolormesh(data1, cmap=my_cmap)
pl.colorbar()

x6492ojm

x6492ojm2#

我想通过一个修复来扩展Bart的答案,它消除了颜色条中的线条伪影。一些历史:直到今天,这些线条伪影仍然存在,并且没有得到很好的解决(参见Matplotlib: Add a custom colorbar that runs from full transparent to full color (remove artifacts)why does my colorbar have lines in it?)。然而,具有Alpha通道的每种颜色都只是颜色与其背景的混合。因此,如果您知道背景,您可以计算相应的非alpha颜色(参见https://www.viget.com/articles/equating-color-and-transparency/)。
下面的解决方案假设,实际的透明度是不必要的数字。如果一个人使用真实的阿尔法在数字和自己的色彩Map表计算非阿尔法颜色值,如果需要的话。

import numpy as np
import matplotlib.pylab as pl
from matplotlib.colors import ListedColormap

# Random data
data1 = np.random.random((4,4))

# Choose colormap which will be mixed with the alpha values
cmap = pl.cm.RdBu

# Get the colormap colors
my_cmap = cmap(np.arange(cmap.N))
# Define the alphas in the range from 0 to 1
alphas = np.linspace(0, 1, cmap.N)
# Define the background as white
BG = np.asarray([1., 1., 1.,])
# Mix the colors with the background
for i in range(cmap.N):
    my_cmap[i,:-1] = my_cmap[i,:-1] * alphas[i] + BG * (1.-alphas[i])
# Create new colormap which mimics the alpha values
my_cmap = ListedColormap(my_cmap)

# Plot
f, axs = pl.subplots(1,2, figsize=(8,3))
h = axs[0].pcolormesh(data1, cmap=pl.cm.RdBu)
cb = f.colorbar(h, ax=axs[0])

h = axs[1].pcolormesh(data1, cmap=my_cmap)
cb = pl.colorbar(h, ax=axs[1])
f.show()

xhv8bpkk

xhv8bpkk3#

Bart提供的解决方案确实为图添加了Alpha通道透明度。该方法的问题是它不能很好地处理顺序色图或发散色图。顺序色图试图近似线性地增加它们的亮度,从苍白色到饱和色,因此,颜色的强度随着值的强度增加而增加。在此之上添加Alpha通道通道意味着浅色阴影主要是如果你在一个苍白的背景上渲染色彩Map表(我认为这是很典型的),色彩Map表在感知上不再是均匀的,较小的值看起来更小。
另一种将现有色图转换为具有alpha通道的方法是尽可能多地将白色从原始色图的RGB颜色中分离出来。通过将“白色分量”转换为透明度,并重新调整RGB值以校正alpha值,我们可以确保新的透明色图将与原始色图完全相同地渲染,如果它是在白色背景下呈现。

import matplotlib.pyplot as plt
import matplotlib.colors
import matplotlib.cm
import numpy as np

def rgb_white2alpha(rgb, ensure_increasing=False):
    """
    Convert a set of RGB colors to RGBA with maximum transparency.
    
    The transparency is maximised for each color individually, assuming
    that the background is white.
    
    Parameters
    ----------
    rgb : array_like shaped (N, 3)
        Original colors.
    ensure_increasing : bool, default=False
        Ensure that alpha values are strictly increasing.
    
    Returns
    -------
    rgba : numpy.ndarray shaped (N, 4)
        Colors with maximum possible transparency, assuming a white
        background.
    """
    # The most transparent alpha we can use is given by the min of RGB
    # Convert it from saturation to opacity
    alpha = 1. - np.min(rgb, axis=1)
    if ensure_increasing:
        # Let's also ensure the alpha value is monotonically increasing
        a_max = alpha[0]
        for i, a in enumerate(alpha):
            alpha[i] = a_max = np.maximum(a, a_max)
    alpha = np.expand_dims(alpha, -1)
    # Rescale colors to discount the white that will show through from transparency
    rgb = (rgb + alpha - 1) / alpha
    # Concatenate our alpha channel
    return np.concatenate((rgb, alpha), axis=1)
    

def cmap_white2alpha(name, ensure_increasing=False, register=True):
    """
    Convert colormap to have the most transparency possible, assuming white background.
    
    Parameters
    ----------
    name : str
        Name of builtin (or registered) colormap.
    ensure_increasing : bool, default=False
        Ensure that alpha values are strictly increasing.
    register : bool, default=True
        Whether to register the new colormap.

    Returns
    -------
    cmap : matplotlib.colors.ListedColormap
        Colormap with alpha set as low as possible.
    """
    # Fetch the cmap callable
    cmap = plt.get_cmap(name)
    # Get the colors out from the colormap LUT
    rgb = cmap(np.arange(cmap.N))[:, :3]  # N-by-3
    # Convert white to alpha
    rgba = rgb_white2alpha(rgb, ensure_increasing=ensure_increasing)
    # Create a new Colormap object
    cmap_alpha = matplotlib.colors.ListedColormap(rgba, name=name + "_alpha")
    if register:
        matplotlib.cm.register_cmap(name=name + "_alpha", cmap=cmap_alpha)
    return cmap_alpha

# Get original Reds colormap
cmap_og = plt.get_cmap("Reds")

# Get our new version of the colormap with white2alpha
cmap_w2a = cmap_white2alpha("Reds")

# Replicate Bart's method, which adds linear alpha without rescaling RGB values
rgba_lin = cmap(np.arange(cmap_og.N))
rgba_lin[:,-1] = np.linspace(0, 1, cmap_og.N)
cmap_lin_alpha = ListedColormap(rgba_lin)

# Generate some data to plot
data1 = np.random.random((5, 5))
data2 = np.expand_dims(np.arange(25), axis=0)

# Plot the colormap scale bars
plt.figure(figsize=(18, 5), facecolor=[.7, .7, .7])
# Original Reds colormap
plt.subplot(3, 1, 1, facecolor="w")
plt.pcolormesh(data2, cmap=cmap_og)
plt.title("Reds cmap", fontsize=12, rotation=0)
# White converted to alpha
plt.subplot(3, 1, 2, facecolor="w")
plt.pcolormesh(data2, cmap=cmap_w2a)
plt.title("white2alpha", fontsize=12, rotation=0)
# Linear alpha added
plt.subplot(3, 1, 3, facecolor="w")
plt.pcolormesh(data2, cmap=cmap_lin_alpha)
plt.title("linear alpha", fontsize=12, rotation=0)
plt.show()

# Plot randomly generated data
for bg in ["w", "g"]:
    plt.figure(figsize=(20, 5), facecolor=[.7, .7, .7])
    # Plot original Reds colormap
    plt.subplot(1, 3, 1, facecolor=bg)
    plt.pcolormesh(data1, cmap=cmap_og)
    plt.title("Reds cmap")
    plt.colorbar()
    # Plot Reds colormap with white converted to alpha
    plt.subplot(1, 3, 2, facecolor=bg)
    plt.pcolormesh(data1, cmap=cmap_w2a)
    plt.title("Reds white2alpha cmap")
    plt.colorbar()
    # Plot Reds colormap with linear alpha channel
    plt.subplot(1, 3, 3, facecolor=bg)
    plt.pcolormesh(data1, cmap=cmap_lin_alpha)
    plt.title("Reds + linear alpha")
    plt.colorbar()
    # Render
    plt.show()

生成的颜色Map表如下所示:

并且分别在白色轴和绿色轴上呈现随机值:

如图所示,将白色转换为Alpha与添加线性增加的Alpha通道而不重新缩放RGB值相比,产生的结果在感知上更线性,颜色更丰富。
将白色转换为alpha的缺点是alpha值不会线性增加,这对您可能很重要。

# Plot the alpha values of each of the new colormaps
plt.figure(figsize=(10, 6))
plt.plot(cmap_w2a.colors[:, -1], label="white2alpha")
plt.plot(cmap_lin_alpha.colors[:, -1], label="linear_alpha")
plt.xlabel("Index", fontsize=12)
plt.ylabel("Alpha", fontsize=12)
plt.grid()
plt.legend(fontsize=12)
plt.show()

如果您确实需要alpha值线性增加,您可以使用线性增加的alpha值,但重新调整RGB值以尽可能地校正透明度。结果不会完美,因为色彩Map表太透明而无法呈现原始颜色,因此需要考虑一些权衡。我在此colab notebook中实现了此选项和相关选项。

相关问题