Tutorial de detección de texto en documentos densos

Audiencia

El objetivo de este tutorial es ayudarte a desarrollar aplicaciones con la API Cloud Vision de Google para detectar texto en documentos. Se presupone que conoces las estructuras y técnicas de programación básicas, pero, aunque seas un programador principiante, deberías poder seguir y ejecutar este tutorial sin dificultad, y luego usar la documentación de referencia de la API Cloud Vision para crear aplicaciones básicas.

Requisitos previos

Anotar una imagen con la función de OCR de texto de documento

En este tutorial se explica cómo crear una aplicación básica de la API Vision que hace una DOCUMENT_TEXT_DETECTION solicitud y, a continuación, procesa la fullTextAnnotation respuesta.

Un fullTextAnnotation es una respuesta jerárquica estructurada del texto UTF-8 extraído de la imagen, organizado como Páginas → Bloques → Párrafos → Palabras → Símbolos:

  • Page es una colección de bloques, además de metainformación sobre la página: tamaños, resoluciones (la resolución X y la resolución Y pueden ser diferentes).

  • Block representa un elemento "lógico" de la página; por ejemplo, un área cubierta por texto, una imagen o un separador entre columnas. Los bloques de texto y de tabla contienen la información principal necesaria para extraer el texto.

  • Paragraph es una unidad estructural de texto que representa una secuencia ordenada de palabras. De forma predeterminada, se considera que las palabras están separadas por saltos de palabra.

  • Word es la unidad de texto más pequeña. Se representa como una matriz de símbolos.

  • Symbol representa un carácter o un signo de puntuación.

La fullTextAnnotation también puede proporcionar URLs de imágenes web que coincidan parcial o totalmente con la imagen de la solicitud.

Fragmento de código completo

Mientras lees el código, te recomendamos que consultes la referencia de Python de la API Cloud Vision.

import argparse
from enum import Enum

from google.cloud import vision
from PIL import Image, ImageDraw



class FeatureType(Enum):
    PAGE = 1
    BLOCK = 2
    PARA = 3
    WORD = 4
    SYMBOL = 5


def draw_boxes(image, bounds, color):
    """Draws a border around the image using the hints in the vector list.

    Args:
        image: the input image object.
        bounds: list of coordinates for the boxes.
        color: the color of the box.

    Returns:
        An image with colored bounds added.
    """
    draw = ImageDraw.Draw(image)

    for bound in bounds:
        draw.polygon(
            [
                bound.vertices[0].x,
                bound.vertices[0].y,
                bound.vertices[1].x,
                bound.vertices[1].y,
                bound.vertices[2].x,
                bound.vertices[2].y,
                bound.vertices[3].x,
                bound.vertices[3].y,
            ],
            None,
            color,
        )
    return image


def get_document_bounds(image_file, feature):
    """Finds the document bounds given an image and feature type.

    Args:
        image_file: path to the image file.
        feature: feature type to detect.

    Returns:
        List of coordinates for the corresponding feature type.
    """
    client = vision.ImageAnnotatorClient()

    bounds = []

    with open(image_file, "rb") as image_file:
        content = image_file.read()

    image = vision.Image(content=content)

    response = client.document_text_detection(image=image)
    document = response.full_text_annotation

    # Collect specified feature bounds by enumerating all document features
    for page in document.pages:
        for block in page.blocks:
            for paragraph in block.paragraphs:
                for word in paragraph.words:
                    for symbol in word.symbols:
                        if feature == FeatureType.SYMBOL:
                            bounds.append(symbol.bounding_box)

                    if feature == FeatureType.WORD:
                        bounds.append(word.bounding_box)

                if feature == FeatureType.PARA:
                    bounds.append(paragraph.bounding_box)

            if feature == FeatureType.BLOCK:
                bounds.append(block.bounding_box)

    # The list `bounds` contains the coordinates of the bounding boxes.
    return bounds




def render_doc_text(filein, fileout):
    """Outlines document features (blocks, paragraphs and words) given an image.

    Args:
        filein: path to the input image.
        fileout: path to the output image.
    """
    image = Image.open(filein)
    bounds = get_document_bounds(filein, FeatureType.BLOCK)
    draw_boxes(image, bounds, "blue")
    bounds = get_document_bounds(filein, FeatureType.PARA)
    draw_boxes(image, bounds, "red")
    bounds = get_document_bounds(filein, FeatureType.WORD)
    draw_boxes(image, bounds, "yellow")

    if fileout != 0:
        image.save(fileout)
    else:
        image.show()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("detect_file", help="The image for text detection.")
    parser.add_argument("-out_file", help="Optional output file", default=0)
    args = parser.parse_args()

    render_doc_text(args.detect_file, args.out_file)

Esta sencilla aplicación realiza las siguientes tareas:

  • Importa las bibliotecas necesarias para ejecutar la aplicación
  • Toma tres argumentos y los transfiere a la función main():
    • image_file: el archivo de imagen de entrada que se va a anotar
    • output_file: nombre del archivo de salida en el que Cloud Vision generará una imagen de salida con los polígonos dibujados.
  • Crea una instancia de ImageAnnotatorClient para interactuar con el servicio.
  • Envía la solicitud y devuelve una respuesta.
  • Crea una imagen de salida con cuadros dibujados alrededor del texto.

Análisis detallado del código

Importar bibliotecas

import argparse
from enum import Enum

from google.cloud import vision
from PIL import Image, ImageDraw

Importamos bibliotecas estándar:

  • argparse para permitir que la aplicación acepte nombres de archivos de entrada como argumentos
  • enum para la enumeración FeatureType
  • io para E/S de archivos

Otras importaciones:

  • La clase ImageAnnotatorClient de la biblioteca google.cloud.vision para acceder a la API Vision.
  • El módulo types de la biblioteca google.cloud.vision para crear solicitudes.
  • Las bibliotecas Image y ImageDraw de la biblioteca PIL se usan para crear la imagen de salida con los recuadros dibujados en la imagen de entrada.

Ejecutar la aplicación

parser = argparse.ArgumentParser()
parser.add_argument("detect_file", help="The image for text detection.")
parser.add_argument("-out_file", help="Optional output file", default=0)
args = parser.parse_args()

render_doc_text(args.detect_file, args.out_file)

Aquí, simplemente analizamos los argumentos transferidos y los transferimos a la función render_doc_text().

Autenticar a la API

Antes de comunicarte con el servicio de la API Vision, debes autenticar tu servicio con las credenciales que hayas obtenido previamente. En una aplicación, la forma más sencilla de obtener credenciales es usar las credenciales predeterminadas de la aplicación (ADC). De forma predeterminada, la biblioteca de cliente de Cloud intentará obtener las credenciales de la variable de entorno GOOGLE_APPLICATION_CREDENTIALS, que debe configurarse para que apunte al archivo de clave JSON de tu cuenta de servicio (consulta Configurar una cuenta de servicio para obtener más información).

Hacer la solicitud a la API y leer los límites del texto de la respuesta

Ahora que nuestro servicio de la API Vision está listo, podemos acceder a él llamando al método document_text_detection de la instancia ImageAnnotatorClient.

La biblioteca de cliente encapsula los detalles de las solicitudes y respuestas de la API. Consulta la referencia de la API Vision para obtener información completa sobre la estructura de una solicitud.

def get_document_bounds(image_file, feature):
    """Finds the document bounds given an image and feature type.

    Args:
        image_file: path to the image file.
        feature: feature type to detect.

    Returns:
        List of coordinates for the corresponding feature type.
    """
    client = vision.ImageAnnotatorClient()

    bounds = []

    with open(image_file, "rb") as image_file:
        content = image_file.read()

    image = vision.Image(content=content)

    response = client.document_text_detection(image=image)
    document = response.full_text_annotation

    # Collect specified feature bounds by enumerating all document features
    for page in document.pages:
        for block in page.blocks:
            for paragraph in block.paragraphs:
                for word in paragraph.words:
                    for symbol in word.symbols:
                        if feature == FeatureType.SYMBOL:
                            bounds.append(symbol.bounding_box)

                    if feature == FeatureType.WORD:
                        bounds.append(word.bounding_box)

                if feature == FeatureType.PARA:
                    bounds.append(paragraph.bounding_box)

            if feature == FeatureType.BLOCK:
                bounds.append(block.bounding_box)

    # The list `bounds` contains the coordinates of the bounding boxes.
    return bounds

Una vez que la biblioteca de cliente haya gestionado la solicitud, nuestra respuesta contendrá un objeto AnnotateImageResponse, que consta de una lista de resultados de anotación de imagen, uno por cada imagen enviada en la solicitud. Como solo hemos enviado una imagen en la solicitud, vamos a analizar el TextAnnotation completo y a recoger los límites de la función del documento especificada.

Ejecutar la aplicación

Para ejecutar la aplicación, puedes descargar este archivo receipt.jpg (puede que tengas que hacer clic con el botón derecho en el enlace) y, a continuación, pasar la ubicación donde descargaste el archivo en tu máquina local a la aplicación del tutorial (doctext.py).

Aquí tienes el comando de Python, seguido de las imágenes de salida de la anotación de texto.

$ python doctext.py receipt.jpg -out_file out.jpg

En la siguiente imagen se muestran palabras en recuadros amarillos y frases en rojo.

¡Enhorabuena! Has realizado la detección de texto con las anotaciones de texto completo de Google Cloud Vision.