Python - 查找相似颜色的最佳方法

22 投票
10 回答
25454 浏览
提问于 2025-04-17 10:15

我写了一个函数,可以在图片中找到某种颜色,并返回它的坐标 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 中,简单的平方欧几里得距离就能解决问题:

if ((X-X1)^2 + (Y-Y1)^2 + (Z-Z1)^2) <= (Tol^2) then
  ...

LCH 和 HSL 稍微复杂一些,因为它们有圆柱形的色调,但通过一些数学运算可以解决这个问题,然后在这里也使用平方欧几里得距离。

在大多数情况下,我为每个通道添加了“单独的参数”来设置容差(使用一个全局容差,以及替代的“修正值” HueTol := Tolerance * hueModLightTol := Tolerance * LightMod)。


看起来基于 XYZ 的颜色空间(如 LAB 和 LCH)在我的许多场景中表现最好。虽然 HSL 在某些情况下也能产生很好的结果,而且从 RGB 转换过来也更便宜,不过 RGB 也很不错,能满足我大部分的需求。

10 个回答

3

不要用这个:

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 是你能接受的颜色通道之间的最大差异。

它的做法是从你想要的颜色值中减去每个通道的值,然后取绝对值,最后找出这些绝对值中的最大值。

8

这里有一个经过优化的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
26

计算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和HSVcolorsys模块似乎有一些函数可以将RGB转换。它的文档中也链接了这些页面,值得一读,以理解为什么RGB的欧几里得距离并不太适用:

补充 2:

根据这个回答,这个库可能会很有用:http://code.google.com/p/python-colormath/

撰写回答