【问题标题】:Given a start color and a middle color, how to get the remaining colors? (Python)给定一个起始颜色和一个中间颜色,如何获得剩余的颜色? (Python)
【发布时间】:2019-07-28 16:21:53
【问题描述】:

我正在尝试围绕 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)

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

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

【问题讨论】:

标签: python user-interface matplotlib colors color-scheme


【解决方案1】:

如果您使用 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)) = (104, 117.5, 136)

【讨论】:

  • 抱歉,您的回答让我有些困惑。你能把它包装成一个函数并用sns.palplot 绘制颜色,这样我就可以看到发生了什么?
  • 你应该使用模加模256。
  • @O.rka 将 rgb 轴视为 3D 空间中的 xyz 坐标。 X是红色,y是蓝色,z是绿色。那么颜色是一个点,从开始颜色到结束颜色形成一个向量。然后您可以扩展该向量以“沿同一条线”获得更多颜色。您也可以通过将 xyz 轴设置为色调饱和度和亮度来执行相同的插值。
【解决方案2】:

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

这些不是最好的裁判,但我认为它们提供了一些关于这种现象的东西......

所以...除了这个警告...这里有一个 RGB 解决方案,但在 Lab 或 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]

创建调色板

# the 3 colors you specified
first_color = '#009392'
last_color  = '#d0587e'
mid_color   = color_mixer('#2b89c8', '#7c7ac6')

# create hex colors
result = create_pallette(first_color, mid_color, last_color, 5)

result
# ['#009392', '#298aac', '#5381c7', '#916ca2', '#d0587e']

看起来像这样:

【讨论】:

【解决方案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()

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

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

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)

上面的公式应该是不言自明的。当 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()

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


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

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

【讨论】:

  • 我想这个想法是找出插值模型,OP 网站使用该模型从中获取颜色。有了这个模型,人们可能会用它来推断。
  • 从产生的颜色来看,OP 的网站似乎与 RGB 中的线性插值没有什么不同。
  • 嗯,不。您在此处显示的所有颜色都不接近问题中的颜色。您还可以查看我在问题下方的评论中发布的图片,看看这些颜色是如何完全没有线性插值的。
  • 确实,他们确实使用 LCH 颜色空间来创建线性插值。看看他们的main.min.js,你会在里面找到:chroma.scale([a, i]).mode("lch").colors(n),说明他们使用chroma.js来创建调色板。 Herechroma.scale 的文档。 This 是他们的插值器的源代码。是linear
猜你喜欢
  • 1970-01-01
  • 2015-09-29
  • 2015-09-19
  • 1970-01-01
  • 2014-11-21
  • 2014-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多