2012-01-14 4 views
6

Ho fatto una funzione per trovare un colore all'interno di un'immagine, e restituire x, y. Ora ho bisogno di aggiungere una nuova funzione, dove posso trovare un colore con una tolleranza data. Dovrebbe essere facile?Python - Trova colori simili, meglio

codice per trovare il colore in immagine, e tornare 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 

Risultato:

Tratto dalle risposte normali metodi di confronto tra due colori sono in distanza euclidea o distanza Chebyshev.

ho deciso di utilizzare per la maggior parte (al quadrato) distanza euclidea, e molteplici colori-spazi diversi. LAB, deltaE (LCH), XYZ, HSL e RGB. Nel mio codice, la maggior parte degli spazi colore usa la distanza euclidea quadrata per calcolare la differenza.

Ad esempio con LAB, RGB e XYZ semplice euc quadrata. distanza fa il trucco:

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

LCH, e HSL è un po 'più complicato come entrambi hanno una tonalità cilindrica, ma qualche pezzo di matematica risolve questo, allora è su all'utilizzo eucl quadrata. anche qui.

Nella maggior parte dei casi, ho aggiunto "parametri separati" per la tolleranza per ciascun canale (utilizzando 1 tolleranza globale e "modificatori" alternativi HueTol := Tolerance * hueMod o LightTol := Tolerance * LightMod).


sembra spazi colore costruiti sulla cima di XYZ (LAB, LCH) non eseguire meglio in molti dei miei scenari. Anche se in alcuni casi l'HSL produce ottimi risultati, ed è molto più economico convertire da RGB, anche l'RGB è ottimo e soddisfa gran parte delle mie esigenze.

+0

Si dovrebbe restituire qualcosa se non si trova il colore nell'immagine. cioè, un codice di errore. –

+0

Come si definisce la tolleranza? Intervalli separati per 'r',' g' e 'b'? –

+0

Sono con John: cosa hai già provato? Potresti guardare a [cosine similarity] (https://en.wikipedia.org/wiki/Cosine_similarity) e cercare le implementazioni Python. –

risposta

17

Calcolare le distanze tra i colori RGB, in un modo che è significativo per gli occhi, non è così facile prendere semplicemente la distanza euclidea tra i due vettori RGB.

C'è un articolo interessante su questo qui: http://www.compuphase.com/cmetric.htm

L'esempio di implementazione in C è questo:

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)); 
} 

non dovrebbe essere troppo difficile da porta a Python.

EDIT:

In alternativa, come suggerito in this answer, è possibile utilizzare HLS and HSV. Il modulo colorsys sembra avere funzioni per effettuare la conversione da RGB. La sua documentazione collega anche a queste pagine, che vale la pena leggere per capire perché RGB distanza euclidea non funziona davvero:

EDIT 2:

Secondo this answer, questa libreria dovrebbe essere utile: http://code.google.com/p/python-colormath/

+0

Vedere la mia risposta per una versione ottimizzata di 'Python'. – Developer

0

Semplice:

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

Supponendo che rtol, Gtol, e btol sono le tolleranze per R, G e B, rispettivamente, perché non fare:

if abs(px[0]- r) <= rtol and \ 
    abs(px[1]- g) <= gtol and \ 
    abs(px[2]- b) <= btol: 
    return x, y 
1

Invece di questo:

if px[0] == r and px[1] == g and px[2] == b: 

Prova questo:

if max(map(lambda a,b: abs(a-b), px, (r,g,b))) < tolerance: 

Dove tolerance è la differenza massima che sei disposto ad accettare in uno qualsiasi dei canali di colore.

Quello che fa è sottrarre ogni canale dai valori di destinazione, prendere i valori assoluti, quindi il massimo di quelli.

+0

'operatore di importazione'. –

+0

@SLACKY, prima devi "importare operatore". (Ciò detto, questa è ancora una formula per la distanza euclidea: questo non ti darà i risultati che ti aspetti visivamente). – Bruno

+0

@Bruno: la mia metrica è anche peggiore della distanza euclidea! Non ho messo l'accento su quella parte (ma ho messo in risalto la tua risposta per farlo). Se la tolleranza è piccola potrebbe non avere importanza, ma se la tolleranza è grande probabilmente sarà importante. –

2

Ecco un ottimizzata versione Python adattato da asnwer Bruno s':

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 

utilizzo:

>>> import numpy 
>>> rgb1 = numpy.array([1,1,0]) 
>>> rgb2 = numpy.array([0,0,0]) 
>>> ColorDistance(rgb1,rgb2) 
2.5495097567963922 
+0

Da quello che ho visto, 'x ** 0.5' è molto più lento di' from math import sqrt', quindi usa 'sqrt (x)'. Ma se tu "importi matematica" e usi "math.sqrt (x)" vedrai poca o nessuna differenza. – JHolta

+0

'(35,255,24)' vs '(38,38,120)' restituisce 'nan' –

0

da 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) 

basta un po' correggi e sei pronto per partire.