? DETECCIÓN DE MOVIMIENTO en CIERTA ÁREA | Python – OpenCV

Por Administrador

Supongamos que nos han encomendado una tarea, el detectar movimiento en cierta área mediante visión artificial. Entonces tenemos un video de entrada, en donde vamos a detectar dicho movimiento, sin embargo el área donde debemos trabajar no es toda la imagen, sino parte de ella. En este tutorial veremos como realizar la detección de movimiento en cierta porción de una imagen con ayuda de la sustracción de fondo que vimos en el post anterior.

Detección de movimiento en cierta área o lugar de la imagen con OpenCV y Python

Ya habíamos visto en acción a la detección de movimiento en un tutorial pasado: ? DETECCIÓN DE MOVIMIENTO (Con sustracción de imágenes) – OpenCV y Python. En aquel tutorial empleamos la sustracción de imágenes, y tratamos de detectar el movimiento. En esta ocasión usaremos Background Subtractor MOG (sustracción de fondo) que es uno de los algoritmos que habíamos visto en el anterior tutorial y este lo aplicaremos solo en cierta área de la imagen. ¡Empecemos!.

Como te decía, supongamos que nos han pedido detectar movimiento en cierto lugar de un edificio. Entonces nos muestran los videos que capta una cámara, en la cual podemos ver el área que necesitamos analizar, pero además otras áreas que no necesitamos, es entonces que debemos planificar que hacer. Además para nuestro ejemplo, el mover la cámara de lugar para que capture solo el área que deseamos no es una posibilidad. Por lo que te planteo una solución a este problema a continuación:

import cv2
import numpy as np

cap = cv2.VideoCapture('aeropuerto.mp4')

fgbg = cv2.bgsegm.createBackgroundSubtractorMOG()
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))

Línea 1 y 2: Importamos OpenCV y numpy.

Línea 4: Especificamos el video de entrada. Por cierto, puedes encontrar el video completo que usé para este tutorial aquí.

Línea 6: Especificamos el algoritmo de  sustracción de fondo que estaremos usando, BackgroundSubtractorMOG.

Línea 7: Usaremos este más adelante para mejorar la imagen binaria luego de aplicar la sustracción de fondo.

while True:

    ret, frame = cap.read()
    if ret == False: break

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Dibujamos un rectángulo en frame, para señalar el estado
    # del área en análisis (movimiento detectado o no detectado)
    cv2.rectangle(frame,(0,0),(frame.shape[1],40),(0,0,0),-1)
    color = (0, 255, 0)
    texto_estado = "Estado: No se ha detectado movimiento"

    # Especificamos los puntos extremos del área a analizar
    area_pts = np.array([[240,320], [480,320], [620,frame.shape[0]], [50,frame.shape[0]]])
    
    # Con ayuda de una imagen auxiliar, determinamos el área
    # sobre la cual actuará el detector de movimiento
    imAux = np.zeros(shape=(frame.shape[:2]), dtype=np.uint8)
    imAux = cv2.drawContours(imAux, [area_pts], -1, (255), -1)
    image_area = cv2.bitwise_and(gray, gray, mask=imAux)

Línea 11 y 12: Se realiza la lectura de los fotogramas.

Línea 14: Transformamos frame de BGR a escala de grises.

Línea 18 a 20: Estas líneas nos servirán para visualizar la información de la detección o no de movimiento en el área.

Línea 23: Especificamos los puntos extremos del área que deseamos analizar. Estos han sido ordenados de forma horaria.

Línea 27 y 28: Creamos una matriz auxiliar de ceros, del mismo ancho y alto que frame. Luego en esta dibujamos el contorno formando por los puntos dados en area_pts.

Figura 1: Visualización de imAux.

Línea 29: Con ayuda de cv2.bitwise_and, obtenemos una imagen en donde el área blanca de la línea 28 se mostrará gray. Creo que esta parte se explica mejor visualizando uno de los fotogramas obtenidos, veamos:

Figura 2: Visualización de image_area.

# Obtendremos la imagen binaria donde la región en blanco representa
# la existencia de movimiento
fgmask = fgbg.apply(image_area)
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
fgmask = cv2.dilate(fgmask, None, iterations=2)

Línea 33: Aplicamos la sustracción de fondo sobre image_area.

Línea 34 y 35: Aplicamos transformaciones morfológicas (opening y dilate) para mejorar la imagen binaria obtenida en la línea 33.

Figura 3: Visualización de fgmask.

# Encontramos los contornos presentes en fgmask, para luego basándonos
# en su área poder determina si existe movimiento
cnts = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
for cnt in cnts:
    if cv2.contourArea(cnt) > 500:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(frame, (x,y), (x+w, y+h),(0,255,0), 2)
        texto_estado = "Estado: Alerta Movimiento Detectado!"
        color = (0, 0, 255)	

Línea 39: Usamos cv2.findContours, para encontrar los contornos presentes en fgmask.

Línea 40 a 45: Analizamos cada contorno encontrado. Cuando un contorno tenga un área mayor a 500, se dibujará un recuadro delimitador que rodeará al área blanca en movimiento. Además, ya que se ha detectado movimiento sobre esa área se visualizará "Estado: Alerta Movimiento Detectado!" en la parte superior de la imagen. Por último se asignará el color rojo a la variable color.

    # Visuzalizamos el alrededor del área que vamos a analizar
    # y el estado de la detección de movimiento		
    cv2.drawContours(frame, [area_pts], -1, color, 2)
    cv2.putText(frame, texto_estado , (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 1, color,2)

    cv2.imshow('fgmask', fgmask)
    cv2.imshow("frame", frame)

    k = cv2.waitKey(70) & 0xFF
    if k == 27:
        break

cap.release()
cv2.destroyAllWindows()

Línea 49 y 50: Visualizamos en frame, el alrededor del área que estamos analizando, además del texto que aparecerá en la sección superior de la imagen, indicando si se ha detectado o no movimiento en el área seleccionada.

Línea 53 a 61: Visualizamos fgmask y frame. El proceso finalizará hasta que se presione ESC o hasta que se acabe el video de entrada. Finalmente se cierran las ventanas de visualización.

Veamos los resultados:

Figura 4: Resultados del programa realizado. No se ha detectado movimiento.


Figura 5: Resultados del programa realizado. Se ha detectado movimiento.

Como veremos a continuación, hay una persona que no fue detectada. Bien esto es entendible, ya que como lo vimos en el tutorial anterior para la sustracción de fondo, si el objeto o la persona en este caso posee un color similar al del fondo, entonces se puede tener problemas al momento de aplicar sustracción de fondo. 

Figura 6: El movimiento de una persona no fue detectada.

Y hemos llegado al final de este tutorial, que por cierto espero que te haya gustado. ¡Nos vemos en el siguiente!.