Anleitung zur Erkennung von dichtem Dokumenttext

Zielgruppe

Diese Anleitung soll Ihnen die Anwendungsentwicklung mithilfe der Dokumenttexterkennung der Google Cloud Vision API erleichtern. Es wird davon ausgegangen, dass Sie mit grundlegenden Programmierkonstrukten und ‑techniken vertraut sind. Aber auch als Einsteiger in die Programmierarbeit sollten Sie dieser Anleitung gut folgen und im Anschluss unter Verwendung der Referenzdokumentation für die Cloud Vision API einfache Anwendungen erstellen können.

Vorbereitung

Python

Bild mit Dokumenttext-OCR annotieren

In dieser Anleitung wird anhand einer einfachen Vision API-Anwendung die Verwendung der Anfrage DOCUMENT_TEXT_DETECTION sowie die Verarbeitung der Antwort fullTextAnnotation erläutert.

Eine fullTextAnnotation ist eine strukturierte hierarchische Antwort zum UTF‑8-Text, der aus dem Bild extrahiert wurde. Die Auflistung erfolgt nach Seiten→Blöcken→Absätzen→Wörtern→Symbolen:

  • Page ist eine Sammlung von Blöcken mit Metainformationen zur Seite: Größen und Auflösungen. Die X- und die Y‑Auflösung können unterschiedlich sein.

  • Block steht für ein „logisches“ Element der Seite, beispielsweise einen Textbereich, ein Bild oder eine Spaltentrennung. Die Text- und Tabellenblöcke enthalten die wichtigsten Informationen für die Textextrahierung.

  • Paragraph ist eine strukturelle Texteinheit, die eine geordnete Wortfolge darstellt. Standardmäßig wird davon ausgegangen, dass zwischen Wörtern Worttrennungen vorhanden sind.

  • Word ist die kleinste Texteinheit. Sie wird als eine Reihe von Symbolen dargestellt.

  • Symbol steht für ein Zeichen oder Satzzeichen.

Mit der fullTextAnnotation können auch URLs zu Webbildern angegeben werden, die teilweise oder vollständig mit dem Bild in der Anfrage übereinstimmen.

Vollständige Codeliste

Wir empfehlen, beim Lesen des Codes die Python-Referenz für die Cloud Vision API hinzuzuziehen.

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)

Diese einfache Anwendung führt folgende Aufgaben aus:

  • Importiert die für die Ausführung der Anwendung erforderlichen Bibliotheken
  • Übergibt drei Argumente an die Funktion main():
    • image_file: die eingegebene Bilddatei, die annotiert werden soll
    • output_file: der Name der Ausgabedatei, in der mit Cloud Vision ein Ausgabebild mit vieleckigen Rahmen (Polygonen) generiert wird
  • Erstellt die Instanz ImageAnnotatorClient für die Interaktion mit dem Dienst
  • Sendet die Anfrage und gibt eine Antwort zurück
  • Erstellt ein Ausgabebild, in dem der Text eingerahmt ist

Erläuterung des Codes