2010-08-25 20 views
6

Imagínese que cómo algunos colores básicos:¿Cuál es la forma más precisa de distinguir uno de los 8 colores?

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

Quiero tener una función, que obtiene un 3-tupla como un parámetro (como (206, 17, 38)), y debe devolver el color que se es. Por ejemplo, (206, 17, 38) es rojo, y (2, 2, 0) es negro y (0, 255, 0) es verde. ¿Cuál es la forma más precisa de elegir uno de los 8 colores?

+0

2,2,0 técnicamente no es negro, al igual que no es 240240240 técnicamente gris. – Chris

+4

Es una aproximación. Es el objetivo de este guión, obviamente. –

Respuesta

11

Respuesta corta : utilice la distancia euclidiana en un espacio de color independiente del dispositivo (fuente: Color difference artículo en Wikipedia). Como RGB depende del dispositivo, primero debe asignar los colores a uno de los espacios de color independientes del dispositivo.

Sugiero convertir RGB a Lab*. Para citar nuevamente Wikipedia:

A diferencia de los modelos de color RGB y CMYK, de color Lab está diseñado para aproximar la visión humana.

Here's a recipe para hacer la conversión. Una vez que tenga los valores L, a, b, calcule la distancia euclidiana entre su color y todos los colores de referencia y elija el más cercano.


En realidad, el módulo python-colormath Python en Google Code (bajo GPL v3) es capaz de convertir entre muchos diferentes espacios de color y calcula las diferencias de color, así.

+1

¡Excelente! ¡python-colormath es lo que necesitaba! ¡EXACTAMENTE! – Graf

+0

El python-colormath hace referencia a http://www.brucelindbloom.com/ - una fuente excelente si quiere entender las matemáticas detrás de las conversiones. – Bolo

3

Trate los colores como vectores y cuente la distancia entre el dado y cada uno de ellos y elija el que sea el menos. La distancia más simple puede ser: |a1 - a2| + |b1 - b2| + |c1 - c2|.

Lea esto también: http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf, se describe una mejor función de distancia.

+2

RGB depende del dispositivo y, por lo tanto, no es un buen espacio de color para medir la diferencia de color (consulte aquí: http://en.wikipedia.org/wiki/Color_difference) – Bolo

3

Usa rgb_to_hsv para convertir. Entonces coincida con el color con el tono armario

Para su ejemplo sería rojo porque el matiz coincide exactamente con

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

Aquí está un ejemplo de cómo encontrar la coincidencia más cercana

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

Eso no me funciona, pruébelo usted mismo con color (2,2,0), que aparentemente es negro, tu código dice que es naranja. – Graf

1

Espero que así sea como se supone que funciona: convierte los colores a hsv, luego toma la distancia euclidiana (cuadrada) a todos los colores disponibles y devuelve la coincidencia más cercana.

Principalmente una versión fija del código 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) 

Toda la lista por comprensión cobarde se vería mucho mejor con una simple clase Color que soporta la clasificación y la distancia (pero se puede hacer eso por la práctica ;-).

+0

Tal vez eso funcione, gracias por su comentario, pero ya encontré una mejor solución. – Graf

+0

No, eso no funciona tan bien, probé el color (2,2,0) y decía que era verde. Creo que convertir a hsv no es la mejor idea. En general, se sugiere que solo use la distancia euclidiana en dos colores Lab, no en colores RGB o colores hsv. – Graf

+0

@Graf: gracias por la información. Supongo que deberíamos dejar cosas así a las personas que realmente saben lo que están haciendo y usar el módulo python-colormath ;-) –

3

No soy de ninguna manera un experto en color, pero he estado buscando desesperadamente un convertidor de nombres de color RGB/HEX/HSV en python. Después de investigar un poco, creo que hice una solución formidable.De acuerdo con IfLoop en this post:

Si al final utilizando la distancia cartesiana para comparar colores, que normalmente debe traducir los elementos de entrada en forma lineal, el espacio de color de percepción, tales como laboratorio o YUV. Ni RGB ni HSV son lineales, por lo que la distancia cartesiana no se relaciona mucho con dos colores similares realmente. - IfLoop Jul 27 '11 a las 21:15

Por lo tanto, el código de Jochen Ritzel no siempre devolverá el color adecuado, como señaló Graf. Esto se debe a que tanto RGB como HSV son espacios de color lineales. Necesitamos usar un espacio de color perceptual lineal como YUV.

Lo que hice fue tomar el código de Jochen Ritzel y reemplazar el rgb al código hsv con el código rgb a yuv basado en 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.') 

Ahora nos parece terminar con los colores de la derecha casi todo el tiempo:

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

Más ejemplos:

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

Hasta ahora parece que mi versión parece estar funcionando casi perfectamente, pero tenga en cuenta: Es probable que si un color rgb es demasiado brillante o demasiado oscuro, es probable que le devuelvan 'BLANCO' o 'NEGRO'. Para resolver esto, deberá agregar colores más claros y más oscuros a su diccionario de colores. Además, agregar más colores como 'BROWN' y 'GRAY' (y así sucesivamente) al diccionario de colores también arrojará mejores resultados.

+1

Esto es principalmente lo que el OP pidió si él/ella quería un código fuente real. La mayoría de las personas olvida las diferencias entre los espacios de color. RGB es para dispositivos, y HSV no es lineal. Las comparaciones con euclidiano deben hacerse en YUV (el laboratorio también funciona, pero está hecho principalmente para compararlo con la visión humana). –

+0

Gracias por proporcionar una respuesta. Cuando lo intenté, (45, 106, 168), imprimió VERDE. Pero, de hecho, ese color está cerca del AZUL. – Indrajeet

0

El módulo de colores de mi biblioteca Goulib lo hace bastante bien, y mucho más. Define una clase de Color que puede ingresarse desde varios espacios de color y agruparse en un diccionario de Paleta. Varias paletas están predefinidas, especialmente una que está indexada por los nombres html/matplotlib. Cada color recieves automagicamente un nombre del índice del color más cercano en esta paleta, medida en el espacio Lab (delta E)

ver demostración aquí http://nbviewer.jupyter.org/github/Goulu/Goulib/blob/master/notebooks/colors.ipynb

Cuestiones relacionadas