1.はじめに
この記事では、Google Gemini のAPIを使用して、バッチ処理で画像から文字を読み取るAI-OCRを作りました。もし、仮にAI-OCRを作成される機会がありましたら、ご参考まで。
2.背景
2.1.動機
教育訓練講座(SAMURAI ENGINEER AIデータサイエンスコース)を半年間受講し、機械学習の基礎を学びました。
得た知識を使って、是非実現したいことがありました。それがAI-OCRの作成です。
私は現在、とある企業の人事・給与事務の統括をしており、部下の人員削減の一方、業務量の増大に日々頭を抱えています。その私が今、一番恐れているのが、4月に新入社員が入ってくることによる(一過性ですが)業務量の増大です。
人を雇うと、
- 人事システムに人事基礎データ(氏名、住所、生年月日など)登録
- マイナンバーを収集し、人事システムへ登録
- 社員証の発行(外注ではなく、私の部署でカード発行機で発行している)
- 雇用保険加入手続き
- 厚生年金加入手続き
- 健康保険加入手続き
といった事務を2週間くらいでやり遂げなければならず、結構負担です。
ところが諸事情により、未だにマイナンバーなどの収集は「紙」です。
そこで前作[AI-OCRを自作しました(2025.2)](https://qiita.com/jupiter-san/items/d7b3e2a70c8624c43c45 )にて、自前でAI-OCRを作成することにチャレンジしました。
結果として、識字性能に沢山の課題を残しました。
問題点
① 数字の読み取り精度がイマイチ
② 漢字の読み取りはできない(学習データの入手が難しいため、鍛えることができない)
がんばって課題をひとつひとつ解決する方法を考えていく傍ら、もうすぐ実運用を開始しなければならない4月1日がやってきます! ああ、どうしよう。。。
そんなとき、最近の生成AIは「マルチモーダル」であることを思い出しました。
ご存知のとおり、「マルチモーダル」モデルは、文字・画像・音声 を入力して答えを返してくれるスグレモノです!
画像を入力して、書かれている内容を返してくれればいいやん!と気づきました。
画像をAPIでQUERYし、応答された「社員番号」をテキストに吐き出すバッチ処理だ。
なんか面白そう!やってみたい!
尚、「マイナンバー」などの個人情報をパブリックネットワークに出すのは、個人情報保護法に抵触する恐れがあるので、今回はやりません。
2.2.開発環境
処理自体は、ローカルのリソースを食うものではないので、普通の事務処理用PCでできます。
私は、VSCode上の jupyter notebook 上で動かしています。
3.準備
3.1. GooGle APIキーの取得
まず、Google AI Studioにアクセスします。
このように、利用規約の同意を求められますので、同意します。
左上の 「Get API key」を押下します。
「キー APIキーを作成」を押下するとAPIキーを取得できます。
今回は、Gemini2.0Flashを使用します。
無料枠があります。(注意点としては、「サービスの改善に使用」が「はい」となっていることです。これは、投入したデータを学習に使用することを言っていますので、配慮が必要なデータは投入しないほうが無難です。)
尚、有料契約の場合は学習に使用しないことになっています。価格としては安価(100万トークンあたり$0.10)なので、実際に使う場合には有料契約もいいかもと考えています。
https://ai.google.dev/gemini-api/docs/pricing?hl=ja
3.2. 読ませる画像の準備
3.3. 試行
次のようなコードを用意しました。
(参考にしたサイト https://qiita.com/junpei_style/items/6736ab2d9b4098993e90)
import os
import google.generativeai as genai
from google.generativeai import types
import PIL.Image
apikey ="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
file = os.path.join(path,'test_img_in/20250313151106-0001.tif')
image = PIL.Image.open(file)
# プロンプト
contents=["画像に写っている郵便番号と住民票住所", image]
# モデルは gemini-2.0-flash を使用
genai.configure(api_key=apikey)
model = genai.GenerativeModel(model_name='gemini-2.0-flash')
response = model.generate_content(contents)
print(response.text)
おお!なんとすばらしい!
ちゃんと読み取れています。
4.実践
実運用するにあたり、次のコードを用意しました。
test_img_inフォルダに置いたすべての画像をひとつひとつ読み、画像から郵便番号と住民票住所を読み取って、キーとして使うファイル名と一緒に output.txtファイルに書き出す処理です。
import os
import google.generativeai as genai
from google.generativeai import types
import PIL.Image
path = os.getcwd()
GOOGLE_API_KEY ="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
out_list = []
# 画像ファイルがあるフォルダ
img_dir = os.path.join(path,"test_img_in")
for current_dir,dirs,files in os.walk(img_dir):
for file in files:
file1 = os.path.join(img_dir,file)
image = PIL.Image.open(file1)
contents=["画像に写っている郵便番号と住民票住所を読み取って、[郵便番号,住民票住所]という形式で返してほしい", image]
genai.configure(api_key=GOOGLE_API_KEY)
model = genai.GenerativeModel(model_name='gemini-2.0-flash-lite')
response = model.generate_content(contents)
res = response.text
out_list.append([file,res])
o_file = os.path.join(path,"output.txt")
with open(o_file, 'w') as o:
for ele in out_list:
st1 = ele[0] + "," + ele[1]
print(st1, end="\n", file=o)
5.結果
output_txt
20250313151131-0001.tif,[702-8013, 岡山県岡山市南区飽浦]
20250313151119-0001.tif,[946-5231, 福岡市中央区鳥飼2丁目1-11]
20250313151106-0001.tif,[865-0063, 熊本県玉名市中尾 123]
完璧です。
画像枚数が多いと、次のエラーを吐いて止まってしまいます。(私の場合、40枚くらいです。)
(情報源:https://qiita.com/7shi/items/54e45181052c914a4d45)
要は、無料お試しはここまでで、本格運用するなら有料契約を検討してね!ということみたいです。
6.参考にした情報
本当に世の中にはすごい方がおられまして、その方々が公開していただいている情報は宝の山です。
今回もそれを参考にさせていただいております。
最大の謝辞を述べさせていただきます。
[Geminiのドキュメント]
https://ai.google.dev/gemini-api/docs/pricing?hl=ja
[Gemini-APIのかきかた]
https://qiita.com/junpei_style/items/6736ab2d9b4098993e90
[429エラーについて]
https://qiita.com/7shi/items/54e45181052c914a4d45