Algoritmo de comparación de imágenes


Estoy tratando de comparar imágenes para averiguar si son diferentes. Primero traté de hacer una correleation Pearson de los valores RGB, que funciona también bastante bien a menos que las imágenes son un poco desplazado. Así que si una tiene un 100% imágenes idénticas, pero uno es un poco movido, me sale un mal valor de correlación.

¿Alguna sugerencia para un algoritmo mejor?

Por cierto, estoy hablando de comparar miles de imágenes...

Editar: Aquí hay un ejemplo de mis fotos (microscópico):

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 los mismos pero un poco desplazados/cortados, im3 debe ser reconocido como completamente diferente...

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

Author: snowflake, 2009-11-30

8 answers

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

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

Aquí hay una implementación usando algunas de las ideas con Scipy, usando las tres imágenes anteriores (guardadas como im1.jpg, im2.jpg, im3.jpg, respectivamente). El resultado final muestra im1 comparado consigo mismo, como una línea de base, y luego cada imagen comparada 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)

Así que tenga en cuenta que im1 en comparación con sí mismo da una puntuación de 42105, im2 en comparación con im1 no está lejos de eso, pero im3 en comparación 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 y cómo podría mejorarlo.

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

 36
Author: Peter Hansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:46:54

Tengo uno hecho esto con una comparación de histograma de imagen. Mi algoritmo básico era este:

  1. Dividir la imagen en rojo, verde y azul
  2. Cree histogramas normalizados para los canales 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 histograma del histograma de otra imagen y calcula la distancia

Aquí hay algún 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 histogramas, se 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 diverjan, mayor será la distancia.

Funcionó bastante bien para las fotos para mí, pero falló en gráficos como textos y logotipos.

 13
Author: Otto Allmendinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-14 12:35:37

Realmente necesitas especificar mejor la pregunta, pero, mirando 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 normalizada de correlación cruzada en Python, pero hay una función similar fftconvolve() y puedes 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 ni normalizada en absoluto.

La ubicación del valor de pico de la salida indica el desplazamiento entre las dos imágenes, y la magnitud del pico indica la similitud. Debe haber una manera de pesarlo / normalizarlo para que pueda notar la diferencia entre un buen partido y un mal partido.

Esta no es una respuesta tan buena como quiero, ya que no descubrí cómo normalizarlo todavía, pero lo actualizaré si lo averiguo, y te dará una idea para investigar.

 6
Author: endolith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-04 19:05:27

Si su problema es acerca de píxeles desplazados, tal vez debería comparar con una transformada de frecuencia.

El FFT debería estar bien (numpy tiene una implementación para matrices 2D), pero siempre escucho que las Wavelets 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 buen impulso de rendimiento reutilizando el mismo codificar... No se si numpy se basa en FFTW, pero si no es tal vez usted podría tratar de investigar un poco allí.

Aquí tienes un prototipo... puedes jugar un poco con él para ver qué umbral encaja con 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 no es nil, entonces puede cambiar una de las imágenes 1 px en cada dirección y comparar de nuevo, si la diferencia es más bajo que en el paso anterior, puede repetir el cambio en la dirección del gradiente y restar hasta que la diferencia sea menor que un cierto umbral o aumente de nuevo. Esto debería funcionar si el radio del núcleo difuminado es mayor que el desplazamiento de las imágenes.

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

 6
Author: fortran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-18 11:39:31

He hecho algún curso de procesamiento de imágenes hace mucho tiempo, y recuerdo que al hacer coincidir normalmente comencé con hacer la escala de grises de la imagen, y luego afilar los bordes de la imagen para que solo vea los bordes. Usted (el software) puede entonces cambiar y restar las imágenes hasta que la diferencia sea mínima.

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

Lo hago piense que, en el mejor de los casos, puede reducir radicalmente las posibles coincidencias, pero tendrá que comparar personalmente las posibles coincidencias para determinar que son realmente iguales.

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

 2
Author: extraneon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-30 15:12:42

En primer lugar, la correlación es una medida muy intensiva de CPU bastante inexacta para la similitud. ¿Por qué no simplemente ir por la suma de los cuadrados si las diferencias entre píxeles individuales?

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

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

 1
Author: jilles de wit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-30 12:27:13

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 (suma de la diferencia absoluta) con vectores de movimiento haría a.

  • Cambie la imagen de comparación en consecuencia

  • calcula la correlación de pearson que estabas tratando de hacer

La medición de turnos no es difícil.

  • Tome una región (digamos alrededor de 32x32) en la imagen de comparación.
  • Desplázalo por x píxeles en horizontal y y píxeles en dirección vertical.
  • Calcular el SAD (suma de la diferencia absoluta) w.r.t. imagen original
  • 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 desplazamiento

Nota:

Si el SAD viene muy alto para todos los valores de x e y, entonces puede asumir de todos modos que las imágenes son altamente no es necesaria una medición diferente y por turnos.

 0
Author: Shailesh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-30 11:21:26

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

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é impresionante el guion de ese Copo de nieve funcionó para mí 8 años después!

 0
Author: Doug Null,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-01 22:38:50