2015年前後から、クラウドサービスが色々と出てきました。
簡単に、AI、チャットボットが使えたり、データベースやストレージを借りることもできます。
今回は、画像のAIで画像の中から文字を検出してくれるOCRについて紹介します。
本記事はこんな方におすすめです。

お客様アンケートの集計を自動化したい
本記事の内容
- GCPでのAPI設定
- 方法その1 ウェブリクエスト
- 方法その2 ライブラリ
- 検出したデータを処理
この記事を読むと、Google Cloud PlatformのAPIの設定から、Vision APIでOCRが使えるようになります。
今回は、左の画像を右のようにデータ化します。


*データは一部
目次
GCP VisionAPIの価格
まず、気になるのは利用料金ではないでしょうか?
価格は次のリンクの公式にのっています。

データ1000までは無料ですね。
そのあと、1000ごとに$1.50かかりますが、200円もしないので、ほとんど気にする必要はないでしょう。
このように、簡単にすぐ使える上に、とても安く提供されています。
GCPでのAPI設定
VisionAPIをGCPで有効にしていきます。
GCPについて
GCPはGoogleCloudPlatformの略です。
AmazonのAWSやIBM Cloud、WindowsのAzureと同じクラウドサービスです。
サーバーが簡単に構築できたり、AIの処理の実装などを手伝ってくれます。
まず、GCPにアクセスします。
プロジェクトの作成
GCPではプロジェクト単位で使うサービスなどを管理できます。
まず、プロジェクトを作成します。
GCPのダッシュボードの「プロジェクト選択」から「新しいプロジェクト」を選択します。

もしくは、下記のリンクに直接飛びます。
適当な名前でプロジェクトを作成します。

これで、新しいプロジェクトが作成され、次のようなダッシュボードがもらえます。
ここで、色々なサービスを管理します。

Vision APIを有効にする
VisionAPIを使えるようにします。
ナビゲーションメニューより、API>ライブラリを選択します。

次に、Vision APIを検索します。
「vition api」を入力して、Vision APIを選びます。


APIを有効にします。

これで必要なAPIを有効にしたので、接続するための認証情報を作っていきます。
Google APIの認証を取得
ナビケーションメニューから、API>認証情報へいきます。

認証情報を作成から、サービスアカウントを作成します。

サービスアカウント名と説明を入力して、作成を押します。

サービスアカウントができるので、リンクを押します。

キーの項目から、鍵を追加から新しい鍵を作成を押します。

JSONを作成します。

ダウンロードしましょう。

このJSONファイルはpythonのライブラリを使う場合に使用します。
APIキーの取得
APIキーはpythonだけでなく、ウェブのリクエストでOCR認識の結果を受け取るときに使います。
再度、認証情報のところに行きます。

次は、APIキーを選択します。

APIキーが発行されます。

このAPIキーを使うと、python以外でもウェブのリクエストを飛ばすことでOCR認識できます。
必要に応じて、右下のキー制限でアクセス制限をつけると良いでしょう。
方法その1 ウェブリクエスト
ウェブのリクエストの方法では、取得したAPIキーを使います。
特に、新しいライブラリのインストールが必要ということはありません。
API_KEY = ''に取得したAPIキーを入れます。
import requests
import base64
import json
API_KEY = ''
GOOGLE_CLOUD_VISION_API_URL = 'https://vision.googleapis.com/v1/images:annotate?key='
# TEXT_DETECTION:比較的短い文字
# DOCUMENT_TEXT_DETECTION:文章
DETECTION_TYPE = "DOCUMENT_TEXT_DETECTION"
def request_cloud_vison_api(image_base64, type="DOCUMENT_TEXT_DETECTION"):
""" http のリクエストでVisionAPIにアクセス """
api_url = GOOGLE_CLOUD_VISION_API_URL + API_KEY
req_body = json.dumps({
'requests': [{
'image': {
'content': image_base64.decode('utf-8')
},
'features': [{
'type': type,
'maxResults': 10,
}]
}]
})
res = requests.post(api_url, data=req_body)
return res.json()
def img_to_base64(filepath):
""" 画像データをエンコード """
with open(filepath, 'rb') as img:
img_byte = img.read()
return base64.b64encode(img_byte)
def render_doc_text(file_path):
result = request_cloud_vison_api(image_base64=img_to_base64(file_path),
type=DETECTION_TYPE)
data_list = []
# データの取得 textAnnotationsに座標とテキスト fullTextAnnotationにテキスト
result_list = result["responses"][0]["textAnnotations"]
for d in result_list:
data_list.append([d['boundingPoly']['vertices'], d['description']])
# 1つ目除外
data_list = data_list[1:len(data_list)]
return data_list
render_doc_text関数に画像ファイルを渡すことで、検出した文字と座標を取得します。
このコードを「google_ocr_api.py」として保存します。
方法その2 ライブラリ
次に、google-cloud-visionライブラリを使った方法です。
こちらでは、先ほどのJSONファイルを使います。
ライブラリのインストール
pipを使って、ライブラリをインストールしましょう
pip install google-cloud-vision
環境変数の設定
先ほど取得した、jsonファイルは環境変数に登録します。
UbuntuやMacの場合
export GOOGLE_APPLICATION_CREDENTIALS="jsonファイルパス"
Windowsの場合、コントロールパネル>システム>環境変数から設定しましょう。

設定が終われば、あとはコードです。
from pathlib import Path
from google.cloud import vision
def render_doc_text(filein):
client = vision.ImageAnnotatorClient()
p = Path(__file__).parent / filein
with p.open('rb') as image_file:
content = image_file.read()
image = vision.Image(content=content)
data_list = []
response = client.document_text_detection(image=image)
document = response.full_text_annotation
for page in document.pages:
for block in page.blocks:
for paragraph in block.paragraphs:
for word in paragraph.words:
box = [{'x':v.x, 'y':v.y} for v in word.bounding_box.vertices]
text = [symbol.text for symbol in word.symbols]
data_list.append([box, ''.join(text)])
return data_list
このrender_doc_text関数は、ウェブリクエストの時と同じデータを取得します。
ですので、どちらを使ってもOKです。
このコードを「google_ocr_lib.py」として保存します。
検出したデータの処理
ここからは、個人的にこういう処理するかなといったイメージになるので、参考にしていただけたらと思います。
紹介した各方法で、render_doc_text関数を使うと、検出した文字と検出した箇所が四角の座標で取得できます。
次のようなイメージです。
左が検出前、右が検出後です。


最終的に取得したいデータは、四角の各ポイントではなく、中心座標と文字とします。
これは個人的に処理しやすいと思ったからです。

課題点
検出した文字を見ると以下のような課題があります。
ポイント
文字の区切りがおかしい。例、商品1は「商品」「1」となっている
検出できない文字、検出文字が違う
2つ目は動かしてもらって、中身を見るとわかりますが、これはどうしようもないので、少なくとも1つ目は解決していきます。
サンプルコード
render_doc_textで取得したデータを最終的にx,y,文字のpandasのDataFrameにします。
いくつか関数を用意しています。
| 関数名 | 機能 |
| rect_to_point | 座標の中心を算出 |
| rect_ave_height_degree | 検出した四角の平均高さ、傾きを算出 高さは、文字が近接しているかの判断に使っています。 文字高さ分に近ければ、近接文字扱いするとかです。 傾きは画像の傾き補正に使おうと思いましたが、使っていません。 |
| join_nearest | 近接文字を結合します。 |
実際のコードを紹介します。
from google_ocr_lib import render_doc_text
# from google_ocr_api import render_doc_text
import numpy as np
import math
import pandas as pd
def rect_to_point(rect):
""" 文字rectの中心座標 """
x = []
y = []
for d in rect:
if 'x' in d:
x.append(d['x'])
if 'y' in d:
y.append(d['y'])
return sum(x)/len(x), sum(y)/len(y)
def rect_ave_height_degree(data_list):
""" 平均高さ、傾き """
height = []
degree = []
for rect in data_list:
x = []
y = []
for d in rect[0]:
if 'x' in d:
x.append(d['x'])
if 'y' in d:
y.append(d['y'])
# データ不足しているのは無視します。
if len(x)==4 and len(y)==4:
height.append(((y[3]-y[0])+(y[2]-y[1]))/2)
if (x[1]-x[0]) == 0:
tan = 0
else:
tan = (y[1] - y[0]) / (x[1] - x[0])
atan = np.arctan(tan) * 180 / math.pi
degree.append(atan)
# 平均値を返す。場合によっては中央値でもよい。
return sum(height)/len(height), sum(degree)/len(degree)
def join_nearest(data_list, height):
""" 近接の文字結合 """
ret_data = []
data_list.reverse()
# データなくなるまで
while data_list:
d = data_list.pop()
most_near = 0
# 近接文字なくなるまで
while most_near!=-1:
rect = d[0]
most_near = -1
if ('x' in rect[1]) and ('y' in rect[1]):
curent_x = rect[1]['x']
curent_y = rect[1]['y']
# 右に文字高さの 1/2 以内の近さがあれば
temp_data = data_list.copy()
for ind2, d2 in enumerate(temp_data):
rect2 = d2[0]
if ('x' in rect2[0]) and ('y' in rect2[0]):
other_x = rect2[0]['x']
other_y = rect2[0]['y']
# 距離の近いところ
if (abs(other_x-curent_x)<height/2) and (abs(other_y-curent_y)<height/2): most_near = ind2 break # 文字連結 if most_near>0:
# 連結
new_rect = []
new_rect.append(rect[0])
new_rect.append(rect2[1])
new_rect.append(rect2[2])
new_rect.append(rect[3])
new_str = d[1] + d2[1]
d = [new_rect, new_str]
# 連結したデータも削除
del data_list[ind2]
else:
ret_data.append(d)
break
return ret_data
if __name__ == '__main__':
# OCR検知
data_list = render_doc_text('sample.png')
# 高さ、角度
height, degree = rect_ave_height_degree(data_list)
# 近接文字結合
data_list = join_nearest(data_list, height)
# 中央座標に変換して、DataFrameに変更
new_data_list = []
for d in data_list:
x, y = rect_to_point(d[0])
new_data_list.append([x, y, d[1]])
df = pd.DataFrame(data=new_data_list, columns=['x', 'y', 'text'])
print(df)
このコードを「main.py」として、1,2行目でgoogle_ocr_libかgoogle_ocr_apiを読んでいます。
使いたいほうを使ってください。
実行すると、DataFrameにデータが入りますので、CSVにデータ保存したら、他にも処理を追加したりすると良いでしょう。
近接文字をするしないでは次のように差が出ます。
左が、何もしない状態、右が近接文字をつけた場合。
これは、データによっては役に立たないかもしれませんが、帳票の画像からデータを抜き取るときはこういった工夫がいるのではないでしょうか?


他の活用方法は「python 活用 できることまとめ」も参考にしてください。
Pythonスキルが身に付いたら、ぜひスキルを生かして稼いでいきましょう!