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 redes neuronales profundas con 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 hasta 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 sé que es mejor la versión 3 (y de hecho está por salir Yolo v4)… pero recuerden que este artículo 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ó: 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 CUDA) para 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 específico -que en breve comentaré- con la clase y la posición de cada objeto. 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 si haces una imagen de 4K de 5 Megas, luego la red neuronal la reducirá a 416 pixeles de ancho, por lo que tendrás un coste adicional de ese preprocesado en tiempo, memoria y CPU.

Intenta tener fotos del/los objetos con distintas condiciones de luz, es decir, no tengas imágenes de gatitos “siempre al sol”. Mejor serán 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 100 pixeles” mal-acostumbrarás la red y sólo detectará en imágenes cuando sea de esas dimensiones (peligro de overfitting).

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 convolucional pueda generalizar 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 mediante Pip, puedes ejecutarlo simplemente poniendo en línea de comandos del environment labelImg. Se abrirá el editor visual y podrás:

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

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

El lego dataset

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 importantes, siempre puedes 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.

NOTA: en este ejemplo, declaro la variable labels con 1 sóla clase “lego”, pero si quieres identificar más podrías poner [“perro”,”gato”] ó lo que sea que contenga tu dataset.

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 virtualmente nuestro dataset de imágenes y mejorando la capacidad de la red para detectar objetos. Para hacerlo nos apoyamos sobre una librería llamada 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 CNN 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 YOLO 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óla 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 rectangulares en vertical.

Según los objetos 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 Neuronal!

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 con data augmentation, (en total se procesan 1500 imágenes en cada epoch).

Al finalizar verás que se ha creado un archivo nuevo llamado “red_lego.h5” que contiene los pesos de tu nueva red convolucional 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. 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 la clase que clasificó. Con el Lego Dataset he logrado un bonito 63 de mAP… no está 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 -apenas- 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:

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 levemente 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 el próximo artículo comento 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:

Libros Relacionados

26 comments

  1. Jose · June 24

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

    • Na8 · June 24

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

  2. DanY · June 24

    ¡Un gran aporte! Muchas gracias de nuevo

    • Na8 · June 24

      Gracias a ustedes, lectores 😁

  3. Pedro · July 5

    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 · July 5

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

  4. Joaquin · July 21

    Excelente Juan!! Siempre claro y conciso!! Muchas gracias por este aporte!!

  5. ai · July 22

    Hola Juan. No consigo descargar el dataset de lego. ¿Es correcto el link? El libro he podido descargarlo, pero no el dataset. ¿Puedes echarme una mano?
    Mil gracias!!

    • Na8 · July 22

      Hola, gracias por escribir. Una vez adquirido el libro desde leanpub tienes un enlace de “extras”desde donde podrás descargar el dataset.
      Ese enlace lo lleva el sitio leanpub, si no te funciona vuelve a escribirme y vemos cómo poder compartirlo de otra manera

  6. cesar · August 10

    Saludos!
    Excelente post, pregunta, esto se podría hacer directamente sobre una raspberry pi con la intel neural compute stick 2? O debo realizarlo en un PC y luego pasarlo como modelo de inferencia desde el PC hacia raspberry?. Al usar labelimg, sí tengo imágenes sobre las cuales requiero identificar elementos específicos dentro de uno de lo rectángulos, ej, una fruta es el rectángulo mayor pero elementos como tallos, huecos, etc sobre la fruta, también quiero detectar. Excelente tu trabajo!.

    • Na8 · August 22

      Lo lógico sería poder entrenar en un PC (ó en la nube) y que guardes los pesos de la red. Luego los lees (ya entrenados) desde el raspberry. Asegurate tener mismo environment en ambas plataformas.
      Con LabelImg puedes etiquetar de distintos tamaños y distintas clases sin problema!. Saludos y gracias

  7. Genaro · August 21

    El link para el dataset de lego, es un link que redirecciona a un libro.

    • Na8 · August 21

      Si, como se explica en el artículo, para obtener el dataset debes comprar el libro ó se puede descargar gratis. Una vez lo tienes, lo consigues como el material Extra del libro.

  8. Genaro · August 21

    Ya tengo el libro pero, el link del dataset, me redirecciona a el mismo libro.

    • Na8 · August 21

      Que raro! Te enviaré un enlace de gDrive parra que puedas descargarlo.

  9. Thomas · August 22

    Hola Juan, quiero comentarte que tengo un problema con el código, quería saber si hay alguna manera de poder comunicarme contigo para poder solucionar este problema

    • Na8 · August 22

      Hola Thomas, escríbeme por el formulario de contacto si quieres e intentaré ayudar. Saludos!

  10. Enrique · September 9

    Hola, Juan. Excelente material. Una duda: los resultados no fueron tan buenos, ¿Cómo puedo reentrenar el modelo con los pesos ya guardados (red_lego.h5)?

  11. Cesar muñoz araya · September 10

    Hola, me gustaría saber por que el modelo que hiciste Posee dos entradas y no solo una. Otra duda que sale a la vista, es que cuando haces “Data Augmentation” en el bloc que de código:

    if jitter:
    ### scale the image
    scale = np.random.uniform() / 10. + 1.
    image = cv2.resize(image, (0,0), fx = scale, fy = scale)

            ### translate the image
            max_offx = (scale-1.) * w
            max_offy = (scale-1.) * h
            offx = int(np.random.uniform() * max_offx)
            offy = int(np.random.uniform() * max_offy)
    
            image = image[offy : (offy + h), offx : (offx + w)]
    
            ### flip the image
            flip = np.random.binomial(1, .5)
            if flip > 0.5: image = cv2.flip(image, 1)
    
            image = self.aug_pipe.augment_image(image)    
    

    Aquí he notado que no le haces el mismo tratamiento al bbox real. Y siento que si no haces eso, no deberías hacerles tratamientos espaciales a las imágenes para no sesgar el modelo.

    Gracias de antemano por si me respondes.

  12. Daniel C · September 22

    Que tal Juan. Saludos desde Chile. Te sigo hace casi un año…muy bueno tu blog. Me ha acercado al mundo del ML de una manera super práctica.
    Adquirí el libro para apoyar el proyecto. Espero que sigas con muchos otros experimentos.
    Una duda..no logro descargar las imagenes de lego. Me parece que a otros tambien les pasa lo mismo.
    Saludos!

    • Na8 · September 22

      Hola muchas gracias por leer el blog y por comprar el libro. Escríbeme por formulario de contacto y te paso un enlace de Google Drive para poder descargar. En teoría debería funcionar el de Leanpub al adquirir el libro.
      Saludos y seguimos en contacto!

  13. JAIME DAVID QUINCHIMBLA PISUÑA · October 21

    hola como estas? tengo una duda
    si quiero entrenar por ejemplo 500 clases aparte de la clase de lego,
    debo entrenar directamente las 500 clases desde el inicio o puedo entrenarlo de 10 en 10
    porque supongo que entrenar 500 clases y que cada clase contenga 2000 imágenes debe demorarse una eternidad

    ojala me puedas responder, te agradeceria un monton

    • Na8 · October 21

      Hola Jaime, yo personalmente lo haría con las 500 de comienzo si las tienes definidas. Si no, puedes ir entrenando diversas redes especializadas ó hacer transfer learning, pero ese tema aún no lo hemos tocado en el blog 😜

  14. Darkpoe · 25 Days Ago

    Esto no funciona

    • Na8 · 25 Days Ago

      Hola, si puedes comenta en qué parte del Código no te funciona para poder ayudarte. Saludos

  15. jc · 10 Days Ago

    Hola excelente blog! Necesito realizar lectura de imágenes de carpeta y realizarle bounding box.. cómo hago??

Leave a Reply