如何为任意自然数n生成n种不同的颜色?

42

假设n = 100; 我如何生成100个视觉上不同的颜色?这在数学上是否可能?


2
同意大卫的观点。这是一个主观性问题。如果我是色盲呢? - Aryabhatta
4
色差并非主观的概念:http://zh.wikipedia.org/wiki/%E8%89%B2%E5%B7%AE - Otto Allmendinger
1
这个问题已经被问烂了。这里有一个用于视觉上区分颜色的好算法:https://dev59.com/mHI95IYBdhLWcg3w5SSh 。在这个颜色空间中,可以轻松想出大约等分的n个数字。 - BlueRaja - Danny Pflughoeft
@BlueRaja:距离函数只是问题的一部分。你怎么从颜色空间中选择颜色,使得它们之间的距离最大? - Otto Allmendinger
请参见此链接:https://dev59.com/pHRB5IYBdhLWcg3w6bUD#4382138 - Ohad Schneider
显示剩余2条评论
6个回答

48

是的。定义“不同”是根据颜色空间而言的,因此当我们说“最大程度的不同颜色”时,我们的意思是与所有其他颜色尽可能远的颜色。但由于颜色空间不会改变,答案也不会改变。实现一些更符合人眼和人眼看到颜色的方式的东西(比如CIE-lab de2000颜色距离)使得重新进行所有计算很困难,但却使得静态列表易于实现。这里有128个条目。

private static final String[] indexcolors = new String[]{
        "#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059",
        "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87",
        "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80",
        "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100",
        "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F",
        "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09",
        "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66",
        "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C",
        
        "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81",
        "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00",
        "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700",
        "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329",
        "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C",
        "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800",
        "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51",
        "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58"
};
这是第一个256色的图片: max distance (从左到右) (从上到下)。如果你确保每个颜色在颜色空间中尽可能平均地分布,你可能会获得更多不同的颜色。该查找表选择每个附加颜色与所有先前颜色最大区别,而不是指定起始N并映射颜色空间。所以,使用蛮力算法和高级颜色距离算法,您可以自己生成相同的颜色集合。需要一天时间或更长时间。
如果您设置等距列表,您可以获得不同颜色之间的明显编号,例如: 5 colors, 比默认的5种颜色列表更好,最小Δmax为53.2,并带有最小Δmax为61.5的列表。
或者在此列表中选择颜色: 10 color list, # 156FC3 # 165859 # 24C4FF # 30A581 # 957D5C # 213E02 # DE9AF5 # 68D840 # 6E0062 # C25B77,它超过了预先计算的前十个元素中的颜色。
如果您想尝试这个: https://gist.github.com/tatarize/a483db49993e6e0e994ad82ba3e2a22e 可以编辑为接受“num_of_colors”,并且可以长时间运行它以获取一组具有更低的Δmax值(列表中任意两种颜色之间的最大最小距离)。您仍然需要一个预编译的列表。

有时候简单就是美。谢谢。 - Martin Thoma
你的图片出了问题。FFFF00 明显不是青柠色。 - Martin Thoma
我在两个不同的实例上运行了代码,并更改了一个等号,因此它似乎以“#FFFF00”作为第二种颜色。使用黄色作为第二种颜色而不是绿色,它会弹回到不同的值。它们都是技术上正确的。由于它们最远离黑色,但是有显然不同的有效答案来回答什么颜色最不像黑色。这取决于我的价值是> =当前最远还是仅>当前最远。我将更新图形。 - Tatarize
1
@ChristopherGalpin 我不能保证在宇宙热寂之前给你一个答案,但是这里有一些Python代码可以帮助你得到答案。https://gist.github.com/tatarize/ade134305086663b0503f0c413af77e8请记住,当我运行上面的蛮力法时,我花了一个月的时间,尽管每个结果都越来越难。而这里的尝试是为了第二阶段,逐渐改进整体,这意味着需要在 6 * 3 种不同的颜色组件中,在整个结果集上进行 +1、0、-1 的操作。并且继续尝试,直到没有改进为止。 - Tatarize
https://user-images.githubusercontent.com/3302478/153757526-ffb1-4b9d-97dd-83dcc3fa60c3.png 提供了一个相对简单的早期结果。每个附加项之间的距离为101.2、79.6、58.3、53.2。在遇到一堆代码尝试进行许多位操作后,这个值很快就上升到54,而且需要很长时间才能完成。 - Tatarize
显示剩余4条评论

37

编辑:

我在这个领域没有任何专业知识,我的数学技能也很平均。但我认为,这个问题的解决方案比许多答案所暗示的更加复杂和有趣,因为最近我试图做类似的事情,但没有找到解决方法。

颜色差异

颜色的感知当然是主观的,但人类之间存在相当大的一致性。例如,我们都可以同意红、绿、蓝是非常不同的颜色,即使色盲的人也会同意黑白是非常不同的。

RGB

计算机系统中最常见的颜色表示是向量 (r, g, b),它建议一个简单的距离函数,如下:

RGB颜色差异

让我们将 rgb 的范围设置为 [0, 1],看看它是如何工作的:

  1. 红色 (1, 0, 0) 和红色 (1, 0, 0) 的距离为 0,这是显然的
  2. 红色 (1, 0, 0) 和黄色 (1, 1, 0) 的距离为 1,小于下一个例子中的距离
  3. 红色 (1, 0, 0) 和蓝色 (0, 0, 1) 的距离是 sqrt(2),这是合理的

到目前为止,一切都很好。然而问题在于,蓝色和红色距离黑色 (0, 0, 0) 的距离相同为1,但观察图像时,这似乎并不成立:

黑底上的蓝、红色

此外,黄色(1, 1, 0)和洋红色(1, 0, 1)都与白色(1, 1, 1)的距离为1,这似乎也没有什么意义:

白底上的黄、洋红色

HSL和HSV

我认为可以安全地假设HSL和HSV颜色方案的类似度量存在相同的问题。这些颜色方案不是为了比较颜色而设计的。

CIEDE2000

幸运的是,已经有科学家在尝试找到一种好的比较颜色的方法。他们提出了一些复杂的方法,其中最新的方法是CIEDE2000

CIEDE2000

(完整的公式在文章中描述得非常复杂)

这种度量考虑了人的感知,比如我们似乎无法很好地区分蓝色的不同色调。因此,建议将其作为我们的颜色差异函数。

颜色选取算法

朴素的解决方案

一些答案提出了以下算法:

colors = []
for n in range(n):
    success=False
    while not success:
        new_color = random_color()
        for color in colors:
            if distance(color, new_color)>far_enough:
                colors.append(new_color)
                success = True
                break

这个算法存在一些问题:

  1. 颜色的间距不够优化。如果我们将颜色想象成一条线上的数字,三个数字的最佳间距如下所示:

    |a-----b-----c|

    在不移动a、b和c的情况下添加一个数字显然比重新排列所有颜色更糟糕。

  2. 该算法无法保证终止。如果列表中不存在与现有颜色足够远的颜色,循环将永远继续。

正确的解决方案

嗯... 我没有一个。


本页面旨在为正确实现CIEDE2000色差公式提供有用信息。http://www.ece.rochester.edu/~gsharma/ciede2000/ - Carlos Gutiérrez
1
在我的在线工具http://phrogz.net/css/distinct-colors.html中,我正是使用了CIE。 - Phrogz
1
请查看 LabHCL 颜色空间(HCL 可能是为人类生成颜色的最佳选择,但它具有凹形域)。一个值得注意的工具是 medialab 的 iWantHue 工具: http://tools.medialab.sciences-po.fr/iwanthue/ - kumarharsh

6

100种颜色很多,但您可以尽可能地在HSB或HSL空间中分散它们;在RGB中实现可能很困难。

例如,您可以决定使用10种不同的色调,4种不同的饱和度级别和3种不同的亮度设置,这将为您提供多达120种颜色。您需要仔细选择饱和度和亮度值;人眼是复杂和混乱的传感器。如果将颜色空间视为一个圆锥体,则可能需要在每个亮度/饱和度级别上使用不同数量的色调。

这里是维基百科关于HSB的链接:HSL和HSV条目


2
在HSL空间中这样做的问题是,所有亮度为0的颜色都是相同的;而亮度和饱和度均为0的颜色也是相同的。因此,看似相距甚远的点实际上并不具有视觉上的差异。对我来说,将颜色稀疏地分布在RGB中似乎更好。 - Jason Orendorff
@Jason:谁说你必须把颜色空间看作一个立方体?(虽然RGB更容易。) - kennytm
是的,“尽可能稀疏”可能不是我想说的。第二段话稍微澄清了一些事情,但我会编辑它来使其更加清晰。 - Carl Norum
1
我撤回之前的话,我的确是指“尽可能地稀疏”。将HSB颜色空间视为圆锥即可。 - Carl Norum
3
HSB(或HSL)的真正问题在于,无论是立方体、圆锥体还是球体,色相值按照等数值间隔分布并不会产生相等数量的视觉差异。HSB比使用RGB更好,但你需要沿着轴进行非线性映射以获得最佳的视觉分离效果。 - Phrogz

4
你想将其转换为HSL,然后在保持其他两个值不变的情况下迭代色相(H)的值。
对于每个值,您需要从HSL转换回RGB
请参见我的答案这里这里
如果您的N非常大,因此颜色在视觉上不明显,那么可以在所有相同的色相上重新迭代,并更改其他组件以变化饱和度或亮度。因此,您可以拥有最大数量的色相值可用,一旦达到该值,您就可以开始使用不同的饱和度或亮度重新开始。

1
这不使用较暗和较饱和的颜色。 - Otto Allmendinger
如果你想的话,你可以轻松地通过随机化这些值来保持色调的良好混合。 - Brian R. Bondy
1
@Brian:这将轻松生成许多类似灰色的颜色。 - kennytm
@KennyTM 不会的。我的最初建议是保持饱和度和亮度的恒定值,并迭代你的色相值。当然,如果您想解决@Otto Allmendinger的问题,可以稍微随机一下它们。 - Brian R. Bondy
1
@Brian: 所以你正在使用很小的颜色子集,这很容易导致视觉上无法区分的颜色。 - kennytm
@KennyTM:同意,已更新以解决该问题。 - Brian R. Bondy

2

虽然这不是对你问题的回答,但是如果n有一个最大值并且你的应用程序允许,你可以使用预定义的颜色列表,例如:

http://en.wikipedia.org/wiki/List_of_colors

其中一个优点是你可以在提示框中显示易读的颜色名称以供色盲人士查看。


1
如果他需要选择3种颜色,他如何避免选择看起来非常相似的“茜素红”、“紫红色”和“洋红色”? - Otto Allmendinger
@Otto - 使用你发布的颜色差异公式,或者使用仅包含“看起来不同”的颜色列表。(顺便说一句,你提到的这三个颜色对我来说非常不同,但有些其他颜色我认为是相同的,遗传学吧) - Carlos Gutiérrez
好的,我们可以认为它们都是红色的,这太接近了。颜色差异方程只是谜题的一部分,你必须想出一个比顺序随机选择颜色并检查它们是否与现有颜色太接近的更好方法(我会在我的答案中写出详细信息)。 - Otto Allmendinger

1
首先,不要使用RGB空间;对于这个问题来说,很难找到一个更糟糕的颜色空间。(根据您是将颜色用于显示还是打印,您可能会有大量无法区分的接近黑色或接近白色的颜色。)
如果您使用Lab空间,则有感知色彩模型(CIE 1996?和CIE 2000)可用于测量颜色的视觉接近度(分别用于打印和显示)。
您没有说明是否要计算一次颜色并存储结果,还是需要在运行时重新计算颜色(在这种情况下,是否必须是确定性的)。显然,任何关于如何最好生成该集合的讨论都取决于此。
虽然我建议均匀地划分颜色空间的轴(例如划分为8个部分),并将其用作初始点,这比任何随机过程都要高效得多。当然,您只需要将任何点与其邻居进行比较(仅当它们已经在集合中时),这将为您节省大量比较。

诱人的是使用LAB颜色空间,但许多这些坐标不会映射回RGB空间或人类可见的色域 - http://en.wikipedia.org/wiki/Lab_color_space。这使得随机选择颜色变得困难。 - Mark Ransom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接