febrero 23, 2022 10:00 am

Jesús

Si has seguido esta serie de artículos sobre OCR con Tesseract desde el principio, te habrás dado cuenta de que rara vez podemos escanear perfectamente el texto de una imagen.

Bueno, no sin un mínimo procesamiento o ajuste previo. 

Si bien podemos alterar el comportamiento de Tesseract mediante flags y opciones, como whitelisting, blacklisting, PSM (muy pronto un extenso artículo sobre este tema), entre otras, no debemos olvidar que, al final del día, estamos lidiando con imágenes.

¿Qué quiere decir esto? ¿Qué implicaciones tiene?

Sabiendo cuál es el texto que queremos extraer, podemos aplicar un sinnúmero de transformaciones para realzar los caracteres a escanear, a la vez que reducimos el ruido y la prevalencia de elementos sin importancia, como objetos, rostros, el fondo, entre otros.

Para ello, acudiremos, como habrás adivinado, a la librería por excelencia de computer vision, OpenCV.

El propósito de este artículo es servir de guía o ejemplo sobre cómo podemos explotar el poder de OpenCV para mejorar sustancialmente los resultados de OCR de Tesseract.

Al final de este artículo sabrás:

  • Cuáles son los elementos que dificultan la extracción de texto en una imagen.
  • Cómo usar diversas transformaciones morfológicas con OpenCV para resaltar el texto de una imagen.
  • Cómo extraer el texto de una imagen con Tesseract, después de haberla procesado con OpenCV.

¿Estás listo? ¡Comencemos!

¡ATENTO!

Todas las transformaciones que veremos en breve han sido cubiertas en sus respectivos artículos aquí en DataSmarts, por lo que no las explicaremos en profundidad.


Si deseas aprender más sobre alguna de ellas, te recomiendo que utilices la función de búsqueda en el blog o, mejor aún, envíame un correo a jesus@datasmarts.net, y con gusto te guiaré.

¿Por Qué Tesseract No Puede Escanear una Imagen?

Mira la imagen que está abajo:

Esta es la imagen que procesaremos más adelante, en la sección práctica del artículo.

Sin embargo, antes de proseguir, vale la pena preguntarnos, ¿por qué Tesseract no puede reconocer el texto en esta imagen?

Para nosotros es muy fácil distinguir el “12-14” en la misma, pero para Tesseract resulta prácticamente imposible debido a, en parte, estas razones:

  • El fondo es complejo y texturizado.
  • La iluminación del fondo es inconsistente, presentando mayor claridad de un lado que del otro.
  • Hay numerosas regiones negras a lo largo de la imagen, potencialmente dificultando la tarea de extraer el texto que nos interesa.

Como vemos, Tesseract por sí solo no cuenta con la capacidad de leer lo que para nosotros resulta evidente.

Por tal motivo, necesitamos procesar profundamente esta imagen para poder obtener un resultado deseable.

Así pues, sin más preámbulos, pongámonos manos a la obra.

Creación del Entorno de Desarrollo con Anaconda

A continuación vemos la estructura del proyecto:

Como notarás, nuestro proyecto se compone primordialmente del script process.py, ubicado dentro del directorio datasmarts.

La imagen que procesaremos, la cual vimos arriba, es challenge.png.

Te recomiendo que descargues el código completo asociado a este post en el siguiente formulario:

El siguiente paso lógico es crear y configurar el ambiente de Anaconda. Para ello, ubícate en la raíz del proyecto y ejecuta este comando:

conda env create -f pytesseract-image-processing

La configuración del ambiente (pytesseract-image-processing) es esta:

Para activar el ambiente, ejecuta:

conda activate pytesseract-image-processing

Pasemos a la siguiente sección.

Refinamiento de Resultados de Tesseract con OpenCV

Abre el archivo datasmarts/process.py, e inserta estas líneas, las cuales importan las dependencias del programa:

Acto seguido, definimos el menú del programa, el cual se compone solamente del parámetro -i/--image, la ruta a la imagen de entrada:

Cargamos la imagen, la pasamos a escala de grises, y aplicamos thresholding mediante el algoritmo de Otsu para resaltar el texto de interés, a la vez que restamos prominencia a los elementos del fondo:

Calculamos la transformada de distancia, la cual determina la distancia de cada píxel al píxel más cercano cuyo valor es cero (negro). Luego normalizamos el resultado para que cada píxel esté en el rango [0, 255] y mostramos el resultado:

Aplicamos nuevamente Otsu, pero esta vez al resultado de la operación anterior. Lo que buscamos es que el texto se mantenga en primer plano, mientras eliminamos aún más ruido y elementos indeseados del fondo:

Ahora aplicamos una aplicación morfológica conocida como “apertura”, que lo que hace es dilatar y luego erosionar. El efecto conseguido es la desconexión de grupos de píxeles comúnmente asociados al ruido y elementos que no queremos:

Lo siguiente que haremos es extraer contornos suficientemente grandes de la imagen, con el fin de quedarnos sólamente con los números:

El próximo paso es aislar el área correspondiente a los contornos del resto de la imagen. Para ello, hallamos la cáscara convexa (convex hull) que envuelve todos los números, y la usamos para crear y aplicar una máscara para aislar los números:

Por fin, usamos Tesseract para extraer el texto. Fíjate que estamos utilizando un whitelist de sólo dígitos:

Finalmente, mostramos la imagen después de todo el procesamiento y destruimos las ventanas creadas por el programa:

¡ATENTO!

Antes de ejecutar el código, asegúrate de hallarte en la raíz del proyecto para que los comandos funcionen correctamente.

Para correr el programa, ejecuta esta instrucción en tu terminal:

python datasmarts/process.py -i challenge.png 

En primer lugar, veremos el resultado de aplicar el thresholding de Otsu a la imagen original:

Vemos que los números aparecen en blanco en primer plano.

La siguiente imagen corresponde a la transformada de distancia. Los números se ven mucho más claros, y los elementos del fondo casi han desaparecido:

Después veremos el resultado de aplicar Otsu a la transformada de distancia:

Claramente, nos deshicimos de un montón de ruido y elementos irrelevantes del fondo.

Luego, aparecerá el producto de aplicar la transformación de apertura a la imagen anterior, lo que nos ayudó a conseguir que el 1 se separara del grupo de píxeles blancos en la parte superior. Esto nos ayudará más adelante a mejorar el resultado entregado por Tesseract:

Las últimas dos imágenes se corresponden a la máscara asociada al convex hull de los números, y al resultado final de todo el procesamiento, respectivamente:

¿Será que Tesseract si logra leer los dígitos esta vez?

Al consultar la consola, veremos que, en efecto, ¡Tesseract lo logró!:

1214

Genial, ¿no?

Resumen

Hoy aprendimos a combinar el poder de OpenCV con el de Tesseract para poder extraer texto de imágenes extremadamente complicadas de leer.

Como descubrimos, Tesseract es, primariamente, una librería enfocada en la extracción de texto, y si bien cuenta con múltiples herramientas que nos permiten alterar su comportamiento, cuando se trata de procesar, alterar, modificar y limpiar imágenes, no hay sustituto para OpenCV.

Cuando una imagen tiene un fondo tan complicado como la de hoy, la funcionalidad de segmentación básica de Tesseract se queda corta, por lo que operaciones como thresholding, transformadas, morfologías, aperturas, dilataciones y máscaras, todas soportadas por OpenCV, son nuestra mejor apuesta.

Te invito a descargar el código a continuación:

¡Hasta la próxima!

Sobre el Autor

Jesús Martínez es el creador de DataSmarts, un lugar para los apasionados por computer vision y machine learning. Cuando no se encuentra bloggeando, jugando con algún algoritmo o trabajando en un proyecto (muy) cool, disfruta escuchar a The Beatles, leer o viajar por carretera.