iMind Developers Blog

iMind開発者ブログ

PythonからCloud Vision APIを叩いてOCR

概要

PythonからCloud Vision APIを呼び出して画像やPDFから文字を読む。

バージョン情報

  • Python 3.7.4
  • google-cloud-vision==0.40.0

インストール

pip install google-cloud-vision

Cloud Visionでできること

下記のようなことができる。なかなか多彩。

サンプルデータ

本例では下記のような画像(pngとpdfの2種類を用意)を解析させる。

f:id:imind:20191207200722p:plain

サービスアカウントの作成

本例では認証はサービスアカウントを作成して落としたjsonファイルを利用して行う。

Cloud Vision自体のRoleは特に設定する必要がないらしいので、連携させて使うことが多いCloud Storageの権限だけ持たせて鍵を作る。

Web UIの「IAM & Admin」から「Service Account」を選んで「Create Service Account」で下記のような感じで入力。

f:id:imind:20191207212006p:plain

次のページで適当に「Storage Admin」あたりを入れておいて次のページへ移動。

f:id:imind:20191207212053p:plain

CREATE KEYで鍵をjsonファイルを作成してDONE。

落としたjsonファイルは仮にexample.jsonという名前にしてサンプルコードを動かす直下に置いておくこと。

画像ファイルからOCR

DOCUMENT_TEXT_DETECTIONを使うと画像形式のドキュメントからテキストを取得できる。

上述のサンプルデータ(example.png)を解析させてみる。

from google.cloud import vision

# service accountのjsonからclientの生成
client = vision.ImageAnnotatorClient.from_service_account_json(
        'example.json')

# 対象画像の読み込み
with open('example.png', 'rb') as fp:
    content = fp.read()
image = vision.types.Image(content=content)

# APIに投げる
response = client.document_text_detection(image=image)
document = response.full_text_annotation

# テキストの取得
document.text
    #=> 'Sheet1\nCanis lupus familiaris\n2歳くらい\n雑種(柴系?)\nかわいい\nとてもかわいい\n目に入れても痛くない\nいや、痛い\n'

こんな感じで画像からテキストを取得できる。

APIが有効になっていないと下記のようなエラーが出る。表示されたURLに行って有効にしておく。

PermissionDenied: 403 Cloud Vision API has not been used in project xxxxxxxxxxxx before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/vision.googleapis.com/overview?project=xxxxxxxxxxxx then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

テキストは単語や文字ごとに座標も格納されている。下記は単語ごとに座標を取った例。

for page in document.pages:
    for block in page.blocks:
        for paragraph in block.paragraphs:
            for word in paragraph.words:
                box = [(v.x, v.y) for v in word.bounding_box.vertices]
                text = []
                for symbol in word.symbols:
                    text.append(symbol.text)
                print(box, ''.join(text))

実行結果

[(517, 1), (589, 1), (589, 32), (517, 32)] Sheet1
[(281, 56), (337, 56), (337, 77), (281, 77)] Canis
[(350, 56), (407, 56), (407, 77), (350, 77)] lupus
[(416, 56), (508, 56), (508, 77), (416, 77)] familiaris
[(276, 85), (374, 85), (374, 108), (276, 108)] 2歳くらい
[(284, 118), (319, 118), (319, 142), (284, 142)] 雑種
[(331, 118), (337, 118), (337, 142), (331, 142)] (
[(342, 118), (386, 118), (386, 142), (342, 142)] 柴系
[(396, 118), (402, 118), (402, 142), (396, 142)] ?
[(408, 118), (414, 118), (414, 142), (408, 142)] )
[(281, 150), (370, 150), (370, 171), (281, 171)] かわいい
[(280, 180), (429, 180), (429, 206), (280, 206)] とてもかわいい
[(274, 212), (499, 212), (499, 234), (274, 234)] 目に入れても痛くない
[(284, 242), (320, 242), (320, 267), (284, 267)] いや
[(325, 242), (331, 242), (331, 267), (325, 267)] 、
[(336, 242), (382, 242), (382, 267), (336, 267)] 痛い

PDFファイルからOCR

上述サンプルデータのPDF版(example.pdf)を解析させてみる。

from google.cloud import vision

client = vision.ImageAnnotatorClient.from_service_account_json(
        'example.json')

# DOCUMENT_TEXT_DETECTIONを指定
feature = vision.types.Feature(
        type=vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION)

# 入力
with open('example.pdf', 'rb') as fp:
    content = fp.read()
input_config = vision.types.InputConfig(
        content=content, mime_type='application/pdf')

# リクエスト
req = vision.types.AnnotateFileRequest(
        features=[feature], input_config=input_config)
res = client.batch_annotate_files(requests=[req])

# 1つ目のレスポンスの内容を取得(requestsを1個しか送ってないのでresponseは1つのみ)
first_res = res.responses[0]

取れた中身を確認してみる。

# ページ数
first_res.total_pages
    #=> 1

# DOCUMENT_TEXT_DETECTIONではfull_text_annotationが返ってくる
annotation = first_res.responses[0].full_text_annotation

# 抽出されたテキスト
annotation.text
    #=> 'Sheet1\nCanis lupus familiaris\n2歳くらい\n雑種(柴系?)\nかわいい\nとてもかわいい\n目に入れても痛くない\nいや、痛い\nページ1\n'

非同期実行してtimeoutも指定したい場合は下記参照。

https://cloud.google.com/vision/docs/pdf

試しにparagraphごとのテキストを抽出してみる。

# テキストをparagraphごとに抽出してみる
for page in annotation.pages:
    for block in page.blocks:
        for paragraph in block.paragraphs:
            paragraph_text = []
            for word in paragraph.words:
                for symbol in word.symbols:
                    paragraph_text.append(symbol.text)
            print(''.join(paragraph_text))

    #=> Sheet1Canislupusfamiliaris2歳くらい雑種(柴系?)かわいいとてもかわいい目に入れても痛くないいや、痛い
    #=> ページ1

今回の画像では上部のテキストとフッタのテキストの2つにparagraphが分割されるようだ。

paragraphの座標を取ってみる。

for vert in paragraph.bounding_box.vertices:
    print(vert.x, vert.y)

    #=> 796 2180
    #=> 896 2180
    #=> 896 2212
    #=> 796 2212

改定履歴

Author: Masato Watanabe, Date: 2019-12-07, 記事投稿