提供起始颜色和中间颜色,如何获得剩余颜色?(Python)

2024-09-30 20:29:26 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试建立一个调色板约2种颜色:青色和玫瑰色

我找到了这个网站:https://learnui.design/tools/data-color-picker.html#palette 它可以完成我所要查找的一半,所以我想用python中的matplotlibseabornpalettable和/或{}。在

有没有一种方法来插值下一种颜色将被赋予一系列渐变色?

例如,我在网站上给出了start_color和{}。它给了我6种颜色,从start_colorend_color。有没有办法做到这一点,但使end_color成为middle_color,并继续渐变?在

^{1}$

enter image description here

我想让青色start_color保持第一种颜色,使玫瑰end_color成为{}(在3到4之间),然后让调色板完成,使6种颜色。在

我想试着得到RGB值,然后做一些建模来找出它会往哪里去,但我想可能有一个更简单的方法来做到这一点。在


Tags: 方法httpsdata网站颜色toolsstartcolor
3条回答

您可以将颜色视为颜色空间中的一个点,该空间通常由三个或四个维度组成,如RGB或HSL。要在这个空间中的两个点之间创建线性插值,只需遵循这两点创建的直线即可。根据颜色空间的不同,你会得到不同的颜色延续。在

下面,我使用matplotlib来显示选项板,colormath用于可以通过pip install colormath安装的转换。这座图书馆使这项工作比以前容易得多。在

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()

enter image description here

线性外推的基本思想是简单地扩展由两种颜色定义的向量。最大的问题是当我们碰到颜色空间的“墙”时。例如,考虑颜色空间RGB,其中红色从0变为255。插值线碰到255墙后会发生什么?一种颜色不能比红色更红。我认为你可以继续的一种方法是将这条线视为一束光线,它可以“反弹”或“反射”出rgb空间的墙壁。在

有趣的是,colormath似乎并不介意其颜色对象的参数超出其限制。它继续创建一个具有无效十六进制值的color对象。这有时会在外推法中发生。为了防止这种情况发生,我们可以设置RGB值的上限:

^{pr2}$

或者让它从墙上“反射”回来。在

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)

上面的等式应该是不言自明的。当一个RGB通道降到零下时,翻转它的标志以“反射”离开零壁,同样,当它超过1时,将其反射回零。以下是使用该方法的一些外推结果:

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()

enter image description here

请注意,因为色调是一个圆形轴,在HSV或HSL这样的颜色空间中,它会绕回来,如果您将结束颜色放在调色板的中间,您可能会返回到接近开始颜色的位置。在


看到这些插值在颜色空间中的路径是非常迷人的。看看吧。请注意,从墙上反弹产生的效果。在

enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here

我可能会在某个时候把它变成一个开源项目。在

这里有一个解决方案,只在RGB颜色空间中的颜色之间进行简单的插值。这有问题。。。RGB中颜色之间的欧几里得距离与人类的感知没有直接关系。所以。。。如果你真的想成为一个(以一种好的方式)关于你的颜色是如何被感知的专家,你可能想进入实验室或HCL做这样的事情。在

这些不是最好的裁判,但他们提供了一些关于这种现象的东西,我想。。。在

所以。。。把警告放在一边。。。这里有一个RGB的解决方案,但在实验室或HCL中可能更好。:)

助手/设置

import numpy as np

# hex (string) to rgb (tuple3)
def hex2rgb(hex):
    hex_cleaned = hex.lstrip('#')
    return tuple(int(hex_cleaned[i:i+2], 16) for i in (0, 2 ,4))

# rgb (tuple3) to hex (string)
def rgb2hex(rgb):
    return '#' + ''.join([str('0' + hex(hh)[2:])[-2:] for hh in rgb])

# weighted mix of two colors in RGB space (takes and returns hex values)
def color_mixer(hex1, hex2, wt1=0.5):
    rgb1 = hex2rgb(hex1)
    rgb2 = hex2rgb(hex2)
    return rgb2hex(tuple([int(wt1 * tup[0] + (1.0 - wt1) * tup[1]) for tup in zip(rgb1, rgb2)]))

# create full palette
def create_palette(start_color, mid_color, end_color, num_colors):
    # set up steps
    # will create twice as many colors as asked for
    # to allow an explicit "mid_color" with both even and odd number of colors
    num_steps = num_colors  
    steps = np.linspace(0, 1, num_steps)[::-1]

    # create two halves of color values
    pt1 = [color_mixer(first_color, mid_color, wt) for wt in steps]
    pt2 = [color_mixer(mid_color,  last_color, wt) for wt in steps[1:]]

    # combine and subsample to get back down to 'num_colors'
    return (pt1 + pt2)[::2]

创建调色板

^{pr2}$

看起来像这样:

enter image description here

如果使用RGB颜色,则可以找到矢量并对其进行缩放:

#009392 = (0, 147, 146)
#d0587e = (208, 88, 126)

# slope
(208, 88, 126) - (0, 147, 146) = (208, -59, -20)


k = 4
for n in range(1,k+1):
    color = (0, 147, 146) + (n/k*(208, -59, -20)) 

例如(0,147,146)+(2/4*(208,-59,-20))=(104117.5136)

相关问题 更多 >