这是我多次伪解决但从未完全找到解决方案的问题。

问题是想出一种方法来生成 N 颜色,尽可能区分 N 是一个参数。

有帮助吗?

解决方案

我对此的第一个想法是“如何在空间中生成 N 个向量,使彼此之间的距离最大化”。

您可以看到 RGB(或您使用的构成颜色空间基础的任何其他比例)只是向量。看一眼 随机点选取. 。一旦你有了一组最大化分开的向量,你可以将它们保存在哈希表或其他东西中以供以后使用,然后对它们执行随机旋转以获得你想要的彼此最大分开的所有颜色!

更多地思考这个问题,最好将颜色以线性方式映射,可能按字典顺序 (0,0,0) → (255,255,255),然后均匀分布。

我真的不知道这会发挥多大作用,但应该如此,让我们说:

n = 10

我们知道有 16777216 种颜色 (256^3)。

我们可以用 带扣算法515 查找按字典顺序索引的颜色。\frac {\binom {256^3} {3}} {n} * i. 。您可能必须编辑算法以避免溢出,并可能添加一些小的速度改进。

其他提示

最好在“感知均匀”的色彩空间中找到最大距离的颜色,例如CIELAB(使用 L*、a*、b* 坐标之间的欧几里得距离作为距离度量),然后转换为您选择的色彩空间。通过调整色彩空间以近似人类视觉系统中的非线性来实现感知均匀性。

一些相关资源:

彩色布鲁尔 - 设计用于最大程度区分地图上使用的颜色集。

逃离 RGBland:选择统计图形的颜色 - 描述一组用于生成商品的算法的技术报告(即hcl 颜色空间中最大程度可区分的颜色集。

下面是一些代码,用于在指定亮度的 HSL 色轮周围均匀分配 RGB 颜色。

class cColorPicker
{
public:
    void Pick( vector<DWORD>&v_picked_cols, int count, int bright = 50 );
private:
    DWORD HSL2RGB( int h, int s, int v );
    unsigned char ToRGB1(float rm1, float rm2, float rh);
};
/**

  Evenly allocate RGB colors around HSL color wheel

  @param[out] v_picked_cols  a vector of colors in RGB format
  @param[in]  count   number of colors required
  @param[in]  bright  0 is all black, 100 is all white, defaults to 50

  based on Fig 3 of http://epub.wu-wien.ac.at/dyn/virlib/wp/eng/mediate/epub-wu-01_c87.pdf?ID=epub-wu-01_c87

*/

void cColorPicker::Pick( vector<DWORD>&v_picked_cols, int count, int bright )
{
    v_picked_cols.clear();
    for( int k_hue = 0; k_hue < 360; k_hue += 360/count )
        v_picked_cols.push_back( HSL2RGB( k_hue, 100, bright ) );
}
/**

  Convert HSL to RGB

  based on http://www.codeguru.com/code/legacy/gdi/colorapp_src.zip

*/

DWORD cColorPicker::HSL2RGB( int h, int s, int l )
{
    DWORD ret = 0;
    unsigned char r,g,b;

    float saturation = s / 100.0f;
    float luminance = l / 100.f;
    float hue = (float)h;

    if (saturation == 0.0) 
    {
      r = g = b = unsigned char(luminance * 255.0);
    }
    else
    {
      float rm1, rm2;

      if (luminance <= 0.5f) rm2 = luminance + luminance * saturation;  
      else                     rm2 = luminance + saturation - luminance * saturation;
      rm1 = 2.0f * luminance - rm2;   
      r   = ToRGB1(rm1, rm2, hue + 120.0f);   
      g = ToRGB1(rm1, rm2, hue);
      b  = ToRGB1(rm1, rm2, hue - 120.0f);
    }

    ret = ((DWORD)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)));

    return ret;
}


unsigned char cColorPicker::ToRGB1(float rm1, float rm2, float rh)
{
  if      (rh > 360.0f) rh -= 360.0f;
  else if (rh <   0.0f) rh += 360.0f;

  if      (rh <  60.0f) rm1 = rm1 + (rm2 - rm1) * rh / 60.0f;   
  else if (rh < 180.0f) rm1 = rm2;
  else if (rh < 240.0f) rm1 = rm1 + (rm2 - rm1) * (240.0f - rh) / 60.0f;      

  return static_cast<unsigned char>(rm1 * 255);
}

int _tmain(int argc, _TCHAR* argv[])
{
    vector<DWORD> myCols;
    cColorPicker colpick;
    colpick.Pick( myCols, 20 );
    for( int k = 0; k < (int)myCols.size(); k++ )
        printf("%d: %d %d %d\n", k+1,
        ( myCols[k] & 0xFF0000 ) >>16,
        ( myCols[k] & 0xFF00 ) >>8,
        ( myCols[k] & 0xFF ) );

    return 0;
}

这不是你设置颜色的顺序的一个因素吗?

就像如果你使用 Dillie-Os 的想法,你需要尽可能多地混合颜色。0 64 128 256 是从一个到下一个。但 0 256 64 128 在一个轮子里会更“分开”

这有道理吗?

我读过人眼无法区分少于 4 个值的地方。所以这是需要记住的事情。以下算法无法对此进行补偿。

我不确定这正是您想要的,但这是随机生成不重复颜色值的一种方法:

(注意,前面的伪代码不一致)

//colors entered as 0-255 [R, G, B]
colors = []; //holds final colors to be used
rand = new Random();

//assumes n is less than 16,777,216
randomGen(int n){
   while (len(colors) < n){
      //generate a random number between 0,255 for each color
      newRed = rand.next(256);
      newGreen = rand.next(256);
      newBlue = rand.next(256);
      temp = [newRed, newGreen, newBlue];
      //only adds new colors to the array
      if temp not in colors {
         colors.append(temp);
      }
   }
}

优化此以获得更好可见性的一种方法是比较每种新颜色与数组中所有颜色之间的距离:

for item in color{
   itemSq = (item[0]^2 + item[1]^2 + item[2]^2])^(.5);
   tempSq = (temp[0]^2 + temp[1]^2 + temp[2]^2])^(.5);
   dist = itemSq - tempSq;
   dist = abs(dist);
}
//NUMBER can be your chosen distance apart.
if dist < NUMBER and temp not in colors {
   colors.append(temp);
}

但这种方法会显着减慢你的算法速度。

另一种方法是放弃随机性并系统地检查每 4 个值并向上面示例中的数组添加颜色。

我知道这是一篇旧文章,但我在寻找该主题的 PHP 解决方案时发现了它,最后得到了一个简单的解决方案:

function random_color($i = null, $n = 10, $sat = .5, $br = .7) {
    $i = is_null($i) ? mt_rand(0,$n) : $i;
    $rgb = hsv2rgb(array($i*(360/$n), $sat, $br));
    for ($i=0 ; $i<=2 ; $i++) 
        $rgb[$i] = dechex(ceil($rgb[$i]));
    return implode('', $rgb);
}

function hsv2rgb($c) { 
    list($h,$s,$v)=$c; 
    if ($s==0) 
        return array($v,$v,$v); 
    else { 
        $h=($h%=360)/60; 
        $i=floor($h); 
        $f=$h-$i; 
        $q[0]=$q[1]=$v*(1-$s); 
        $q[2]=$v*(1-$s*(1-$f)); 
        $q[3]=$q[4]=$v; 
        $q[5]=$v*(1-$s*$f); 
        return(array($q[($i+4)%6]*255,$q[($i+2)%6]*255,$q[$i%6]*255)); //[1] 
    } 
}

因此,只需调用 random_color() 函数,其中 $i 标识颜色,$n 表示可能颜色的数量,$sat 表示饱和度,$br 表示亮度。

为了实现“最可区分”,我们需要使用像 Lab(或任何其他感知线性颜色空间)这样的感知颜色空间,而不是 RGB。另外,我们可以量化这个空间以减小空间的大小。

生成包含所有可能的量化条目的完整 3D 空间,并运行 K 均值算法 k=N. 。得到的中心/“平均值”应该彼此之间最有区别。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top