Idea general
Opción 1: Cargue ambas imágenes como matrices ( scipy.misc.imread
) y calcule una diferencia de elemento (píxel por píxel). Calcule la norma de la diferencia.
Opción 2: cargar ambas imágenes. Calcule algún vector de características para cada uno de ellos (como un histograma). Calcule la distancia entre vectores de características en lugar de imágenes.
Sin embargo, hay algunas decisiones que tomar primero.
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 su 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 lo mismo.
¿Las imágenes están bien alineadas?
Si no, es posible que desee ejecutar primero la correlación cruzada, 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? (¿La luminosidad / contraste es lo mismo?)
Si no, 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 Sobel o Prewitt, aplicar algún umbral), luego comparar los bordes en la primera imagen con los bordes en 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. Desenfocar 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 que no son iguales a cero) para medir cuánto ha cambiado la imagen. El primero le dirá cuánto está apagada la imagen, el segundo le dirá cuántos píxeles difieren.
Ejemplo
Supongo que sus imágenes están bien alineadas, tienen el 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 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 la matriz) para obtener intensidad. No es necesario hacerlo para imágenes en escala de grises (por ejemplo .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 elegir normalizar a [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
Ejecuta la main
función:
if __name__ == "__main__":
main()
Ahora puede poner todo esto en un script y ejecutar 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 la 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
PD : guión compare.py completo .
Actualización: técnicas relevantes
Como la pregunta es sobre una secuencia de video, donde es probable que los cuadros sean casi iguales, y se busca algo inusual, me gustaría mencionar algunos enfoques alternativos que pueden ser relevantes:
- sustracción y segmentación de fondo (para detectar objetos en primer plano)
- flujo óptico escaso (para detectar movimiento)
- comparar histogramas u otras estadísticas en lugar de imágenes
Recomiendo echar un vistazo al libro "Learning 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 último da 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 Python.
La versión más simple de la resta de fondo:
- Aprenda el valor promedio μ y la desviación estándar σ para cada píxel del fondo
- compare los valores actuales de píxeles con el rango de (μ-2σ, μ + 2σ) o (μ-σ, μ + σ)
Las versiones más avanzadas tienen en cuenta las series temporales para cada píxel y manejan escenas no estáticas (como mover árboles 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) o a algunos de ellos (flujo óptico disperso). Para estimar el flujo óptico disperso, puede usar el método Lucas-Kanade (también se implementa en OpenCV). Obviamente, si hay mucho flujo (promedio alto sobre los valores máximos del campo de velocidad), entonces algo se mueve en el cuadro y las imágenes posteriores son más diferentes.
La comparación de histogramas puede ayudar a detectar cambios repentinos entre cuadros consecutivos. Este enfoque se utilizó en Courbon et al, 2010 :
Similitud de cuadros consecutivos. Se mide la distancia entre dos cuadros consecutivos. Si es demasiado alto, significa que el segundo cuadro está dañado y, por lo tanto, se elimina la imagen. La distancia Kullback-Leibler , o entropía mutua, en los histogramas de los dos cuadros:
donde p y q son los histogramas de los marcos se utiliza. El umbral se fija en 0.2.