区分8种颜色的最准确方法是什么?
想象一下我们有一些基本的颜色:
RED = Color ((196, 2, 51), "RED")
ORANGE = Color ((255, 165, 0), "ORANGE")
YELLOW = Color ((255, 205, 0), "YELLOW")
GREEN = Color ((0, 128, 0), "GREEN")
BLUE = Color ((0, 0, 255), "BLUE")
VIOLET = Color ((127, 0, 255), "VIOLET")
BLACK = Color ((0, 0, 0), "BLACK")
WHITE = Color ((255, 255, 255), "WHITE")
我想要一个函数,它接收一个三元组作为参数(比如 (206, 17, 38)),然后返回这个颜色是什么。比如说,(206, 17, 38) 是红色,(2, 2, 0) 是黑色,(0, 255, 0) 是绿色。要选择8种颜色中最准确的方法是什么呢?
6 个回答
使用rgb_to_hsv来进行颜色转换。然后找到和目标颜色最接近的色调。
在你的例子中,最接近的颜色是红色,因为它的色调完全匹配。
>>> from colorsys import rgb_to_hsv
>>> rgb_to_hsv(192,2,51)
(0.83333333333333337, 0, 192)
>>> rgb_to_hsv(206, 17, 38)
(0.83333333333333337, 0, 206)
>>>
下面是一个找到最接近颜色的例子。
>>> from colorsys import rgb_to_hsv
>>>
>>> colors = dict((
... ((196, 2, 51), "RED"),
... ((255, 165, 0), "ORANGE"),
... ((255, 205, 0), "YELLOW"),
... ((0, 128, 0), "GREEN"),
... ((0, 0, 255), "BLUE"),
... ((127, 0, 255), "VIOLET"),
... ((0, 0, 0), "BLACK"),
... ((255, 255, 255), "WHITE"),))
>>>
>>> color_to_match = (206,17,38)
>>>
>>> print min((abs(rgb_to_hsv(*k)[0]-rgb_to_hsv(*color_to_match)[0]),v) for k,v in colors.items())
(0.0, 'RED')
我不是颜色方面的专家,但我一直在努力寻找一个可以把RGB/HEX/HSV颜色转换成简单颜色名称的Python工具。经过一些研究,我觉得我找到了一个不错的解决方案。根据IfLoop在这篇帖子中的说法:
如果你使用笛卡尔距离来比较颜色,通常应该把输入转换成线性、感知的颜色空间,比如Lab或Yuv。RGB和HSV都不是线性的,所以用笛卡尔距离来判断两种颜色的相似度并不准确。 – IfLoop 2011年7月27日 21:15
因此,Jochen Ritzel的代码并不总是能返回正确的颜色,正如Graf所指出的。这是因为RGB和HSV都是线性颜色空间。我们需要使用像YUV这样的线性感知颜色空间。
所以我做的就是把Jochen Ritzel的代码中的rgb到hsv的部分替换成了rgb到yuv的代码,参考了这篇帖子。
colors = dict((
((196, 2, 51), "RED"),
((255, 165, 0), "ORANGE"),
((255, 205, 0), "YELLOW"),
((0, 128, 0), "GREEN"),
((0, 0, 255), "BLUE"),
((127, 0, 255), "VIOLET"),
((0, 0, 0), "BLACK"),
((255, 255, 255), "WHITE"),))
def rgb_to_ycc(r, g, b): #http://bit.ly/1blFUsF
y = .299*r + .587*g + .114*b
cb = 128 -.168736*r -.331364*g + .5*b
cr = 128 +.5*r - .418688*g - .081312*b
return y, cb, cr
def to_ycc( color ):
""" converts color tuples to floats and then to yuv """
return rgb_to_ycc(*[x/255.0 for x in color])
def color_dist( c1, c2):
""" returns the squared euklidian distance between two color vectors in yuv space """
return sum( (a-b)**2 for a,b in zip(to_ycc(c1),to_ycc(c2)) )
def min_color_diff( color_to_match, colors):
""" returns the `(distance, color_name)` with the minimal distance to `colors`"""
return min( # overal best is the best match to any color:
(color_dist(color_to_match, test), colors[test]) # (distance to `test` color, color name)
for test in colors)
if __name__ == "__main__":
r = input('r: ')
g = input('g: ')
b = input('b: ')
color_to_match = (r, g, b)
print min_color_diff( color_to_match, colors)
input('Press enter to exit.')
现在我们几乎每次都能得到正确的颜色:
>>> color_to_match = (2, 2, 0) #Graf's test
>>> print min_color_diff( color_to_match, colors)
>>>
(6.408043991348166e-05, 'BLACK')
更多例子:
>>> color_to_match = (131, 26, 26)
>>> print min_color_diff( color_to_match, colors)
>>>
(0.027661314571288835, 'RED')
>>> color_to_match = (69, 203, 136)
>>> print min_color_diff( color_to_match, colors)
>>>
(0.11505647737959283, 'GREEN')
到目前为止,我的版本似乎运行得非常好,但请注意:如果一个RGB颜色太亮或太暗,可能会返回'白色'或'黑色'。要解决这个问题,你需要在颜色字典中添加更亮和更暗的颜色。此外,像'棕色'和'灰色'(等等)这样的颜色也可以添加到颜色字典中,这样会得到更好的结果。
简单来说:在一个不依赖设备的颜色空间中使用欧几里得距离(来源:维基百科的颜色差异文章)。因为RGB颜色模式是依赖设备的,所以你需要先把颜色转换到一个不依赖设备的颜色空间。
我建议把RGB转换成Lab*。再引用一下维基百科的话:
与RGB和CMYK颜色模型不同,Lab颜色是为了更接近人类的视觉感知而设计的。
这里有一个转换的步骤。一旦你得到了L
、a
、b
的值,就可以计算你的颜色和所有参考颜色之间的欧几里得距离,选择最接近的那个。
其实,python-colormath这个Python模块(在GPL v3下)可以在不同的颜色空间之间转换,并且也能计算颜色差异。