Tutorial de deteção de texto em documentos densos

Público-alvo

O objetivo deste tutorial é ajudar a desenvolver aplicações através da API Google Cloud Vision Document Text Detection. Parte do princípio de que tem familiaridade com as construções e as técnicas de programação básicas, mas, mesmo que seja um programador iniciante, deve conseguir seguir e executar este tutorial sem dificuldade e, em seguida, usar a documentação de referência da Cloud Vision API para criar aplicações básicas.

Pré-requisitos

Anotar uma imagem com o OCR de texto de documentos

Este tutorial explica passo a passo uma aplicação básica da API Vision que faz um DOCUMENT_TEXT_DETECTION pedido e, em seguida, processa a fullTextAnnotation resposta.

Um fullTextAnnotation é uma resposta hierárquica estruturada para o texto UTF-8 extraído da imagem, organizado como Pages→Blocks→Paragraphs→Words→Symbols:

  • Page é uma coleção de blocos, além de metainformações sobre a página: tamanhos, resoluções (a resolução X e a resolução Y podem diferir).

  • Block representa um elemento "lógico" da página, por exemplo, uma área coberta por texto, uma imagem ou um separador entre colunas. Os blocos de texto e tabelas contêm as principais informações necessárias para extrair o texto.

  • Paragraph é uma unidade estrutural de texto que representa uma sequência ordenada de palavras. Por predefinição, as palavras são consideradas separadas por quebras de palavras.

  • Word é a unidade de texto mais pequena. É representada como uma matriz de símbolos.

  • Symbol representa um caráter ou uma marca de pontuação.

O elemento fullTextAnnotation também pode fornecer URLs de imagens Web que correspondam parcial ou totalmente à imagem no pedido.

Listagem de código completa

À medida que lê o código, recomendamos que o siga consultando a referência Python da Cloud Vision API.

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 aplicação simples realiza as seguintes tarefas:

  • Importa as bibliotecas necessárias para executar a aplicação
  • Recebe três argumentos e transmite-os à função main():
    • image_file: o ficheiro de imagem de entrada a anotar
    • output_file—o nome do ficheiro de saída no qual o Cloud Vision vai gerar uma imagem de saída com caixas poligonais desenhadas
  • Cria uma instância do ImageAnnotatorClient para interagir com o serviço
  • Envia o pedido e devolve uma resposta
  • Cria uma imagem de saída com caixas desenhadas à volta do texto

Uma visão mais detalhada do código

Importar bibliotecas

import argparse
from enum import Enum

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

Importamos bibliotecas padrão:

  • argparse para permitir que a aplicação aceite nomes de ficheiros de entrada como argumentos
  • enum para a enumeração FeatureType
  • io para E/S de ficheiros

Outras importações:

  • A classe ImageAnnotatorClient na biblioteca google.cloud.vision para aceder à API Vision.
  • O módulo types na biblioteca google.cloud.vision para criar pedidos.
  • As bibliotecas Image e ImageDraw da biblioteca PIL são usadas para criar a imagem de saída com caixas desenhadas na imagem de entrada.

Executar a aplicação

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)

Aqui, analisamos simplesmente os argumentos transmitidos e transmitimo-los à função render_doc_text().

Autenticação na API

Antes de comunicar com o serviço da API Vision, tem de autenticar o seu serviço através de credenciais adquiridas anteriormente. Numa aplicação, a forma mais simples de obter credenciais é usar as Credenciais padrão da aplicação (ADC). Por predefinição, a biblioteca de cliente do Google Cloud tenta obter credenciais da variável de ambiente GOOGLE_APPLICATION_CREDENTIALS, que deve ser definida para apontar para o ficheiro de chave JSON da sua conta de serviço (consulte Configurar uma conta de serviço para mais informações).

Fazer o pedido da API e ler os limites do texto da resposta

Agora que o nosso serviço Vision API está pronto, podemos aceder ao serviço chamando o método document_text_detection da instância ImageAnnotatorClient.

A biblioteca de cliente encapsula os detalhes dos pedidos e das respostas à API. Consulte a referência da API Vision para ver informações completas sobre a estrutura de um pedido.

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

Depois de a biblioteca cliente processar o pedido, a nossa resposta vai conter um AnnotateImageResponse, que consiste numa lista de resultados de anotações de imagens, um para cada imagem enviada no pedido. Uma vez que enviámos apenas uma imagem no pedido, percorremos a anotação de texto completa e recolhemos os limites da funcionalidade de documento especificada.

Executar a aplicação

Para executar a aplicação, pode transferir este ficheiro receipt.jpg (pode ter de clicar com o botão direito do rato no link) e, em seguida, transmitir a localização onde transferiu o ficheiro na sua máquina local para a aplicação do tutorial (doctext.py).

Segue-se o comando Python, seguido das imagens de saída da anotação de texto.

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

A imagem seguinte mostra palavras em caixas amarelas e frases em vermelho.

Parabéns! Fez a deteção de texto com as anotações de texto completo do Google Cloud Vision!