? CONTADOR DE AUTOS (Fácil) | OpenCV con Python

Por Administrador

En el tutorial de hoy te mostraré como crear un contador de autos sencillo con ayuda de OpenCV y Python que actuará sobre un carril de una autopista (esto para mantener el tutorial sencillo, pero si te animas a aplicarlo a toda la autopista sería genial). Para ello usaremos el contenido que hemos visto en tutoriales anteriores, en especial la sustracción de fondo. ¡Vamos a empezar!.

Conteo de autos dada cierta sección de una autopista/calle con OpenCV y Python

Para esta aplicación nos vamos a ayudar de la sustracción de fondo, de la cual ya había hecho un post en donde detectábamos la presencia de movimiento sobre cierta área dada. Como en ese post, vamos a determinar cierta porción de la autopista para detectar a los autos en movimiento. Así que para empezar vamos a crear un script llamándolo conteo_autos.py 

import cv2
import numpy as np
import imutils

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

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

while True:

    ret, frame = cap.read()
    if ret == False: break
    frame = imutils.resize(frame, width=640)

    # Especificamos los puntos extremos del área a analizar
    area_pts = np.array([[330, 216], [frame.shape[1]-80, 216], [frame.shape[1]-80, 271], [330, 271]])

Línea 1 a 3: Importamos OpenCV, numpy e imutils.

Línea 5: Especificamos el video que va a ser leído. El video de entrada puedes encontrarlo aquí.

Línea 7 y 8: Indicamos que vamos a usar background sustractor mog, luego especificamos kernel el cual usaremos más adelante para mejorar la imagen binaria obtenida luego de aplicar la sustracción de fondo.

Línea 9: Declaramos en 0 a car_counter, que será nuestro contador de autos.

Línea 13 a 15: Leemos los fotogramas y luego con ayuda de imutils los redimensionamos a 640 pixeles de ancho. Esto ya que no necesitamos de fotogramas tan grandes con los cuales trabajar.

Línea 18: Especificamos los 4 puntos extremos que rodean la porción del quito carril de la autopista. Estos los he ordenado de forma horaria.

Más adelante también dibujaremos una línea amarilla aproximadamente en la mitad del área seleccionada. El área de interés dibujada y la línea amarilla que dibujaremos más adelante se verían de la siguiente manera:

Figura 1: Visualización del área de interés y de la línea amarilla en aproximadamente el centro de esa área, que nos servirá para tomar como referencia para contar a los autos.

La siguiente sección del código será similar a la del tutorial anterior ? DETECCIÓN DE MOVIMIENTO en CIERTA ÁREA | Python – OpenCV. Estaremos creando una imagen auxiliar de ceros, para luego dibujar en blanco nuestra área de interés.

# 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(frame, frame, mask=imAux)	

Línea 22: Creamos una imagen del mismo tamaño que frame, esta será una matriz de ceros que crearemos gracias a np.zeros.

Línea 23: Dibujamos el contorno que forman los puntos que habíamos especificado en area_pts, en blanco.

Figura 2: Visualización de imAux.

Línea 24: Ahora haremos que en vez de esa porción blanca aparezca frame. Veamos como se vería:

Figura 3: Visualización de image_area.

Procederemos aplicar la sustracción de fondo a la imagen recientemente obtenida, 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.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)
fgmask = cv2.dilate(fgmask, None, iterations=5)

Línea 28: Aplicamos la sustracción de fondo a image_area, de donde en negro veremos el fondo de la escena, mientras que en blanco al objeto en movimiento (auto).

Figura 4: Izq. Visualización de la imagen de entrada donde está dibujada el área de interés y la línea amarilla. Der. Aplicación de la sustracción de fondo sobre la porción del quinto carril de la autopista.

Línea 29 a 31: Usaremos transformaciones morfológicas para mejorar la imagen binaria. Cabe destacar que en la línea 31 he especificado 5 iteraciones para la dilatación, esto lo hago para incrementar y conectar las áreas blancas que representan el auto y que pueden estar desconectadas, de este modo podemos obtener un gran área blanca.

Figura 5: Izq. Visualización de la imagen de entrada donde está dibujada el área de interés y la línea amarilla. Der. Imagen binaria en donde se han aplicado transformaciones morfológicas.

# Encontramos los contornos presentes de fgmask, para luego basándonos
# en su área poder determinar si existe movimiento (autos)
cnts = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

Línea 35: Empleamos cv2.findContours para encontrar los contornos presentes en fgmask.

Una vez que tenemos los contornos, vamos a tratar de analizar cada uno de ellos para asegurarnos que se trate de los autos.

for cnt in cnts:
    if cv2.contourArea(cnt) > 1500:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,255), 1)

Línea 37 y 38: Procedemos a analizar cada contorno contenido en cnts. Luego comparamos si el área del contorno es lo suficientemente grande como para ser considerada un auto. Para ello comparamos el área en pixeles del contorno con 1500, de tal modo que si el contorno es mayor a este valor, seguiremos con el proceso, caso contrario lo descartamos.

Línea 39 y 40: Con ayuda de cv2.boundingRect, obtenemos los puntos x, y, w que corresponde al ancho y h al alto del contorno. Luego en la línea 40 dibujaremos un rectángulo que rodee al contorno.

Figura 6: Visualización de uno de los autos detectados mediante la sustracción de fondo y que es rodeado por un rectángulo amarillo.

Ahora que tenemos el recuadro que rodea al contorno, procederemos a usar el punto superior derecho, para que cuando pase por cierta sección en el eje x, el auto sea contado.

Figura 7: Para poder contar los autos, tomaremos el punto superior derecho del recuadro delimitador dibujado y veremos si está presente en x entre 440 y 460, si es así el auto será contado haciendo que la línea amarilla central se torne verde.

# Si el auto ha cruzado entre 440 y 460 abierto, se incrementará
# en 1 el contador de autos
if 440 < (x + w) < 460:
    car_counter = car_counter + 1
    cv2.line(frame, (450, 216), (450, 271), (0, 255, 0), 3)

Línea 44: Si el punto superior derecho del contorno está entre 440 y 460 se procederá a incrementar en 1 el contador de autos, además que se dibujará una línea verde en el mismo lugar donde teníamos la línea amarilla. Puedes echarle un ojo a la figura 7.

Pero te estarás preguntando de donde sale este rango (entre 440 y 460 en el eje x). Pues bien, la línea amarilla central que pusimos de referencia está ubicada en el eje  x en 450 (lo podremos ver más adelante en la línea 50), por lo que he restado y sumado 10 pixeles a cada lado. Esto junto a la velocidad a la que se presentan los autos nos permite contar cada auto, ya que solo una vez pasará entre 440 y 460.

NOTA: El contador podría fallar si un auto va demasiado lento o se queda parado en ese lugar (entre 440 y 460 en x), en ese caso el contador de autos se incrementaría y tendríamos fallos, por lo que para este tutorial he asumido que los autos siempre van a la velocidad que se encuentra en el video.

Finalmente vamos a la visualización del área de interés, la línea amarilla central, el contador y frame.

    # Visualización del conteo de autos
    cv2.drawContours(frame, [area_pts], -1, (255, 0, 255), 2)
    cv2.line(frame, (450, 216), (450, 271), (0, 255, 255), 1)
    cv2.rectangle(frame, (frame.shape[1]-70, 215), (frame.shape[1]-5, 270), (0, 255, 0), 2)
    cv2.putText(frame, str(car_counter), (frame.shape[1]-55, 250),
                cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0,255,0), 2)
    cv2.imshow('frame', frame)

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

cap.release()
cv2.destroyAllWindows()

Línea 49 y 50: Dibujamos el contorno dados los puntos de area_pts, y luego vamos a especificar una línea en la mitad de esta área, para que los autos una vez pasen por ese lugar sean contados.

Línea 51 a 52: Visualizamos un rectángulo verde en la parte derecha del carril que estamos analizando, para dentro de él realizar el conteo de los autos según la información que tengamos en car_counter. Visualizamos frame, y si se presiona la tecla ESC el proceso se rompe y se cierran las ventanas de visualización.

Figura 8: Resultados del programa realizado para el conteo de autos con OpenCV y Python.

Como podemos ver, el conteo se realiza de una forma correcta y se han avanzado a contar un total de 12 autos (puedes verlo en el video tutorial) que son los que han pasado por el quinto carril. Si quieres profundizar un poco, podrías realizar este análisis en el resto de carriles y puedes dejarme en los comentarios que te pareció. Y bien esto fue todo por el tutorial de hoy. ¡Cuídate mucho, nos vemos en un siguiente tutorial! chao chao.