What is the most accurate way to distinguish one of 8 colors?

Imagine some basic colors:

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

I want to have a function that receives a 3-tuple as a parameter (e.g. (206, 17, 38)), and it should return the color that it is. For example, (206, 17, 38) is red, and (2, 2, 0) is black, and (0, 255, 0) is green. What is the most accurate way to choose one of 8 colors?

+6
python rgb
source share
6 answers

Short answer: Use Euclidean distance in a device-independent color space (source: Wikipedia color difference ). Since RGB is device-dependent, you must first match your colors to one of the device-independent color spaces.

I suggest converting RGB to Lab * . To quote Wikipedia again:

Unlike the RGB and CMYK color models, the Color Lab is designed to approximate human vision.

Here is the recipe for the conversion. After you get the values L , a , b , calculate the Euclidean distance between your color and all the reference colors and select the closest one.


In fact, the python-colormath Python module in Google Code (under GPL v3) is able to convert many different color spaces and also calculates color differences .

+11
source share

Treat the colors as vectors and count the distance between the data and each of them and choose the one that is the smallest. The simplest distance can be: |a1 - a2| + |b1 - b2| + |c1 - c2| |a1 - a2| + |b1 - b2| + |c1 - c2| .

Read also: http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf , there is a more detailed distance function here.

+3
source share

Use rgb_to_hsv to convert. Then match the color with the shade of the cabinet

For your example, it will be RED because the hue matches exactly

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

Here is an example of how to find the closest match

 >>> 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') 
+3
source share

I am by no means a specialist in color, but I was desperately looking for RGB / HEX / HSV for a simple color name converter in python. After some research, I believe I made a huge decision. According to IfLoop in this post :

If you finish using the Cartesian distance to compare colors, you should usually translate the inputs into a linear, perceptual color space, such as Lab or Yuv. Neither RGB nor HSV are linear, and therefore Cartesian distance is not very similar to similar two colors. - IfLoop Jul 27 '11 at 21:15

Consequently, the Jochen Ritzel code does not always return the correct color, as the Count pointed out. This is because RGB and HSV are linear color spaces. We need to use a linear perceptual color space such as YUV.

So what I did, I took the Jochen Ritzel code and replaced rgb with hsv code with rgb code with yuv 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( (ab)**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 get the right colors almost every time:

 >>> color_to_match = (2, 2, 0) #Graf test >>> print min_color_diff( color_to_match, colors) >>> (6.408043991348166e-05, 'BLACK') 

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

It still seems that my version works almost perfectly, but note:. It is likely that if the rgb color is too bright or too dark, you will probably get it back. WHITE 'or' BLACK. 'To solve this problem, you need to add lighter and darker colors to the color dictionary. Also adding more colors such as β€œBROWN” and β€œGRAY” (etc.) to the color dictionary will also bring better results.

+3
source share

I hope this is how it should work: it converts the colors to hsv, then takes the (square) Euclidean distance to all available colors and returns the closest match.

Mostly a fixed version of gnibblers code.

 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( (ab)**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) 

All understanding of the funky list will look much better with a simple Color class that supports sorting and distance (but you can do this for practice ;-).

+1
source share

The color module of my Goulib library does this pretty well and more. It defines the Color class, which can be used from several color spaces and grouped into a palette dictionary. Several palettes are preinstalled, in particular, they are indexed by the names html / matplotlib. Each color automatically gets the name from the closest color index in this palette, measured in the laboratory space (deltaE)

see demo here http://nbviewer.jupyter.org/github/Goulu/Goulib/blob/master/notebooks/colors.ipynb

0
source share

All Articles