2012-01-14 22 views
6

我已经做了一个函数来查找图像中的颜色,并返回x,y。现在我需要添加一个新功能,在这里我可以找到具有给定容差的颜色。应该很容易?Python - 查找类似的颜色,最好的方法

代码找到图像色彩,并返回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也非常棒,并且满足了我的大部分需求。

+0

如果在图像中找不到颜色,应该返回一些内容。即错误代码。 – 2012-01-14 17:17:02

+0

你如何定义容差? 'r','g'和'b'单独的范围? – 2012-01-14 17:17:44

+0

我与约翰:你已经尝试过什么?您可以查看[余弦相似度](https://en.wikipedia.org/wiki/Cosine_similarity)并搜索Python实现。 – 2012-01-14 17:20:11

回答

17

以一种对眼睛有意义的方式计算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/

+0

请参阅我的答案,了解最佳的Python版本。 – Developer 2012-12-31 05:04:24

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 
2

假设RTOL,几何公差,和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 
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是您愿意接受任何颜色通道的最大差异。

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

+0

'import operator'。 – 2012-01-14 17:43:01

+0

@SLACKY,你需要先导入运算符。 (这就是说,这仍然是一个欧几里得距离的公式:这不会给你你期望的结果) – Bruno 2012-01-14 17:43:47

+0

@布鲁诺:我的指标甚至比欧几里德距离更差!我没有把重点放在那部分(但是为了这样做而提出了你的回答)。如果容差很小,那么可能并不重要,但如果容差很大,这可能很重要。 – 2012-01-14 17:45:36

2

这里是一个优化的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 
+0

从我所看到的,'x ** 0.5'比'从数学导入sqrt'慢得多,然后使用'sqrt(x)'。但是,如果你“输入数学”并使用'math.sqrt(x)',你将看到几乎没有区别。 – JHolta 2013-08-08 06:29:32

+0

'(35,255,24)'vs'(38,38,120)'returns'nan' – 2017-07-31 17:52:49

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) 

你只需要一点点修好了,你准备好了。