2009-12-18 20 views
35

Tratando de resolver el problema de evitar la carga de imágenes duplicadas.Comparar dos imágenes, el modo python/linux

Tengo dos archivos JPG. Mirándolos, puedo ver que son, de hecho, idénticos. Pero, por algún motivo, tienen un tamaño de archivo diferente (uno es extraído de una copia de seguridad, el otro es otra carga) y tienen una suma de comprobación md5 diferente.

¿Cómo puedo comparar eficientemente y con confianza dos imágenes en el mismo sentido en que un ser humano podría ver que son claramente idénticas?

Ejemplo: http://static.peterbe.com/a.jpg y http://static.peterbe.com/b.jpg

actualización

escribí este guión:

import math, operator 
from PIL import Image 
def compare(file1, file2): 
    image1 = Image.open(file1) 
    image2 = Image.open(file2) 
    h1 = image1.histogram() 
    h2 = image2.histogram() 
    rms = math.sqrt(reduce(operator.add, 
          map(lambda a,b: (a-b)**2, h1, h2))/len(h1)) 
    return rms 

if __name__=='__main__': 
    import sys 
    file1, file2 = sys.argv[1:] 
    print compare(file1, file2) 

Entonces descargado las dos imágenes visualmente idénticos y ejecutar el script. Salida:

58.9830484122 

¿Alguien me puede decir cuál debe ser el corte adecuado?

Actualización II

La diferencia entre a.jpg b.jpg y es que el segundo ha sido guardado con PIL:

b=Image.open('a.jpg') 
b.save(open('b.jpg','wb')) 

Al parecer, esto se aplica algunas modificaciones de calidad muy muy ligeros. ¡Ahora resolví mi problema aplicando el mismo salvado de PIL al archivo que estaba subiendo sin hacer nada con él y ahora funciona!

+0

Por lo que puede que ninguno de ellos tenga los datos EXIF. –

+2

'reduce (operator.add (...))' -> 'suma (...)'. – jfs

+0

Por lo que vale (principalmente en referencias a información de fondo) esto es como una versión simplificada de esta pregunta: http://stackoverflow.com/questions/1819124/image-comparison-algorithm –

Respuesta

25

Hay un proyecto de OSS que utiliza WebDriver para tomar capturas de pantalla y luego compara las imágenes para ver si hay algún problema (http://code.google.com/p/fighting-layout-bugs/)). Lo hace abriendo el archivo en una secuencia y luego comparando cada bit.

Puede hacer algo similar con PIL.

EDIT:

Después de más investigación he encontrado

h1 = Image.open("image1").histogram() 
h2 = Image.open("image2").histogram() 

rms = math.sqrt(reduce(operator.add, 
    map(lambda a,b: (a-b)**2, h1, h2))/len(h1)) 

en http://snipplr.com/view/757/compare-two-pil-images-in-python/ y http://effbot.org/zone/pil-comparing-images.htm

+0

Apliqué esa técnica, pero para los dos imágenes a las que he vinculado anteriormente, el RMS es 58.9. Si comparo a.jpg con a.jpg obtengo 0.0 que se espera. Cambié uno en gimp dibujando algo de mierda en él y el RMS llegó a 675.6 –

+9

Este código encuentra el error RMS entre los histogramas de las imágenes, ** NO ** entre las imágenes mismas. Considere que dos imágenes pueden tener histogramas idénticos pero ser completamente diferentes, por ejemplo, si "codificó" los píxeles. – musicinmybrain

+6

Cabe señalar que si reemplaza el mapa + reduce la llamada con la comprensión de la lista equivalente, obtendrá un aumento de velocidad muy agradable (aproximadamente un 80% más rápido en mi Mac). Use el siguiente código (disculpas por el pobre formato): 'diff_squares = [(h1 [i] - h2 [i]) ** 2 para i en xrange (len (h1))]; rms = math.sqrt (suma (diff_squares)/len (h1)); '. Esto está en Python 2.7.3 y he replicado los resultados en OS X y Linux. YMMV, pero la diferencia es lo suficientemente grande como para justificar una investigación. – CadentOrange

11

Creo que se debe decodificar las imágenes y hacer un píxel por píxel de comparación para ver si están razonablemente similar.

Con PIL y Numpy puede hacerlo con bastante facilidad:

import Image 
import numpy 
import sys 

def main(): 
    img1 = Image.open(sys.argv[1]) 
    img2 = Image.open(sys.argv[2]) 

    if img1.size != img2.size or img1.getbands() != img2.getbands(): 
     return -1 

    s = 0 
    for band_index, band in enumerate(img1.getbands()): 
     m1 = numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size) 
     m2 = numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size) 
     s += numpy.sum(numpy.abs(m1-m2)) 
    print s 

if __name__ == "__main__": 
    sys.exit(main()) 

Esto le dará un valor numérico que debe ser muy cercano a 0 si las imágenes son exactamente lo mismo.

Tenga en cuenta que las imágenes que se desplazan/giran se informarán como muy diferentes, ya que los píxeles no coincidirán uno por uno.

+0

Los histogramas de color normalizados (en la pregunta del OP) no se ven afectados por los cambios/rotaciones. Puede reducir la sensibilidad de su algoritmo a rotaciones/cambios pequeños mediante disminución de resolución antes de la modificación, p. Ej. suma 10x10 cuadros de píxeles y difiere esos 100x menos "píxeles". – hobs

1

Puede compararlo usando PIL (iterar a través de píxeles/segmentos de la imagen y comparar) o si está buscando una comparación de copia idéntica completa, intente comparar el hash MD5 de ambos archivos.

2

En primer lugar, debo señalar que son no idénticos; b ha sido recomprimido y perdió calidad. Puedes ver esto si miras con cuidado un buen monitor.

Para determinar que son subjetivamente "iguales", tendría que hacer algo como lo que fortran sugirió, aunque deberá establecer arbitrariamente un umbral para "uniformidad". Para hacer s independiente del tamaño de la imagen, y para manejar canales un poco más sensiblemente, consideraría hacer la distancia Euclidiana RMS (raíz cuadrada media) en el espacio de color entre los píxeles de las dos imágenes. No tengo tiempo para escribir el código en este momento, pero básicamente para cada píxel, a calcular

(R_2 - R_1) ** 2 + (G_2 - G_1) ** 2 + (B_2 - B_1) ** 2 

, añadiendo en un

(A_2 - A_1) ** 2

plazo si la imagen tiene un canal alfa, etc. El resultado es el cuadrado de la distancia del espacio de color entre las dos imágenes. Encuentre la media (promedio) en todos los píxeles, luego tome la raíz cuadrada del escalar resultante. Luego, decida un umbral razonable para este valor.

O bien, puede decidir que las copias de la misma imagen original con diferente compresión con pérdida no son realmente "lo mismo" y seguir con el hash del archivo.

4

el problema de saber qué hace que algunas características de la imagen sean más importantes que otras es un programa científico completo. Yo sugeriría algunas alternativas dependiendo de la solución que desea:

  • si su problema es ver si hay un volteo de bits en sus archivos JPEG, y luego tratar de la imagen de la imagen de diferencia (tal vez había una edición menor a nivel local?),

  • para ver si las imágenes son globalmente la misma, utilizar la distancia Kullback Leibler a comparar sus histogramas,

  • para ver si tiene algún cambio qualittative, antes de aplicar otras respuestas, filtrar su imagen usando las siguientes funciones para aumentar la importancia de las frecuencias de alto nivel:

código:

def FTfilter(image,FTfilter): 
    from scipy.fftpack import fft2, fftshift, ifft2, ifftshift 
    from scipy import real 
    FTimage = fftshift(fft2(image)) * FTfilter 
    return real(ifft2(ifftshift(FTimage))) 
    #return real(ifft2(fft2(image)* FTfilter)) 


#### whitening 
def olshausen_whitening_filt(size, f_0 = .78, alpha = 4., N = 0.01): 
    """ 
    Returns the whitening filter used by (Olshausen, 98) 

    f_0 = 200/512 

    /!\ you will have some problems at dewhitening without a low-pass 

    """ 
    from scipy import mgrid, absolute 
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]] 
    rho = numpy.sqrt(fx**2+fy**2) 
    K_ols = (N**2 + rho**2)**.5 * low_pass(size, f_0 = f_0, alpha = alpha) 
    K_ols /= numpy.max(K_ols) 

    return K_ols 

def low_pass(size, f_0, alpha): 
    """ 
    Returns the low_pass filter used by (Olshausen, 98) 

    parameters from Atick (p.240) 
    f_0 = 22 c/deg in primates: the full image is approx 45 deg 
    alpha makes the aspect change (1=diamond on the vert and hor, 2 = anisotropic) 

    """ 

    from scipy import mgrid, absolute 
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]] 
    rho = numpy.sqrt(fx**2+fy**2) 
    low_pass = numpy.exp(-(rho/f_0)**alpha) 

    return low_pass 

(copia descarada de http://www.incm.cnrs-mrs.fr/LaurentPerrinet/Publications/Perrinet08spie)

16

De here

La forma más rápida para determinar si dos imágenes tienen exactamente el mismo contenido es conseguir que la diferencia entre las dos imágenes, y luego calcular el cuadro delimitador de las regiones distintas de cero en esta imagen. Si las imágenes son idénticas, todos los píxeles en la imagen de diferencia son cero, y la función de cuadro delimitador devuelve None.

import ImageChops 

def equal(im1, im2): 
    return ImageChops.difference(im1, im2).getbbox() is None 
10

El uso de ImageMagick, sólo tiene que utilizar en su concha [o llame a través de la biblioteca del sistema operativo desde un programa]

compare image1 image2 output 

Esto creará una imagen de salida con las marcadas diferencias

compare -metric AE -fuzz 5% image1 image2 output 

Le dará un factor de borrosidad del 5% para ignorar las diferencias menores de píxeles. Se puede obtener más información de here

+3

¿Hay alguna forma de que pueda determinar mediante programación si el 'output' resultante está en blanco? – Flimm

+0

Puede usar 'NULL:' como 'salida'. Luego imprimirá el valor en stdout. – aspyct

2

He probado este y funciona de la mejor manera y ¡extremadamente rápido!

def rmsdiff_1997(im1, im2): 
    "Calculate the root-mean-square difference between two images" 

    h = ImageChops.difference(im1, im2).histogram() 

    # calculate rms 
    return math.sqrt(reduce(operator.add, 
     map(lambda h, i: h*(i**2), h, range(256)) 
    )/(float(im1.size[0]) * im1.size[1])) 

here link para referencia