区分8种颜色的最准确方法是什么?

7 投票
6 回答
3919 浏览
提问于 2025-04-16 03:16

想象一下我们有一些基本的颜色:

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 个回答

3

使用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')
5

我不是颜色方面的专家,但我一直在努力寻找一个可以把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颜色太亮或太暗,可能会返回'白色'或'黑色'。要解决这个问题,你需要在颜色字典中添加更亮和更暗的颜色。此外,像'棕色'和'灰色'(等等)这样的颜色也可以添加到颜色字典中,这样会得到更好的结果。

14

简单来说:在一个不依赖设备的颜色空间中使用欧几里得距离(来源:维基百科的颜色差异文章)。因为RGB颜色模式是依赖设备的,所以你需要先把颜色转换到一个不依赖设备的颜色空间。

我建议把RGB转换成Lab*。再引用一下维基百科的话:

与RGB和CMYK颜色模型不同,Lab颜色是为了更接近人类的视觉感知而设计的。

这里有一个转换的步骤。一旦你得到了Lab的值,就可以计算你的颜色和所有参考颜色之间的欧几里得距离,选择最接近的那个。


其实,python-colormath这个Python模块(在GPL v3下)可以在不同的颜色空间之间转换,并且也能计算颜色差异

撰写回答