algorithm修改一个颜色,使其不如背景相似

我创建了一个20球互相reflection的演示,背景充满了纯色。 对于每个R,G,B元组组件,随机selectrandint(0, 255)颜色

问题是一些球以与背景非常相似的颜色结束,使他们难以看见。 我想避免这种情况,如果select的颜色过于相似 (在给定的阈值内),或者将其从背景颜色上移开 ,则可以滚动另一种颜色。

如何计算相似度指数作为阈值? 或者如何改变一种颜色,比如说less一点蓝呢?

明显的免责声明:我对颜色理论一无所知,所以我不知道上述概念是否存在!

我在Python编码,但我正在寻找的概念和策略,所以伪代码是好的(或只是理论)。

编辑:

澄清几点:

  • 每个球都有自己的随机颜色。 我希望它们尽可能保持随机,并尽可能地探索色彩空间(无论是RGB还是HSV, pygame接受这两种格式来定义颜色)

  • 我只是想避免每种颜色与背景“太接近”。 问题不仅仅是关于色调:对于深蓝色的背景(或者“全蓝”和“白色”蓝色等),我完全可以使用浅蓝色的球体。

  • 现在,我不在乎球的颜色是否与另一个球类似(甚至相同)。 避免这是一个奖金,但这是一个小问题。

  • 背景颜色是一个常量,单一的RGB颜色。 现在我正在使用“基本”蓝色(0, 0, 255) ,但我没有解决这个问题。 所以考虑背景为任意颜色(任意色调,亮度,饱和度等)。

EDIT2:

TL,DR版本:只要给出一种方法来创建X随机颜色,以便没有任何东西在任意(但是单一和恒定)颜色的背景下“变得太多”

您可以使用颜色构成(三维)颜色空间的事实,并计算此颜色空间中的距离。 然后,您需要在此颜色空间中定义度量标准以查找两种颜色之间的距离。

例如,在两点x =(x1,x2,x3)和y =(y1,y2,y3)之间的欧几里得空间中的距离由下式给出d(x,y)= sqrt((y1-x1)*(y1- x1)+(y2-x2)*(y2-x2)+(y3-x3)*(y3-x3))。

现在,您可以计算球的颜色和背景颜色之间的距离,如果太小,则创建一个新的随机颜色并再次testing。

你会发现既不是RGB也不是HSV是这个任务的好色彩空间。 维基百科关于色差的文章使用了L a b色彩空间,并给出了几个可能的指标。

这个主题上还有一个关于stackoverflow的讨论。

联合国网页无障碍标准页面( http://www.un.org/webaccessibility/1_visual/13_colourcontrast.shtml )确实表明了确保网站上正确文本对比的标准,同时牢记有些用户是色盲。 这对你来说可能是特别重要的,因为你的一些用户也可能带有色盲(如果他们具有相同的亮度,很难从绿色背景中分辨出一个红色的球)。

联合国网页指向( http://juicystudio.com/services/luminositycontrastratio.php#specify)Juicy工作室亮度色彩对比率分析仪,其中说明背景文本必须有一个4.5:1的对比度,除了我相信与你的情况类似:

  • 大文本:大型文本和大型文本图像的对比度至less为3:1

现在什么是对比度? 进一步超链接(这很有趣!)我们得到一个W3页面,定义对比度为:

 Contrast Ratio = (L1 + 0.05)/(L2 + 0.05) 

其中L1和L2是颜色的相对发光。 L1是亮的颜色,L2是不太亮的颜色。 再次,我们遵循指向相同的W3页面的链接:

 L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as: if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4 if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4 if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4 and RsRGB, GsRGB, and BsRGB are defined as: RsRGB = R8bit/255 GsRGB = G8bit/255 BsRGB = B8bit/255 

注意 :上面的^符号^是指幂(x 2)

我们有一个非常好的对比algorithm框架(这对colorblind友好!)

  • 确定每种颜色的R,G,B值
  • 确定每种颜色的亮度
  • 如果较亮的颜色与最低亮度颜色的对比度小于3,则重新滚动!

通过为每个组件生成一个随机的0-255值来使用RGB值可能不仅会创建与背景相似的颜色,还可能会以球的颜色非常相似。

我可能会select一个不太随意的方法,并创建保证不同的颜色。 您可以为色调范围内的每个〜32度创建颜色,并交替亮度或饱和度(使用HSV颜色模型而不是RGB)。

所以像这样的东西:

 numColors = 20; stepSize = 360 / (numColors / 2 + 1); // hue step size for(i = 0; i < numColors; i++){ color = HSV(i / 2 * stepSize, 0.5 + (i % 2) * 0.5, 1.0) } 

这将创造20种颜色,很好地分布。 这假设你正在使用整数math,所以i = 0i = 1将创建相同的色调值。

当然,这并不是太好。如果你有100个颜色值来创建,色相间隔可能不够,你会以相似的颜色再次结束。 在这种情况下,你也可以改变V (亮度)值。

我不知道你的背景颜色是什么样的,但HSV使得比较颜色也更容易(只比较3个组件,不要忘记HUE是圆形的(0 == 360))。

更新:

由于我没有真正回答你的问题,而是提供了另一种方法,下面是如何为任意背景和完全随机的彩球看起来像一个algorithm(这是一个蛮力的方法,但应该适用于你的使用情况):

 // background color, currently equals RGB(0, 0, 255) bg = HSV(240, 1.0, 1.0) // number of colors to create is equal to the amount of balls numColors = balls.length // set threshold to compare colors. you can tweak this to your liking threshold = 0.15 for(i = 0; i < numColors; i++){ do { // assuming rnd returns a random float 0-1 inclusive color = HSV(rnd() * 360, rnd(), rnd()) // calculate delta values, normalize hue to 0-1 dH = (180 - abs(abs(bg.h - color.h) - 180)) / 360 dS = bg.s - color.s dV = bg.v - color.v // "distance" between bg and color difference = sqrt(dH * dH + dS * dS + dV * dV) } while(difference < threshold) ball[i].color = color } 

既然你提到你有一个固定的背景,球的颜色仍然可以是一个随机的,但他们必须落在一定的范围,仍然恭维背景。

基本。 在我们这样做之前,您需要了解基础知识。 考虑以下颜色:

 Black #000000 rgb(0,0,0) Red #FF0000 rgb(255,0,0) Green #00FF00 rgb(0,255,0) Blue #0000FF rgb(0,0,255) Yellow #FFFF00 rgb(255,255,0) Cyan #00FFFF rgb(0,255,255) Pink #FF00FF rgb(255,0,255) Gray #C0C0C0 rgb(192,192,192) White #FFFFFF rgb(255,255,255) 

颜色混合RGB [(0..255),(0..255),(0..255)]创建如上所述的新颜色。

计算负面颜色计算负面颜色就像变成青色的红色,变成绿色变成紫色,变成蓝色变成黄色。

 Red #FF0000 rgb(255,0,0) -> Cyan #00FFFF rgb(0,255,255) Green #00FF00 rgb(0,255,0) -> Purple #FF00FF rgb(255,0,255) Blue #0000FF rgb(0,0,255) -> Yellow #FFFF00 rgb(255,255,0) 

互补色

根据计算补充颜色的参考: http : //serennu.com/colour/rgbtohsl.php

关于HSL

HSL根据色调,饱和度和亮度表示颜色,为这三种颜色的每一个赋予一个数字。

色调是色轮上的颜色位置,用0°到359°的度数表示,代表车轮的360°; 0°是红色的,180°是红色的相反颜色的青色,依此类推。

饱和度是颜色的强度,它是多么沉闷或明亮。 饱和度越低,颜色看起来越暗淡(灰色)。 这表示为百分比,100%是饱和饱和度,最亮,0%没有饱和度,灰色。

亮度是多么轻的颜色。 与饱和度略有不同。 颜色越白,亮度越高,黑度越高,亮度越低。 所以100%亮度变成白色,0%亮度变成黑色,“纯”色变成50%亮度。

看到饱和度和亮度之间的区别比解释它更容易。 如果您想澄清,请尝试查看颜色计算器页面上的亮度和饱和度变化,select相当明亮的颜色作为您的初始颜色。

所以HSL符号看起来像这样,按顺序给出色相,饱和度和亮度值:t

红色:0°100%50%淡粉色:0°100%90%青色:180°100%50%以下是步骤:

  1. 将您的颜色转换为HSL。

  2. 将Hue值改变为与Hue相反的Hue值(例如,如果色相为50°,则相反将为230°,即180°左右)。

  3. 保持饱和度和亮度值不变。

  4. 将这个新的HSL值转换回原来的颜色符号(RGB或其他)。

像EasyRGB.com这样的站点可以为你做从RGB到HSL的通用转换,反之亦然。

编程示例在PHP中完成,根据参考

从RGB到HSL的转换

蓝色#0000FF rgb(0,0,255)以上的值可以表示为红色hex00 +绿色hex00 +蓝色hexFF

 $redhex = substr($hexcode,0,2); $greenhex = substr($hexcode,2,2); $bluehex = substr($hexcode,4,2); 

它也可以表示为红色十进制0 +绿色十进制0 +蓝色十进制255

 $var_r = (hexdec($redhex)) / 255; $var_g = (hexdec($greenhex)) / 255; $var_b = (hexdec($bluehex)) / 255; 

现在将这些值插入rgb2hsl例程。 下面是我的PHP版本的EasyRGB.com的转换的通用代码:

input是$ var_r,$ var_g和$ var_b从上面输出HSL相当于$ h,$ s和$ l – 这些再次表示为1的分数,就像input值

 $var_min = min($var_r,$var_g,$var_b);ttt $var_max = max($var_r,$var_g,$var_b); $del_max = $var_max - $var_min; $l = ($var_max + $var_min) / 2; if ($del_max == 0) { $h = 0; $s = 0; } else { if ($l < 0.5) { $s = $del_max / ($var_max + $var_min); } else { $s = $del_max / (2 - $var_max - $var_min); }; $del_r = ((($var_max - $var_r) / 6) + ($del_max / 2)) / $del_max; $del_g = ((($var_max - $var_g) / 6) + ($del_max / 2)) / $del_max; $del_b = ((($var_max - $var_b) / 6) + ($del_max / 2)) / $del_max; if ($var_r == $var_max) { $h = $del_b - $del_g; } elseif ($var_g == $var_max) { $h = (1 / 3) + $del_r - $del_b; } elseif ($var_b == $var_max) { $h = (2 / 3) + $del_g - $del_r; }; if ($h < 0) { $h += 1; }; if ($h > 1) { $h -= 1; }; }; 

所以现在我们把颜色作为HSL值,variables$ h,$ s和$ l。 在这个阶段,这三个输出variables再次保持为1的分数,而不是度数和百分比。 所以,例如青色(180°100%50%)会出现为$ h = 0.5,$ s = 1和$ l = 0.5。

接下来find相反色调的值,即180°或0.5°的值(我确信math家有一个更优雅的方式来对此进行点分):

计算相反的色调,$ h2

  $h2 = $h + 0.5; if ($h2 > 1) { $h2 -= 1; }; 

补色的HSL值现在是$ h2,$ s,$ l。 所以我们准备将其转换回RGB(再次,我的EasyRGB.com公式的PHP版本)。 请注意input和输出格式这次是不同的,请参阅我的代码顶部的评论:

input是补色HSL值,保存在$ h2,$ s,$ l中作为1的分数输出是正常的255 255 255格式的RGB,保存在$ r,$ g,$ b中使用函数hue_2_rgb转换色调,如图所示在这个代码的末尾

  if ($s == 0) { $r = $l * 255; $g = $l * 255; $b = $l * 255; } else { if ($l < 0.5) { $var_2 = $l * (1 + $s); } elset { $var_2 = ($l + $s) - ($s * $l); }; $var_1 = 2 * $l - $var_2; $r = 255 * hue_2_rgb($var_1,$var_2,$h2 + (1 / 3)); $g = 255 * hue_2_rgb($var_1,$var_2,$h2); $b = 255 * hue_2_rgb($var_1,$var_2,$h2 - (1 / 3)); }; // Function to convert hue to RGB, called from above function hue_2_rgb($v1,$v2,$vh) { if ($vh < 0) { $vh += 1; }; if ($vh > 1) { $vh -= 1; }; if ((6 * $vh) < 1) { return ($v1 + ($v2 - $v1) * 6 * $vh); }; if ((2 * $vh) < 1) { return ($v2); }; if ((3 * $vh) < 2) { return ($v1 + ($v2 - $v1) * ((2 / 3 - $vh) * 6)); }; return ($v1); }; 

在那个例程之后,我们终于有了255 255 255(RGB)格式的$ r,$ g和$ b,我们可以把它转换成六位数的hex:

  $rhex = sprintf("%02X",round($r)); $ghex = sprintf("%02X",round($g)); $bhex = sprintf("%02X",round($b)); $rgbhex = $rhex.$ghex.$bhex; 

$ rgbhex是我们的答案 – hex中的补色。

由于你的颜色背景是蓝色或0,0,255 HSL是

色调(H):240度/饱和度(S):100%/明度(L):4.9%

240的对面是60的圆,然后转换回RGB给出#181800的值

我想扩展@ ashes999创建大纲的想法。

我的代码将会像Python一样可以离开我的头顶(我从来没有写过一行Python,但是我已经翻了一两遍)。

 def lerp(a,b,value): # assuming your library was not kind enough to give you this for free return a + ((b - a) * value); def drawRandomBall(bgColour,radius,point): var ballColour = Colour(random(0,255),random(0,255),random(0,255); var outlineColour = Colour(lerp(bgColour.R,255 - ballColour.R,0.5),lerp(bgColour.G,255 - ballColour.G,0.5),lerp(bgColour.B,255 - ballColour.B,0.5)); fillCircle(point,radius+1,outlineColour); fillCircle(point,radius,ballColour); 

它看起来可能不是特别漂亮,对于生产代码来说也不是很理想,但它应该足以作为一个快速修复。 我没有testing过这个代码,因为我并不完全有一个“在屏幕上跳动的球”程序,我可以毁坏它来testing它。


编辑:

看到使用的实际代码后,情况已经翻了一番,所以我提供这个解决scheme。

将第276-284行replace为以下内容:

 # Colour generator def generateColourNonBG(leeway): color = (randint(0,255), randint(0,255), randint(0,255)) while color[0] >= BG_COLOR[0]-leeway and color[0] =< BG_COLOR[0] + leeway and color[1] >= BG_COLOR[1]-leeway and color[1] =< BG_COLOR[1] + leeway and color[2] >= BG_COLOR[2]-leeway and color[2] =< BG_COLOR[2] + leeway: color = (randint(0,255), randint(0,255), randint(0,255)) # Ball generator def generateBall(): Ball(generateColourNonBG(5), radius=randint(10, radius), position=[randint(100, screen.get_size()[0]-radius), randint(100, screen.get_size()[0]-radius)], velocity=[randint(50, 50+vel[0]), randint(50, 50+vel[1])], ) # Create the balls balls = pygame.sprite.Group() for _ in xrange(BALLS): balls.add(generateBall()) 

在这个新的简化版本中,工厂方法会吐出球,而专门的方法会产生颜色。 颜色生成器testing随机生成的颜色,以查看它是否在与背景颜色接近的特定范围内。 如果所有三个颜色通道都处于BG的两侧,则颜色被认为太接近BG颜色。 所以如果回旋余地是0,只有与BG完全匹配的颜色才会被拒绝。 我select了5作为默认值,拒绝BG颜色和5种颜色。 由于BG颜色是白色的,我不确定它是否会拒绝黑色,因为数字环绕。 这可以通过使用最小和最大函数来避免,但是为了简洁,我省略了它们。

对于RGB格式,这似乎工作得不错,而且是微不足道的:

diversity_score =(abs(r1-r2)+ abs(g1-g2)+ abs(b1-b2))/ 765.0

这会给你0.0到1.0之间的分数。 越低,区分两种颜色就越困难。

这应该是显而易见的,但为了完整:r,g,b值必须首先被转换为浮点数,并且假定它们的最大值是255。