用于创建彩色轮子的函数

这个问题我已经假想解决了很多次,但是从来没有找到一个解决方案。

问题是要找到一种生成 N颜色的方法,在 N是参数的情况下,这些颜色尽可能易于区分。

18865 次浏览

我读到过人眼无法区分四个数值之间的差别。所以你要记住这一点。下面的算法不能弥补这一点。

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

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

//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个值,并在上面的例子中为数组添加颜色。

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

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

这说得通吗?

我对此的第一个想法是“如何在距离最大的空间中生成 N 个矢量。”

你可以看到 RGB (或任何其他尺度,你使用的形式,在颜色空间的基础)只是向量。看看 随机选点。一旦你有了一组最大化分离的向量,你可以把它们保存在哈希表或者其他什么东西中,然后对它们进行随机旋转,得到所有你想要的颜色,这些颜色彼此之间最大程度地分离!

更多地考虑这个问题,最好是以线性的方式映射颜色,可能是(0,0,0)→(255,255,255)字典,然后均匀地分布它们。

我真的不知道这会有多大效果,但它应该,让我们说:

n = 10

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

我们可以使用 皮带扣算法515来查找词典索引的颜色。< img src = “ https://i.stack.imgur.com/gEuCs.gif”alt = “ frac { binom {256 ^ 3}{3}}{ n } * i”>。您可能必须编辑算法以避免溢出,并可能添加一些小的速度改进。

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

一些相关资源:

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

转义 RGBland: 为统计图形选择颜色 -一份技术报告,描述了一套在 hcl 颜色空间中生成良好(即最大可区分)颜色集的算法。

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

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;
}
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的亮度。

为了实现“最易区分”,我们需要使用一个感知颜色空间,如实验室(或任何其他感知线性颜色空间)以外的 RGB。此外,我们可以量化这个空间,以减少空间的大小。

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