Detección de Objetos con Python

En este artículo podrás ver de manera práctica cómo crear tu propio detector de objetos que podrás utilizar con imagenes estáticas, video o cámara. Avanzaremos paso a paso en una Jupyter Notebook con el código completo usando Keras sobre Tensorflow.

Antes de empezar te recomiendo que leas mis artículos anteriores sobre Visión Artificial, que te ayudarán con las bases teóricas sobre las que nos apoyamos en este ejercicio:

Agenda

Tenemos mucho por delante! Antes que nada debo aclarar que próximamente un nuevo artículo explicará toda la teoría que hoy aplicaremos, pero mientras llega… pasemos a la acción!

  • ¿En qué consiste la Detección Yolo?
    • Algunos parámetros de la red
    • El proyecto propuesto
  • Lo que tienes que instalar (y todo el material)
  • Crear un dataset: Imágenes y Anotaciones
    • Recomendaciones para la imágenes
    • Anotarlo todo
    • El lego dataset
  • El código Python
    • Leer el dataset
    • Train y Validación
    • Data Augmentation
    • Crear la red YOLO
    • Crear la red de Detección
    • Generar las Anclas
    • Entrenar
    • Revisar los Resultados
    • Probar la red!
  • Conclusiones
  • Material Adicional

¿En qué consiste la detección YOLO?

Vamos a hacer un detector de objetos en imágenes utilizando YOLO, un tipo de técnica muy novedosa (2016), acrónimo de “You Only Look Once” y que es la más rápida del momento, permitiendo su uso en video en tiempo real.

Esta técnica utiliza un tipo de red Neuronal Convolucional llamada Darknet para la clasificacion de imágenes y le añade la parte de la detección, es decir un “cuadradito” con las posiciones x e y, alto y ancho del objeto encontrado.

La dificultad de esta tarea es enorme:, poder localizar las áreas de las imágenes, que para una red neuronal es tan sólo una matriz de pixeles de colores, posicionar múltiples objetos y clasificarlos. YOLO lo hace todo “de una sola pasada” a su red convolucional. En resultados sobre el famoso COCO Dataset clasifica y detecta 80 clases de objetos distintos y etiquetar y posicionar 1000 objetos (en 1 imagen!!!)

NOTA PARA los Haters del ML (si es que los hay): Este código se basa en varios trozos de código de diversos repos de Github y estaré usando una arquitectura de YOLOv2 aunque YA Sé que es mejor la versión 3 y de hecho está por salir Yolo v4… pero recuerden que esto es con fines didácticos! No me odies y sé comprensivo, toma tu pastilla todas las noches, gracias.

Aunque ahondaré en la Teoría en un próximo artículo, aquí comentaré varios parámetros que manejaremos con esta red y que debemos configurar.

(Algunos) Parámetros de la red

  • Tamaño de imagen que procesa la red: este será fijo, pues encaja con el resto de la red y es de 416 pixeles. Todas las imágenes que le pasemos serán redimensionadas antes de entrar en la red.
  • Cantidad de cajas por imagen: Estás serán la cantidad de objetos máximos que queremos detectar.
  • etiquetas: estas serán las de los objetos que queramos detectar. En este ejemplo sólo detectaremos 1 tipo de objeto, pero podrían ser múltiples.
  • epochs: la cantidad de iteraciones sobre TODO el dataset que realizará la red neuronal para entrenar. (Recuerda, que a muchas épocas tardará más tiempo y también el riesgo de overfitting)
  • train_times: este valor se refiera a la cantidad de veces de entrenar una MISMA imagen. Esto sirve sobre todo en datasets pequeños, además que haremos algo de data augmentation sobre las imágenes cada vez.
  • saved_weights_name: una vez entrenada la red, guardaremos sus pesos en este archivo y lo usaremos para hacer las predicciones.

El proyecto Propuesto: Detectar personajes de Lego

Será porque soy padre, ó será porque soy Ingeniero… al momento de pensar en un objeto para detectar se me ocurrió esto: Legos! ¿Quien no tiene legos en su casa?… Por supuesto que puedes crear tu propio dataset de imagenes y anotaciones xml para detectar el ó los objetos que tu quieras.

Lo que tienes que instalar

Primero que nada te recomiendo que crees un nuevo Environment de Python 3.6.+ e instales estas versiones de librerías que usaremos.

En consola escribe:

Y luego lo ACTIVAS para usarlo en windows con:

ó en Linux / Mac con:

y luego instala los paquetes:

Aclaraciones: usamos una versión antigua de Tensorflow. Si tienes GPU en tu máquina, puedes usar la versión apropiada de Tensorflow y aprovecharlo.

Si vas a crear tu propio dataset como se explica a continuación, deberás instalar LabelImg, que requiere:

Si no, puedes usar el dataset de legos que provee el blog y saltarte la parte de crear el dataset.

Otros archivos que deberás descargar:

Crea un dataset: Imágenes y Anotaciones

Vale, pues es hora de crear un repositorio de miles de imágenes para alimentar tu red de detección.

En principio te recomendaría que tengas al menos unas 1000 imágenes de cada clase que quieras detectar. Y de cada imagen deberás tener un archivo xml con un formato que en breve comentaré con la clase y la posición de cada objeto. Pero recuerda que al detectar imágenes podemos tener más de un objeto, entonces puedes tener imágenes que tienen a más de un objeto.

Recomendaciones para las imágenes:

Algunas recomendaciones para la captura de imágenes: si vas a utilizar la cámara de tu móvil, puede que convenga que hagas fotos con “pocos megapixeles”, pues recuerda que si haces una imagen de 4K de 5 Megas, luego la red la reducirá a 416 pixeles de ancho, por lo que tendrá un coste ese preprocesado en tiempo, memoria y CPU.

Intenta tener fotos del/los objetos con distintas condiciones de luz, es decir, no tengas imagenes de gatitos siempre al sol. Si no, tendrás imágenes de interior, al aire libre, con poca luz, etc.

Intenta tener imágenes “torcidas”(rotadas), parciales y de distintos tamaños del objeto. Si sólo tienes imágenes en donde tu objeto supongamos que “mide 100pixeles” mal-acostumbrarás la red y sólo detectará en imágenes cuando sea de esas dimensiones.

Variaciones del mismo objeto: Si tu objeto es un gato, intenta clasificar gatos de distintos colores, razas y en distintas posiciones, para que la red pueda generaliza el conocimiento.

Anotarlo Todo

Muy bien, ya tienes tus imágenes hechas y guardadas en un directorio.

Ahora deberás crear un archivo XML donde anotarás cada objeto, sus posiciones x,y su alto y ancho.

El xml será de este tipo:

Y lo puedes hacer a mano… ó puedes usar un editor como labelImg.

Si lo instalaste con Pip, puedes ejecutarlo simplemente poniendo en línea de comandos labelImg. Se abrirá el editor y podrás

  • seleccionar un directorio como fuente de imágenes
  • otro directorio donde guardará los xml.

En el editor deberás crear una caja sobre cada objeto que quieras detectar en la imagen y escribir su nombre. Cuando terminas le das a Guardar y Siguiente!

El lego dataset

Si quieres puedes utilizar el Lego-Dataset de imágenes y anotaciones (170MB) que creé para este artículo y consta de 300 imágenes. Son fotos tomadas con móvil de diversos personajes lego. Realmente son 100 fotos y 200 variaciones en zoom y recortes. Y sus correspondientes 300 archivos de anotaciones xml.

Dicho esto, recuerda que siempre es mejor más y más imágenes para entrenar.

El código Python

Usaremos Keras sobre Tensorflow para crear la red!, manos a la obra.

En el artículo copiaré los trozos de código más importante, pero recuerda descargar la notebook Jupyter con el código completo desde Github.

Leer el Dataset

Primer paso, será el de leer las anotaciones xml que tenemos creadas en un directorio e ir iterando los objetos para contabilizar las etiquetas.

Train y Validación

Separaremos un 20% de las imágenes y anotaciones para testear el modelo. En este caso se utilizará el set de Validación al final de cada época para evaluar métricas, pero nunca se usará para entrenar.

¿Porque usar Train, test y validación?

Data Augmentation

El Data Augmentation sirve para agregar pequeñas alteraciones ó cambios a las imágenes de entradas aumentando nuestro dataset de imágenes y mejorando la capacidad de la red para detectar objetos. Para hacerlo nos apoyamos sobre una librería imgaug que nos brinda muchas funcionalidades como agregar desenfoque, agregar brillo, ó ruido aleatoriamente a las imágenes. Además podemos usar OpenCV para voltear la imagen horizontalmente y luego recolocar la “bounding box”.

Crear la Red de Clasificación

La red es conocida como Darknet y está compuesta por 22 capas convolucionales que básicamente aplican BatchNormalizarion, MaxPooling y activación por LeakyRelu para la extracción de características, es decir, los patrones que encontrará en las imágenes (en sus pixeles) para poder diferenciar entre los objetos que queremos clasificar.

Va alternando entre aumentar y disminuir la cantidad de filtros y kernel de 3×3 y 1×1 de la red convolucional.

No olvides descargar y copiar en el mismo directorio donde ejecutes la notebook los pesos de la red Darknet, pues en este paso se cargaran para incializar la red.

Crear la Red de Detección

Esta red, utilizará la anterior (clasificación) y utilizará las features obtenidas en sus capas convolucionales de salida para hacer la detección de los objetos, es decir las posiciones x e y, alto y ancho. Para ello se valdrá de unas Anclas, en nuestro caso serán 5. Las Anclas son unas “ventanas”, o unas bounding boxes de distintos tamaños, pequeños, mediano grande, rectangulares o cuadrados que servirán para hacer “propuestas de detección”.

En total, la red crea una grilla de 13×13 y en cada una realizará 5 predicciones, lo que da un total de 845 posibles detecciones para cada clase que queremos detectar. Si tenemos 10 clases esto serían 8450 predicciones, cada una con la clase y sus posiciones x,y ancho y alto. Lo más impresionante de esta red YOLO es que lo hace Todo de 1 sólo pasada! increíble!

Para refinar el modelo y que detecte los objetos que hay realmente, utilizará dos funciones con las cuales descartará áreas vacías y se quedará sólo con las mejores propuestas. Las funciones son:

  • IOU: Intersection Over Union, que nos da un porcentaje de acierto del área de predicción contra la “cajita” real que queremos predecir.
  • Non Maximum suppression: nos permite quedarnos de entre nuestras 5 anclas, con la que mejor se ajusta al resultado. Esto es porque podemos tener muchas áreas diferentes propuestas que se superponen. De entre todas, nos quedamos con la mejor y eliminamos al resto.

Entonces, pensemos que si en nuestra red de detección de 1 sólo clase detectamos 1 lego, esto quiere decir que la red descarto a las 844 restantes propuestas.

Prometo más teoría y explicaciones en un próximo artículo 🙂

NOTA: por más que para explicar lo haya separado en 2 redes (red YOLO y red de detección), realmente es 1 sóla red convolucional, pues están conectadas y al momento de entrenar, los pesos se ajustan “como siempre” con el backpropagation.

Generar las Anclas

Como antes mencioné, la red utiliza 5 anclas para cada una de las celdas de 13×13 para realizar las propuestas de predicción. Pero… qué tamaño tienen que tener esas anclas? Podríamos pensar en 5 tamaños distintos, algunos pequeños, otros más grandes y que se adapten a las clases que queremos detectar. Por ejemplo, el ancla para detectar siluetas de personas serán seguramente rectangulares en vertical.

Pues según lo que quieras detectar, ejecutaremos un pequeño script que utiliza k-means y determina los mejores 5 clusters (de dimensiones) que se adapten a tu dataset.

Entrenar la Red!

Basta de bla bla… y a entrenar la red. Como dato informativo, en mi ordenador Macbook de 4 núcleos y 8GB de RAM, tardó 7 horas en entrenar las 300 imágenes del dataset de lego con 7 épocas (y 5 veces cada imagen).

Al finalizar verás que se ha creado un archivo nuevo llamado “red_lego.h5” que contiene los pesos de la red creada.

Revisar los Resultados

Los resultados vienen dados por una métrica llamada mAP y que viene a ser un equivalente a un F1-Score pero para imágenes, teniendo en cuenta los falsos positivos y negativos. Comentaremos más sobre esto en el próximo post, pero ten en cuenta que si bien la ventaja de YOLO es la detección en tiempo real, su contra es que es “un poco” peor en accuracy que otras redes que son lentas, lo podemos notar al ver que las “cajitas” no se ajustan del todo con el objeto detectado ó puede llegar a confundir a veces la clase que clasificó. Con el Lego Dataset he logrado un bonito 63 de mAP… no está nada mal. Recordemos que este valor de mAP se obtiene al final de la última Epoch sobre el dataset de Validación (que no se usa para entrenar) y en mi caso eran 65 imágenes.

Probar la Red

Para finalizar, podemos probar la red con imágenes nuevas, distintas que no ha visto nunca, veamos cómo se comporta la red!

Crearemos unas funciones de ayuda para dibujar el rectángulo sobre la imagen original y guardar la imagen nueva:

Recuerda que utilizaremos el archivo de pesos creado al entrenar, para recrear la red (esto nos permite poder hacer predicciones sin necesidad de reentrenar cada vez).

Como salida tendremos una nueva imagen llamada “lego_girl_detected.png” con la detección realizada.

Esta imagen me fue prestada por @Shundeez_official, muchas gracias! Les recomiendo ver su cuenta de Instagram que es genial!

Imágenes pero también Video y Cámara!

Puedes modificar un poco la manera de realizar predicciones para utilizar un video mp4 ó tu cámara web.

Para aplicarlo a un video:

Luego de procesar el video, nos dejará una versión nueva del archivo mp4 con la detección que realizó cuadro a cuadro.

Y para usar tu cámara: (presiona ‘q’ para salir)

Conclusiones y…

Esta fue la parte práctica de una de las tareas más interesantes dentro de la Visión Artificial, que es la de lograr hacer detección de objetos. Piensen todo el abanico de posibilidades que ofrece poder hacer esto! Podríamos con una cámara contabilizar la cantidad de coches y saber si hay una congestión de tráfico, podemos contabilizar cuantas personas entran en un comercio, si alguien toma un producto de una estantería y mil cosas más! Ni hablar en robótica, donde podemos hacer que el robot vea y pueda coger objetos, ó incluso los coches de Tesla con Autopilot… Tiene un gran potencial!

Además en este artículo quería ofrecer el código que te permita entrenar tus propios detectores, para los casos de negocio que a ti te importan!

En un próximo artículo espero escribir sobre la Teoría que hoy pusimos en práctica sobre Detección de Objetos.

1 millón de Gracias!

Este artículo es muy especial para mi, por varias cosas: una es que el Blog ha conseguido la marca de 1.000.000 de visitas en estos 2 años y medio de vida y estoy muy contento de seguir escribiendo -a pesar de muchas adversidades de la vida-. Gracias por las visitas, por leerme, por los comentarios alentadores y el apoyo!

Libro en proceso

Con este artículo y por el hito conseguido me animo a lanzar un primer borrador de lo que será “El libro del blog” y que algún día completaré y publicaré.

Los invito a todos a comprarlo si pueden colaborar con este proyecto y también está la opción de conseguirlo gratis, porque sé que hay muchos lectores que son estudiantes y puede que no tengan medios ó recursos para pagar y no por eso quiero dejar de compartirlo.

Todos los que lo adquieran ahora, podrán seguir obteniendo todas las actualizaciones que iré haciendo con el tiempo y descargar el material extra.

Suscripción al Blog

Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!

NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!

Todo el Material

Recuerda todo lo que tienes que descargar:

Y enlaces a otros artículos de interés:

6 comments

  1. Jose · 16 Days Ago

    Genial Juan!!, hace tiempo que estaba esperando que hicieras algo con YOLO.
    Ya tengo el libro!!!, ha sido una estupenda idea, suerte.

    • Na8 · 16 Days Ago

      Jajaja muchas gracias! Me alegro que guste el artículo. Tienes el material extra con el libro para descargar, un saludo

  2. DanY · 16 Days Ago

    ¡Un gran aporte! Muchas gracias de nuevo

    • Na8 · 16 Days Ago

      Gracias a ustedes, lectores 😁

  3. Pedro · 5 Days Ago

    Hola Juan.
    Tengo un portatil i5, 4 núcleos y 16 GB RAM. Llevo 90 min y todavía va por la epoch #1. Utlizas GPU??

    • Na8 · 5 Days Ago

      Es con CPU, a mi me tardó sobre 7 u 8 horas. Aproximadamente 1 hora por epoch

Leave a Reply