给定一个起始颜色和一个中间颜色,如何获得剩余的颜色?(Python)

O.r*_*rka 6 python user-interface color-scheme colors matplotlib

我正在尝试围绕 2 种颜色构建一个调色板:蓝绿色和玫瑰色

我发现这个网站:https://learnui.design/tools/data-color-picker.html#palette 哪些可以做什么,我一直在寻找的一半,所以我想尝试使用要做到这一点在Python matplotlibseabornpalettable,和/或colorsys

有没有办法在渐变中插入一系列颜色的下一个颜色?

例如,从我给的网站start_colorend_color. 它给了我 6 种颜色,从start_colorend_color. 有没有办法做到这一点,但作出end_colormiddle_color,并继续梯度?

from palettable.cartocolors.diverging import TealRose_7
import matplotlib as mpl
import seaborn as sns

start_color = "#009392"
end_color = "#d0587e"

# https://learnui.design/tools/data-color-picker.html#palette
colors = ['#009392', '#0091b2', '#2b89c8', '#7c7ac6', '#b366ac', '#d0587e']

sns.palplot(colors)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我想让蓝绿色start_color保持第一种颜色,使玫瑰end_color成为middle_color(在 3 和 4 之间),然后让调色板完成总共 6 种颜色。

我打算尝试获取 RGB 值,然后进行某种类型的建模以确定它会去哪里,但我认为可能有更简单的方法来做到这一点。

dar*_*sky 10

您可以将颜色视为颜色空间中的一个点,该空间通常由三个或四个维度(如 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()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

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

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

rgb_colors = np.maximum(np.minimum(rgb, [1, 1, 1]), [0, 0, 0])
Run Code Online (Sandbox Code Playgroud)

或者让它从墙上“反射”回来,可以这么说。

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)
Run Code Online (Sandbox Code Playgroud)

上面的方程应该是不言自明的。当 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()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

请注意,因为 Hue 是一个圆形轴,所以在 HSV 或 HSL 等颜色空间中,它会回绕,如果您将最终颜色放在调色板的中间,您很可能会返回到您的起始颜色附近。


看到这些插值在色彩空间中所采用的路径,真是令人着迷。看一看。请注意从墙壁上反弹的效果。

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明

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