Como crear tu propio DETECTOR DE OBJETOS con Haar Cascade | Python y OpenCV

Por Administrador

¿Recuerdas que habíamos detectado rostros dentro de una imagen y video con ayuda de haar cascades en posts anteriores? Si no lo has visto o no lo recuerdas puedes echarle un ojo a este post: 👨 DETECCIÓN DE ROSTROS 👩 con Haar Cascades Python – OpenCV. En aquel tutorial debíamos usar un archivo .xml que contenía el modelo (pre entrenado) necesario para realizar la detección de rostros. Sin embargo los rostros no son lo único que muchas veces se desea detectar para distintos proyectos, es entonces cuando es necesario crear un detector de objetos personalizado, pero ¿cómo hacerlo?, el tutorial de hoy voy a enseñarte paso a paso como realizar un detector de objetos usando haar cascades con ayuda del software Cascade Trainer Gui. ¿Empezamos?.

CONTENIDO

  • Cascade Trainer GUI
    • Instalar Cascade Trainer GUI
  • Preparando los datos para el entrenamiento
  • Entrenamiento del clasificador en cascada
    • ¿Cuál es el siguiente paso luego de que se ha completado el entrenamiento?
  • Probando el detector de objetos
  • Referencias

Cascade Trainer GUI

Figura 1: Logo de Cascade Trainer GUI.

Según el sitio web de su creador, Cascade Trainer GUI es un programa que ofrece una interfaz gráfica, la cual facilita el uso de herramientas de OpenCV para el entrenamiento y prueba de clasificadores. Este programa ofrece entrenar, probar y mejorar modelos de clasificadores en cascada.

Un aspecto a tener en cuenta es que este programa únicamente puede usarse en Windows, por lo que si estás usando otro sistema operativo necesitarás seguir el tutorial de OpenCV para el entrenamiento de clasificadores en cascada.

Por cierto, a pesar de que este programa cuenta con muchas opciones de uso, en este tutorial solo me centraré en el apartado de entrenamiento Train.

Instalar Cascade Trainer GUI

Para realizar la instalación debes dirigirte al link: https://amin-ahmadi.com/cascade-trainer-gui/, allí podrás encontrar una guía de usuario, con: introducción, instalación, uso y finalmente el apartado de descarga, en donde podrás elegir el ejecutable ya sea para Windows x86 o Windows x64.

Figura 2: Ejecutables de descarga de Cascade Trainer GUI.

Una vez descargado, deberás seguir los pasos de la ventana de instalación.

Preparando los datos de entrenamiento

Para realizar el entrenamiento será necesario contar con gran cantidad (cientos o miles) de imágenes en donde esté presente el objeto que deseemos detectar (conjunto de muestras positivas) y otro conjunto de imágenes donde NO esté presente dicho objeto (conjunto de muestras negativas). Estas imágenes no deben ser muy grandes, ya que esto podría provocar lentitud en la detección.

Para este tutorial trataré de detectar un muñeco de Majin Boo, por lo que necesitaré tomar imágenes donde aparezca (imágenes positivas) y otras donde no esté presente este muñeco (imágenes negativas).

Figura 3: Imágenes positivas y negativas recolectadas para el entrenamiento.

Toma en cuenta además, que para obtener mejores resultados de detección, las imágenes de entrenamiento deben tener la mayor variedad posible, tomando en cuenta el ambiente en donde va a trabajar el detector.

NOTA: Tal vez te estés preguntando por la cantidad exacta de imágenes tanto negativas como positivas para poder llevar a cabo el entrenamiento del clasificador, pero en realidad no hay una receta para determinar dicha cantidad. Entonces ¿cuántas imágenes debo usar en un principio?, pues bien, lo que te podría recomendar es que experimentes con unos cientos en un principio y conforme a los resultados que obtengas, podrías ir agregando más (claro que si tienes un conjunto de datos bastante grande, mejor).

Para realizar el entrenamiento en Cascade Trainer GUI, necesitaremos tener las imágenes positivas y negativas almacenadas en dos carpetas ‘p’ y ‘n’ respectivamente.

Figura 4: Imágenes positivas y negativas deben estar almacenadas en carpetas llamadas ‘p’ y ‘n’ para usar Cascade Trainer GUI.

Como te decía, en este tutorial voy a estar usado como objeto a detectar un muñeco de Majin boo, entonces procedamos a preparar las imágenes positivas y negativas para el entrenamiento, por lo que crearé un programa (capturandoObjetos.py) que nos ayude con esta tarea.

import cv2
import numpy as np
import imutils
import os

Datos = 'p'
if not os.path.exists(Datos):
    print('Carpeta creada: ',Datos)
    os.makedirs(Datos)

cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)

x1, y1 = 190, 80
x2, y2 = 450, 398

count = 0
while True:

    ret, frame = cap.read()
    if ret == False: break
    imAux = frame.copy()
    cv2.rectangle(frame,(x1,y1),(x2,y2),(255,0,0),2)

    objeto = imAux[y1:y2,x1:x2]
    objeto = imutils.resize(objeto,width=38)
    #print(objeto.shape)

    k = cv2.waitKey(1)
    if k == ord('s'):
        cv2.imwrite(Datos+'/objeto_{}.jpg'.format(count),objeto)
        print('Imagen guardada:'+'/objeto_{}.jpg'.format(count))
        count = count +1
    if k == 27:
        break

    cv2.imshow('frame',frame)
    cv2.imshow('objeto',objeto)

cap.release()
cv2.destroyAllWindows()

Línea 1 a 4: Importamos OpenCV, numpy, imutils y os.

Línea 6 a 9: Como hemos hecho en tutoriales anteriores, crearemos una carpeta llamada p, para almacenar las imágenes positivas, luego haremos el mismo procedimiento para la carpeta n en donde se almacenarán las imágenes negativas.

Línea 11: Indicamos que realizaremos un video streaming. Si llegas a obtener problemas con esta línea por los parámetros puedes cambiarla por: cap = cv2.VideoCapture(0).

Línea 13 y 14: Establecemos los valores correspondientes a las coordenadas de un rectángulo. Lo que quiero lograr aquí es dibujar un rectángulo en el centro de la pantalla para ir guardando imágenes que se encuentren dentro de este rectángulo, en el caso de las imágenes positivas estaremos almacenando imágenes de Majin boo. Estas coordenadas podrán ser modificadas de acuerdo al objeto que desees identificar.

Figura 5: Visualización del rectángulo dibujado en el centro de la imagen.

Línea 16: Iniciamos un contador que nos ayudará con el nombre de las imágenes a ser almacenadas.

Línea 19 a 22: Leemos cada fotograma, luego en la línea 21 tomamos una copia de frame que se almacenará en imAux, mientras que en 22 dibujamos un rectángulo con las coordenadas de las líneas 13 y 14.

Línea 24 y 25: Recortamos de imAux la región contenida dentro del rectángulo dibujado, esta imagen se almacenará en la variable objeto. ¿Recuerdas que habíamos dicho que las imágenes de entrenamiento deben ser pequeñas?, pues bien vamos a redimensionar objeto a un ancho de 38 pixeles y por lo tanto un alto de 46 (puedes cambiar estos valores de redimensionamiento).

Línea 26: Si des comentamos esta línea podremos imprimir el alto, ancho y la cantidad de canales que contiene objeto.

Línea 29 a 32: Cuando presionemos la tecla s, se almacenará en la carpeta p o n la imagen contenida en objeto, esta tendrá un nombre llamado ‘objeto’, seguido del valor de count.  También se imprimirá un mensaje en consola, con el nombre de la imagen guardada. En la línea 32 aumentamos en 1 a count.

Línea 33 y 34: Si se presiona ESC, el bucle se romperá.

Línea 36 a 40: Visualizamos frame y objeto.  Y luego de realizado el proceso podremos liberar la captura del video streaming y cerrar todas las ventanas.

Figura 6: Algunas imágenes positivas y negativas almacenadas en las carpetas p y n.

Este programa (capturandoObjetos.py) nos servirá para capturar tanto las imágenes positivas como negativas, en mi caso he almacenado 40 imágenes positivas y 350 negativas, todas estas imágenes con 38 pixeles de ancho y 46 de alto.

Entrenamiento del clasificador en cascada

Una vez que tenemos las imágenes listas para el entrenamiento, nos dirigimos al programa Cascade Trainer GUI, en el tendremos la siguiente ventana:

Figura 7: Cascade Trainer GUI, apartado: Train.

A. En esta sección debes seleccionar la carpeta en donde estan contenidas las carpetas p y n.

B. Aquí necesitamos especificar el porcentaje de imágenes positivas a usar, en este caso he establecido 100.

C. Especificamos la cantidad de imágenes negativas a usar para el entrenamiento, en mi caso serían 350.

Luego pasaremos al aparatado Common.

Figura 8: Cascade Trainer GUI, apartado: Common.

En este apartado no modificaré ningún valor, por lo que el número de etapas queda en 20.

Ahora vamos con el apartado Cascade.

Figura 9: Cascade Trainer GUI, apartado: Cascade.

A. Ingresamos el ancho que poseen las imágenes de entrenamiento, en mi caso 38 pixeles.

B. Ingresamos el alto igualmente, de las imágenes de entrenamiento, en mi caso 46 pixeles.

C. Finalmente damos clic en Start y esperamos a que se realice el entrenamiento. En mi caso se tardó aproximadamente 5 minutos.

¿Cuál es el siguiente paso luego de que se ha completado el entrenamiento?

Una vez que se ha completado el entrenamiento, ve a la carpeta donde estaban alojadas n y p, allí vas a poder encontrar nuevos archivos y una carpeta llamada clasiffier.

Figura 10: Nuevos archivos encontrados en la carpeta en donde están n y p.

En la figura 10 podrás ver los nuevos archivos que se han creado luego del entrenamiento. Vamos a usar únicamente la carpeta classifier, por lo que puedes descartar los otros 3 archivos.

Ahora demos un vistazo a lo que hay dentro de la carpeta classifier:

Figura 11: Archivos dentro de la carpeta classifier, creada luego del entrenamiento.

Dentro de classifier  vas a encontrar algunos archivos, pero el que vamos a tomar es cascade.xml que corresponde al modelo obtenido luego del entrenamiento, este archivo lo voy a pegar en la carpeta principal en donde estaba n y p, para poder realizar pruebas.

Probando el detector de objetos

Ya tenemos el modelo cascade.xml, es hora de probar nuestro detector. Vamos a desarrollar un programa llamado detectando.py, veamos:

import cv2

cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)

majinBooClassif = cv2.CascadeClassifier('cascade.xml')

while True:
    
    ret,frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    toy = majinBooClassif.detectMultiScale(gray,
    scaleFactor = 5,
    minNeighbors = 91,
    minSize=(70,78))

    for (x,y,w,h) in toy:
        cv2.rectangle(frame, (x,y),(x+w,y+h),(0,255,0),2)
        cv2.putText(frame,'Majin Boo',(x,y-10),2,0.7,(0,255,0),2,cv2.LINE_AA)

    cv2.imshow('frame',frame)
    
    if cv2.waitKey(1) == 27:
        break
cap.release()
cv2.destroyAllWindows()

Este programa es muy similar al que vimos en el post de detección de rostros, por lo que para más profundidad sobre las líneas empleadas, así como los parámetros usados para detectMultiScale, por favor visita ese tutorial.

Línea 5: Vamos a leer el modelo entrenado.

Línea 12 a 13: Todas las dectecciones se almacenarán en toy,  para ello he establecido un valor de scaleFactor de 5, minNeighbors 91, minSize de (70,78), luego de varias pruebas. Te recomiendo usar una barra deslizante o slider para que puedas modificar estos valores conforme estés haciendo pruebas, esto te ahorrará tiempo para que establezcas los mejores valores para tu detector.

Figura 12: Pruebas del detector de objetos.

Como puedes apreciar en la figura 12, aparecen un montón de detecciones, esto lo obtuve usando los parámetros con scaleFactor de 1.3 y minNeighbors 10. Estos resultados podrían abrumarnos, pero al modificar los valores de cada parámetro y al añadir minSize, los resultados mejoraron, ya lo verás a continuación.

Línea 17 a 19: Desempaquetamos la información contenida en toy, entonces por cada detección tendremos las coordenadas x, y, ancho y alto. Tomaremos esta información para dibujar un rectángulo que rodee el objeto detectado y además añadiremos un nombre, MajinBoo.

Veamos las detecciones:

Figura 13: Detección de Majin Boo usando el modelo entrenado.

Se cumplió con el objetivo de detectar a MajinBoo, sin embargo en ocasiones la detección puede fallar, e incluso pueden aparecer falsos positivos, por lo que hay que recordar que habíamos entrenado con un banco de imágenes pequeño. Conforme realices las pruebas, podrás ir añadiendo más imágenes significativas, es más podrías usar aquellas detecciones de falsos positivos para añadirlas a la carpeta n y volver a entrenar el clasificador.

Y esto ha sido todo por este tutorial, nos vemos en un siguiente :).

Referencias: