Saltar al contenido principal
InicioTutorialesPython

Tutorial completo sobre reconocimiento óptico de caracteres (OCR) en Python con Pytesseract

Domina los fundamentos del reconocimiento óptico de caracteres en OCR con PyTesseract y OpenCV.
Actualizado 11 sept 2024  · 11 min leer

¿Por qué aprender reconocimiento óptico de caracteres (OCR)?

Extraer texto de imágenes y documentos manualmente puede ser muy tedioso y llevar mucho tiempo. Afortunadamente, el OCR (Reconocimiento Óptico de Caracteres) puede automatizar este proceso, permitiéndote convertir esas imágenes en archivos de texto editables y con capacidad de búsqueda.

Las técnicas que vas a aprender pueden aplicarse en muchos ámbitos:

  • Digitalizar documentos físicos: Convierte documentos escaneados, PDF o fotos de texto en archivos editables y con capacidad de búsqueda.
  • Automatizar la introducción de datos: Extrae información de formularios, facturas, recibos y otros documentos para rellenar automáticamente bases de datos u hojas de cálculo.
  • Mejorar la accesibilidad: Crea versiones de texto digital de libros, menús o señales para personas con discapacidad visual. A continuación, se pueden utilizar herramientas de texto a voz para leer el texto en voz alta.
  • Coches autónomos: Reconocer las señales de tráfico y las matrículas para una navegación segura.

El tutorial se centrará en el motor Tesseract OCR y su API Python - PyTesseract. Antes de empezar a escribir código, repasemos brevemente algunas de las bibliotecas populares dedicadas al OCR.

Las mejores bibliotecas OCR de código abierto en Python

Dado que el OCR es un problema actual muy popular, muchas bibliotecas de código abierto intentan resolverlo. En esta sección, trataremos las que han ganado más popularidad debido a su alto rendimiento y precisión.

Teseracto

Tesseract OCR es un motor de reconocimiento óptico de caracteres de código abierto que es el más popular entre los desarrolladores. Como otras herramientas de esta lista, Tesseract puede tomar imágenes de texto y convertirlas en texto editable.

Ventajas

  • Biblioteca ampliamente utilizada y madura con una gran comunidad
  • Admite más de 100 idiomas
  • Libre y de código abierto

Desventajas

  • La precisión puede ser menor en comparación con algunas soluciones basadas en el aprendizaje profundo
  • Opciones de configuración limitadas

OCR fácil

EasyOCR es una biblioteca Python diseñada para el Reconocimiento Óptico de Caracteres (OCR) sin esfuerzo. Hace honor a su nombre ofreciendo un enfoque fácil de usar para la extracción de texto de imágenes.

Ventajas

  • Fácil de usar y de configurar
  • Alta precisión con modelos de aprendizaje profundo
  • Admite varios idiomas desde el principio

Desventajas

  • Dependen de modelos preentrenados, que pueden ser de gran tamaño
  • Puede ser más lento que Tesseract para tareas más sencillas

Keras OCR

Keras-OCR es una biblioteca de Python construida sobre Keras, un popular marco de aprendizaje profundo. Proporciona modelos de OCR listos para usar y un proceso de formación integral para crear nuevos modelos de OCR.

Ventajas

  • Enfoque basado en el aprendizaje profundo, que ofrece una gran precisión para varios tipos de texto
  • Selección y entrenamiento de modelos personalizables
  • Puede ser más preciso en diseños complejos o texto manuscrito

Desventajas

  • Requiere GPU para un rendimiento óptimo
  • Curva de aprendizaje más pronunciada debido a su naturaleza de aprendizaje profundo
  • Entrenar modelos personalizados puede llevar mucho tiempo

Aquí tienes una tabla que resume sus diferencias, ventajas e inconvenientes:

Nombre del paquete

Ventaja

Desventajas

Teseracto (pytesseract)

Maduro, ampliamente utilizado, amplio apoyo

Más lento, menor precisión en trazados complejos

EasyOCR

Fácil de usar, múltiples modelos

Menor precisión, personalización limitada

Keras-OCR

Mayor precisión, personalizable

Requiere GPU, curva de aprendizaje más pronunciada

En este tutorial, nos centraremos en PyTesseract, que es la API Python de Tesseract. Aprenderemos a extraer texto de imágenes sencillas, a dibujar cuadros delimitadores alrededor del texto y realizaremos un caso práctico con un documento escaneado.

Guía paso a paso del OCR con PyTesseract y OpenCV

Instalación

PyTesseract funciona sobre el motor oficial de Tesseract, que es un software CLI independiente. Antes de instalar pytesseract, debes tener instalado el motor. A continuación encontrarás instrucciones de instalación para diferentes plataformas.

Para Ubuntu o WSL2 (mi elección):

$ sudo apt update && sudo apt upgrade
$ sudo apt install tesseract-ocr
$ sudo apt install libtesseract-dev

Para Mac utilizando Homebrew:

$ brew install tesseract

Para Windows, sigue las instrucciones de esta página de GitHub.

A continuación, crea un nuevo entorno virtual. Utilizaré Conda:

$ conda create -n ocr python==3.9 -y
$ conda activate ocr

Después, debes instalar pytesseract para hacer OCR y opencv para manipular imágenes:

$ pip install pytesseract
$ pip install opencv-python

Si estás siguiendo este tutorial en Jupyter, ejecuta estos comandos en la misma sesión de terminal para que tu nuevo entorno virtual se añada como núcleo:

$ pip install ipykernel
$ ipython kernel install --user --name=ocr

Ahora, podemos empezar a escribir código.

Uso básico

Comenzamos importando las bibliotecas necesarias:

import cv2
import pytesseract

Nuestra tarea consiste en leer el texto de la siguiente imagen:

Una imagen de muestra para realizar el OCR con PyTesseract y OpenCV

En primer lugar, definimos la trayectoria de la imagen y la introducimos en la función cv2.imread:

# Read image
easy_text_path = "images/easy_text.png"
easy_img = cv2.imread(easy_text_path)

A continuación, pasamos la imagen cargada a la función image_to_string de pytesseract para extraer el texto:

# Convert to text
text = pytesseract.image_to_string(easy_img)
print(text)
This text is
easy to extract.

¡Así de fácil! Convirtamos lo que acabamos de hacer en una función:

def image_to_text(input_path):
   """
   A function to read text from images.
   """
   img = cv2.imread(input_path)
   text = pytesseract.image_to_string(img)

   return text.strip()

Utilicemos la función en una imagen más difícil:

Otra imagen de muestra para realizar el OCR con PyTesseract y OpenCV

La imagen ofrece un mayor desafío, ya que hay más símbolos de puntuación y texto en distintos tipos de letra.

# Define image path
medium_text_path = "images/medium_text.png"

# Extract text
extracted_text = image_to_text(medium_text_path)
print(extracted_text)
Home > Tutorials » Data Engineering

Snowflake Tutorial For Beginners:
From Architecture to Running
Databases

Learn the fundamentals of cloud data warehouse management using
Snowflake. Snowflake is a cloud-based platform that offers significant
benefits for companies wanting to extract as much insight from their data as
quickly and efficiently as possible.

Jan 2024 - 12 min read

Nuestra función funcionó casi a la perfección. Confundió uno de los puntos y signos ">", pero por lo demás el resultado es aceptable.

Dibujar cuadros delimitadores alrededor del texto

Una operación habitual en OCR es dibujar cuadros delimitadores alrededor del texto. Esta operación está soportada en PyTesseract.

Primero, pasamos una imagen cargada a la función image_to_data:

from pytesseract import Output

# Extract recognized data from easy text
data = pytesseract.image_to_data(easy_img, output_type=Output.DICT)

La parte Output.DICT garantiza que los detalles de la imagen se devuelvan como un diccionario. Echemos un vistazo al interior:

data
{'level': [1, 2, 3, 4, 5, 5, 5, 4, 5, 5, 5],
'page_num': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'block_num': [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'par_num': [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'line_num': [0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2],
'word_num': [0, 0, 0, 0, 1, 2, 3, 0, 1, 2, 3],
'left': [0, 41, 41, 236, 236, 734, 1242, 41, 41, 534, 841],
'top': [0, 68, 68, 68, 68, 80, 68, 284, 309, 284, 284],
'width': [1658, 1550, 1550, 1179, 380, 383, 173, 1550, 381, 184, 750],
'height': [469, 371, 371, 128, 128, 116, 128, 155, 130, 117, 117],
'conf': [-1, -1, -1, -1, 96, 95, 95, -1, 96, 96, 96],
'text': ['', '', '', '', 'This', 'text', 'is', '', 'easy', 'to', 'extract.']}

El diccionario contiene mucha información sobre la imagen. En primer lugar, fíjate en las teclas conf y text. Ambos tienen una longitud de 11:

len(data["text"])
11

Esto significa que pytesseract dibujó 11 casillas. El conf significa confianza. Si es igual a -1, el recuadro correspondiente se dibuja alrededor de bloques de texto y no de palabras sueltas.

Por ejemplo, si te fijas en los cuatro primeros valores de width y height, son grandes en comparación con el resto, porque esos recuadros se dibujan alrededor de todo el texto en el centro, y luego para cada línea de texto y la propia imagen en general.

Also:

  • left es la distancia desde la esquina superior izquierda del cuadro delimitador hasta el borde izquierdo de la imagen.
  • top es la distancia desde la esquina superior izquierda del cuadro delimitador hasta el borde superior de la imagen.
  • width y height son la anchura y la altura del cuadro delimitador.

Utilizando estos datos, vamos a dibujar los recuadros encima de la imagen en OpenCV.

En primer lugar, extraemos de nuevo los datos y su longitud:

from pytesseract import Output

# Extract recognized data
data = pytesseract.image_to_data(easy_img, output_type=Output.DICT)
n_boxes = len(data["text"])

A continuación, creamos un bucle para el número de cajas encontradas:

for i in range(n_boxes):
   if data["conf"][i] == -1:
       continue

Dentro del bucle, creamos una condición que se salta la iteración actual del bucle si conf es igual a -1. Omitir las cajas delimitadoras más grandes mantendrá limpia nuestra imagen.

A continuación, definimos las coordenadas de la caja actual, concretamente las ubicaciones de las esquinas superior izquierda e inferior derecha:

for i in range(n_boxes):
   if data["conf"][i] == -1:
       continue
   # Coordinates
   x, y = data["left"][i], data["top"][i]
   w, h = data["width"][i], data["height"][i]

   # Corners
   top_left = (x, y)
   bottom_right = (x + w, y + h)

Tras definir algunos parámetros de la caja, como el color de la caja y el grosor en píxeles, pasamos toda la información a la función cv2.rectangle:

for i in range(n_boxes):
   if data["conf"][i] == -1:
       continue
   # Coordinates
   x, y = data["left"][i], data["top"][i]
   w, h = data["width"][i], data["height"][i]

   # Corners
   top_left = (x, y)
   bottom_right = (x + w, y + h)

   # Box params
   green = (0, 255, 0)
   thickness = 3  # pixels

   cv2.rectangle(
       img=easy_img, pt1=top_left, pt2=bottom_right, color=green, thickness=thickness
   )

La función dibujará los recuadros encima de las imágenes originales. Guardemos la imagen y echemos un vistazo:

# Save the image
output_image_path = "images/text_with_boxes.jpg"
cv2.imwrite(output_image_path, easy_img)
True

Una imagen de muestra con cuadros delimitadores dibujados alrededor de cada palabra. Realizado con PyTesseract y OpenCV.

¡El resultado es justo lo que queríamos!

Ahora, volvamos a poner todo lo que hicimos en una función:

def draw_bounding_boxes(input_img_path, output_path):
   img = cv2.imread(input_img_path)

   # Extract data
   data = pytesseract.image_to_data(img, output_type=Output.DICT)
   n_boxes = len(data["text"])

   for i in range(n_boxes):
       if data["conf"][i] == -1:
           continue
       # Coordinates
       x, y = data["left"][i], data["top"][i]
       w, h = data["width"][i], data["height"][i]

       # Corners
       top_left = (x, y)
       bottom_right = (x + w, y + h)

       # Box params
       green = (0, 255, 0)
       thickness = 1  # The function-version uses thinner lines

       cv2.rectangle(img, top_left, bottom_right, green, thickness)

   # Save the image with boxes
   cv2.imwrite(output_path, img)

Y utiliza la función en el texto semiduro:

output_path = "images/medium_text_with_boxes.png"

draw_bounding_boxes(medium_text_path, output_path)

Otra imagen de muestra con cuadros delimitadores dibujados alrededor de cada palabra. Realizado con PyTesseract y OpenCV.

Incluso para la imagen más difícil, ¡el resultado es perfecto!

Estudio de caso: OCR en un archivo PDF con Python

Hagamos un estudio de caso sobre un archivo PDF escaneado de muestra. En la práctica, es muy probable que trabajes con PDF escaneados en lugar de con imágenes, como ésta:

Un documento escaneado de muestra a punto de ser sometido a OCR mediante PyTesseract.

Puedes descargar el PDF desde esta página de mi GitHub.

El siguiente paso es instalar la biblioteca pdf2image, que requiere un software de procesamiento de PDF llamado Poppler. Aquí tienes instrucciones específicas para cada plataforma:

Para Mac:

$ brew install poppler
$ pip install pdf2image

Para Linux y WSL2:

$ sudo apt-get install -y poppler-utils
$ pip install pdf2image

Para Windows, puedes seguir las instrucciones de la documentación de PDF2Image.

Tras la instalación, importamos los módulos correspondientes:

import pathlib
from pathlib import Path

from pdf2image import convert_from_path

La función convert_from_path convierte un PDF dado en una serie de imágenes. Aquí tienes una función que guarda cada página de un archivo PDF como una imagen en un directorio determinado:

def pdf_to_image(pdf_path, output_folder: str = "."):
   """
   A function to convert PDF files to images
   """
   # Create the output folder if it doesn't exist
   if not Path(output_folder).exists():
       Path(output_folder).mkdir()

   pages = convert_from_path(pdf_path, output_folder=output_folder, fmt="png")

   return pages

Vamos a ejecutarlo en nuestro documento:

pdf_path = "scanned_document.pdf"

pdf_to_image(pdf_path, output_folder="documents")
[<PIL.PngImagePlugin.PngImageFile image mode=RGB size=1662x2341>]

La salida es una lista que contiene un único objeto de imagen PngImageFile. Echemos un vistazo al directorio documents:

$ ls documents
2d8f6922-99c4-4ef4-a475-ef81effe65a3-1.png

La imagen está ahí, así que alimentemos nuestra función image_to_text que creamos al principio e imprimamos los primeros cientos de caracteres del texto extraído:

scanned_img_path = "documents/2d8f6922-99c4-4ef4-a475-ef81effe65a3-1.png"

print(image_to_text(scanned_img_path)[:377])
PEU Business report

New customer's development
and increasing the sale of product

My country economy at this season keeps escaping from Odoba of business though holds a crude oil
high so on unstable element that continues still, and recovering gradually and well.
In the IT industry, there is an influence such as competing intensification in narrowing investment field.

Si comparamos el texto con el archivo, todo funciona bien: se conserva el formato y el espaciado, y el texto es preciso. Entonces, ¿cómo compartimos el texto extraído?

Pues bien, ¡el mejor formato para compartir un texto PDF extraído es otro archivo PDF! PyTesseract tiene una función image_to_pdf_or_hocr que toma cualquier imagen con texto y la convierte en un archivo PDF sin procesar, en el que se puede buscar texto. Utilicémoslo en nuestra imagen escaneada:

raw_pdf = pytesseract.image_to_pdf_or_hocr(scanned_img_path)

with open("searchable_pdf.pdf", "w+b") as f:
   f.write(bytearray(raw_pdf))

Y éste es el aspecto de searchable_pdf:

Un GIF que muestra el resultado del OCR realizado en un documento PDF escaneado.

Como puedes ver, puedo resaltar y copiar texto del archivo. Además, se conservan todos los elementos del PDF original.

Técnicas de preprocesamiento de imágenes para OCR en OpenCV

No existe un enfoque único para el OCR. Las técnicas que hemos tratado hoy pueden no funcionar con otros tipos de imágenes. Te recomiendo que experimentes con distintas técnicas de preprocesamiento de imágenes y configuraciones de Tesseract para encontrar los ajustes óptimos para imágenes concretas.

El factor más importante en el OCR es la calidad de la imagen. Las imágenes correctamente escaneadas, totalmente verticales y de alto contraste (blanco y negro) suelen funcionar mejor con cualquier programa de OCR. Recuerda que el hecho de que tú puedas leer el texto no significa que tu ordenador pueda hacerlo.

Si tus imágenes no satisfacen los altos estándares de calidad de Tesseract y el resultado es un galimatías, hay algunos pasos de preprocesamiento que puedes realizar.

1. Conversión a escala de grises

Primero, empieza por convertir las imágenes en color a escala de grises. Esto puede mejorar la precisión al eliminar las variaciones de color que podrían confundir el proceso de reconocimiento. En OpenCV, esto tendrá el siguiente aspecto:

def grayscale(image):
   """Converts an image to grayscale.

   Args:
       image: The input image in BGR format.

   Returns:
       The grayscale image.
   """
   return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

2. Reducción del ruido

No todas las imágenes, especialmente los documentos escaneados, vienen con fondos prístinos y uniformes. Además, algunas imágenes pueden proceder de documentos antiguos en los que las páginas se han deteriorado por el paso del tiempo. He aquí un ejemplo:

Una muestra de imagen ruidosa sobre la que realizar el OCR.

Aplica técnicas como filtros de eliminación de ruido (por ejemplo, desenfoque medio) para reducir los artefactos de ruido en la imagen que pueden dar lugar a interpretaciones erróneas durante el OCR. En OpenCV, puedes utilizar la función medianBlur:

def denoise(image):
   """Reduces noise in the image using a median blur filter.

   Args:
       image: The input grayscale image.

   Returns:
       The denoised image.
   """
   return cv2.medianBlur(image, 5)  # Adjust kernel size as needed

3. Afilado

En algunos casos, afinar la imagen puede realzar los bordes y mejorar el reconocimiento de caracteres, especialmente en el caso de imágenes borrosas o de baja resolución. El afilado puede realizarse aplicando un filtro Laplaciano en OpenCV:

def sharpen(image):
   """Sharpens the image using a Laplacian filter.

   Args:
       image: The input grayscale image.

   Returns:
       The sharpened image (be cautious with sharpening).
   """
   kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
   return cv2.filter2D(image, -1, kernel)

4. Binarización

Para determinadas imágenes, la binarización (convertir la imagen a blanco y negro) puede ser beneficiosa. Experimenta con distintas técnicas de umbralización para encontrar la separación óptima entre el primer plano (texto) y el fondo.

Sin embargo, la binarización puede ser sensible a las variaciones de iluminación y no siempre es necesaria. Aquí tienes un ejemplo del aspecto de una imagen binarizada:

Una imagen de muestra: izquierda original, derecha versión binarizada en OpenCV.

Para realizar la binarización en OpenCV, puedes utilizar la función adaptiveThreshold:

def binarize(image):
   """Binarizes the image using adaptive thresholding.

   Args:
       image: The input grayscale image.

   Returns:
       The binary image.
   """
   thresh = cv2.adaptiveThreshold(
       image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2
   )

   return thresh

5. Otras técnicas

Existen muchas otras técnicas de preprocesamiento, como:

  • Dilatación: hacer más gruesas las fuentes pequeñas y finas para mejorar el reconocimiento
  • Erosión: erosionar el texto en negrita para mejorar la precisión. Común en documentos históricos con fuentes gruesas.
  • Detección astuta de bordes
  • Corrección de inclinación: corregir la inclinación (asimetría) de las líneas de texto. Común en documentos escaneados incorrectamente

Puedes obtener más información sobre las mejoras de la calidad de imagen en esta página de la documentación de Tesseract.

Conclusión

En este artículo, has dado los primeros pasos para conocer el problema dinámico que es el OCR. Primero cubrimos cómo extraer texto de imágenes sencillas, y luego pasamos a imágenes más difíciles con formato complejo.

También hemos aprendido un flujo de trabajo integral para extraer texto de PDF escaneados y cómo volver a guardar el texto extraído como PDF para que se pueda buscar en él. Hemos completado el artículo con algunos consejos para mejorar la calidad de la imagen con OpenCV antes de pasarla a Tesseract.

Si quieres aprender más sobre la resolución de problemas relacionados con las imágenes, aquí tienes algunos recursos sobre visión por ordenador que puedes consultar:


Photo of Bex Tuychiev
Author
Bex Tuychiev
LinkedIn

Soy un creador de contenidos de ciencia de datos con más de 2 años de experiencia y uno de los mayores seguidores en Medium. Me gusta escribir artículos detallados sobre IA y ML con un estilo un poco sarcastıc, porque hay que hacer algo para que sean un poco menos aburridos. He publicado más de 130 artículos y un curso DataCamp, y estoy preparando otro. Mi contenido ha sido visto por más de 5 millones de ojos, 20.000 de los cuales se convirtieron en seguidores tanto en Medium como en LinkedIn. 

Preguntas frecuentes sobre OCR en Python

¿Por qué soy tan alto?

Genética.

Temas

¡Continúa tu viaje de aprendizaje de Python!

Certificación disponible

Course

Procesado de imágenes en Python

4 hr
42K
Aprende a procesar, transformar y manipular imágenes a tu antojo.
See DetailsRight Arrow
Start Course
Ver másRight Arrow