<p>您可以将颜色视为颜色空间中的一个点,该空间通常由三个或四个维度组成,如RGB或HSL。要在这个空间中的两个点之间创建线性插值,只需遵循这两点创建的直线即可。根据颜色空间的不同,你会得到不同的颜色延续。在</p>
<p>下面,我使用<code>matplotlib</code>来显示选项板,<code>colormath</code>用于可以通过<code>pip install colormath</code>安装的转换。这座图书馆使这项工作比以前容易得多。在</p>
<pre><code>import colormath
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from colormath.color_objects import sRGBColor, HSVColor, LabColor, LCHuvColor, XYZColor, LCHabColor
from colormath.color_conversions import convert_color
def hex_to_rgb_color(hex):
return sRGBColor(*[int(hex[i + 1:i + 3], 16) for i in (0, 2 ,4)], is_upscaled=True)
def plot_color_palette(colors, subplot, title, plt_count):
ax = fig.add_subplot(plt_count, 1, subplot)
for sp in ax.spines: ax.spines[sp].set_visible(False)
for x, color in enumerate(colors):
ax.add_patch(mpl.patches.Rectangle((x, 0), 0.95, 1, facecolor=color))
ax.set_xlim((0, len(colors)))
ax.set_ylim((0, 1))
ax.set_xticks([])
ax.set_yticks([])
ax.set_aspect("equal")
plt.title(title)
def create_palette(start_rgb, end_rgb, n, colorspace):
# convert start and end to a point in the given colorspace
start = convert_color(start_rgb, colorspace).get_value_tuple()
end = convert_color(end_rgb, colorspace).get_value_tuple()
# create a set of n points along start to end
points = list(zip(*[np.linspace(start[i], end[i], n) for i in range(3)]))
# create a color for each point and convert back to rgb
rgb_colors = [convert_color(colorspace(*point), sRGBColor) for point in points]
# finally convert rgb colors back to hex
return [color.get_rgb_hex() for color in rgb_colors]
start_color = "#009392"
end_color = "#d0587e"
number_of_colors = 10
colorspaces = (sRGBColor, HSVColor, LabColor, LCHuvColor, LCHabColor, XYZColor)
start_rgb = hex_to_rgb_color(start_color)
end_rgb = hex_to_rgb_color(end_color)
fig = plt.figure(figsize=(number_of_colors, len(colorspaces)), frameon=False)
for index, colorspace in enumerate(colorspaces):
palette = create_palette(start_rgb, end_rgb, number_of_colors, colorspace)
plot_color_palette(palette, index + 1, colorspace.__name__, len(colorspaces))
plt.subplots_adjust(hspace=1.5)
plt.show()
</code></pre>
<p><a href="https://i.stack.imgur.com/zcW1c.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/zcW1c.png" alt="enter image description here"/></a></p>
<p>线性外推的基本思想是简单地扩展由两种颜色定义的向量。最大的问题是当我们碰到颜色空间的“墙”时。例如,考虑颜色空间RGB,其中红色从0变为255。插值线碰到255墙后会发生什么?一种颜色不能比红色更红。我认为你可以继续的一种方法是将这条线视为一束光线,它可以“反弹”或“反射”出rgb空间的墙壁。在</p>
<p>有趣的是,<code>colormath</code>似乎并不介意其颜色对象的参数超出其限制。它继续创建一个具有无效十六进制值的color对象。这有时会在外推法中发生。为了防止这种情况发生,我们可以设置RGB值的上限:</p>
^{pr2}$
<p>或者让它从墙上“反射”回来。在</p>
<pre><code>rgb_colors = []
for color in rgb:
c = list(color)
for i in range(3):
if c[i] > 1:
c[i] = 2 - c[i]
if c[i] < 0:
c[i] *= -1
rgb_colors.append(c)
</code></pre>
<p>上面的等式应该是不言自明的。当一个RGB通道降到零下时,翻转它的标志以“反射”离开零壁,同样,当它超过1时,将其反射回零。以下是使用该方法的一些外推结果:</p>
<pre><code>def create_palette(start_rgb, end_rgb, n, colorspace, extrapolation_length):
# convert start and end to a point in the given colorspace
start = np.array(convert_color(start_rgb, colorspace, observer=2).get_value_tuple())
mid = np.array(convert_color(end_rgb, colorspace, observer=2).get_value_tuple())
# extrapolate the end point
end = start + extrapolation_length * (mid - start)
# create a set of n points along start to end
points = list(zip(*[np.linspace(start[i], end[i], n) for i in range(3)]))
# create a color for each point and convert back to rgb
rgb = [convert_color(colorspace(*point), sRGBColor).get_value_tuple() for point in points]
# rgb_colors = np.maximum(np.minimum(rgb, [1, 1, 1]), [0, 0, 0])
rgb_colors = []
for color in rgb:
c = list(color)
for i in range(3):
if c[i] > 1:
c[i] = 2 - c[i]
if c[i] < 0:
c[i] *= -1
rgb_colors.append(c)
# finally convert rgb colors back to hex
return [sRGBColor(*color).get_rgb_hex() for color in rgb_colors]
start_color = "#009392"
end_color = "#d0587e"
number_of_colors = 11
colorspaces = (sRGBColor, HSVColor, LabColor, LCHuvColor, LCHabColor, XYZColor, LuvColor)
start_rgb = hex_to_rgb_color(start_color)
end_rgb = hex_to_rgb_color(end_color)
fig = plt.figure(figsize=(6, len(colorspaces)), frameon=False)
for index, colorspace in enumerate(colorspaces):
palette = create_palette(start_rgb, end_rgb, number_of_colors, colorspace, extrapolation_length=2)
plot_color_palette(palette, index + 1, colorspace.__name__, len(colorspaces))
plt.subplots_adjust(hspace=1.2)
plt.show()
</code></pre>
<p><a href="https://i.stack.imgur.com/epsWn.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/epsWn.png" alt="enter image description here"/></a></p>
<p>请注意,因为色调是一个圆形轴,在HSV或HSL这样的颜色空间中,它会绕回来,如果您将结束颜色放在调色板的中间,您可能会返回到接近开始颜色的位置。在</p>
<hr/>
<p>看到这些插值在颜色空间中的路径是非常迷人的。看看吧。请注意,从墙上反弹产生的效果。在</p>
<p><a href="https://i.stack.imgur.com/ffaa6.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ffaa6.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/KTkNg.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/KTkNg.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/ceb5Y.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ceb5Y.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/4c4kr.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4c4kr.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/E6Q5i.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/E6Q5i.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/FkIpm.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/FkIpm.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/i7ZVq.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/i7ZVq.png?s=256" alt="enter image description here"/></a>
<a href="https://i.stack.imgur.com/qx3b9.png?s=256" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/qx3b9.png?s=256" alt="enter image description here"/></a></p>
<p>我可能会在某个时候把它变成一个开源项目。在</p>