使用未知的颜色集合大小生成视觉上不同的颜色

时间:2011-10-05 02:41:10

标签: algorithm language-agnostic colors

我正在尝试为图表控件生成颜色。我希望颜色在视觉上与众不同。我不仅希望颜色与相邻颜色区别开来,而且还要生成所有颜色。

我也不想拥有已知的颜色集合大小。我已经看到的一些算法需要知道要着色的事物的数量。我想为我的颜色生成器实现一个GetNextColor(),所以我不知道在选择最终会有多少颜色时,选择前面的数字会感觉不对。

我不只是试图用不同的颜色绘制一堆东西,我对这个问题感兴趣并想要一些反馈。

我在这里:

  • 使用HSV色彩空间。
  • 色调是[0-360]中的值,其中0 和360是相同的(红色)。
  • Hue从0开始,我到27岁(所以 当它循环时它不会落在它开始的相同颜色上 on),采取MOD 360。
  • 对于S和V(都在0和1之间),我开始时的数字很低 0.25
  • 贯穿约20个色调
  • 然后选择像.85
  • 这样的高数字
  • 贯穿20个色调
  • 然后开始二等分以获得最远的距离 尚未使用的值。

这不是一个非常有效的方法,它运作正常,但它可能会很多 更科学。它开始时有很多想法然后 变成了这个烂摊子。

关于如何优雅地做到这一点的任何想法?

(这应该没关系,但是我正在使用C#,当我回到计算机上时,我会发布代码。我已经掌握了所有这些内容。)

2 个答案:

答案 0 :(得分:5)

我认为你的问题应分为两个问题:

  1. 如何将颜色映射到n维笛卡尔空间,并定义颜色之间的欧几里德距离函数,以便距离反映人类观察者的差异。
  2. 给定一个n维立方体,生成一系列点,使得到目前为止生成的任何两个点之间的欧几里德距离最小化。
  3. 现在回答:

    1. 使用CIEDE2000 Color-Difference Formula计算色差。 CIEDE2000 formula基于LCH颜色空间(亮度,色度和色调)。 LCH颜色空间表示为圆柱体(参见图像here)。

      然而,差异公式是高度非线性的。因此,将颜色映射到正方形网格中是不可能的,这样欧几里德距离就会给出CIEDE2000的色差。

      在不太准确的模型上设置,我们可以使用CIE76色差公式,该公式基于Lab color space ( L*a*b*)。我们可以直接在这个颜色空间上使用欧几里德距离来测量差异。 RGB或CMYK值与L * a * b *之间没有简单的公式转换,因为RGB和CMYK颜色模型与设备有关。首先需要将RGB或CMYK值转换为特定的绝对色彩空间,例如sRGB或Adobe RGB。此调整将取决于设备,但来自变换的结果数据将与设备无关,允许将数据转换为CIE 1931颜色空间,然后转换为L * a * b *。 This文章解释了程序和公式。

    2. 对于L * a * b *颜色空间和CIE76色差公式 - 我们需要解决3D立方体的问题。

      我相信你最好的策略是将立方体分成8个立方体,这将产生27个点。使用这些要点。现在将8个立方体中的每一个分成另外8个立方体。对于这些立方体中的每一个,已经使用了27个点中的12个,所以你剩下15 * 8个新点。在每个附加步骤n中,您可以生成15 * 8 ^ n个附加点。

      应对每个步骤中的点集进行排序,以使两个连续点之间的最小距离最大化。我不知道怎么做 - I've just posted a question

    3. 修改

      我已在https://cstheory.stackexchange.com/上转发并得到了一个很好的答案。请参阅https://cstheory.stackexchange.com/questions/8609/sorting-points-such-that-the-minimal-euclidean-distance-between-consecutive-poin

答案 1 :(得分:2)

如果您以线性方式映射整个色彩空间,那么您的下一个色彩将使用2的幂映射到它。您的第一个选择将是中心,您的第二个选择将在开始和中心之间。你的第三选择是在中心和结束之间。

一些JavaScript来说明。

// initialize start and end of our linear transform
var START = 0;
var END = 100;

// next function
var _level = 1;
var _index = 1;
function next() {
    var pow2 = 2 << (_level - 1);
    var result = (END-START) / pow2;
    result = result * _index
    _index = (_index + 2) % pow2;
    if(_index == 1) {
        _level++;
    }
    return result;
}

// testing
for(var i=0; i<32; i++) 
    console.log(next());