2010-08-25 3 views
6

Imaginez que nous comment certaines couleurs de base:Quel est le moyen le plus précis de distinguer l'une des 8 couleurs?

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

Je veux avoir une fonction, qui obtient un 3-tuple comme paramètre (comme (206, 17, 38)), et il doit retourner la couleur qui c'est. Par exemple, (206, 17, 38) est rouge et (2, 2, 0) est noir et (0, 255, 0) est vert. Quel est le moyen le plus précis de choisir l'une des 8 couleurs?

+0

2,2,0 est pas techniquement noir, tout comme 240.240.240 est pas techniquement gris. – Chris

+4

C'est une approximation. C'est l'objectif de ce script évidemment. –

Répondre

11

Réponse courte : utilisez la distance euclidienne dans un espace de couleur indépendant du périphérique (source: Color difference article dans Wikipedia). Comme le RVB dépend du périphérique, vous devez d'abord mapper vos couleurs sur l'un des espaces colorimétriques indépendants du périphérique. Je suggère de convertir RGB en Lab*. Pour citer Wikipedia nouveau:

Contrairement aux modèles de couleurs RVB et CMJN, couleur Lab est conçu pour rapprocher vision humaine.

Here's a recipe pour effectuer la conversion. Une fois que vous avez les valeurs L, a, b, calculez la distance euclidienne entre votre couleur et toutes les couleurs de référence et choisissez la plus proche.


En fait, le python-colormath Module Python sur Google Code (sous licence GPL v3) est capable de convertir entre de nombreux espaces de couleurs différentes et calcule les différences de couleurs et.

+1

Excellent! Python-Colormath est ce dont j'avais besoin! EXACTEMENT! – Graf

+0

Les références de python-colormath http://www.brucelindbloom.com/ - une excellente source si vous voulez comprendre les maths derrière les conversions. – Bolo

3

Traiter les couleurs comme des vecteurs et compter la distance entre le donné et chacun d'eux et choisir celui qui est le moins. La distance la plus simple peut être: |a1 - a2| + |b1 - b2| + |c1 - c2|.

Lisez ceci aussi: http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf, il y a une meilleure fonction de distance décrite.

+2

RGB est dépendant de l'appareil et donc ce n'est pas un bon espace de couleur pour mesurer la différence de couleur (voir ici: http://en.wikipedia.org/wiki/Color_difference) – Bolo

3

Utilisez rgb_to_hsv pour convertir. correspondre ensuite la couleur avec la teinte placard

Pour votre exemple, il serait rouge parce que la teinte correspond exactement à

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

Voici un exemple de la façon de trouver la plus proche

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

Cela ne fonctionne pas pour moi, essayez-le vous-même avec la couleur (2,2,0), qui est apparemment noir, votre code le dit orange. – Graf

1

J'espère que c'est la façon dont il est censé fonctionner: Il convertit les couleurs en hsv, puis prend la distance euclidienne (au carré) pour toutes les couleurs disponibles et renvoie la correspondance la plus proche.

Principalement une version fixe du code 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) 

Toute la compréhension de la liste géniale serait beaucoup mieux avec une simple classe Color qui prend en charge le tri et la distance (mais vous pouvez le faire pour la pratique ;-).

+0

Peut-être que cela fonctionne, merci pour votre commentaire, mais j'ai déjà trouvé une meilleure solution. – Graf

+0

Non cela ne fonctionne pas aussi bien, j'ai essayé la couleur (2,2,0) et il a dit qu'il est vert. Je pense que convertir en hsv n'est pas la meilleure idée. Il est généralement recommandé de n'utiliser la distance euclidienne que sur deux couleurs Lab, et non sur les couleurs RVB ou les couleurs hsv. – Graf

+0

@Graf: merci pour l'info. Je suppose que nous devrions laisser des choses comme ça aux gens qui savent réellement ce qu'ils font et utiliser le module python-colorormath ;-) –

3

Je ne suis en aucun cas un expert en couleurs, mais je cherchais désespérément un convertisseur de noms de couleurs RVB/HEX/HSV en python. Après avoir fait quelques recherches, je crois que j'ai fait une solution formidable.Selon IfLoop en this post:

Si vous vous retrouvez à l'aide de la distance cartésienne pour comparer les couleurs, vous devriez normalement traduire les entrées dans un linéaire, l'espace couleur perceptuel, telles que Lab ou YUV. Ni le RGB ni le HSV ne sont linéaires, et la distance cartésienne ne correspond pas vraiment aux deux couleurs similaires. - IfLoop 27 juillet '11 à 21:15

Par conséquent, le code de Jochen Ritzel ne reviendra pas toujours la bonne couleur, comme Graf a souligné. C'est parce que RGB et HSV sont des espaces de couleurs linéaires. Nous devons utiliser un espace de couleurs perceptives linéaire comme YUV.

Donc ce que j'ai fait, c'est que j'ai pris le code de Jochen Ritzel et remplacé le code rgb par du code hsv avec le code rgb to yuv basé sur 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.') 

Maintenant, nous semble finir avec les bonnes couleurs presque à chaque fois:

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

Plus exemples:

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

Jusqu'à présent, il semble que ma version semble fonctionner presque parfaitement, mais s'il vous plaît noter: Il est probable que si une couleur RVB est trop clair ou trop sombre, vous obtiendrez probablement retourné «BLANC» ou «NOIR». Pour résoudre ce problème, vous devrez ajouter des couleurs plus claires et plus foncées à votre dictionnaire de couleurs. Ajouter également plus de couleurs comme 'BROWN' et 'GREY' (et ainsi de suite) au dictionnaire de couleurs donnera également de meilleurs résultats.

+1

Ceci est principalement ce que le PO a demandé s'il/elle voulait un code source réel. La plupart des gens oublient les différences entre les espaces de couleur. RVB est pour les périphériques, et HSV est non linéaire. Les comparaisons avec euclidean devraient être faites dans YUV (le laboratoire fonctionne également, mais il est principalement fait pour comparer avec la vision humaine). –

+0

Merci d'avoir fourni une réponse. Quand j'ai essayé, (45, 106, 168), il a imprimé VERT. Mais en fait, cette couleur est proche de BLEU. – Indrajeet

0

Le module de couleurs de ma bibliothèque Goulib le fait assez bien, et bien plus encore. Il définit une classe Color pouvant être créée à partir de plusieurs espaces colorimétriques et regroupée dans un dictionnaire Palette. Plusieurs palettes sont prédéfinies, notamment celle qui est indexée par les noms html/matplotlib. Chaque couleur automagiquement un nom recieves de l'index de la couleur la plus proche dans cette palette, mesurée dans l'espace Lab (deltaE)

voir la démo ici http://nbviewer.jupyter.org/github/Goulu/Goulib/blob/master/notebooks/colors.ipynb

Questions connexes