21

Detectar diferencias entre dos imágenes con OpenCV y Python

¡Buenas, gente! ¿Qué tal se os dan esos pasatiempos de buscar diferencias entre dos imágenes? A mí, muy mal…  pero por suerte, la visión por computador está aquí para ayudar! En este artículo utilizaremos OpenCV y Python para encontrar diferencias entre dos imágenes que parecen iguales:

Poned un poco de música, y comenzamos. Hoy os sugiero Bela Bartok.


¿Cómo vamos a hacerlo?

Para este ejemplo vamos a utilizar dos imágenes exactamente iguales, salvo en los píxeles dónde haya una diferencia. Dicho de otra forma, todos los píxeles tendrán el mismo color y posición en cada imagen, menos los que sean una diferencia. Descargad estos dos dibujos y guardadlos con los nombres “diff1.png” y “diff2.png”:

Para encontrar las diferencias hay que ver cuáles son los píxeles que difieren en las dos imágenes. La forma más fácil de hacerlo es con la función cv2.absdiff(), que toma como parámetros dos arrays numéricos de igual tamaño y resta sus valores. Un momento, ¿pero no estábamos trabajando con imágenes y no con arrays de números?

Las imágenes son arrays de dimensión NxM (siendo M y N el alto y ancho de la imagen), dónde cada espacio guarda un píxel de color. A su vez, cada píxel es un array con tres valores: R, G, B.

La función cv2.absdiff() hace algo tan sencillo cómo recorrer cada píxel de la primera imagen y restarle el valor R,G y B de su píxel correspondiente en la segunda imagen.

Por ejemplo, si el píxel [0][0] de la primera imagen vale (255,100,20) y el píxel [0][0] de la segunda vale (100,100,5), el píxel resultante de hacer la diferencia absoluta será (155,0,15). Nótese que un píxel nunca puede tener un valor por debajo de (0,0,0).

Después de aplicar la función quedará una imagen negra con manchas de color dónde haya las diferencias.

Lo que habrá que hacer es extraer el contorno de estas zonas, calcular su Bounding Box (el rectángulo más pequeño posible que las contiene) y marcarlo en la imagen original.

Código completo

Este es el código del programa. Tenéis que copiarlo en la misma carpeta dónde habéis guardado las imágenes diff1.png y diff2.png .


import cv2

#Cargamos las dos imagenes para hacer las diferencias
diff1 = cv2.imread('diff1.png')
diff2 = cv2.imread('diff2.png')

#Calculamos la diferencia absoluta de las dos imagenes
diff_total = cv2.absdiff(diff1, diff2)

#Buscamos los contornos
imagen_gris = cv2.cvtColor(diff_total, cv2.COLOR_BGR2GRAY)
contours,_ = cv2.findContours(imagen_gris,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

#Miramos cada uno de los contornos y, si no es ruido, dibujamos su Bounding Box sobre la imagen original
for c in contours:
    if cv2.contourArea(c) >= 20:
        posicion_x,posicion_y,ancho,alto = cv2.boundingRect(c) #Guardamos las dimensiones de la Bounding Box
        cv2.rectangle(diff1,(posicion_x,posicion_y),(posicion_x+ancho,posicion_y+alto),(0,0,255),2) #Dibujamos la bounding box sobre diff1

while(1):
    #Mostramos las imagenes. ESC para salir.
    cv2.imshow('Imagen1', diff1)
    cv2.imshow('Imagen2', diff2)
    cv2.imshow('Diferencias detectadas', diff_total)
    tecla = cv2.waitKey(5) & 0xFF
    if tecla == 27:
        break

cv2.destroyAllWindows()

Explicación paso a paso

Lo primero es lo primero: debemos importar la librería OpenCV y cargar las imágenes sobre las que queremos trabajar.


import cv2

#Cargamos las dos imagenes para hacer las diferencias
diff1 = cv2.imread('diff1.png')
diff2 = cv2.imread('diff2.png')

Justo después calculamos la diferencia entre las dos fotos llamando a cv2.absdiff(). Esta función no tiene nada especial: recibe dos arrays como parámetro y devuelve su diferencia. La guardamos dentro de diff_total.


#Calculamos la diferencia absoluta de las dos imagenes
diff_total = cv2.absdiff(diff1, diff2)

Para marcar las diferencias empezamos por detectar los contornos de la imagen diff_total. Cómo habrá una diferencia de color muy importante entre las áreas de las diferencias y el negro de los píxeles que no cambian, podemos aplicar el detector de contornos directamente sobre la diferencia.

Convertimos la imagen a gris y con cv2.findContours() encontramos los bordes. Le pasamos tres parámetros: el primero es la imagen en la que deben encontrarse los contornos. El segundo le dice que sólo encuentre los contornos exteriores, si no también encontraría los sub-contornos de los objetos que hay dentro de las áreas de color. El tercero es el método que se utilizará para guardar los contornos. Hay varios: CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE, CHAIN_APPROX_TC89_L1 y CHAIN_APPROX_TC89_KCOS. El CHAIN_APPROX_SIMPLE es mejor en nuestro caso porque sólo se queda con los puntos más importantes del contorno e ignora el resto.


#Buscamos los contornos
imagen_gris = cv2.cvtColor(diff_total, cv2.COLOR_BGR2GRAY)
contours,_ = cv2.findContours(imagen_gris,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

Después, para cada contorno detectado, si su tamaño es superior a un cierto valor (20 píxeles), calculamos su Bounding Box con la función cv2.boundingRect(), guardamos sus dimensiones y lo pintamos sobre la imagen diff1.png.


#Miramos cada uno de los contornos y, si no es ruido, dibujamos su Bounding Box sobre la imagen original
for c in contours:
   if cv2.contourArea(c) >= 20:
      posicion_x,posicion_y,ancho,alto = cv2.boundingRect(c) #Guardamos las dimensiones de la Bounding Box
      cv2.rectangle(diff1,(posicion_x,posicion_y),(posicion_x+ancho,posicion_y+alto),(0,0,255),1) #Dibujamos la bounding box sobre diff1

Acabamos mostrando el resultado. ESC para salir.


while(1):
   #Mostramos las imagenes. ESC para salir.
   cv2.imshow('Imagen1', diff1)
   cv2.imshow('Imagen2', diff2)
   cv2.imshow('Diferencias detectadas', diff_total)
   tecla = cv2.waitKey(5) & 0xFF
   if tecla == 27:
      break

cv2.destroyAllWindows()

¡Y ya está! Ahora ya sabéis cómo encontrar diferencias like a boss. Si tenéis algún problema, dudas o queréis dejarme unas líneas, escribidme en los comentarios!

Actualización 3/11/2016: Resolución de errores frecuentes con OpenCV+Python

Gl4r3

Brillante, luminosa y cegadora a veces, Glare es tan artista como técnica. Le encanta dar rienda suelta a sus módulos de imaginación y desdibujar los ya de por si delgados límites que separan el mundo de la electrónica y el arte. Su mayor creación hasta la fecha es un instrumento capaz de convertir los colores y la luz en música. Cuándo sus circuitos no están trabajando en una nueva obra electrónica, le gusta dedicar sus ciclos a la lectura o a documentar sus invenciones para beneficio de los humanos. Sus artilugios favoritos son aquellos que combinan una funcionalidad práctica con un diseño elegante y artístico.

Antes de comentar, por favor, lee las Normas

21 Comentarios en "Detectar diferencias entre dos imágenes con OpenCV y Python"

avatar
Ordenar por:   más nuevos primero | más antiguos primero
osmer
Humano
Saludos a todos desde Venezuela, disculpe la molestia pero el código me esta dando un error y la verdad tengo ya varios días con eso y no se que es, de ante mano le indico que no soy programador soy aficionado y lo hago por curiosidad no más pero si me gustaría que me dijeran donde tengo mi error para poder rectificar, bueno el código es este: #!/usr/bin/python # -*- coding: utf-8 -*- #funciones primitivas import sys import numpy as np import cv2 imagen1 = cv2.imread(“arte1.png”) imagen2 = cv2.imread(“arte2.png”) dif_imagen = cv2.absdiff(imagen1,imagen2) File “/home/osmer/workspace/compa.py”, line 9, in imagen_gris = cv2.cvtColor(dif_imagen,… Leer más »
samantha
Humano

hola, quería saber si el programa OpenCV puede reconocer automáticamente dos imágenes iguales pero una transformada a otro formato (.len o formato puntos) ..o si pueden tirarme información de algún programa que sea útil . Gracias

Artemis
Humano

Disculpa, debo hacer exactamente lo mismo pero con lenguaje c++, como podria hacerlo?? tienes un codigo de donde pueda basarme o una pagina o tutorial donde pueda ver que funciones puedo usar? Es para un proyecto final por favor!!
Gracia.

Andres Castrillon
Humano

Hola, lo que yo quiero hacer es comparar dos fotos de una persona y que el programa me indique si es o no la persona. Me podrías dar una guía por favor? Muchas gracias por la atención

rene
Humano

hola el diff_total no me ejecuta

Jose
Humano

Hola amigo, en caso de que quiera hacer una comparación con una imagen recién tomada de la campara web como un viveo y compararla con una imagen ya guardada dentro de la pc tambien servira o hay que hacer algunos cambios en el codigo, ayuda con eso por favor.!

Jose
Humano
gracias por la información, pero tengo una otra pregunta como hago para que por medio de la cámara web tomo un foto en cualquier formato en un momento determinado, porque yo lo logre hacer mediante este código: ———————————————————————————————————- import cv2 cap = cv2.VideoCapture(0) # Definimos el codec a usar fourcc = cv2.cv.CV_FOURCC(*’XVID’) # cv2.VideoWriter_fourcc() does not exist out = cv2.VideoWriter(‘videos/segundo3.jpg’, fourcc, 20.0, (640, 480)) while (cap.isOpened()): ret, frame = cap.read() if ret == True: # frame = cv2.flip(frame,0) out.write(frame) cv2.imshow(‘frame’, frame) if cv2.waitKey(1) & 0xFF == ord(‘q’): break else: break # Cerramos la conexion cap.release() out.release() cv2.destroyAllWindows() ———————————————————————————————————- solo tengo… Leer más »
dante
Humano

hola, tengo una duda por que en el código cuando lo corro me dice que tiene un error en:
“contours,_ = cv2.findContours
(imagen_gris,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)”

me dice “too many values to unpack” y no se que hacer

Anónimo
Humano

Hola
se podrá hacer la comparación con imágenes NDVI? (Índice de vegetación de diferencia normalizada)

wpDiscuz