Представим себе, как работают некоторые основные цвета:
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 цветов?
Краткий ответ: используйте евклидово расстояние в независимом от устройства цветовом пространстве (источник: Статья о разнице цветов в Википедии). Поскольку RGB зависит от устройства, вы должны сначала сопоставить свои цвета с одним из независимых от устройства цветовых пространств.
Я предлагаю преобразовать RGB в L a b * . Еще раз процитируем Википедию:
В отличие от цветовых моделей RGB и CMYK, Цвет лаборатории предназначен для приблизительной человеческое зрение.
Вот рецепт для преобразования. Получив значения L
, a
, b
, вычислите евклидово расстояние между вашим цветом и всеми эталонными цветами и выберите ближайший.
На самом деле, модуль Python python-colormath в Google Code (под GPL v3) способен преобразовывать между множеством различных цветовых пространств, а также вычисляет цветовые различия .
Используйте rgb_to_hsv для преобразования. Затем сопоставьте цвет с оттенком туалета
В вашем примере это будет КРАСНЫЙ, потому что оттенок точно соответствует
>>> 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')
Я надеюсь, что так оно и должно работать: оно преобразует цвета в hsv, затем вычисляет (возведенное в квадрат) евклидово расстояние до всех доступных цветов и возвращает наиболее близкое соответствие.
В основном это исправленная версия кода грызунов.
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
, который поддерживает сортировку и расстояние (но вы можете сделать это для практики ;-).
Рассмотрите цвета как векторы и посчитайте расстояние между данным и каждым из них и выберите тот, который наименьший. Простейшее расстояние может быть: |a1 - a2| + |b1 - b2| + |c1 - c2|
.
Прочтите это также: http://answers.yahoo.com/question/index?qid=20071202234050AAaDGLf, там описана более подходящая функция расстояния.