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 немного сложнее, поскольку оба имеют цилиндрический оттенок, но какая-то часть математики решает это, тогда она использует квадрат eucl. и здесь тоже.

В большинстве в этих случаях я добавил "отдельные параметры" для допуска для каждого канала (используя 1 глобальный допуск и альтернативные "модификаторы" HueTol := Tolerance * hueMod или LightTol := Tolerance * LightMod).


похоже, что цветовые области, построенные поверх XYZ (LAB, LCH), лучше всего работают во многих моих сценариях. Tho HSL дает очень хорошие результаты в некоторых случаях, и гораздо дешевле конвертировать в из RGB, RGB также отличный tho и заполняет большинство моих потребностей.

6 ответов


вычисление расстояний между цветами 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 не должен быть слишком сложным.

EDIT:

в качестве альтернативы, как предложено в ответ, вы могли бы использовать HLS и HSV. The colorsys модуль, похоже, имеет функции для преобразования из RGB. Его документация также ссылается на эти страницы, которые стоит прочитать, чтобы понять, почему RGB Евклидово расстояние на самом деле не работа:

EDIT 2:

по данным ответ эта библиотека должна быть полезной: http://code.google.com/p/python-colormath/


предполагая, что 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

вот оптимизированный Python версия адаптирована из Бруноasnwer:

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

вместо этого:

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 - это максимальная разница, которую вы готовы принять в любом из цветовых каналов.

что он делает, это вычесть каждый канал из ваших целевых значений, взять абсолютные значения, а затем максимум из них.


просто:

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

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

вам просто нужно немного исправить и вы готовы пойти.