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é Ya publicado, en papel y digital!!.

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

76 comments

  1. Jose · June 24, 2020

    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, 2020

      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, 2020

    ¡Un gran aporte! Muchas gracias de nuevo

    • Na8 · June 24, 2020

      Gracias a ustedes, lectores 😁

  3. Pedro · July 5, 2020

    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, 2020

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

  4. Joaquin · July 21, 2020

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

    • Na8 · March 22, 2021

      Gracias por participar y visitar el blog!, un saludo

  5. ai · July 22, 2020

    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, 2020

      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, 2020

    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, 2020

      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, 2020

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

    • Na8 · August 21, 2020

      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, 2020

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

    • Na8 · August 21, 2020

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

  9. Thomas · August 22, 2020

    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, 2020

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

  10. Enrique · September 9, 2020

    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)?

    • Na8 · March 22, 2021

      Puedes indicar que lea los pesos desde el son de configuración, en el atributo “pretrained_weights”.
      Saludos y gracias por escribir

  11. Cesar muñoz araya · September 10, 2020

    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.

    • Na8 · March 22, 2021

      Hola César, gracias por escribir. Te contesto: la red tiene 2 entradas, porque una es la propia imagen y la otra es la información del xml donde se indican las coordenadas del objeto que tiene que detectar (pueden ser más de uno).
      Con respecto a la fase de augmentation, si ves detenidamente el código, verás que se asegura de mantener las “bbox” en la posición adecuada, pese a alterar la imagen. El cambio más simple es el de hacer un volteo horizontal, con lo que si una imagen estaba en 0px a la izquierda, pasará a estar en 416.
      Saludos!

  12. Daniel C · September 22, 2020

    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, 2020

      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, 2020

    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, 2020

      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 · November 3, 2020

    Esto no funciona

    • Na8 · November 3, 2020

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

  15. jc · November 18, 2020

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

    • Na8 · December 5, 2020

      Descarga el Código que está todo ahí, saludos

  16. Hector Matias Gonzalez · January 23, 2021

    Buenas, estoy usando tensorflow2 y me da el siguiente error: RuntimeError: The layer has never been called and thus has no defined output shape. ¿Cómo podría solucionarlo? Muchas gracias.

    • Sacha · February 2, 2021

      Me pasa lo mismo

    • Heliodoro · March 15, 2021

      Me pasa lo mismo

    • Na8 · March 22, 2021

      Hola, el tutorial esta pensado para correr en Tensorflow 1.13.2 (como se indica en el artículo), si creas un environment con esas especificaciones funcionará correctamente.
      De todas maneras voy a intentar revisarlo para tf 2 cuando pueda, es que sinceramente estoy con mucha carga laboral últimamente.
      Saludos y gracias por escribir

    • javier · June 25, 2021

      Buenas, conseguiste solucionar el error?

  17. Federico Gonzalez Brizzio · February 21, 2021

    Hola Juan,

    Gracias por el tutorial, super claro. Como la versión cpu demora muchísimo en procesar, me pareció buena idea montar el código sobre google colab y aprovechar la gpu. El problema es qué hay errores con las nuevas versiones de tensorflow.

    Sería genial actualizar el tutorial para tensorflow 2 con gpu. Alguna recomendación sobre los problemas entre versiones? Me gustaría hacerlo correr correctamente y luego si te interesa compartirlo como complemento a este post.

    Saludos desde Ushuaia!

    • Na8 · March 22, 2021

      Hola Federico, gracias por escribir. El ejercicio estaba pensado para Tensorflow 1.13.2. Habría que hacer una migración para TF 2 y también para el uso de cuda con GPU. Me encantaría poder hacerlo pero ahora mismo estoy sobrepasado de tareas y debo posponerlo. Si logras avances me cuentas! Yo lo intentaré dentro de unas semanas y si lo consigo te escribo. Un saludo!

  18. Jesus · March 8, 2021

    Hola. Revisando el codigo en el repo de GitHub, he visto que en la funcion “leer_anotaciones()” se devuelven dos variables, una de ella es “seen_label” que entiendo que se corresponde luego con la variable “train_label”. Mi pregunta es, ¿donde se vuelve a usar esa variable? Esperaba verla en el metodo train() de la clase Yolo, al igual que se le pasa por ejemplo “train_images”, pero no la he visto. ¿Como es posible entonces que la red aprenda a clasificar cada imagen de pieza de lego con su bounding box?

    Jesus

    • Na8 · March 22, 2021

      Hola Jesus, gracias por escribir. Si, las labels contienen la información de la posición de los objetos, con lo cual lo necesita el problema de tipo aprendizaje supervisado.
      Si te fijas, se le pasa las Labels al constructor del objeto YOLO() y ahí dentro de la clase lo usa en self.labels para entrenar y ajustar los pesos de la red neuronal -al momento de hace el backpropagation-.
      Un saludo!

      • Cesar muñoz araya · March 22, 2021

        Pero, que sentido tiene entregarle las cordenadas de las cajas a la red como input, de hecho, cuando uno hace inferencia en la red nunca le entrega un solo valor de caja …
        En varias implementaciones basadas en la que tienes tu, no han podido explicar adecuadamente el motivo y es un tanto frustrante. Lei por github que se debe a un bug de keras, pero … no estoy tan seguro del motivo.

        • Na8 · March 23, 2021

          Hola Cesar, recién estaba mirando el código y me apuré a responder antes. Realmente veo que la info que va en label es simplemente “[lego]” es decir, la clase del objeto que detecta.
          La info de la posición de la bounding box está dentro de “train_imgs”.

          Como bien dices, en el input del modelo se le pasan 2 inputs: Model([input_image, self.true_boxes], output)

          En el BatchGenerator se sobreescribe el método getItem() para cumplir con esa doble entrada al momento de entrenar.

          Al hacer el predict, se le pasa la imagen y un “dummy_array” con zeros, entonces cuando el predict detecta la (o las) clase de lego, por cada una también habrá calculado el “error” de las bounding boxes. Y ese error es la posición x e y de las cajas. Como habíamos puesto ceros, coincidirá ese error con la posición en pixeles.

          Espero que se aclare un poco el comportamiento!!
          Saludos

  19. Cesar muñoz araya · March 22, 2021

    En cuanto a como elegir “las anclas” (anchors en el paper yolo9000), el autor joseph redmon et al, dice que la distancia usada en el algoritmo debe ser d(centers, bbox) = 1 -1 IoU(center, bbox), entre las cajas y los centros, es decir que el k-means que trae por defecto Sklearn no es el adecuado, ya que no prioriza una buena ancla para deteccion de objeto si no las cajas mas similares a las de tu data set. eso significa que es posible que no obtengas buenas cajas por ejemplo cajas horizontales.

    Recomiendo implementar una algoritmo propio de k-means

  20. Óscar · March 25, 2021

    Hola, Juan. Lo primero, gran blog el tuyo y el libro que has escrito.Lo tengo desde hace meses y me está viniendo muy bien como apoyo a un Máster que estoy realizando.
    Te comento, por curiosidad estoy tratando de implementar este código por ver como funciona (copia y pega, no te voy a engañar).

    El caso es que cuando voy a entrenar la red me salta este error:

    instanciamos al modelo

    yolo = YOLO(input_size = tamanio,
    labels = labels,
    max_box_per_image = 5,
    anchors = anchors)

    AttributeError Traceback (most recent call last)
    in
    3 labels = labels,
    4 max_box_per_image = 5,
    —-> 5 anchors = anchors)

    in init(self, input_size, labels, max_box_per_image, anchors)
    24 self.true_boxes = Input(shape=(1, 1, 1, max_box_per_image , 4))
    25
    —> 26 self.feature_extractor = FullYoloFeature(self.input_size)
    27
    28 print(self.feature_extractor.get_output_shape())

    in init(self, input_size)
    149
    150 self.feature_extractor = Model(input_image, x)
    –> 151 self.feature_extractor.load_weights(FULL_YOLO_BACKEND_PATH)
    152
    153 def normalize(self, image):

    ~\anaconda3\envs\DeteccionImagenes\lib\site-packages\keras\engine\topology.py in load_weights(self, filepath, by_name)
    2617 load_weights_from_hdf5_group_by_name(f, self.layers)
    2618 else:
    -> 2619 load_weights_from_hdf5_group(f, self.layers)
    2620
    2621 if hasattr(f, ‘close’):

    ~\anaconda3\envs\DeteccionImagenes\lib\site-packages\keras\engine\topology.py in load_weights_from_hdf5_group(f, layers)
    3040 “””
    3041 if ‘keras_version’ in f.attrs:
    -> 3042 original_keras_version = f.attrs[‘keras_version’].decode(‘utf8’)
    3043 else:
    3044 original_keras_version = ‘1’

    AttributeError: ‘str’ object has no attribute ‘decode’

    ¿sabrías decirme a qué se debe y como solucionarlo?

    Gracias de antemano.

    • Na8 · March 26, 2021

      Hola Oscar, gracias por escribir y por el libro! ¿Puedes confirmar que usas las versiones que comentamos en el artículo?, es decir:
      pip install tensorflow==1.13.2
      pip install keras==2.0.8
      Saludos!

      • jesus · March 28, 2021

        Hola Juan:
        Genial el blog.
        Para probar tu propuesta he creado en Anaconda varios entornos, y en todos surgen diferentes problemas.
        Usando CPU (Sin GPU).
        tensorflow 1.13.2
        keras=2.08
        python 3.6

        Error:
        ~\Anaconda3\envs\prueba\lib\site-packages\keras\engine\topology.py in load_weights_from_hdf5_group(f, layers)
        3040 “””
        3041 if ‘keras_version’ in f.attrs:
        -> 3042 original_keras_version = f.attrs[‘keras_version’].decode(‘utf8’)
        3043 else:
        3044 original_keras_version = ‘1’

        AttributeError: ‘str’ object has no attribute ‘decode’

        *** Si lo haces con GPU da problemas similares
        Si metes versiones más recientes de tensorflow , llegas a instanciar pero da errores al intentar entrenar la red….

        Alguna idea??

        • Jose Coronel · April 25, 2021

          Yo tengo el mismo problema y no he podido encontrar solucion 😦

        • Paco Serrador · April 6, 2022

          Hola. Yo tenía el mismo problema, seguramente porque Anaconda no es exactamente Python. Después de buscar en Internet vi que el motivo está en que quiere «descodificar» un String. Entré en el archivo que aparece en el error que reportas, con la ruta «.conda/env/prueba… etc,etc», que se llama ‘topology.py’ y comenté las líneas en que intenta volver a «decode» un objeto que ya es String. Como verás, son varios if/else, dejé sin efecto la línea en que intenta decodificar y solo dejé activa la que le da un valor fijo. Tal que así

          “””if ‘keras_version’ in f.attrs:
          original_keras_version = f.attrs[‘keras_version’] #.decode(‘utf8’)
          else:”””
          original_keras_version = ‘1’

          y después de esto ya funcionó. Espero que te sirva.

  21. aldo · April 10, 2021

    impecable! muchas gracias… soy aficionado a python hara un año o dos… lo empece como hobby y se empezo a hacer vicio… muchas gracias

    • Na8 · April 10, 2021

      Hola Aldo, gracias por escribir! Adelante con python! Un vicio sano,😄saludos

  22. Gari · April 13, 2021

    Hola Juan: muchas gracias por el blog, es muy claro e intuitivo. Te escribo para saber si has podido actualizar el ejemplo para versiones de Tensorflow actuales. Ya he leído que está diseñado para Tensorflow menor que 2. Pero me da miedo bajar de versión y que luego me dé problemas en otros códigos. Tuve ya problemas de versiones entre Tensorflow y Keras.
    Gracias!!!

  23. Emiliano Ocampo · April 19, 2021

    muy bueno…me leí todo y tengo en mente entrenar a una red neuronal para detectar partes de una estructura (antenas)…es para mi laburo…necesito investigar mucho pero gusto esto y creo me va a ayudar. Tengo una consulta, como debería llevarlo a la practica para usarlo en día a día…necesito que detecte objetos en muchas fotos que le voy a ir ingresando y que me las vaya separando, solo eso…que me clasifique los objetos de una imagen. Ej: todas las tuercas que se vean en una foto…

    • Na8 · April 22, 2021

      Si, lo puedes usar perfectamente.
      Con el modelo entrando lo puedes consumir como más te guste:
      un simple script de python desde línea de comando,
      puedes crearte una interfaz gráfica TKinter,
      Ó puedes crear un servicio desde una API rest propia (por ejemplo con Clase)
      Saludos!

  24. Candela Buteler · April 29, 2021

    Hola Juan!! Muchas gracias y felicitaciones por todo este trabajo!! Estoy tratando de implementar este codigo en un problema de identificación de tortugas marinas y me sale un error que no he podido solucionar…
    En la parte que dice

    run k_mean to find the anchors

    annotation_dims = []

    for image in train_imgs:
    cell_w = image[‘width’]/grid_w
    cell_h = image[‘height’]/grid_h

    for obj in image[‘object’]:
    relative_w = (float(obj[‘xmax’]) – float(obj[‘xmin’]))/cell_w
    relatice_h = (float(obj[“ymax”]) – float(obj[‘ymin’]))/cell_h
    annotation_dims.append(tuple(map(float, (relative_w,relatice_h))))

    annotation_dims = np.array(annotation_dims)
    centroids = run_kmeans(annotation_dims, num_anchors)

    write anchors to file

    print(‘\naverage IOU for’, num_anchors, ‘anchors:’, ‘%0.2f’ % avg_IOU(annotation_dims, centroids))
    print_anchors(centroids)

    sale el ERROR:

    KeyError Traceback (most recent call last)
    in
    91
    92 for image in train_imgs:
    —> 93 cell_w = image[‘width’]/grid_w
    94 cell_h = image[‘height’]/grid_h
    95

    KeyError: ‘width

    Como que no encuentra esos atributos de las imagenes. Podrías ayudarme.??

    • Na8 · June 20, 2021

      Hola Candela, pudiste resolver el problema? Si no, dime y te escribo por email, pues sería conveniente ver el código completo, aunque sospecho que puede ser por tener una versión distinta de alguna librería.
      Saludos

  25. cora san · May 1, 2021

    hola NA8 oye amigo busco hacer un reconocimiento de botellas de plastico, carton y vidrio
    me podrias ayudar porfavor ?

  26. BRYAN · June 21, 2021

    Hola Juan!
    Tal vez hay como incluir líneas de código para contar cuantos legos van apareciendo?

    Gracias

  27. S Rincon · August 1, 2021

    Hola Juan!!
    Porfavor me indicas donde debo crear el archivo .py, pues no me esta reconociendo las librerías que descargue con pip.
    Muchas Gracias!

  28. Bryan R. · September 10, 2021

    Hola Juan, quisiera saber en que parte del código se usa el backpropagation y como hace para que reconozca los objetos a través de este algoritmo, gracias.

    • Na8 · November 29, 2021

      Usamos la función de custom loss para que la propia api de Keras (y tensoflow backend) nos abstraiga del backpropagation que realiza internamente al hacer el “fit()” para entrenar al modelo.
      Un saludo!

  29. Emmanuel · January 31, 2022

    Que tal, me gustaría saber si existe alguna forma de determinar el tamaño del bbox, sean en pixeles o en mm, gracias.

  30. Raúl · February 2, 2022

    Hola
    Despues de entrenar 7 horas… me sale

    Detectados 0

    ¿Porque? se graba automaticamente los pesos?? he hecho los mismos pasos y con las mismas versiones…en un entorno de python

    Gracias por tu ayuda

    • Na8 · April 9, 2022

      Hola, gracias por escribir.
      Habría que ver con más detalle qué está ocurriendo para que la red neuronal no logre detectar los objetos.
      Si puedes contacta conmigo por el formulario e intentamos intercambiar notebooks o archivos para ver cómo poder solucionarlo.
      Un saludo

  31. Jerry · February 13, 2022

    Buenas mi estimado amigo, leí cada punto de su redacción y me parece muy bueno. Mi duda es… ¿Se puede reconocer cualquier imagen sin la necesidad de etiquetar o entrenar la IA nuevamente? ¿O es obligatorio pasar por todo de nuevo? … Verá, me encuentro en un proyecto de «Reconocimiento de imágenes de cerdos» empleando Yolo, no hace muchos días le mostré a mi tutor mi procedimiento y él desea que la IA reconozca las imágenes que mi tutor me envía… Y no obtuve mucho éxito al intentar hacer lo que mi tutor requería, «lo que le hace falta a su proyecto nada más es consumir el modelo», según la petición de él… Por favor, si fuera tan amable de ayudarme con eso, le estaría eternamente agradecido

    • Na8 · April 9, 2022

      Hola Jerry, gracias por escribir, disculpa la tardanza en responder, espero mi respuesta aun te pueda ayudar.
      Por lo que cuentas pareciera que tu modelo tuviera alguna clase de overfitting y no fuera capaz de detectar “imágenes antes no vistas”.
      Entonces primero deberías intentar agrandar tu dataset con mas imágenes.
      Luego, una vez entrenado es cierto que no hace falta RE-ENTRENAR el modelo cada vez. Lo puedes guardar en un archivo serializado y hacer predicciones directamente.
      Tengo otro artículo que tal vez pueda ayudarte/orientarte a cómo hacerlo. Tu propio Servicio de Machine Learning
      Un saludo

  32. Jessica Rendón · April 1, 2022

    Hola Juan, estoy en proceso de realizar mi proyecto de grado, he leído este artículo y me he basado en él para hacer la detección de drones, sin embargo, tengo problemas con el notebook, específicamente en la parte de importaciones, pues librerías como cv2, keras, imgaug, no los reconoce en el notebook, si me pudieras ayudar, quedaría muy agradecida.

  33. Hanck · April 2, 2022

    Hola Juan tengo problemas con la función _interval_overlap, espero me pudieras ayudar

    • Na8 · April 9, 2022

      Hola, Hank, si pudieras ser más específico y decirme el problema que tienes, ó si puedes enviar la info al formulario de contacto, así lo puedo ver e intento ayudarte.
      Saludos

  34. Pedro Carrasco · April 23, 2022

    Estimado, muchas gracias por compartir tu conocimiento.

    despues de 1000 minutos, no detecta nada, sin embargo tuve 2 percances durante.
    1) full_yolo_backend.h5 no estaba disponible en el link que indicas, asi que lo baje desde otra parte.
    2) el computador me entro en hibernacion durante el proceso de entrenamiento.

    empezare de nuevo de todas formas, será posible que actualices el link de full_yolo_backend.h5

    desde ya muchas gracias.

    • Na8 · April 23, 2022

      Hola! Mañana lo reviso y te paso enlace. Gracias por avisar!

    • Na8 · May 7, 2022

      Te paso el enlace de los pesos de Darknet ya que sin previo aviso cambió de ruta!:
      La nueva ruta de descarga es esta: full_yolo_backend.h5.
      Un saludo

  35. wolfan rodriguez · April 24, 2022

    hola juan, yo apenas estoy aprendiendo y habia visto antes clasificadores sin yolo, el mas comun con 10 clases diferentes, por lo tanto hay que hacer 10 conjuntos de imagenes de cada clase, yo estoy empezando un proyecto, en el cual necesitare muchas clases mas, talvez 100, y en cada imagen pueden haber 1,2,3,4 o mas clases de las que necesito detectar, con yolo, uno hace un solo conjunto de imagenes? no necesito separar los conjuntos? ya que con labelImg se van creando las clases? asi es como funciona? gracias ,espero tu respuesta pronto

  36. Pedro Carrasco · May 3, 2022

    genial, me ha funcionado todo, pero ahora que estoy intentando entrenar para otro modelo, no me queda claro el tema de la anclas, como se generan, ya que cada vez que ejecuto la funcion, entrega valores diferentes.

  37. Javier · May 6, 2022

    Hola, Juan, google me indica que la URL del archivo «Pesos iniciales de la red Darknet de Yolov2 (192MB)» no es correcta o no existe

    • Na8 · May 7, 2022

      Hola, el enlace original (era del usuario creador) esta roto, por lo que lo he reemplazado con uno nuevo, desde donde podes encontrar el archivo. La nueva URL es esta: full_yolo_backend.h5.
      Gracias por avisarme, un saludo

      • Javier · May 8, 2022

        Muchas gracias :3

Leave a Reply to jesusCancel reply