Python - 找到相似的颜色,最好的方法

时间:2012-01-14 17:06:46

标签: python colors find

我已经创建了一个函数来查找图像中的颜色,然后返回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 * hueModLightTol := Tolerance * LightMod)。


似乎在XYZ(LAB,LCH)之上构建的色彩空间在我的许多场景中表现最佳。在某些情况下,HSL会产生非常好的效果,而且转换为RGB要便宜得多,RGB也很棒,并且满足了我的大部分需求。

9 个答案:

答案 0 :(得分:19)

以一种对眼睛有意义的方式计算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应该不会太难。

修改

或者,如this answer中所述,您可以使用HLS and HSVcolorsys模块似乎具有从RGB进行转换的功能。它的文档也链接到这些页面,值得阅读,以了解为什么RGB欧几里德距离不起作用:

编辑2:

根据this answer,此库应该有用:http://code.google.com/p/python-colormath/

答案 1 :(得分:3)

这是一个优化的 Python 版本,改编自 Bruno 的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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

而不是:

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是您愿意在任何颜色渠道中接受的最大差异。

它的作用是从目标值中减去每个通道,取绝对值,然后取最大值。

答案 4 :(得分:1)

这是Bruno和Developer的答案的矢量化Python(numpy)版本(即,近似值here的实现),它接受一对形状为{{1}的numpy数组},其中各个行按(x, 3)的顺序排列,并且各个颜色值∈[0,1]。

您可以将其减少为两个,而以可读性为代价。我不能完全确定它是否是最优化的版本,但它应该足够好。

[R, G, B]

它是根据上面开发人员的每个元素版本进行评估的,并产生相同的结果(在千分之二的情况下保存浮动精度错误)。

答案 5 :(得分:1)

函数here的更清晰的python实现,该函数采用2条图像路径,使用cv.imread读取它们,并输出一个矩阵,每个矩阵单元具有不同的颜色。您可以更改它以使其轻松匹配2种颜色

        import numpy as np
        import cv2 as cv    
        
        def col_diff(img1, img2):
            img_bgr1 = cv.imread(img1) # since opencv reads as B, G, R
            img_bgr2 = cv.imread(img2)
            r_m = 0.5 * (img_bgr1[:, :, 2] + img_bgr2[:, :, 2])
            delta_rgb = np.square(img_bgr1- img_bgr2)
            cols_diffs = delta_rgb[:, :, 2] * (2 + r_m / 256) + delta_rgb[:, :, 1] * (4) + 
                            delta_rgb[:, :, 0] * (2 + (255 - r_m) / 256)
            cols_diffs = np.sqrt(cols_diffs)            
            # lets normalized the values to range [0 , 1] 
            cols_diffs_min = np.min(cols_diffs)
            cols_diffs_max = np.max(cols_diffs)
            cols_diffs_normalized = (cols_diffs - cols_diffs_min) / (cols_diffs_max - cols_diffs_min)
            
            return np.sqrt(cols_diffs_normalized)

答案 6 :(得分:0)

简单:

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

答案 7 :(得分:0)

来自pyautogui source code

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)

你只需要一点修理,你就可以准备好了。

答案 8 :(得分:0)

这是一个简单的函数,不需要任何库:

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