在这里找到 Fordi 和 Markus Jarderot 在一个 python 函数中建议的混合方法,该函数逐渐混合或混合两种颜色 A 和 B。
“混合”模式对于在两种颜色之间进行插值很有用。如果将一种半透明颜色绘制在另一种(可能是半透明的)颜色之上,“混合”模式(使用t=0)对于计算结果颜色很有用。 gamma 校正会产生更好的结果,因为它考虑到物理光强度和感知亮度(人类)是非线性相关的事实。
import numpy as np
def mix_colors_rgba(color_a, color_b, mode="mix", t=None, gamma=2.2):
"""
Mix two colors color_a and color_b.
Arguments:
color_a: Real-valued 4-tuple. Foreground color in "blend" mode.
color_b: Real-valued 4-tuple. Background color in "blend" mode.
mode: "mix": Interpolate between two colors.
"blend": Blend two translucent colors.
t: Mixing threshold.
gamma: Parameter to control the gamma correction.
Returns:
rgba: A 4-tuple with the result color.
To reproduce Markus Jarderot's solution:
mix_colors_rgba(a, b, mode="blend", t=0, gamma=1.)
To reproduce Fordi's solution:
mix_colors_rgba(a, b, mode="mix", t=t, gamma=2.)
To compute the RGB color of a translucent color on white background:
mix_colors_rgba(a, [1,1,1,1], mode="blend", t=0, gamma=None)
"""
assert(mode in ("mix", "blend"))
assert(gamma is None or gamma>0)
t = t if t is not None else (0.5 if mode=="mix" else 0.)
t = max(0,min(t,1))
color_a = np.asarray(color_a)
color_b = np.asarray(color_b)
if mode=="mix" and gamma in (1., None):
r, g, b, a = (1-t)*color_a + t*color_b
elif mode=="mix" and gamma > 0:
r,g,b,_ = np.power((1-t)*color_a**gamma + t*color_b**gamma, 1/gamma)
a = (1-t)*color_a[-1] + t*color_b[-1]
elif mode=="blend":
alpha_a = color_a[-1]*(1-t)
a = 1 - (1-alpha_a) * (1-color_b[-1])
s = color_b[-1]*(1-alpha_a)/a
if gamma in (1., None):
r, g, b, _ = (1-s)*color_a + s*color_b
elif gamma > 0:
r, g, b, _ = np.power((1-s)*color_a**gamma + s*color_b**gamma,
1/gamma)
return tuple(np.clip([r,g,b,a], 0, 1))
请看下面如何使用它。在“混合”模式下,左右颜色完全匹配color_a 和color_b。在“混合”模式下,t=0 的左侧颜色是color_a 与color_b(和白色背景)混合时产生的颜色。在示例中,color_a 然后变得越来越半透明,直到到达color_b。
请注意,如果 alpha 值为 1.0,则混合和混合是等效的。
为了完整起见,这里是重现上述情节的代码。
import matplotlib.pyplot as plt
import matplotlib as mpl
def plot(pal, ax, title):
n = len(pal)
ax.imshow(np.tile(np.arange(n), [int(n*0.20),1]),
cmap=mpl.colors.ListedColormap(list(pal)),
interpolation="nearest", aspect="auto")
ax.set_xticks([])
ax.set_yticks([])
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_title(title)
_, (ax1, ax2, ax3, ax4) = plt.subplots(nrows=4,ncols=1)
n = 101
ts = np.linspace(0,1,n)
color_a = [1.0,0.0,0.0,0.7] # transparent red
color_b = [0.0,0.0,1.0,0.8] # transparent blue
plot([mix_colors_rgba(color_a, color_b, t=t, mode="mix", gamma=None)
for t in ts], ax=ax1, title="Linear mixing")
plot([mix_colors_rgba(color_a, color_b, t=t, mode="mix", gamma=2.2)
for t in ts], ax=ax2, title="Non-linear mixing (gamma=2.2)")
plot([mix_colors_rgba(color_a, color_b, t=t, mode="blend", gamma=None)
for t in ts], ax=ax3, title="Linear blending")
plot([mix_colors_rgba(color_a, color_b, t=t, mode="blend", gamma=2.2)
for t in ts], ax=ax4, title="Non-linear blending (gamma=2.2)")
plt.tight_layout()
plt.show()
Formulas:
Linear mixing (gamma=1):
r,g,b,a: (1-t)*x + t*y
Non-linear mixing (gama≠1):
r,g,b: pow((1-t)*x**gamma + t*y**gamma, 1/gamma)
a: (1-t)*x + t*y
Blending (gamma=1):
a: 1-(1-(1-t)*x)*(1-y)
s: alpha_b*(1-alpha_a)*a
r,g,b: (1-s)*x + s*y
Blending (gamma≠1):
a: 1-(1-(1-t)*x)*(1-y)
s: alpha_b*(1-alpha_a)/a
r,g,b: pow((1-s)*x**gamma + s*y**gamma, 1/gamma)
最后,here 是一本关于伽马校正的有用读物。