python - imagenes - compare two images opencv
Algoritmo de comparación de imágenes (8)
Creo 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 diferencia absoluta) con vectores de movimiento haría.
cambiar la imagen de comparación en consecuencia
- calcular la correlación Pearson que estabas tratando de hacer
La medida de cambio no es difícil.
- Tome una región (digamos aproximadamente 32x32) en la imagen de comparación.
- Cambie por x píxeles en horizontal yy píxeles en dirección vertical.
- Calcule la imagen original del SAD (suma de la diferencia absoluta) wrt
- Haga esto para varios valores de xey en un rango pequeño (-10, +10)
- Encuentre el lugar donde la diferencia es mínima
- Elija ese valor como el vector de movimiento de cambio
Nota:
Si el SAD está llegando a ser muy alto para todos los valores de xey, de todos modos se puede suponer que las imágenes son muy diferentes y que la medición de desplazamiento no es necesaria.
Intento comparar imágenes entre sí para descubrir si son diferentes. Primero traté de hacer una correlación de Pearson de los valores RGB, que también funciona bastante bien, a menos que las imágenes cambien un poco. Entonces, si tengo imágenes 100% idénticas pero una se mueve un poco, obtengo un mal valor de correlación.
¿Alguna sugerencia para un mejor algoritmo?
Por cierto, estoy hablando de comparar miles de imgages ...
Editar: Aquí hay un ejemplo de mis imágenes (microscópicas):
im1:
im2:
im3:
im1 e im2 son lo mismo, pero un poco desplazado / cortado, im3 debe reconocerse como completamente diferente ...
Editar: ¡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
En primer lugar, la correlación es una medida de similitud muy intensiva en la CPU bastante inexacta. ¿Por qué no simplemente ir por la suma de los cuadrados si las diferencias entre los 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 sea la mejor coincidencia. Asegúrese de calcular su variable de coincidencia (es decir, la correlación) solo sobre el subconjunto de píxeles que pueden coincidir 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 consulte SIFT este es un método muy potente que (en teoría, de todos modos) puede combinar elementos en imágenes de manera independiente de la traducción, la rotación y la escala.
Hace un año, se formuló una pregunta similar y tiene numerosas respuestas, incluida una sobre la pixelización de las imágenes, que iba a sugerir al menos como un paso de precalificación (ya que excluiría imágenes muy 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 que usa algunas de las ideas de Scipy, utilizando las tres imágenes anteriores (guardadas como im1.jpg, im2.jpg, im3.jpg, respectivamente). El resultado final muestra im1 comparado consigo mismo, como 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 muy por debajo 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. Probé un poco de filtrado previo para evitar perder 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 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 alcance de la matanza que uno podría esperar, por lo que es difícil dar una respuesta específica que tenga eso en cuenta.
He hecho un curso de procesamiento de imágenes hace mucho tiempo, y recuerde que al hacer coincidir normalmente comencé a hacer la escala de grises de la imagen y luego a agudizar los bordes de la imagen para que solo vea los bordes. Usted (el software) puede cambiar y restar las imágenes hasta que la diferencia sea mínima.
Si esa diferencia es mayor que el umbral que establece, las imágenes no son iguales y puede pasar al siguiente. Las imágenes con un umbral menor pueden analizarse a continuación.
Creo que, en el mejor de los casos, puedes reducir radicalmente las posibles coincidencias, pero necesitarás comparar personalmente las posibles coincidencias para determinar que son realmente iguales.
Realmente no puedo mostrar el código como lo estaba hace mucho tiempo, y usé Khoros / Cantata para ese curso.
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é asombroso fue el guión de Snowflake que funcionó para mí 8 años después!
Realmente necesita 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 sé de 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 máximo 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 ver la diferencia entre una buena coincidencia y una mala coincidencia.
Esta no es una respuesta tan buena como quiero, ya que aún no he descubierto cómo normalizarla, pero la actualizaré si la resuelvo, y le dará una idea para que investigue.
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 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 código ... No lo hago Sé si Numpy 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íxel de las dos imágenes. Si la diferencia no es nula, puede desplazar una de las imágenes 1 px en cada dirección y comparar nuevamente, si la diferencia es menor que en el paso anterior, puede repetir el desplazamiento en la dirección del gradiente y restar hasta que la diferencia es menor que un cierto umbral o aumenta de nuevo. Eso debería funcionar si el radio del núcleo borroso 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 exposiciones múltiples o hacer panoramas, como Pano Tools .
Tengo uno hecho esto con una comparación de histograma de imagen. Mi algoritmo básico fue este:
- Divide la imagen en rojo, verde y azul
- Cree histogramas normalizados para los canales rojo, verde y azul y conéctelos en un vector
(r0...rn, g0...gn, b0...bn)
donde n es el número de "cubos", 256 debería ser suficiente - reste este histograma del histograma de otra imagen y calcule 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 histogramas, 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 es la distancia.
Funcionó bastante bien para las fotos pero falló en gráficos como textos y logotipos.