Imagine we how some basic colors:
RED = Color ((196, 2, 51), \"RED\")
ORANGE = Color ((255, 165, 0), \"ORANGE\")
YELLOW = Color ((255, 205, 0), \"YELLOW\")
GREEN
I'm by no means a color expert, but I've been desperately looking for a RGB/HEX/HSV to simple color names converter in python. After doing some research I believe I made a formidable solution. According to IfLoop in this post:
If you end up using Cartesian distance to compare colors, you should normally translate the inputs into a linear, perceptual color space, such as Lab or Yuv. Neither RGB nor HSV are linear, and so cartesian distance doesn't much relate to similar two colors really are. – IfLoop Jul 27 '11 at 21:15
Therefore, Jochen Ritzel's code won't always return the proper color, as Graf pointed out. This is because both RGB and HSV are linear color spaces. We need to use a linear perceptual color space like YUV.
So what I did was I took Jochen Ritzel's code and replaced the rgb to hsv code with the rgb to yuv code based on this post.
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.')
Now we seem to end up with the right colors almost every time:
>>> color_to_match = (2, 2, 0) #Graf's test
>>> print min_color_diff( color_to_match, colors)
>>>
(6.408043991348166e-05, 'BLACK')
More examples:
>>> 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')
So far it appears that my version seems to be working almost perfectly, but please note: It is likely that if an rgb color is too bright or too dark, you will probably get returned 'WHITE' or 'BLACK.' To resolve this you will need to add lighter and darker colors to your colors dictionary. Also adding more colors like 'BROWN' and 'GRAY' (and so on) to the colors dictionary will also return better results.