Algoritmo de comparación de imágenes

Estoy tratando de comparar imágenes entre sí para averiguar si son diferentes. Primero traté de hacer una correlación de Pearson de los valores RGB, que funciona también bastante bien a menos que las imágenes estén un poco cambiadas. Por lo tanto, si tengo un 100% de imágenes idénticas pero una se mueve un poco, obtengo un valor de correlación incorrecto.

¿Alguna sugerencia para un mejor algoritmo?

Por cierto, me refiero a comparar miles de imágenes …

Edición: Aquí hay un ejemplo de mis imágenes (microscópicas):

im1:

introduzca la descripción de la imagen aquí

im2:

introduzca la descripción de la imagen aquí

im3:

introduzca la descripción de la imagen aquí

im1 e im2 son iguales, pero un poco desplazados / cortados, im3 debe ser reconocido como completamente diferente …

Edit: El problema se resuelve con las sugerencias de Peter Hansen! Funciona muy bien Gracias a todas las respuestas! Algunos resultados se pueden encontrar aquí http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

Related of "Algoritmo de comparación de imágenes"

Hace un año se hizo una pregunta similar y tiene numerosas respuestas, incluida una relacionada con la pixelización de las imágenes, que sugeriría al menos como un paso de precalificación (ya que excluiría muy poco las imágenes que no son similares).

También hay enlaces a preguntas aún anteriores que tienen aún más referencias y buenas respuestas.

Aquí hay una implementación que usa algunas de las ideas con Scipy, usando sus tres imágenes anteriores (guardadas como im1.jpg, im2.jpg, im3.jpg, respectivamente). La salida final muestra im1 en comparación con sí mismo, como una línea de base, y luego cada imagen en comparación con las demás.

>>> import scipy as sp >>> from scipy.misc import imread >>> from scipy.signal.signaltools import correlate2d as c2d >>> >>> def get(i): ... # get JPG image as Scipy array, RGB (3 layer) ... data = imread('im%s.jpg' % i) ... # convert to grey-scale using W3C luminance calc ... data = sp.inner(data, [299, 587, 114]) / 1000.0 ... # normalize per http://en.wikipedia.org/wiki/Cross-correlation ... return (data - data.mean()) / data.std() ... >>> im1 = get(1) >>> im2 = get(2) >>> im3 = get(3) >>> im1.shape (105, 401) >>> im2.shape (109, 373) >>> im3.shape (121, 457) >>> c11 = c2d(im1, im1, mode='same') # baseline >>> c12 = c2d(im1, im2, mode='same') >>> c13 = c2d(im1, im3, mode='same') >>> c23 = c2d(im2, im3, mode='same') >>> c11.max(), c12.max(), c13.max(), c23.max() (42105.00000000259, 39898.103896795357, 16482.883608327804, 15873.465425120798) 

Entonces, tenga en cuenta que im1 comparado con sí mismo da una puntuación de 42105, im2 comparado con im1 no está muy lejos de eso, pero im3 comparado con cualquiera de los otros da mucho menos de la mitad de ese valor. Tendría que experimentar con otras imágenes para ver qué tan bien podría funcionar esto y cómo podría mejorarlo.

El tiempo de ejecución es largo … varios minutos en mi máquina. Intentaría realizar un filtrado previo para evitar perder tiempo comparando imágenes muy diferentes, tal vez con el truco de “comparar el tamaño de archivo jpg” mencionado en las respuestas a la otra pregunta, o con la pixelización. El hecho de que tenga imágenes de diferentes tamaños complica las cosas, pero no proporcionó suficiente información sobre el scope de la masacre que uno podría esperar, por lo que es difícil dar una respuesta específica que tenga eso en cuenta.

He hecho una con una comparación de histogtwig de imagen. Mi algoritmo básico era este:

  1. Dividir imagen en rojo, verde y azul.
  2. Cree histogtwigs normalizados para el canal rojo, verde y azul y concaténelos en un vector (r0...rn, g0...gn, b0...bn) donde n es el número de “cubos”, 256 debería ser suficiente
  3. resta este histogtwig del histogtwig de otra imagen y calcula la distancia

Aquí hay un código con numpy y pil

 r = numpy.asarray(im.convert( "RGB", (1,0,0,0, 1,0,0,0, 1,0,0,0) )) g = numpy.asarray(im.convert( "RGB", (0,1,0,0, 0,1,0,0, 0,1,0,0) )) b = numpy.asarray(im.convert( "RGB", (0,0,1,0, 0,0,1,0, 0,0,1,0) )) hr, h_bins = numpy.histogram(r, bins=256, new=True, normed=True) hg, h_bins = numpy.histogram(g, bins=256, new=True, normed=True) hb, h_bins = numpy.histogram(b, bins=256, new=True, normed=True) hist = numpy.array([hr, hg, hb]).ravel() 

Si tiene dos histogtwigs, puede obtener la distancia de esta manera:

 diff = hist1 - hist2 distance = numpy.sqrt(numpy.dot(diff, diff)) 

Si las dos imágenes son idénticas, la distancia es 0, cuanto más divergen, mayor será la distancia.

Funcionó bastante bien para las fotos pero fracasó en gráficos como textos y logotipos.

Realmente necesitas especificar mejor la pregunta, pero al mirar esas 5 imágenes, todos los organismos parecen estar orientados de la misma manera. Si este es siempre el caso, puede intentar hacer una correlación cruzada normalizada entre las dos imágenes y tomar el valor máximo como su grado de similitud. No conozco una función de correlación cruzada normalizada en Python, pero hay una función fftconvolve () similar y usted puede hacer la correlación cruzada circular usted mismo:

 a = asarray(Image.open('c603225337.jpg').convert('L')) b = asarray(Image.open('9b78f22f42.jpg').convert('L')) f1 = rfftn(a) f2 = rfftn(b) g = f1 * f2 c = irfftn(g) 

Esto no funcionará como está escrito ya que las imágenes son de diferentes tamaños, y la salida no está ponderada o normalizada en absoluto.

La ubicación del valor pico de la salida indica el desplazamiento entre las dos imágenes, y la magnitud del pico indica la similitud. Debería haber una forma de ponderarlo / normalizarlo para que pueda distinguir la diferencia entre una buena coincidencia y una mala.

Esta no es una respuesta tan buena como la que quiero, ya que aún no he descubierto cómo normalizarla, pero la actualizaré si la descubro y le daré una idea que analizar.

Si su problema se trata de píxeles desplazados, tal vez debería comparar con una transformación de frecuencia.

La FFT debería estar bien ( numpy tiene una implementación para matrices 2D ), pero siempre escucho que las Ondas son mejores para este tipo de tareas ^ _ ^

Sobre el rendimiento, si todas las imágenes son del mismo tamaño, si recuerdo bien, el paquete FFTW creó una función especializada para cada tamaño de entrada FFT, por lo que puede obtener un aumento de rendimiento agradable reutilizando el mismo código … No lo hago No sé si el número está basado en FFTW, pero si no es así, podrías intentar investigar un poco allí.

Aquí tienes un prototipo … puedes jugar un poco con él para ver qué umbral se ajusta a tus imágenes.

 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.fft.fft2(numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size)) m2 = numpy.fft.fft2(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()) 

Otra forma de proceder podría ser difuminar las imágenes y luego restar los valores de píxeles de las dos imágenes. Si la diferencia es no nula, entonces puede cambiar una de las imágenes 1 px en cada dirección y comparar de nuevo, si la diferencia es menor que en el paso anterior, puede repetir los cambios en la dirección del gradiente y restar hasta que la diferencia Es inferior a un cierto umbral o aumenta de nuevo. Eso debería funcionar si el radio del núcleo difuminado es mayor que el desplazamiento de las imágenes.

Además, puede probar algunas de las herramientas que se utilizan comúnmente en el flujo de trabajo de la fotografía para combinar exposiciones múltiples o hacer outlooks, como las herramientas de Pano .

He hecho un curso de procesamiento de imágenes hace mucho tiempo, y recuerde que al hacer coincidir, normalmente comencé a hacer la imagen en escala de grises y luego afilar los bordes de la imagen para que solo vea los bordes. Usted (el software) puede desplazar y restar las imágenes hasta que la diferencia sea mínima.

Si esa diferencia es mayor que el umbral que estableció, las imágenes no son iguales y puede pasar a la siguiente. A continuación, se pueden analizar las imágenes con un umbral más pequeño.

Creo que, en el mejor de los casos, puedes reducir radicalmente las posibles coincidencias, pero necesitaré comparar personalmente las posibles coincidencias para determinar si son realmente iguales.

Realmente no puedo mostrar el código como era hace mucho tiempo, y usé Khoros / Cantata para ese curso.

En primer lugar, la correlación es una medida de similitud bastante intensiva en CPU y no precisa. ¿Por qué no elegir la sum de los cuadrados si hay diferencias entre píxeles individuales?

Una solución simple, si el cambio máximo es limitado: genere todas las imágenes desplazadas posibles y encuentre la que mejor se adapte. Asegúrate de calcular la variable de coincidencia (es decir, la correlación) solo sobre el subconjunto de píxeles que pueden combinarse en todas las imágenes desplazadas. Además, su cambio máximo debe ser significativamente más pequeño que el tamaño de sus imágenes.

Si desea utilizar algunas técnicas más avanzadas de procesamiento de imágenes, le sugiero que vea SIFT, este es un método muy poderoso que (teóricamente de todos modos) puede hacer coincidir los elementos de las imágenes independientemente de la traducción, la rotación y la escala.

Supongo que podrías hacer algo como esto:

  • estimar el desplazamiento vertical / horizontal de la imagen de referencia frente a la imagen de comparación. un simple SAD (sum de diferencia absoluta) con vectores de movimiento sería suficiente para.

  • cambiar la imagen de comparación en consecuencia

  • calcula la correlación de Pearson que intentabas hacer

La medición del cambio no es difícil.

  • Tome una región (por ejemplo, 32×32) en la imagen de comparación.
  • Desplazarlo por x píxeles en horizontal y píxeles en dirección vertical.
  • Calcular la imagen original de SAD (sum de diferencia absoluta)
  • Haga esto para varios valores de x e y en un rango pequeño (-10, +10)
  • Encuentra el lugar donde la diferencia es mínima.
  • Elija ese valor como el vector de movimiento de cambio

Nota:

Si el SAD está llegando muy alto para todos los valores de x e y, de todos modos puede suponer que las imágenes son muy diferentes y que la medición de desplazamiento no es necesaria.

Para que las importaciones funcionen correctamente en mi Ubuntu 16.04 (a partir de abril de 2017), instalé Python 2.7 y estas:

 sudo apt-get install python-dev sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk sudo apt-get install python-scipy sudo pip install pillow 

Luego cambié las importaciones de Snowflake a estas:

 import scipy as sp from scipy.ndimage import imread from scipy.signal.signaltools import correlate2d as c2d 

¡Qué maravilloso fue el guión de Snowflake que me funcionó 8 años después!