Python - Найдите похожие цвета, лучший способ
Я сделал функцию, чтобы найти цвет внутри изображения и вернуть x, y. Теперь мне нужно добавить новую функцию, где я могу найти цвет с определенной степенью допуска. Должно быть легко?
Код для поиска цвета в изображении и возврата x, y:
def FindColorIn(r,g,b, xmin, xmax, ymin, ymax):
image = ImageGrab.grab()
for x in range(xmin, xmax):
for y in range(ymin,ymax):
px = image.getpixel((x, y))
if px[0] == r and px[1] == g and px[2] == b:
return x, y
def FindColor(r,g,b):
image = ImageGrab.grab()
size = image.size
pos = FindColorIn(r,g,b, 1, size[0], 1, size[1])
return pos
Результат:
Взятые из ответов обычные методы сравнения двух цветов находятся на евклидовом расстоянии или чебышевском расстоянии.
Я решил в основном использовать (квадрат) эвклидовое расстояние и несколько разных цветовых пространств. LAB, deltaE (LCH), XYZ, HSL и RGB. В моем коде большинство цветовых пространств используют квадрат эвклидова расстояния, чтобы вычислить разницу.
Например, с LAB, RGB и XYZ простой квадрат euc. Расстояние делает трюк:
if ((X-X1)^2 + (Y-Y1)^2 + (Z-Z1)^2) <= (Tol^2) then
...
LCH и HSL немного сложнее, поскольку оба имеют цилиндрический оттенок, но некоторые части математики решают это, а затем используют квадратный эвкл. здесь.
В большинстве случаев я добавил "отдельные параметры" для допуска для каждого канала (используя 1 глобальный допуск и альтернативные "модификаторы" HueTol := Tolerance * hueMod
или LightTol := Tolerance * LightMod
).
Кажется, что цветовые пространства, построенные поверх XYZ (LAB, LCH), лучше всего работают во многих моих сценариях. Tho HSL дает очень хорошие результаты в некоторых случаях, и гораздо дешевле конвертировать в RGB, RGB также отлично подходит для большинства моих потребностей.
Ответы
Ответ 1
Вычисление расстояний между цветами RGB таким способом, который является значимым для глаз, не так просто, как просто взять евклидово расстояние между двумя векторами RGB.
Здесь есть интересная статья об этом: http://www.compuphase.com/cmetric.htm
Пример реализации в C таков:
typedef struct {
unsigned char r, g, b;
} RGB;
double ColourDistance(RGB e1, RGB e2)
{
long rmean = ( (long)e1.r + (long)e2.r ) / 2;
long r = (long)e1.r - (long)e2.r;
long g = (long)e1.g - (long)e2.g;
long b = (long)e1.b - (long)e2.b;
return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
Не должно быть слишком сложно портировать на Python.
РЕДАКТИРОВАТЬ:
В качестве альтернативы, как предлагается в этом ответе, вы можете использовать HLS и HSV. Модуль colorsys
похоже, имеет функции для преобразования из RGB. Его документация также ссылается на эти страницы, которые стоит прочитать, чтобы понять, почему евклидово расстояние RGB на самом деле не работает:
РЕДАКТИРОВАТЬ 2:
Согласно этому ответу, эта библиотека должна быть полезной: http://code.google.com/p/python-colormath/
Ответ 2
Ниже приведена оптимизированная версия Python
, адаптированная из аксессуара Bruno:
def ColorDistance(rgb1,rgb2):
'''d = {} distance between two colors(3)'''
rm = 0.5*(rgb1[0]+rgb2[0])
d = sum((2+rm,4,3-rm)*(rgb1-rgb2)**2)**0.5
return d
использование:
>>> import numpy
>>> rgb1 = numpy.array([1,1,0])
>>> rgb2 = numpy.array([0,0,0])
>>> ColorDistance(rgb1,rgb2)
2.5495097567963922
Ответ 3
Предполагая, что rtol, gtol и btol являются допустимыми для r, g и b соответственно, почему бы и нет:
if abs(px[0]- r) <= rtol and \
abs(px[1]- g) <= gtol and \
abs(px[2]- b) <= btol:
return x, y
Ответ 4
Вместо этого:
if px[0] == r and px[1] == g and px[2] == b:
Попробуйте следующее:
if max(map(lambda a,b: abs(a-b), px, (r,g,b))) < tolerance:
Где tolerance
- максимальная разница, которую вы готовы принять в любом из цветовых каналов.
Что он делает, это вычесть каждый канал из ваших целевых значений, принять абсолютные значения, а затем максимальные значения.
Ответ 5
Простой:
def eq_with_tolerance(a, b, t):
return a-t <= b <= a+t
def FindColorIn(r,g,b, xmin, xmax, ymin, ymax, tolerance=0):
image = ImageGrab.grab()
for x in range(xmin, xmax):
for y in range(ymin,ymax):
px = image.getpixel((x, y))
if eq_with_tolerance(r, px[0], tolerance) and eq_with_tolerance(g, px[1], tolerance) and eq_with_tolerance(b, px[2], tolerance):
return x, y
Ответ 6
from pyautogui исходный код
def pixelMatchesColor(x, y, expectedRGBColor, tolerance=0):
r, g, b = screenshot().getpixel((x, y))
exR, exG, exB = expectedRGBColor
return (abs(r - exR) <= tolerance) and (abs(g - exG) <= tolerance) and (abs(b - exB) <= tolerance)
вам просто нужно немного исправить, и вы готовы к работе.
Ответ 7
Вот простая функция, которая не требует никаких библиотек:
def color_distance(rgb1, rgb2):
rm = 0.5 * (rgb1[0] + rgb2[0])
rd = ((2 + rm) * (rgb1[0] - rgb2[0])) ** 2
gd = (4 * (rgb1[1] - rgb2[1])) ** 2
bd = ((3 - rm) * (rgb1[2] - rgb2[2])) ** 2
return (rd + gd + bd) ** 0.5
предполагая, что rgb1 и rgb2 являются кортежами RBG
Ответ 8
Здесь векторизованная версия Python (numpy) ответов Bruno и Developer (т. Е. Реализация полученного здесь приближения), которая принимает пару массивов numpy формы (x, 3)
где отдельные строки находятся в порядке [R, G, B]
и индивидуальные значения цвета ∈ [0, 1].
Вы можете уменьшить его двумя двухслойными за счет читабельности. Я не совсем уверен, является ли это наиболее оптимизированной версией из возможных, но она должна быть достаточно хорошей.
def colour_dist(fst, snd):
rm = 0.5 * (fst[:, 0] + snd[:, 0])
drgb = (fst - snd) ** 2
t = np.array([2 + rm, 4 + 0 * rm, 3 - rm]).T
return np.sqrt(np.sum(t * drgb, 1))
Он был оценен по сравнению с версией Developer для каждого элемента выше и дает те же результаты (за исключением ошибок с плавающей точностью в двух случаях из тысячи).