Solución:
Idea general
Opción 1: cargue ambas imágenes como matrices (scipy.misc.imread
) y calcule una diferencia por elemento (píxel por píxel). Calcula la norma de la diferencia.
Opción 2: Cargue ambas imágenes. Calcule algún vector de características para cada uno de ellos (como un histograma). Calcule la distancia entre los vectores de características en lugar de las imágenes.
Sin embargo, primero hay que tomar algunas decisiones.
Preguntas
Primero debe responder estas preguntas:
-
¿Son las imágenes de la misma forma y dimensión?
De lo contrario, es posible que deba cambiar el tamaño o recortarlos. La biblioteca PIL ayudará a hacerlo en Python.
Si se toman con la misma configuración y el mismo dispositivo, probablemente sean los mismos.
-
¿Están bien alineadas las imágenes?
Si no es así, es posible que desee ejecutar la correlación cruzada primero, para encontrar primero la mejor alineación. SciPy tiene funciones para hacerlo.
Si la cámara y la escena están quietas, es probable que las imágenes estén bien alineadas.
-
¿La exposición de las imágenes es siempre la misma? (¿Es la luminosidad / el contraste lo mismo?)
Si no es así, es posible que desee normalizar las imágenes.
Pero tenga cuidado, en algunas situaciones esto puede hacer más mal que bien. Por ejemplo, un solo píxel brillante sobre un fondo oscuro hará que la imagen normalizada sea muy diferente.
-
¿Es importante la información de color?
Si desea notar cambios de color, tendrá un vector de valores de color por punto, en lugar de un valor escalar como en la imagen en escala de grises. Necesita más atención al escribir dicho código.
-
¿Hay bordes distintos en la imagen? ¿Es probable que se muevan?
En caso afirmativo, puede aplicar primero el algoritmo de detección de bordes (por ejemplo, calcular el gradiente con la transformación de Sobel o Prewitt, aplicar algún umbral) y luego comparar los bordes de la primera imagen con los bordes de la segunda.
-
¿Hay ruido en la imagen?
Todos los sensores contaminan la imagen con cierta cantidad de ruido. Los sensores de bajo costo tienen más ruido. Es posible que desee aplicar algo de reducción de ruido antes de comparar imágenes. El desenfoque es el enfoque más simple (pero no el mejor) aquí.
-
¿Qué tipo de cambios quieres notar?
Esto puede afectar la elección de la norma a utilizar para la diferencia entre imágenes.
Considere usar la norma de Manhattan (la suma de los valores absolutos) o la norma cero (el número de elementos no es igual a cero) para medir cuánto ha cambiado la imagen. El primero le dirá cuánto está apagada la imagen, el segundo solo le dirá cuántos píxeles difieren.
Ejemplo
Supongo que sus imágenes están bien alineadas, del mismo tamaño y forma, posiblemente con diferente exposición. Para simplificar, los convierto a escala de grises incluso si son imágenes en color (RGB).
Necesitará estas importaciones:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Función principal, leer dos imágenes, convertir a escala de grises, comparar e imprimir resultados:
def main():
file1, file2 = sys.argv[1:1+2]
# read images as 2D arrays (convert to grayscale for simplicity)
img1 = to_grayscale(imread(file1).astype(float))
img2 = to_grayscale(imread(file2).astype(float))
# compare
n_m, n_0 = compare_images(img1, img2)
print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size
Cómo comparar. img1
y img2
son las matrices 2D SciPy aquí:
def compare_images(img1, img2):
# normalize to compensate for exposure difference, this may be unnecessary
# consider disabling it
img1 = normalize(img1)
img2 = normalize(img2)
# calculate the difference and its norms
diff = img1 - img2 # elementwise for scipy arrays
m_norm = sum(abs(diff)) # Manhattan norm
z_norm = norm(diff.ravel(), 0) # Zero norm
return (m_norm, z_norm)
Si el archivo es una imagen en color, imread
devuelve una matriz 3D, canales RGB promedio (el último eje de matriz) para obtener la intensidad. No es necesario hacerlo para imágenes en escala de grises (p. Ej. .pgm
):
def to_grayscale(arr):
"If arr is a color image (3D array), convert it to grayscale (2D array)."
if len(arr.shape) == 3:
return average(arr, -1) # average over the last axis (color channels)
else:
return arr
La normalización es trivial, puede optar por normalizar [0,1] en lugar de [0,255]. arr
es una matriz SciPy aquí, por lo que todas las operaciones son por elementos:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Ejecutar el main
función:
if __name__ == "__main__":
main()
Ahora puede poner todo esto en un script y ejecutarlo con dos imágenes. Si comparamos la imagen consigo misma, no hay diferencia:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
Si desenfocamos la imagen y la comparamos con el original, hay alguna diferencia:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
PS Todo el script compare.py.
Actualización: técnicas relevantes
Como la pregunta es sobre una secuencia de video, donde es probable que los fotogramas sean casi iguales y usted busca algo inusual, me gustaría mencionar algunos enfoques alternativos que pueden ser relevantes:
- Resta y segmentación de fondo (para detectar objetos en primer plano)
- flujo óptico escaso (para detectar movimiento)
- comparar histogramas o algunas otras estadísticas en lugar de imágenes
Recomiendo encarecidamente echar un vistazo al libro “Aprendiendo OpenCV”, Capítulos 9 (Partes de imagen y segmentación) y 10 (Seguimiento y movimiento). El primero enseña a usar el método de sustracción de fondo, el segundo brinda información sobre los métodos de flujo óptico. Todos los métodos se implementan en la biblioteca OpenCV. Si usa Python, sugiero usar OpenCV ≥ 2.3, y su cv2
Módulo de Python.
La versión más simple de la resta de fondo:
- aprender el valor promedio μ y la desviación estándar σ para cada píxel del fondo
- comparar los valores de píxeles actuales con el rango de (μ-2σ, μ + 2σ) o (μ-σ, μ + σ)
Las versiones más avanzadas toman en cuenta series de tiempo para cada píxel y manejan escenas no estáticas (como árboles en movimiento o césped).
La idea del flujo óptico es tomar dos o más cuadros y asignar un vector de velocidad a cada píxel (flujo óptico denso) oa algunos de ellos (flujo óptico disperso). Para estimar el flujo óptico escaso, puede usar el método Lucas-Kanade (también está implementado en OpenCV). Obviamente, si hay mucho flujo (promedio alto sobre los valores máximos del campo de velocidad), entonces algo se está moviendo en el marco y las imágenes subsiguientes son más diferentes.
La comparación de histogramas puede ayudar a detectar cambios repentinos entre fotogramas consecutivos. Este enfoque se utilizó en Courbon et al, 2010:
Similitud de fotogramas consecutivos. Se mide la distancia entre dos fotogramas consecutivos. Si es demasiado alto, significa que el segundo fotograma está dañado y, por lo tanto, la imagen se elimina. La distancia de Kullback-Leibler, o entropía mutua, en los histogramas de los dos fotogramas:
donde pag y q son los histogramas de los fotogramas. El umbral se fija en 0,2.
Una solución sencilla:
Codifique la imagen como jpeg y busque un cambio sustancial en tamaño del archivo.
Implementé algo similar con miniaturas de video y tuve mucho éxito y escalabilidad.
Puede comparar dos imágenes usando funciones de PIL.
import Image
import ImageChops
im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")
diff = ImageChops.difference(im2, im1)
El objeto diff es una imagen en la que cada píxel es el resultado de la resta de los valores de color de ese píxel en la segunda imagen de la primera imagen. Usando la imagen de diferencia puedes hacer varias cosas. El más simple es el diff.getbbox()
función. Te dirá el rectángulo mínimo que contiene todos los cambios entre tus dos imágenes.
Probablemente pueda implementar aproximaciones de las otras cosas mencionadas aquí usando funciones de PIL también.