Ganador del primer puesto:

ReadToMe

Inspiración

Para crear ReadToMe, me inspiré en mis hijos. En particular, en los dos más pequeños, de 3 y 5 años. Ambos aman los libros, pero aún no saben leer. Quería crear algo que les permitiera disfrutar de la lectura incluso cuando no hubiera un adulto para leerles.

Qué hace

ReadToMe utiliza el aprendizaje profundo y la visión artificial, en conjunto con otros servicios disponibles en AWS. Tan solo tiene que mostrarle a AWS DeepLens la página que quiere que lea, y la leerá en voz alta.

Creada porAlex Schultz

Obtenga más información sobre Alex y el proyecto ReadToMe en esta publicación del blog de AWS Machine Learning.

Cómo se desarrolló

A pesar de que el flujo de trabajo suena muy simple, hubo que superar muchos desafíos para que la aplicación funcionara. Estos son los distintos problemas que tuve que resolver.

  • Determinar cuándo la página que se quiere leer está en el encuadre de la cámara
  • Aislar el bloque de texto y limpiar la imagen con OpenCV
  • Aplicar el reconocimiento óptico de caracteres (OCR)
  • Convertir el texto en audio
  • Reproducir el audio con los altavoces

Cada uno de estos pasos supuso desafíos inesperados, y algunos de ellos me hicieron dudar de si iba a poder terminar el proyecto a tiempo.

Determinar cuándo la página que se quiere leer está en el encuadre de la cámara

ReadToMe utiliza el aprendizaje profundo y la visión artificial, en conjunto con otros servicios disponibles en AWS. Tan solo tiene que mostrarle a AWS DeepLens la página que quiere que lea, y la leerá en voz alta. Para que DeepLens pudiera leer una página, tenía que saber de alguna manera si había algo para leer en el encuadre de la cámara. Aquí es donde se utilizó el modelo de aprendizaje profundo.

Busqué online, pero no pude encontrar un modelo preentrenado con el que se pudiera clasificar una página de un libro. Supe entonces que tendría que entrenar un modelo con datos nuevos a fin de poder clasificar este tipo de objeto.

Realicé una búsqueda online rápida con el fin de encontrar imágenes de libros infantiles, que constituirían la información para el entrenamiento. Encontré miles de imágenes de portadas de libros, pero prácticamente ninguna donde se mostrara a alguien sosteniendo un libro en la orientación correcta que pudiera utilizar para realizar el entrenamiento. Necesitaba imágenes de las páginas propiamente dichas, con texto. Afortunadamente, mis cuatro hijos pequeños tienen cientos de libros infantiles. Entonces, una noche tomé alrededor de cuarenta libros y comencé a tomar fotos en las que yo sostenía los libros en diferentes orientaciones, con distintas condiciones de iluminación y también obstruyendo con la mano diferentes partes de la página.

Durante el proceso, comencé a darme cuenta de que los bloques de texto varían mucho en los libros para niños. A veces, había letras blancas sobre un fondo negro, y otras, letras negras sobre un fondo blanco. Otras veces, las letras eran de colores sobre fondos de distintos colores. En ocasiones, el texto estaba en la parte inferior y, en algunos libros, estaba en cualquier lugar de la página, sin seguir una secuencia lógica. Por eso, decidí limitar el alcance y solamente tomar imágenes de libros donde el texto estuviera en una posición “más o menos normal”. Me di cuenta de que, si me empeñaba en utilizar un conjunto de datos demasiado amplio, terminaría con un modelo que consideraría que todo es un bloque de texto. Más adelante quizá pueda experimentar con tipos de datos más variados, pero decidí limitarlos para este proyecto.

Después de la captura de datos, utilicé labelImg para generar los archivos xml de Pascal VOC que se iban utilizar en el entrenamiento. En este punto, me atasqué tratando de descifrar cómo darles a los datos el formato correcto para el entrenamiento con MXNet. Encontré un par de ejemplos donde se utilizaba TensorFlow y decidí hacer lo mismo. Consideré que, si terminaba el proyecto con tiempo de sobra, siempre podría volver atrás y hacerlo funcionar con MXNet. Pude seguir uno de los ejemplos que encontré en YouTube y así obtuve un modelo operativo que detectaba el texto en una página. 

Aplicar el reconocimiento óptico de caracteres

Me sorprendió lo fácil que fue integrar Tesseract en el proyecto. Solo tuve que instalarlo en el dispositivo y después instalar los paquetes de Python con pip. Luego, el flujo de trabajo consistió realmente en una sola llamada de función. Solo hay que proporcionar una imagen para procesar, y el sistema separa el texto. En un principio, iba a utilizar una función de Lambda separada con Tesseract instalado para realizar el OCR, pero terminé incluyendo esta tarea en mi función de Lambda principal dado que era más simple y disminuía el tráfico desde y hacia AWS. La operación de OCR no parecía utilizar mucha potencia de cómputo y, si la comparaba con el viaje de ida y vuelta a AWS, tomaba un tiempo similar. Además, así se procesa más “en el borde”, lo cual es más eficiente y cuesta menos.

Pero hay un pequeño detalle con Tesseract: es muy exigente con la calidad de la imagen. Me costó mucho trabajo darme cuenta de cómo limpiar las imágenes lo suficiente como para que el sistema las pudiera leer. También requiere que el texto esté casi completamente horizontal (algo casi imposible si se tiene en cuenta que quiero que niños en edad preescolar usen la aplicación). Usé OpenCV para la mayor parte de este preprocesamiento de la imagen y, después de algunas iteraciones, pude obtener imágenes que consistían solo en texto en blanco y negro, con un mínimo de ruido. Esto fue clave para que el proyecto funcionara. Aunque los resultados obtenidos fueron mejores de lo que esperaba, todavía hay mucho por mejorar en esta área.

Convertir el texto en audio

Este fue el paso más fácil de todo el proyecto. Una vez que logré que el dispositivo reprodujera audio, pude encapsular esta lógica en una sola llamada de función que simplemente llama a AWS Polly para generar el archivo de audio. El archivo nunca se graba en el disco, simplemente se alimenta la biblioteca de audio con la secuencia de bytes y se elimina cuando finaliza la reproducción. Sí hay algunos archivos mp3 estáticos que se reproducen durante el arranque del servicio Green Grass. Estos archivos de audio son para leerle instrucciones al usuario a fin de que aprenda a usar el dispositivo. Pensé que no había necesidad de llamar a Polly y generar este mensaje, dado que siempre es el mismo. Por eso, lo generé e implementé previamente con Lambda. (Me encanta cuán fácil es integrar los servicios de AWS en mis proyectos).

Reproducir el audio con los altavoces

Green Grass solicita una autorización explícita para todo el hardware al cual accede el código. Una forma de hacer esto es mediante la sección de recursos de grupo de la consola de AWS IoT. Una vez configurada, se implementa esta configuración en DeepLens, con lo que se implementa un archivo JSON en el directorio greengrass del dispositivo.

Para activar la reproducción de audio mediante Lambda, hay que agregar dos recursos. La tarjeta de sonido de DeepLens se encuentra en la ruta “/dev/snd/”. Es necesario agregar “/dev/snd/pcmC0D0p” y “/dev/snd/controlC0” para poder reproducir sonido.

Como nota al margen, resulta necesario volver a agregar esos recursos cada vez que se implementa un proyecto en el dispositivo. En el momento de redacción de este artículo, Green Grass sobrescribe el archivo de recursos con el archivo preconfigurado group.json cada vez que se implementa un proyecto en DeepLens. 

Logros de los que estoy orgulloso

Estoy muy satisfecho con los resultados obtenidos en este proyecto. A pesar de tener un empleo a tiempo completo y cuatro hijos, en apenas unos meses pude crear algo muy genial y que, además, funciona razonablemente bien. Tengo más ideas para proyectos futuros con este dispositivo y me entusiasma la idea de seguir aprendiendo. 

Lo que aprendí

Antes de zambullirme en este proyecto, no tenía experiencia con el aprendizaje profundo ni con la inteligencia artificial en general. Siempre me había interesado el tema, pero me parecía algo que era inaccesible para los “desarrolladores normales”. Gracias a este proceso, descubrí que se pueden crear proyectos realmente útiles con el aprendizaje profundo sin tener que ser un doctor en matemáticas y que, con esfuerzo y paciencia suficiente, cualquiera que tenga una formación aceptable como desarrollador puede comenzar a utilizarlo. 

El futuro de ReadToMe

Tengo algunas ideas para mejorar el proyecto. Una función que me gustaría agregar es que se pueda traducir el texto que se quiere leer. (Me inscribí para obtener acceso temprano al nuevo servicio Amazon Translate, pero aún no recibí la autorización). También quiero continuar mejorando el modelo para ver si se puede aumentar un poco la precisión y lograr que funcione con una gama más amplia de libros.

Por último, hay margen de mejora para la función de limpieza de la imagen de texto con que se alimenta Tesseract. En particular, sería muy útil poder rotar y deformar la imagen antes de enviarla a Tesseract. De esta manera, sería posible seguir leyendo el texto aunque el niño no esté sosteniendo el libro correctamente. El desenfoque por movimiento fue otro problema con el que me encontré a la hora de limpiar las imágenes. Si el libro no se mantiene absolutamente quieto por algunos segundos, la imagen resulta demasiado borrosa como para que el OCR funcione. Leí sobre distintas técnicas para resolver este problema, como promediar imágenes en distintos cuadros o aplicar diferentes filtros a la imagen a fin de suavizar los píxeles. Estoy seguro de que se puede lograr un resultado mejor y más rápido, pero es complicado hacerlo con un dispositivo con recursos limitados.

Recursos útiles

Encontré muchos recursos online que fueron de gran utilidad, pero estos enlaces fueron los más útiles de todos.

(No están en un orden en particular).

https://becominghuman.ai/an-introduction-to-the-mxnet-api-part-1-848febdcf8ab
http://gluon.mxnet.io/chapter08_computer-vision/object-detection.html?highlight=ssd
https://github.com/apache/incubator-mxnet/blob/master/example/image-classification/README.md
https://github.com/zhreshold/mxnet-ssd
https://pythonprogramming.net/introduction-use-tensorflow-object-detection-api-tutorial/

Además, quiero agradecer a todas las personas que participan y contestan preguntas en los foros y, en especial, al equipo de AWS DeepLens por sacarme de apuros tantas veces. :)

Desarrollado con

opencv
deeplens
python
polly
tesseract-ocr
lambda
mxnet
tensorflow