2012-01-14 21 views
6

He hecho una función para encontrar un color dentro de una imagen, y devuelvo x, y. Ahora necesito agregar una nueva función, donde puedo encontrar un color con una tolerancia dada. Debería ser fácil?Python - Encuentra colores similares, la mejor manera

código para encontrar el color de la imagen, y x regresar, 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 

Resultado:

Tomado de las respuestas de los métodos normales de comparación de dos colores se encuentran en la distancia euclídea , o la distancia Chebyshev.

Decidí usar sobre todo la distancia euclidiana (cuadrada) y varios espacios de color diferentes. LAB, deltaE (LCH), XYZ, HSL y RGB. En mi código, la mayoría de los espacios de color usan la distancia euclidiana al cuadrado para calcular la diferencia.

Por ejemplo, con LAB, RGB y XYZ un simple euc cuadrado. la distancia hace el truco:

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

LCH, y HSL es un poco más complicado ya que ambos tienen una tonalidad cilíndrica, pero alguna pieza de matemáticas resuelve que, a continuación, se pasa a utilizar EuCl cuadrado. aquí también.

En la mayoría de estos casos he agregado "parámetros separados" para la tolerancia para cada canal (usando 1 tolerancia global, y "modificadores" alternativos HueTol := Tolerance * hueMod o LightTol := Tolerance * LightMod).


parece como espacios de colores creados por encima de XYZ (LAB, LCH) no funcionará mejor en muchas de mis escenarios. Tho HSL produce muy buenos resultados en algunos casos, y es mucho más barato convertir a RGB, RGB también es excelente, y satisface la mayoría de mis necesidades.

+0

Debe devolver algo si usted no encuentra el color en la imagen. es decir, un código de error. –

+0

¿Cómo está definiendo la tolerancia? Rangos separados para 'r',' g' y 'b'? –

+0

Estoy con John: ¿qué has probado ya? Puede ver [similitud coseno] (https://en.wikipedia.org/wiki/Cosine_similarity) y buscar implementaciones de Python. –

Respuesta

17

Calcular las distancias entre los colores RGB, de una manera que sea significativa para el ojo, no es tan fácil simplemente tomando la distancia euclidiana entre los dos vectores RGB.

Hay un artículo interesante sobre esto aquí: http://www.compuphase.com/cmetric.htm

La implementación de ejemplo en C es la siguiente:

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

no debería ser demasiado difícil de puerto para Python.

EDIT:

Como alternativa, como se sugiere en this answer, podría utilizar HLS and HSV. El módulo colorsys parece tener funciones para hacer la conversión de RGB. Su documentación también enlaces a estas páginas, que vale la pena leer para entender por qué RGB euclidiana distancia no funciona muy bien:

EDIT 2:

de acuerdo con this answer, esta biblioteca debe ser útil: http://code.google.com/p/python-colormath/

+0

Consulte mi respuesta para una versión optimizada de 'Python'. – Developer

0

simple:

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

Suponiendo que rtol, GTOL, y btol son las tolerancias para r, g, y b respectivamente, por qué no hacer:

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

lugar de esto:

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

Prueba esto:

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

Donde tolerance es la diferencia máxima que está dispuesto a aceptar en cualquiera de los canales de color.

Lo que hace es restar cada canal de sus valores objetivo, tomar los valores absolutos, y luego el máximo de esos.

+0

'operador de importación'. –

+0

@SLACKY, necesitarías 'importar operador' primero. (Dicho esto, esta sigue siendo una fórmula para la distancia euclidiana: esto no le dará los resultados que espera visualmente). – Bruno

+0

@ Bruno: ¡mi medida es incluso peor que la distancia euclidiana! No puse ningún énfasis en esa parte (pero subí tu respuesta por hacerlo). Si la tolerancia es pequeña, puede que no importe, pero si la tolerancia es grande, probablemente importe. –

2

Aquí es una versión optimizada Python adaptado de Bruno 's 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 

uso:

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

Por lo que he visto, 'x ** 0.5' es mucho más lento que' desde math import sqrt', luego usa 'sqrt (x)'. Pero si 'importas matemáticas' y usas 'math.sqrt (x)', verás poca o ninguna diferencia. – JHolta

+0

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

0

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

sólo se necesita un poco arregla y estás listo para ir.

Cuestiones relacionadas