2010-08-25 4 views
6

Представьте, что мы, как некоторые основные цвета:Какой самый точный способ отличить один из 8 цветов?

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

Я хочу иметь функцию, которая получает 3-кортеж в качестве параметра (например, (206, 17, 38)), и она должна вернуть цвет, это. Например, (206, 17, 38) является красным, а (2, 2, 0) черным, а (0, 255, 0) - зеленым. Какой самый точный способ выбрать один из 8 цветов?

+0

2,2,0 технически не черный, так же, как 240240240 не является технически серый. – Chris

+4

Это приближение. Очевидно, это цель этого скрипта. –

ответ

11

Короткий ответ : используйте евклидову дистанцию ​​в независимом от устройства цветовом пространстве (источник: Color difference статья в Википедии). Поскольку RGB зависит от устройства, вы должны сначала сопоставить свои цвета с одним из цветовых пространств, не зависящих от устройства.

Предлагаю конвертировать RGB в Lab*. Чтобы снова цитирую Википедию:

В отличие от цветовых моделей RGB и CMYK, Lab цвет предназначен для аппроксимации человеческого зрения.

Here's a recipe, чтобы сделать преобразование. Когда у вас есть значения L, a, b, вычислите Euclidean расстояние между вашим цветом и всеми ссылочными цветами и выберите ближайший.


На самом деле, Python модуль python-colormath на Google Code (под GPL v3) способен преобразовывать между различными цветовыми пространствами и вычисляет цветовые различия, а.

+1

Отлично! python-colormath - это то, что мне нужно! В ТОЧКУ! – Graf

+0

Ссылки на python-colormath http://www.brucelindbloom.com/ - отличный источник, если вы хотите понять математику за конверсиями. – Bolo

3

Относитесь к цветам в виде векторов и подсчитывайте расстояние между данным и каждым из них и выберите тот, который является наименьшим. Самое простое расстояние может быть: |a1 - a2| + |b1 - b2| + |c1 - c2|.

Прочтите это тоже: http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf, существует более совершенная функция расстояния.

+2

RGB зависит от устройства и поэтому не является хорошим цветовым пространством для измерения разницы цветов (см. Здесь: http://en.wikipedia.org/wiki/Color_difference) – Bolo

3

Используйте rgb_to_hsv для преобразования. Затем подобрать цвет шкафа оттенок

Для примера это будет RED, потому что оттенок точно соответствует

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

Это не работает для меня, попробуйте сами с цветом (2,2,0), который, по-видимому, черный, ваш код говорит, что он оранжевый. – Graf

1

Я надеюсь, что так оно и должно работать: оно преобразует цвета в hsv, затем берет (квадрат) эвклидовое расстояние до всех доступных цветов и возвращает самое близкое совпадение.

В основном фиксированная версия кода 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) 

Все фанк список понимание будет выглядеть намного лучше, с помощью простого Color класса, который поддерживает сортировку и расстояния (но вы можете сделать это на практике ;-).

+0

Возможно, это работает, спасибо за ваш комментарий, но я уже нашел лучшее решение. – Graf

+0

Нет, что не работает, я попробовал цвет (2,2,0), и он сказал, что он зеленый. Я думаю, что преобразование в hsv - не лучшая идея. Обычно рекомендуется использовать эвклидовое расстояние только для двух цветов Lab, а не цветов RGB или цветов hsv. – Graf

+0

@Graf: спасибо за информацию. Я думаю, мы должны оставить такие вещи людям, которые действительно знают, что они делают, и использовать модуль python-colormath ;-) –

3

Я никоим образом не специалист по цвету, но я отчаянно искал RGB/HEX/HSV для простого конвертера имен цветов в python. После некоторых исследований, я считаю, что сделал огромное решение.Согласно IfLoop в this post:

Если вы в конечном итоге с помощью декартово расстояние для сравнения цвета, обычно следует переводить вклады в линейную, воспринимаемого цветового пространства, такие как лаборатории или YUV. Ни RGB, ни HSV не являются линейными, и поэтому декартовое расстояние не очень похоже на аналогичные два цвета. - IfLoop Jul 27 '11 at 21:15

Следовательно, код Йохен Ритцеля не всегда возвращает правильный цвет, как указал Граф. Это связано с тем, что RGB и HSV являются линейными цветовыми пространствами. Нам нужно использовать линейное перцептивное цветовое пространство, такое как YUV.

Итак, что я сделал, я взял код Йохен Ритцеля и заменил код rgb на hsv кодом rgb на yuv на основе 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.') 

Теперь мы, кажется, в конечном итоге с правильными цветами почти каждый раз:

>>> 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 слишком яркий или слишком темный, вы, вероятно, получите «WHITE» или «BLACK». Чтобы решить эту проблему, вам нужно добавить более светлые и темные цвета в словарь цветов. Также добавление большего количества цветов, таких как «BROWN» и «GREY» (и т. Д.) В словарь цветов, также принесет лучшие результаты.

+1

Это в основном то, что ОП просил если ему нужен фактический исходный код. Большинство людей забывают о различиях между цветовыми пространствами. RGB - для устройств, а HSV - нелинейный. Сравнения с евклидовыми должны быть сделаны в YUV (Lab также работает, но в основном это делается для сравнения с человеческим видением). –

+0

Спасибо, что ответили. Когда я попытался (45, 106, 168), он напечатал ЗЕЛЕНЫЙ. Но на самом деле этот цвет близок к BLUE. – Indrajeet

0

Модуль цветов моей библиотеки Goulib делает это довольно хорошо и многое другое. Он определяет класс цвета, который может быть введен из нескольких цветовых пространств и сгруппирован в словарь палитры. Предустановлены несколько палитр, в частности, индексируются именами html/matplotlib. Каждый цвет автомагический recieves имени из индекса ближайшего цвета в этой палитре, измеренный в лаборатории пространства (DeltaE)

посмотреть демо здесь http://nbviewer.jupyter.org/github/Goulu/Goulib/blob/master/notebooks/colors.ipynb

Смежные вопросы