PyTesseract: Reconocimiento óptico de caracteres de Python simple

Los seres humanos pueden entender el contenido de una imagen con solo mirar. Percibimos el texto de la imagen como texto y podemos leerlo. Las computadoras no funcionan de la misma manera...

Introducción

Los seres humanos pueden entender el contenido de una imagen con solo mirar. Percibimos el texto de la imagen como texto y podemos leerlo.

Las computadoras no funcionan de la misma manera. Necesitan algo más concreto, organizado de una manera que puedan entender.

Aquí es donde entra en juego el Reconocimiento óptico de caracteres (OCR). Ya sea que se trate del reconocimiento de matrículas de automóviles con una cámara o de documentos escritos a mano que deben convertirse en una copia digital, esta técnica es muy útil. Si bien no siempre es perfecto, es muy conveniente y hace que sea mucho más fácil y rápido para algunas personas hacer su trabajo.

En este artículo profundizaremos en el Reconocimiento Óptico de Caracteres y sus áreas de aplicación. También crearemos un script simple en Python que nos ayudará a detectar caracteres de imágenes y exponerlos a través de una aplicación Flask para un medio de interacción más conveniente.

¿Qué es el reconocimiento óptico de caracteres? {#qué es el reconocimiento óptico de caracteres}

El reconocimiento óptico de caracteres implica la detección de contenido de texto en imágenes y la traducción de las imágenes a texto codificado que la computadora puede entender fácilmente. Una imagen que contiene texto se escanea y analiza para identificar los caracteres que contiene. Tras la identificación, el carácter se convierte en texto codificado por máquina.

¿Cómo se logra realmente? Para nosotros, el texto de una imagen es fácilmente discernible y podemos detectar caracteres y leer el texto, pero para una computadora, todo es una serie de puntos.

Primero se escanea la imagen y los elementos de texto y gráficos se convierten en un mapa de bits, que es esencialmente una matriz de puntos en blanco y negro. Luego, la imagen se procesa previamente y el brillo y el contraste se ajustan para mejorar la precisión del proceso.

La imagen ahora se divide en zonas que identifican las áreas de interés, como dónde están las imágenes o el texto, y esto ayuda a iniciar el proceso de extracción. Las áreas que contienen texto ahora se pueden dividir aún más en líneas, palabras y caracteres, y ahora el software puede hacer coincidir los caracteres a través de la comparación y varios algoritmos de detección. El resultado final es el texto de la imagen que nos dan.

Es posible que el proceso no sea 100 % preciso y que necesite intervención humana para corregir algunos elementos que no se escanearon correctamente. La corrección de errores también se puede lograr usando un diccionario o incluso procesamiento del lenguaje natural (NLP).

La salida ahora se puede convertir a otros medios, como documentos de Word, PDF o incluso contenido de audio a través de tecnologías de texto a voz.

Usos de OCR

Anteriormente, la digitalización de documentos se lograba escribiendo manualmente el texto en la computadora. A través de OCR, este proceso se hace más fácil ya que el documento puede escanearse, procesarse y el texto extraerse y almacenarse en una forma editable, como un documento de Word.

Si tiene un escáner de documentos en su teléfono, como Adobe Scan, probablemente haya encontrado la tecnología OCR en uso.

Los aeropuertos también pueden usar OCR para automatizar el proceso de reconocimiento de pasaportes y extracción de información de ellos.

Otros usos de OCR incluyen la automatización de procesos de entrada de datos, detección y reconocimiento de matrículas de automóviles.

Lo que usaremos

Para este proyecto de OCR, utilizaremos la librería Python-Tesseract, o simplemente PyTesseract, que es un wrapper para Motor Tesseract-OCR de Google.

Elegí esto porque es completamente de código abierto y está siendo desarrollado y mantenido por el gigante que es Google. Siga estas instrucciones para instalar Tesseract en su máquina, ya que PyTesseract depende de ello.

También usaremos el Marco web matraz para crear nuestro servidor OCR simple donde podemos tomar fotos a través de la cámara web o cargar fotos con fines de reconocimiento de caracteres.

También vamos a usar Pipenv ya que también maneja la configuración del entorno virtual y la gestión de requisitos.

Además de eso, también usaremos la biblioteca Almohada que es una bifurcación de la Python Imaging Library (PIL) para manejar la apertura y manipulación de imágenes en muchos formatos en Python.

En esta publicación, nos concentraremos en PyTesseract aunque existen otras bibliotecas de Python que pueden ayudarlo a extraer texto de imágenes como:

  • Textracto: que puede extraer datos de archivos PDF pero es un paquete pesado.
  • Pyocr: ofrece más opciones de detección, como oraciones, dígitos o palabras.

Configuración

Comience instalando Pipenv usando el siguiente comando a través de Pip (en caso de que necesite configurarlo, consulte este).

1
$ pip install pipenv

Cree el directorio del proyecto e inicie el proyecto ejecutando el siguiente comando:

1
$ mkdir ocr_server && cd ocr_server && pipenv install --three

Ya podemos activar nuestro entorno virtual y comenzar a instalar nuestras dependencias:

1
2
$ pipenv shell
$ pipenv install pytesseract Pillow 

En caso de que no vaya a usar Pipenv, siempre puede usar el enfoque de Pip y entorno virtual. Sigue la documentación oficial para ayudarte a comenzar con Pepita y [Ambiente virtual](https://docs.python.org/3/tutorial /venv.html):

Nota: En ese caso, en lugar de pipenv install Pillow, el comando será pip install Pillow.

Implementación

Vamos a implementar este proyecto en 2 fases. En el primero, crearemos el script y, en el siguiente, crearemos una aplicación Flask para que actúe como interfaz.

Guión OCR

Con la configuración completa, ahora podemos crear una función simple que toma una imagen y devuelve el texto detectado en la imagen; este será el núcleo de nuestro proyecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
try:
    from PIL import Image
except ImportError:
    import Image
import pytesseract

def ocr_core(filename):
    """
    This function will handle the core OCR processing of images.
    """
    text = pytesseract.image_to_string(Image.open(filename))  # We'll use Pillow's Image class to open the image and pytesseract to detect the string in the image
    return text

print(ocr_core('images/ocr_example_1.png'))

La función es bastante sencilla, en las primeras 5 líneas importamos Imagen de la biblioteca Pillow y nuestra biblioteca PyTesseract.

Luego creamos una función ocr_core que toma un nombre de archivo y devuelve el texto contenido en la imagen.

Veamos cómo le va al script con una imagen simple que contiene algo de texto:

Y al ejecutar la pieza de código, somos recibidos con esto:

¡Nuestro sencillo script OCR funciona! Obviamente, esto fue algo fácil ya que este es un texto digital, perfecto y preciso, a diferencia de la escritura a mano. Hay mucho más que podemos hacer con la biblioteca PyTesseract, pero más sobre esto más adelante en la publicación.

Primero integremos este script en una aplicación Flask, para que sea más fácil cargar imágenes y realizar operaciones de reconocimiento de caracteres.

Interfaz web de matraz {#interfaz web de matraz}

Nuestro script se puede usar a través de la línea de comandos, pero una aplicación Flask lo haría más fácil de usar y versátil. Por ejemplo, podemos cargar fotos a través del sitio web y mostrar el texto extraído en el sitio web o podemos capturar fotos a través de la cámara web y realizar el reconocimiento de caracteres en ellas.

Si no está familiarizado con el marco Flask, este es un buen tuto para ponerlo al día.

Comencemos instalando el paquete Flask:

1
$ pipenv install Flask

Ahora, definamos una ruta básica:

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route('/')
def home_page():
    return "Hello World!"

if __name__ == '__main__':
    app.run()

Guarde el archivo y ejecute:

1
$ python3 app.py

Si abre su navegador y se dirige a 127.0.0.1:5000 o localhost:5000 debería ver "Hello World!" en la página. Esto significa que nuestra aplicación Flask está lista para los siguientes pasos.

Ahora crearemos una carpeta templates para alojar nuestros archivos HTML. Avancemos y creemos un index.html simple:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
  <head>
    <title>Index</title>
  </head>
  <body>
    Hello World.
  </body>
</html>

Modifiquemos también nuestro app.py para representar nuestra nueva plantilla:

1
2
3
4
5
6
7
8
9
from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def home_page():
    return render_template('index.html')

if __name__ == '__main__':
    app.run()

Observe que ahora hemos importado render_template y lo usamos para representar el archivo HTML. Si reinicia su aplicación Flask, aún debería ver "¡Hola mundo!" en la página de inicio.

Eso es suficiente en el curso acelerado de Flask, ahora integremos nuestro script OCR en la aplicación web.

Primero, agregaremos la funcionalidad para cargar imágenes en nuestra aplicación Flask y pasarlas a la función ocr_core que escribimos anteriormente. Luego representaremos la imagen junto al texto extraído en nuestra aplicación web como resultado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import os
from flask import Flask, render_template, request

# import our OCR function
from ocr_core import ocr_core

# define a folder to store and later serve the images
UPLOAD_FOLDER = '/static/uploads/'

# allow files of a specific type
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])

app = Flask(__name__)

# function to check the file extension
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

# route and function to handle the home page
@app.route('/')
def home_page():
    return render_template('index.html')

# route and function to handle the upload page
@app.route('/upload', methods=['GET', 'POST'])
def upload_page():
    if request.method == 'POST':
        # check if there is a file in the request
        if 'file' not in request.files:
            return render_template('upload.html', msg='No file selected')
        file = request.files['file']
        # if no file is selected
        if file.filename == '':
            return render_template('upload.html', msg='No file selected')

        if file and allowed_file(file.filename):

            # call the OCR function on it
            extracted_text = ocr_core(file)

            # extract the text and display it
            return render_template('upload.html',
                                   msg='Successfully processed',
                                   extracted_text=extracted_text,
                                   img_src=UPLOAD_FOLDER + file.filename)
    elif request.method == 'GET':
        return render_template('upload.html')

if __name__ == '__main__':
    app.run()

Como podemos ver en nuestra función upload_page(), recibiremos la imagen a través de POST y representaremos el HTML de carga si la solicitud es GET.

Verificamos si el usuario realmente ha subido un archivo y usamos la función allowed_file() para verificar si el archivo es de un tipo aceptable.

Al verificar que la imagen es del tipo requerido, la pasamos al script de reconocimiento de caracteres que creamos anteriormente.

La función detecta el texto en la imagen y lo devuelve. Finalmente, como respuesta a la carga de la imagen, representamos el texto detectado junto a la imagen para que el usuario vea los resultados.

El archivo upload.html manejará la publicación de la imagen y la representación del resultado con la ayuda del Motor de plantillas Jinja, que viene con Flask por defecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
 <head>
   <title>Upload Image</title>
 </head>
 <body>

   {% if msg %}
   <h1>{{ msg }}</h1>
   {% endif %}
  
   <h1>Upload new File</h1>
  
   <form method=post enctype=multipart/form-data>
     <p><input type=file name=file>
        <input type=submit value=Upload>
   </form>

   <h1>Result:</h1>
   {% if img_src %}
     <img src="{{ img_src }}">
   {% endif %}
  
   {% if extracted_text %}
     <p> The extracted text from the image above is: <b> {{ extracted_text }} </b></p>
  
   {% else %}
     The extracted text will be displayed here
   {% endif %}
 </body>
</html>

Las plantillas de Jinja nos permiten mostrar texto en escenarios específicos a través de las etiquetas {% if %} {% endif %}. También podemos pasar mensajes desde nuestra aplicación Flask para que se muestren en la página web dentro de las etiquetas {{ }}. Utilizamos un formulario para cargar la imagen en nuestra aplicación Flask.

El resultado es:

cargar página inicial

Ahora, si seguimos adelante y subimos nuestra imagen de antes:

resultado de la página de carga

¡Sí! Nuestra aplicación Flask ha podido integrar la funcionalidad OCR y mostrar el texto en el navegador. Esto facilita el procesamiento de imágenes en lugar de ejecutar comandos en la CLI cada vez que tenemos una nueva imagen para procesar.

Adjuntemos algunas imágenes más para explorar más a fondo los límites de nuestro sencillo script OCR, ya que no funcionará en todas las situaciones.

Por ejemplo, intentemos extraer texto de la siguiente imagen y el resultado se ha resaltado en la imagen:

canciones de versiones de exploración fallidas

Esta es una prueba de que el OCR no siempre es 100% preciso y puede necesitar la intervención humana de vez en cuando.

También probé el script de OCR con mi letra para ver cómo funcionaba, y este es el resultado:

mi letra

Como puede ver, no puede extraer el texto de mi letra como lo hizo con otras imágenes que hemos visto antes. Decidí darle otra oportunidad, esta vez con una imagen de esta fuente, y estos fueron los resultados:

downloaded handwritten

El reconocimiento de caracteres en esta imagen es mucho mejor que en el que usé mi propia letra. Como puede ver, las líneas en la imagen descargada son más gruesas y hay un mejor contraste entre el texto y el fondo y esta podría ser la razón de la mala detección de mi letra.

Esta es un área para explorar más a fondo, puede obtener notas escritas a mano de amigos o colegas y ver qué tan bien el script podrá detectar caracteres. Incluso puede obtener carteles para eventos e intentar escanearlos en busca de texto, las posibilidades son muchas.

Otras opciones de PyTesseract

Python-Tesseract tiene más opciones que puede explorar. Por ejemplo, puede especificar el idioma usando un indicador lang:

1
pytesseract.image_to_string(Image.open(filename), lang='fra')

Este es el resultado de escanear una imagen sin el indicador lang:

Y ahora con la bandera lang:

El marco también está optimizado para detectar mejor los idiomas, como se ve en las capturas de pantalla. (Fuente de imagen).

Sin la bandera lang, la secuencia de comandos omitió algunas palabras en francés, pero después de introducir la bandera, pudo detectar todo el contenido en francés. La traducción no es posible, pero sigue siendo impresionante. La documentación oficial de Tesseract incluye los idiomas admitidos en esta sección.

La orientación y la detección de secuencias de comandos también se encuentran entre las capacidades de PyTesseract y esto ayuda en la detección de las fuentes utilizadas y la orientación del texto en la imagen dada. Si podemos referirnos a la imagen escrita a mano que descargamos anteriormente:

1
print(pytesseract.image_to_osd(Image.open('downloaded_handwritten.png')))

No había información de número de página en la imagen, por lo que no se detectó. El motor Tesseract puede extraer información sobre la orientación del texto en la imagen y la rotación. La confianza de orientación es una figura de la seguridad del motor sobre la orientación detectada para actuar como guía y también para mostrar que no siempre es 100% precisa. La sección de guión denota el sistema de escritura utilizado en el texto y también va seguido del marcador de confianza.

Si buscábamos los caracteres reconocidos y los límites de sus recuadros, PyTesseract logra esto a través de pytesseract.image_to_boxes(Image.open('downloaded_handwritten.png')).

Estas son algunas de las capacidades de PyTesseract, entre otras, como la conversión del texto extraído en una salida PDF o HOCR con capacidad de búsqueda.

Lo que no hemos hecho {#lo que no hemos hecho}

Hemos logrado mucho en esta publicación, pero aún queda mucho por hacer para refinar nuestro proyecto y prepararlo para el mundo real. Primero, podemos agregar estilo a nuestro sitio web y hacerlo más atractivo para el usuario final mediante el uso de CSS. También podemos agregar la opción de cargar y escanear varias imágenes a la vez y mostrar todas sus salidas a la vez. ¿No haría esto más conveniente escanear varios documentos?

El navegador nos permite tocar la cámara de una máquina y capturar imágenes, con el permiso del usuario, por supuesto. Esto puede ser de gran ayuda especialmente en dispositivos móviles. En lugar de que el usuario tenga que capturar y guardar la imagen y luego cargarla en el sitio web, si agregamos la funcionalidad de la cámara, podemos permitir que el usuario realice las operaciones directamente desde la aplicación web Flask. Esto hará que el proceso de escaneo sea más rápido.

Suponga que una aplicación Flask no es lo que pretendía exponer su escáner OCR, también puede crear una herramienta CLI. La herramienta le permitiría ejecutar un comando que incluya la ubicación de la imagen y luego imprimir la salida del escáner en su terminal o enviarla a una base de datos o API. Si elige esta ruta, Docopt es una herramienta fantástica para crear herramientas de línea de comandos usando Python.

Conclusión

A través de Tesseract y la biblioteca Python-Tesseract, hemos podido escanear imágenes y extraer texto de ellas. Este es el Reconocimiento Óptico de Caracteres y puede ser de gran utilidad en muchas situaciones.

Hemos construido un escáner que toma una imagen y devuelve el texto contenido en la imagen y lo integramos en una aplicación Flask como interfaz. Esto nos permite exponer la funcionalidad en un medio más familiar y de una manera que pueda servir a varias personas simultáneamente.

El código fuente de este proyecto está disponible aquí en Github. mple).