这是我多次伪解决过的事情,但从未找到解决方案。
问题是想出一种生成N
颜色的方法,该颜色在N
参数处尽可能地可区分。
Answers:
我对此的第一个想法是“如何在使彼此之间的距离最大化的空间中生成N个向量”。
您会看到RGB(或您在色彩空间中使用的其他比例尺)只是矢量。看看随机点拾取。一旦有了一组向量,它们可以最大程度地分开,就可以将它们保存在哈希表或其他东西中,以备后用,并且只需对它们执行随机旋转,即可获得所需的最大程度彼此分开的颜色!
进一步考虑这个问题,最好以字典方式线性地映射颜色(可能是(0,0,0)→(255,255,255)),然后均匀地分布它们。
我真的不知道这将如何运作,但是从那以后,让我们说:
n = 10
我们知道我们有16777216种颜色(256 ^ 3)。
我们可以使用Buckles算法515查找按字典顺序索引的颜色。。您可能必须编辑算法以避免溢出,并且可能会增加一些较小的速度改进。
最好在“感知均匀”的色彩空间中找到最大距离的颜色,例如CIELAB(使用L *,a *,b *坐标之间的欧式距离作为距离度量),然后转换为您选择的色彩空间。通过调整色彩空间以逼近人类视觉系统中的非线性,可以实现感知均匀性。
一些相关资源:
ColorBrewer-设计用于在地图上最大程度区分的颜色集。
逃逸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;
}
我读过某个地方,人眼无法区分四个以下的值。所以这是要牢记的。以下算法无法对此进行补偿。
我不确定这正是您想要的,但这是随机生成非重复颜色值的一种方法:
(请注意,前面的伪代码不一致)
//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个值并为数组添加颜色。
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
亮度的函数即可。
random_color()
,$i
是生成色相的“种子”,应为0到之间的数字$n
,如果您不输入任何种子(NULL),则函数会随机选择一个。$n
是给定饱和度和亮度可能的颜色数量,即调色板中的颜色数量。我们基本上是将360度的色相分成乘数$n
并$i
用作乘数。换句话说,较高的$n
颜色会给您更多的颜色,较低的颜色$n
会给您较少的颜色,但彼此之间的差异更大。$i
将识别颜色,并且如果您继续使用此功能,它将始终相同。希望对您有所帮助。
为了实现“最独特的”,我们需要使用除RGB以外的类似Lab的感知色彩空间(或任何其他感知线性色彩空间)。同样,我们可以量化该空间以减小空间的大小。
生成具有所有可能的量化条目的完整3D空间,并使用运行K-means算法K=N
。所得的中心/“均值”应大致最可区分。