Michaelです。
今回は、Microsoft Cognitive ServicesのComputer Vision APIで利用できるOCR機能を使って名刺読み取りプログラムを作ってみました。
名刺のデータ化について
お客様との打ち合わせや展示会に参加すると必ず交換される「名刺」ですが、SFAやCRMで管理するために、貰った名刺をデータ化する必要があります。今では、名刺読み取り専用のスキャナーやスマートフォンアプリが販売されており、名刺を簡単にデータ化できますが、そういったツールを持たなければ手入力で名刺の内容を打ち込まなければなりません。
それこそ、数百枚単位で名刺が交換される展示会の後では、名刺のデータ化だけで結構な時間をとられてしまうことも多いのではないでしょうか。
この悩みをCognitive ServicesのOCR機能で解決してみたいと思います。
名刺読み取りのアルゴリズム
名刺は各社でフォーマットが異なり、氏名、会社名等の配置が異なるため、読み取りは難しいようにも見えますが、名刺を見ていると一定の法則性があることがわかります。
・名刺の法則性
項目 | ||
会社名 | 会社名の行にはほぼ「会社」の単語が入る。(「株式会社」、「有限会社」等) | |
氏名 | フォントサイズは「企業ロゴ」、「氏名」、「会社名」の順で大きい傾向にあり、行の高さ上位3位には「氏名」が入る | |
電話番号 | 行に「Tel」、「Phone」、「電話」、「直通」のいずれかが入っている | |
Fax番号 | 行に「Fax」の単語が必ず入る。 電話番号とFax番号は同じ行になることが多いが、Fax番号が電話番号より前に記載されることはない |
|
住所 | ほとんどの場合で、都道府県名が入る。 入らない場合も県庁所在地など、一目で都道府県がわかるものが多い。 | |
必ず「@」が入る。 | ||
URL | 「http」、「www」のいずれかは必ず入る |
Computer Vision APIのOCR機能では、行単位とテキストと、テキストエリアの大きさを取得できるため、名刺の各行について法則性に基づいたデータ抽出を行えば、必要な情報を取得できると考えられます。
名刺読み取り機能の作成
上述の法則性を基に名刺読み取りのスクリプトを作成してみました。
基本的に画像から読み取ったテキスト行に特定の単語が含まれるかどうかを判定するだけのため、Computer Vision APIのレスポンス形式がわかっていれば1日もかからず作成できてしまいます。
(Computer Vision APIについては「Cognitive Services: OCR機能で印刷物の写真からテキストのデータ化をしてみる」もご参考ください)
都道府県、県庁所在地を取得するための「prefecture.json」も単に都道府県名と県庁所在地名をJSON形式の配列で保存しているだけで、簡単に作成できます。
・sample.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#!/usr/bin/env python #coding:utf-8 import requests import json import re def ocr(img_url, lang = "unk"): # Computer Vision API 設定 url = "https://southeastasia.api.cognitive.microsoft.com/vision/v1.0/ocr" # エンドポイント api_key = "********************************" # APIキー # リクエスト本文 body = { 'url': img_url } # リクエストヘッダ headers = { # Request headers 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': api_key } # リクエストパラメータ params = { # Request parameters 'language': lang, 'detectOrientation ': 'true' } # リクエスト res = requests.request("POST", url, headers=headers, json = body, params= params) return res.json() def main(): ### 検索文字列設定 ### # 都道府県、県庁所在地取得 (外部jsonファイル) with open("./prefecture.json", "r") as f: pref = [p.encode("utf-8") for p in json.load(f)] # 対象と検索値の設定 search_words = { "company": ["会社"], "mail": ["@", "@"], "tel": ["tel", "phone", "電話", "直通"], "fax": ["fax"], "address": pref, "url": ["http", "www"] } ### OCRテキスト読み取り ### # 名刺画像URL img_url = "https://*********" # OCR実行 res_data = ocr(img_url, lang = "ja") # 各行のテキスト、高さ抽出 lines = [ { "height": max([int(word["boundingBox"].split(",")[3]) for word in line['words']]), "text": "".join([word["text"] for word in line['words']]) } for reg_i, region in enumerate(res_data['regions']) for line_i, line in enumerate(region['lines']) ] ### 名刺画像内のデータ抽出 ### data = {} for i, line in enumerate(lines): # 設定検索条件に基づいてデータを検索、抽出 for key, words in search_words.items(): if data.get(key) == None: data.update({key: ""}) word_match = re.search("|".join(words).decode("utf-8"), line["text"].lower()) if word_match and data[key] == "": data[key] = line["text"] data.update({"metadata" + str(i): line["text"]}) # TelとFaxが1行になっているものを分割して格納 if data["tel"].lower().find("fax") >= 0: tel_arr = data["tel"].lower().split("fax") data["tel"] = tel_arr[0] data["fax"] = tel_arr[1] ### 氏名の抽出 ### # 行高さの上位数の設定 height_top = 3 # 行高さの上位 name_list = sorted(lines, key=lambda x: x["height"], reverse=True)[:height_top] # 明確に氏名でない項目の削除 not_name = [val for key, val in data.items() if key.find("metadata") < 0] # 住所等すでに氏名でないと判明している項目 name_list = [name["text"] for name in name_list if name["text"] not in not_name] name_list += [""] * (height_top - len(name_list)) data.update({"name" + str(name_i): name for name_i, name in enumerate(name_list)}) ### csvデータ出力 ### # CSVヘッダ情報 output = [["name" + str(j) for j in range(height_top)] + search_words.keys()] # 行の追加 rows = [data[head] if data.get(head) != None else "" for head in output[0]] output.append(rows) # CSV文字列化 csv = "\n".join(['"' + '","'.join(row) + '"' for row in output]) # CSV保存 with open("./meishi.csv", "w") as f: f.write(csv.encode("utf-8")) if __name__ == '__main__': main() |
・prefecture.json
1 2 3 4 5 6 7 |
[ "北海道", "札幌市", #### 中略 #### "沖縄県", "那覇市" ] |
上記コードで、以下のサンプルの名刺画像から情報を取得してみました。
・入力画像
上の名刺画像を読み取った結果、見込み通りに必要なデータを取得できていました。
部分的にハイフンが長音記号になっているところがありますが、簡単な手直しで済み、1枚2秒程度で処理できるため、手入力に比べてはるかに効率的にデータ化ができそうです。
・読み取り結果
name0 | 田中太郎 |
name1 | 節owled |
name2 | ommunrcatton |
fax | .047-700-5005 |
tel | tel.047ー397ー8897 |
url | http//www.knowledgecommunication.jp |
company | 株式会社ナレッジコミュニケーション |
address | 〒272ー0143千葉県市川市相之川3ー13ー23丸伝小川ビル3F |
E-mail:t-tanaka@knowledgecommunication.jp |
今回は、Computer Vision APIのOCR機能を使用して名刺読み取りプログラムを作成してみましたが、簡単なスクリプトでも案外良好に名刺のデータ化をすることができました。
サンプルでは、名刺の画像で試していますが、いくつか実際の名刺の写真で試してみても同様にデータ化ができており、完全ではないにせよデータ入力のサポートになるものには仕上がっているように思いました。
今回は名刺でしたが、フォーマットや規則さえ分かっていれば他の紙ベースの文書にも適応できるので、ぜひ試してみてください。
次回もお楽しみに。