[PYTHON] Ich habe "Receipt OCR" mit der Google Vision API ausprobiert

Einführung

Es gibt eine Technologie namens "OCR (Optical Character Recognition)", die gedruckte oder handgeschriebene Zeichen liest und in Zeichendaten umwandelt.

OCR-Dienste werden für verschiedene Dokumente wie Rechnungen, Quittungen, Visitenkarten und Lizenzen bereitgestellt. Durch die Verwendung von OCR können Sie die Eingabe von Daten reduzieren. Durch die Verknüpfung mit anderen Systemen ist es außerdem möglich, Daten effektiv zu nutzen.

Die von jedem Unternehmen bereitgestellte OCR umfasst Dienstleistungen für Unternehmen und Einzelpersonen. Als OCR, die von Einzelpersonen verwendet werden kann, gibt es "Google Vision API (im Folgenden als Vision API bezeichnet)". Vision API ist ein sehr leistungsstarker Bildanalysedienst von Google. (Klicken Sie hier, um die kostenlose Testversion aufzurufen (https://cloud.google.com/vision?hl=ja).)

Dieses Mal habe ich eine einfache OCR für Quittungen mithilfe der Vision-API ausprobiert.

Empfangs-OCR

Umgebung

Die Umgebung verwendet Google Colaboratory. Die Python-Version ist unten.

import platform
print("python " + platform.python_version())
# python 3.6.9

Lassen Sie uns das Bild anzeigen

Jetzt schreiben wir den Code. Importieren Sie zunächst die Bibliothek, die zum Anzeigen des Bildes erforderlich ist.

import cv2
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib

Bereiten Sie auch ein Beispielbild der Quittung vor. Lassen Sie uns das Bild anzeigen.

img = cv2.imread(input_file) # input_Datei ist der Pfad des Bildes
plt.figure(figsize=[10,10])
plt.axis('off')
plt.imshow(img[:,:,::-1])

image.png

Vision API-Setup

Lassen Sie uns nun dieses Quittungsbild mithilfe der Google Vision-API OCR.

Im Folgenden sind die erforderlichen Vorbereitungen für die Verwendung der Vision-API aufgeführt. Bitte fahren Sie gemäß [hier] fort (https://cloud.google.com/vision/docs/quickstart-client-libraries?hl=JA). Sie müssen die Clientbibliothek installieren und einen Dienstkontoschlüssel ausgeben.

Die Installation der Client-Bibliothek ist wie folgt.

pip install google-cloud-vision

Verwenden Sie den ausgegebenen Dienstkontoschlüssel, um Umgebungsvariablen festzulegen.

import os
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = json_path # json_Pfad ist der Pfad des Dienstkontoschlüssels

Anfrage an API senden / Antwort abrufen

Senden wir nun eine Anfrage an die Vision-API und erhalten eine Antwort.

import io

from google.cloud import vision
from google.cloud.vision import types

client = vision.ImageAnnotatorClient()
with io.open(input_file, 'rb') as image_file:
    content = image_file.read()
image = types.Image(content=content)
response = client.document_text_detection(image=image)

Wenn es fehlerfrei ausgeführt werden kann, kann die Anforderung an die API gesendet und die Antwort abgerufen werden.

Diese Antwort enthält das OCR-Ergebnis der Vision-API. Es enthält verschiedene Informationen wie gelesene Textinformationen, Koordinateninformationen, Sicherheit und Sprache. Hier überprüfen wir die Textinformationen des gelesenen Volltexttextes.

print(response.text_annotations[0].description)
SAVERSONICS
Sieben-Elf
Chiyoda-Laden
8-8 Nibancho, Chiyoda-ku, Tokio
Telefon: 03-1234-5678
Registrierkasse Nr. 31
Dienstag, 01. Oktober 2019 08:45 Verantwortung 012
Quittung
Handgerollter Reisbällchen würziger Kabeljaurogen
Coca-Cola 500ml
Paradu Mini Nail PK03
Mobius One
50 Yen Briefmarke
*130
*140
300
490、
50 Jahre
Zwischensumme (8% ohne Steuern)
¥270
Verbrauchssteuer usw. (8%)
¥21
Zwischensumme (10% ohne Steuern)
¥300
Verbrauchssteuer usw. (10%)
¥30
Zwischensumme (10% einschließlich Steuern)
¥490
Zwischensumme (steuerfrei)
¥50
Gesamt ¥ 1.161
(Steuersatz 8% Ziel
¥291)
(Steuersatz 10% Ziel
¥820)
(Innenverbrauchsteuer usw. 8%
¥21)
(Interne Verbrauchsteuer usw. 10%
¥74)
Bargeldloser Rückzahlungsbetrag
-22
Nanaco Zahlung
¥1,139
Die Kaufdetails sind wie oben.
Nanaco-Nummer
*******9999
Dieser Zeitpunkt
2P
Die Marke [*] unterliegt dem ermäßigten Steuersatz.

Sie können sehen, dass es mit sehr hoher Genauigkeit gelesen wird.

Die Vision-API unterteilt das Bild in Blöcke, Absätze usw., je nachdem, wie die Zeichen gesammelt werden. Lassen Sie uns jeden unterteilten Bereich überprüfen. Definieren Sie zunächst die Funktion (Einzelheiten finden Sie unter Code hier).

from enum import Enum

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

def draw_boxes(input_file, bounds):
    img = cv2.imread(input_file, cv2.IMREAD_COLOR)
    for bound in bounds:
      p1 = (bound.vertices[0].x, bound.vertices[0].y) # top left
      p2 = (bound.vertices[1].x, bound.vertices[1].y) # top right
      p3 = (bound.vertices[2].x, bound.vertices[2].y) # bottom right
      p4 = (bound.vertices[3].x, bound.vertices[3].y) # bottom left
      cv2.line(img, p1, p2, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
      cv2.line(img, p2, p3, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
      cv2.line(img, p3, p4, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
      cv2.line(img, p4, p1, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
    return img

def get_document_bounds(response, feature):
    document = response.full_text_annotation
    bounds = []
    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)
    return bounds

Schreiben wir nun jeden Bereich auf das Bild und zeigen ihn an.

bounds = get_document_bounds(response, FeatureType.BLOCK)
img_block = draw_boxes(input_file, bounds)

bounds = get_document_bounds(response, FeatureType.PARA)
img_para = draw_boxes(input_file, bounds)

bounds = get_document_bounds(response, FeatureType.WORD)
img_word = draw_boxes(input_file, bounds)

bounds = get_document_bounds(response, FeatureType.SYMBOL)
img_symbol = draw_boxes(input_file, bounds)

plt.figure(figsize=[20,20])
plt.subplot(141);plt.imshow(img_block[:,:,::-1]);plt.title("img_block")
plt.subplot(142);plt.imshow(img_para[:,:,::-1]);plt.title("img_para")
plt.subplot(143);plt.imshow(img_word[:,:,::-1]);plt.title("img_word")
plt.subplot(144);plt.imshow(img_symbol[:,:,::-1]);plt.title("img_symbol")

image.png

Es wurde bestätigt, dass der Bereich des Bildes in verschiedene Einheiten wie Block, Absatz, Wort und Symbol unterteilt war.

Textformatierung

Wie Sie sehen können, teilt die Vision-API den Bereich gut auf, in einigen Fällen kann dies jedoch ein Nachteil sein. In diesem Fall werden beispielsweise "handgerollter würziger Onigiri-Kabeljaurogen" und seine Menge "* 130" getrennt. Aufgrund der Art der Quittung werden die Informationen häufig zeilenweise organisiert. Teilen Sie sie daher zeilenweise auf.

Wie kann ich jede Zeile trennen? Die Vision-API enthält zeichenweise Koordinateninformationen (das Symbol bounding_box oben). Das Sortieren von links nach rechts und von oben nach unten nach Koordinatenwerten scheint zu funktionieren. Im Folgenden erstellen wir einen Prozess zum Gruppieren nach Zeilen gemäß den Koordinaten der Zeichen.

def get_sorted_lines(response):
    document = response.full_text_annotation
    bounds = []
    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:
              x = symbol.bounding_box.vertices[0].x
              y = symbol.bounding_box.vertices[0].y
              text = symbol.text
              bounds.append([x, y, text, symbol.bounding_box])
    bounds.sort(key=lambda x: x[1])
    old_y = -1
    line = []
    lines = []
    threshold = 1
    for bound in bounds:
      x = bound[0]
      y = bound[1]
      if old_y == -1:
        old_y = y
      elif old_y-threshold <= y <= old_y+threshold:
        old_y = y
      else:
        old_y = -1
        line.sort(key=lambda x: x[0])
        lines.append(line)
        line = []
      line.append(bound)
    line.sort(key=lambda x: x[0])
    lines.append(line)
    return lines

Lass es uns überprüfen.

img = cv2.imread(input_file, cv2.IMREAD_COLOR)

lines = get_sorted_lines(response)
for line in lines:
  texts = [i[2] for i in line]
  texts = ''.join(texts)
  bounds = [i[3] for i in line]
  print(texts)
  for bound in bounds:
    p1 = (bounds[0].vertices[0].x, bounds[0].vertices[0].y)   # top left
    p2 = (bounds[-1].vertices[1].x, bounds[-1].vertices[1].y) # top right
    p3 = (bounds[-1].vertices[2].x, bounds[-1].vertices[2].y) # bottom right
    p4 = (bounds[0].vertices[3].x, bounds[0].vertices[3].y)   # bottom left
    cv2.line(img, p1, p2, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
    cv2.line(img, p2, p3, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
    cv2.line(img, p3, p4, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)
    cv2.line(img, p4, p1, (0, 255, 0), thickness=1, lineType=cv2.LINE_AA)

plt.figure(figsize=[10,10])
plt.axis('off')
plt.imshow(img[:,:,::-1]);plt.title("img_by_line")
Sieben-Elf
SAVERSONICS
Chiyoda-Laden
8-8 Nibancho, Chiyoda-ku, Tokio
Telefon: 03-1234-5678 Registrierkasse Nr. 31
Dienstag, 01. Oktober 2019 08:45 Verantwortung 012
Quittung
Handgerollter Onigiri würziger Kabeljaurogen * 130
Coca-Cola 500 ml * 140
Paradu Mini Nail PK03300
Mobius One 490,
50 Yen Briefmarke 50 Jahre
Zwischensumme (8% ohne Steuern) ¥ 270
Verbrauchssteuer usw. (8%)
¥21
Zwischensumme (10% ohne Steuern) ¥ 300
Verbrauchssteuer usw. (10%) ¥ 30
Zwischensumme (10% einschließlich Steuern) ¥ 490
Zwischensumme (steuerfrei) ¥ 50
Gesamt ¥ 1.161
(Steuersatz 8% Ziel
¥291)
(Steuersatz 10% Ziel ¥ 820)
(Einschließlich Verbrauchssteuer usw. 8% ¥ 21)
(Interne Verbrauchssteuer usw. 10% ¥ 74)
Bargeldlose Rückgabe-22
Nanaco Zahlung ¥ 1.139
Die Kaufdetails sind wie oben.
Nanaco-Nummer ******* 9999
Diesmal 2P
Die Marke [*] unterliegt dem ermäßigten Steuersatz.

Ich konnte es Zeile für Zeile organisieren.

Textstrukturierung

Durch Textformatierung konnte ich die Zeichenketten organisieren. Dies erleichtert das Abrufen der benötigten Informationen. Um die erforderlichen Informationen abzurufen, kann die Textverarbeitung mit regulären Ausdrücken oder die Verarbeitung in natürlicher Sprache in Betracht gezogen werden.

Dieses Mal extrahieren und strukturieren wir Informationen wie "Datum", "Telefonnummer" und "Gesamtbetrag" mithilfe regulärer Ausdrücke. Siehe auch hier für reguläre Ausdrücke.

import re

def get_matched_string(pattern, string):
    prog = re.compile(pattern)
    result = prog.search(string)
    if result:
        return result.group()
    else:
        return False

pattern_dict = {}
pattern_dict['date'] = r'[12]\d{3}[/\-Jahr](0?[1-9]|1[0-2])[/\-Mond](0?[1-9]|[12][0-9]|3[01])Tag?'
pattern_dict['time'] = r'((0?|1)[0-9]|2[0-3])[:Zeit][0-5][0-9]Protokoll?'
pattern_dict['tel'] = '0\d{1,3}-\d{1,4}-\d{4}'
pattern_dict['total_price'] = r'Insgesamt ¥(0|[1-9]\d*|[1-9]\d{0,2}(,\d{3})+)$'

for line in lines:
  texts = [i[2] for i in line]
  texts = ''.join(texts)
  for key, pattern in pattern_dict.items():
    matched_string = get_matched_string(pattern, texts)
    if matched_string:
      print(key, matched_string)

# tel 03-1234-5678
#Datum 01. Oktober 2019
# time 08:45
# total_Preis Gesamt ¥ 1,161

Ich konnte die Telefonnummer, das Datum, die Uhrzeit und den Gesamtbetrag extrahieren.

Zusammenfassung

Dieses Mal habe ich die Vision-API verwendet, um eine Empfangs-OCR durchzuführen.

Die Vision-API verfügt über eine sehr hochpräzise OCR-Funktion. Es ist auch ein OCR-Dienst, den auch Einzelpersonen problemlos nutzen können. Es ist auch möglich, die gewünschten Informationen zu extrahieren, indem reguläre Ausdrücke und die Verarbeitung natürlicher Sprache auf den Text des OCR-Ergebnisses angewendet werden.

Warum versuchen Sie es nicht mit OCR mit verschiedenen Dokumenten?

Recommended Posts

Ich habe "Receipt OCR" mit der Google Vision API ausprobiert
Ich habe "License OCR" mit der Google Vision API ausprobiert
Ich habe versucht, die Google Cloud Vision-API zu verwenden
Ich habe versucht, mich mit Spring Boot + Spring Security REST API anzumelden
Ich habe die Google Cloud Vision-API zum ersten Mal ausprobiert
Ich habe versucht, Zeichen aus Untertiteln zu extrahieren (OpenCV: Google Cloud Vision API)
Ich habe versucht, unsere Dunkelheit mit der Chatwork-API aufzudecken
Ich habe versucht, eine OCR-App mit PySimpleGUI zu erstellen
Ich habe versucht, die API mit dem Python-Client von echonest zu erreichen
Ich habe fp-Wachstum mit Python versucht
Ich habe versucht, mit Python zu kratzen
Ich habe versucht, mit Elasticsearch Ranking zu lernen!
Ich habe versucht, mit PyCaret zu clustern
Ich habe gRPC mit Python ausprobiert
Ich habe versucht, mit Python zu kratzen
Ich habe versucht, Raspeye und conect + mit der Web-API zu verbinden
Ich habe versucht, Follow Management mit Twitter API und Python (einfach)
Ich habe versucht, den DRF-API-Anforderungsverlauf mit django-request zu speichern
Ich habe versucht, eine Quip-API zu erstellen
Ich habe die Naro-Roman-API 2 ausprobiert
Ich habe versucht, Sätze mit summpy zusammenzufassen
Ich habe maschinelles Lernen mit liblinear versucht
Ich habe versucht, WebScraping mit Python.
Ich habe versucht, Essen mit SinGAN zu bewegen
Ich habe versucht, DeepPose mit PyTorch zu implementieren
Ich habe Teslas API berührt
Ich habe die neuartige API von Naruro ausprobiert
Ich habe versucht, Prolog mit Python 3.8.2 auszuführen.
Ich habe die SMTP-Kommunikation mit Python versucht
Ich habe versucht, Sätze mit GPT-2 zu generieren
Ich habe versucht, LightGBM mit Yellowbrick zu lernen
Einführung der Google Map API mit Schienen
Ich habe versucht, das Gesicht mit OpenCV zu erkennen
Ich habe versucht, die checkio-API zu verwenden
Ich habe versucht, alles zu automatisieren, einschließlich der zweistufigen Authentifizierung von Google OAuth
Ich habe ChatOps mit Slack x API Gateway x Lambda (Python) x RDS ausprobiert
Ich habe versucht, die Docomo-Spracherkennungs-API und die Google Speech-API in Java zu verwenden
Ich habe versucht, die Google-API mit Ruby und Python zu erreichen. Machen Sie die Datenbank zu einer Tabelle und verwalten Sie sie mit Google Drive
Google Cloud Vision API-Beispiel für Python
Ich habe versucht, eine SMS mit Twilio zu senden
Ich habe versucht, schlechte Tweets regelmäßig mit der AWS Lambda + Twitter API zu löschen
Ich habe Linebot mit Flasche (Anaconda) + Heroku ausprobiert
Ich habe versucht, AutoEncoder mit TensorFlow zu visualisieren
Ich habe versucht, mit Hy anzufangen
Ich habe versucht, YOUTUBE Data API V3 zu verwenden
Ich habe versucht, Faktoren mit Titanic-Daten zu analysieren!
Ich habe versucht, mit Kaggles Titanic (kaggle②) zu lernen.
Ich habe versucht, mit Python + opencv nicht realistisch zu rendern
Ich habe versucht, "Sakurai-san" LINE BOT mit API Gateway + Lambda zu erstellen
Ich habe versucht, den Authentifizierungscode der Qiita-API mit Python abzurufen.
Verwenden Sie die Google Cloud Vision-API von Python
Ich habe versucht, die UnityCloudBuild-API von Python zu verwenden
Transkription von Bildern mit der Vision API von GCP
Ich habe eine funktionale Sprache mit Python ausprobiert
Bei der Einführung der Google Cloud Vision-API in Schienen habe ich die Dokumentation befolgt.