2012-01-14 24 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) 

你只需要一點點修好了,你準備好了。