Comparando imagen en url a imagen en sistema de archivos en python

¿Hay una manera rápida y fácil de hacer tal comparación?

He encontrado pocas preguntas de comparación de imágenes de stackoverflow, pero ninguna de las respuestas probadas para esta pregunta.

Tengo archivos de imágenes en mi sistema de archivos y un script que recupera imágenes de urls. Quiero verificar si la imagen en url ya es la misma que está en el disco. Normalmente, cargaría la imagen en el disco y la URL a un objeto PIL y utilizaría la siguiente función que encontré:

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

pero esto no funciona si tiene una imagen guardada en el disco con PIL a medida que se comprime, incluso si im1.save(outfile,quality=100) la calidad a 100 im1.save(outfile,quality=100) .

Mi código está siguiendo actualmente: http://pastebin.com/295kDMsp pero la imagen siempre termina siendo guardada nuevamente.

El título de la pregunta sugiere que tiene dos imágenes exactas para comparar, y eso se hace de manera trivial. Ahora, si tiene imágenes similares para comparar, entonces eso explica por qué no encontró una respuesta completamente satisfactoria: no hay una métrica aplicable a cada problema que dé los resultados esperados (tenga en cuenta que los resultados esperados varían según las aplicaciones). Uno de los problemas es que es difícil, en el sentido de que no existe un acuerdo común, comparar imágenes con múltiples bandas, como las imágenes en color. Para manejar eso, consideraré la aplicación de una métrica dada en cada banda, y el resultado de esa métrica será el valor resultante más bajo. Esto supone que la métrica tiene un rango bien establecido, como [0, 1], y el valor máximo en este rango significa que las imágenes son idénticas (según la métrica dada). Por el contrario, el valor mínimo significa que las imágenes son totalmente diferentes.

Entonces, todo lo que haré aquí es darte dos métricas. Uno de ellos es SSIM y el otro lo llamaré NRMSE (una normalización de la raíz del error cuadrático medio). Elijo presentar el segundo porque es un método muy simple y puede ser suficiente para su problema.

Comencemos con ejemplos. Las imágenes están en este orden: f = imagen original en PNG, g1 = JPEG con una calidad del 50% de f (hecho con convert f -quality 50 g ), g2 = JPEG calidad del 1% de f , h = g2 “iluminada”.

introduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquí

Resultados (redondeados):

  • NRMSE (f, g1) = 0.96
  • NRMSE (f, g2) = 0.88
  • NRMSE (f, h) = 0.63
  • SSIM (f, g1) = 0.98
  • SSIM (f, g2) = 0.81
  • SSIM (f, h) = 0.55

En cierto modo, ambas métricas manejaron bien las modificaciones, pero el SSIM demostró ser más sensible al reportar similitudes más bajas cuando las imágenes eran visualmente distintas, y al reportar un valor más alto cuando las imágenes eran visualmente muy similares. El siguiente ejemplo considera una imagen en color (f = imagen original, y g = JPEG con una calidad del 5%).

introduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquí

  • NRMSE (f, g) = 0.92
  • SSIM (f, g) = 0.61

Por lo tanto, depende de usted determinar cuál es la métrica que prefiere y un valor de umbral para ella.

Ahora, las métricas. Lo que denominé NRMSE es simplemente 1 – [RMSE / ( maxvalminval )]. Donde maxval es la intensidad máxima de las dos imágenes que se comparan, y respectivamente la misma para minval . RMSE viene dado por la raíz cuadrada de MSE: sqrt [(sum (A – B) ** 2) / | A |], donde | A | significa el número de elementos en A. Al hacer esto, el valor máximo dado por RMSE es maxval . Si desea comprender mejor el significado de MSE en imágenes, consulte, por ejemplo, https://ece.uwaterloo.ca/~z70wang/publications/SPM09.pdf . La métrica SSIM (similitud estructural) es más complicada y puede encontrar detalles en el enlace incluido anteriormente. Para aplicar fácilmente las métricas, considere el siguiente código:

 import numpy from scipy.signal import fftconvolve def ssim(im1, im2, window, k=(0.01, 0.03), l=255): """See https://ece.uwaterloo.ca/~z70wang/research/ssim/""" # Check if the window is smaller than the images. for a, b in zip(window.shape, im1.shape): if a > b: return None, None # Values in k must be positive according to the base implementation. for ki in k: if ki < 0: return None, None c1 = (k[0] * l) ** 2 c2 = (k[1] * l) ** 2 window = window/numpy.sum(window) mu1 = fftconvolve(im1, window, mode='valid') mu2 = fftconvolve(im2, window, mode='valid') mu1_sq = mu1 * mu1 mu2_sq = mu2 * mu2 mu1_mu2 = mu1 * mu2 sigma1_sq = fftconvolve(im1 * im1, window, mode='valid') - mu1_sq sigma2_sq = fftconvolve(im2 * im2, window, mode='valid') - mu2_sq sigma12 = fftconvolve(im1 * im2, window, mode='valid') - mu1_mu2 if c1 > 0 and c2 > 0: num = (2 * mu1_mu2 + c1) * (2 * sigma12 + c2) den = (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) ssim_map = num / den else: num1 = 2 * mu1_mu2 + c1 num2 = 2 * sigma12 + c2 den1 = mu1_sq + mu2_sq + c1 den2 = sigma1_sq + sigma2_sq + c2 ssim_map = numpy.ones(numpy.shape(mu1)) index = (den1 * den2) > 0 ssim_map[index] = (num1[index] * num2[index]) / (den1[index] * den2[index]) index = (den1 != 0) & (den2 == 0) ssim_map[index] = num1[index] / den1[index] mssim = ssim_map.mean() return mssim, ssim_map def nrmse(im1, im2): a, b = im1.shape rmse = numpy.sqrt(numpy.sum((im2 - im1) ** 2) / float(a * b)) max_val = max(numpy.max(im1), numpy.max(im2)) min_val = min(numpy.min(im1), numpy.min(im2)) return 1 - (rmse / (max_val - min_val)) if __name__ == "__main__": import sys from scipy.signal import gaussian from PIL import Image img1 = Image.open(sys.argv[1]) img2 = Image.open(sys.argv[2]) if img1.size != img2.size: print "Error: images size differ" raise SystemExit # Create a 2d gaussian for the window parameter win = numpy.array([gaussian(11, 1.5)]) win2d = win * (win.T) num_metrics = 2 sim_index = [2 for _ in xrange(num_metrics)] for band1, band2 in zip(img1.split(), img2.split()): b1 = numpy.asarray(band1, dtype=numpy.double) b2 = numpy.asarray(band2, dtype=numpy.double) # SSIM res, smap = ssim(b1, b2, win2d) m = [res, nrmse(b1, b2)] for i in xrange(num_metrics): sim_index[i] = min(m[i], sim_index[i]) print "Result:", sim_index 

Tenga en cuenta que ssim niega a comparar imágenes cuando la window dada es más grande que ellas. La window suele ser muy pequeña, el valor predeterminado es 11×11, por lo que si las imágenes son más pequeñas que eso, no hay mucha “estructura” (a partir del nombre de la métrica) para comparar y debe usar otra cosa (como la otra función nrmse ) . Probablemente hay una mejor manera de implementar ssim , ya que en Matlab esto se ejecuta mucho más rápido.

Puedes hacer tu propia comparación, usando la diferencia cuadrada. Luego, establecerá un umbral, como el 95% y si son similares, entonces no tiene que descargarlo. Elimina el problema de la compresión.

En la línea de la sugerencia de Bartlomiej Lewandowski , recomendaría comparar la entropía del histogtwig, que es fácil y relativamente rápido de calcular:

 def histogram_entropy(im): """ Calculate the entropy of an images' histogram. Used for "smart cropping" in easy-thumbnails; see also https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py """ if not isinstance(im, Image.Image): return 0 # Fall back to a constant entropy. histogram = im.histogram() hist_ceil = float(sum(histogram)) histonorm = [histocol / hist_ceil for histocol in histogram] 

… Esta función es una que uso en un filtro de recorte cuadrado automático que construí, pero puede usar el valor de entropía para comparar dos imágenes (incluso de tamaño diferente).

Tengo otros ejemplos de la aplicación de este tipo de idea, avíseme con un comentario si desea que le envíe un ejemplo específico a su manera.