【问题标题】:Function for creating color wheels [closed]创建色轮的功能[关闭]
【发布时间】:2010-09-05 05:42:06
【问题描述】:

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

问题是想出一种方法来生成N 颜色,在N 是一个参数的情况下尽可能区分这些颜色。

【问题讨论】:

  • 上次我检查了JFreeChart 有这个精确的算法,因为它是开源的,你可以看看它做了什么。我知道我得到的颜色似乎不是沿着某个圆圈或球体随机分布的,而是更具体地选择的。

标签: algorithm language-agnostic colors color-space


【解决方案1】:

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

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

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

我真的不知道这将如何运作,但它应该是因为,让我们说:

n = 10

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

我们可以使用Buckles Algorithm 515 来查找字典索引颜色。。您可能必须编辑算法以避免溢出,并且可能会增加一些小的速度改进。

【讨论】:

  • 这是不正确的,因为 RGB 颜色空间在感知上并不均匀
  • 我同意这听起来合乎逻辑。 RGB主要是紫色和橙色的混合色,蓝绿色的混合色相对较少……从红外线到深蓝色的色阶是均匀的,所以必须选择沿它等间距的点。需要一个基于彩虹的算法。
  • 请考虑支持/关注 StackExchange Color Theory 网站:area51.stackexchange.com/proposals/110687/color-theory
【解决方案2】:

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

【讨论】:

  • 这可能是最好的解决方案,因为它非常简单。但是还有其他色差公式需要考虑,例如 CIE2000 甚至 CIECAM
【解决方案3】:

一些相关资源:

ColorBrewer - 设计用于在地图上最大程度区分的颜色集。

Escaping RGBland: Selecting Colors for Statistical Graphics - 一份技术报告,描述了一组用于在 hcl 颜色空间中生成良好(即最大可区分)颜色集的算法。

【讨论】:

  • Escaping RGBland 是挑选可感知调色板的必读参考。
【解决方案4】:

这里有一些代码可以在指定亮度的 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;
}

【讨论】:

  • AFAIK 将代码从 C++ 移植到 Java 很简单
  • 不是当我不理解所有位移的东西时:/
  • 我提供了链接到代码作用说明的 URL。
  • 如果我想包含与我提供的背景颜色不同的颜色怎么办?
  • 计算生成的颜色和背景颜色之间的“距离”。不要使用最接近背景的颜色。
【解决方案5】:

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

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

这有意义吗?

【讨论】:

    【解决方案6】:

    我在某个地方读到了人眼无法区分少于 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 个值,并在上面的示例中为数组添加一种颜色。

    【讨论】:

      【解决方案7】:
      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 亮度。

      【讨论】:

      • 你能解释一下在这种情况下“i”是什么吗?这个问题要求 N 个数字。 “i”参数是什么?
      • random_color()上,$i是生成色调的“种子”,应该是0到$n之间的一个数字,如果不输入种子(NULL),函数会选择一个随机一个。 $n 是给定饱和度和亮度的可能颜色数量,即调色板中的颜色数量。我们基本上将 360 色调度拆分为 $n 并使用 $i 作为乘数。换句话说,更高的$n 会给你更多的颜色,更低的$n 会给你更少的颜色但彼此之间的差异更大。 $i 将识别颜色,如果您继续使用此功能,颜色将始终相同。我希望这会有所帮助。
      • 我明白了!感谢您的解释。还有一件事......如果我有背景颜色并且我想尽可能远离所有颜色,有什么建议吗?
      • 您需要将颜色的色调添加 180 度以保持饱和度和值。为此发布一个新问题,在此处粘贴链接,我会进一步解释!
      【解决方案8】:

      为了实现“最可区分”,我们需要使用除 RGB 之外的诸如 Lab 之类的感知色彩空间(或任何其他感知线性色彩空间)。另外,我们可以量化这个空间来减小空间的大小。

      使用所有可能的量化条目生成完整的 3D 空间,并使用 K=N 运行 K-means 算法。由此产生的中心/“手段”应该彼此之间最容易区分。

      【讨论】:

        猜你喜欢
        • 2013-04-29
        • 1970-01-01
        • 2020-03-22
        • 2020-03-26
        • 2021-05-11
        • 1970-01-01
        • 2021-12-20
        • 1970-01-01
        • 2014-07-20
        相关资源
        最近更新 更多