哪种方法可以区分8种颜色中的一种?

时间:2010-08-25 10:54:33

标签: python rgb

想象一下我们如何使用一些基本颜色:

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")

我想要一个函数,它将一个3元组作为参数(如(206,17,38)),它应该返回它的颜色。例如,(206,17,38)是红色,(2,2,0)是黑色,(0,255,0)是绿色。 选择8种颜色中哪一种最准确?

6 个答案:

答案 0 :(得分:12)

简答:在设备无关的颜色空间中使用欧几里德距离(来源:维基百科中的Color difference文章)。由于RGB与设备有关,因此您应首先将颜色映射到与设备无关的颜色空间之一。

我建议将RGB转换为Lab*。再次引用维基百科:

  

与RGB和CMYK颜色模型不同,   实验室颜色旨在近似   人类的愿景。

Here's a recipe进行转化。获得Lab值后,计算颜色与所有参考颜色之间的欧几里德距离,并选择最接近的颜色。


实际上,Google Code上的python-colormath Python模块(在GPL v3下)能够在许多不同的颜色空间之间进行转换,也可以计算颜色差异

答案 1 :(得分:4)

我绝不是色彩专家,但我一直在拼命寻找RGB / HEX / HSV到python中的简单颜色名称转换器。经过一番研究后,我相信我做了一个强大的解决方案。根据{{​​3}}中的IfLoop:

  

如果最终使用笛卡尔距离来比较颜色,通常应将输入转换为线性感知色彩空间,例如Lab或Yuv。 RGB和HSV都不是线性的,因此笛卡尔距离与类似的两种颜色实际上没有多大关系。 - IfLoop 2011年7月27日21:15

因此,正如格拉夫所指出的那样,Jochen Ritzel的代码并不总能恢复正确的颜色。这是因为RGB和HSV都是线性颜色空间。我们需要使用像YUV这样的线性感知色彩空间。

所以我做的是我使用Jochen Ritzel的代码并将rgb代码替换为基于this post的rgb到yuv代码的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"),))

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颜色太亮或太暗,很可能会返回'WHITE '或'黑色'。要解决此问题,您需要为颜色词典添加更浅和更深的颜色。同时在颜色字典中添加更多颜色,如“BROWN”和“GRAY”(等等),也会返回更好的效果。

答案 2 :(得分:3)

将颜色视为向量并计算给定颜色和每个颜色之间的距离,并选择最小的颜色。最简单的距离可以是:|a1 - a2| + |b1 - b2| + |c1 - c2|

同样阅读:http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf,描述的距离函数更好。

答案 3 :(得分:3)

使用rgb_to_hsv进行转换。然后将颜色与壁橱色调匹配

对于您的示例,它将是RED,因为色调完全匹配

>>> 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')

答案 4 :(得分:1)

我希望这是它应该工作的方式:它将颜色转换为hsv,然后将(平方)欧几里德距离转换为所有可用颜色并返回最接近的匹配。

主要是gnibblers代码的固定版本。

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"),))

def to_hsv( color ): 
    """ converts color tuples to floats and then to hsv """
    return rgb_to_hsv(*[x/255.0 for x in color]) #rgb_to_hsv wants floats!

def color_dist( c1, c2):
    """ returns the squared euklidian distance between two color vectors in hsv space """
    return sum( (a-b)**2 for a,b in zip(to_hsv(c1),to_hsv(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)

color_to_match = (127, 255, 255)
print min_color_diff( color_to_match, colors)

使用支持排序和距离的简单Color类,所有时髦列表理解看起来会更好(但是你可以在练习中做到这一点; - )。

答案 5 :(得分:0)

我的Goulib库的颜色模块做得相当好,还有更多。 它定义了一个Color类,可以从多个颜色空间中进行定义,并在Palette字典中进行分组。预定义了几个调色板,特别是由html / matplotlib名称索引的调色板。每个颜色自动从此调色板中最近颜色的索引中获取名称,以实验室空间(deltaE)为单位

在此处查看演示http://nbviewer.jupyter.org/github/Goulu/Goulib/blob/master/notebooks/colors.ipynb