レシートのOCR
yomitokuもしくは Google Cloud Vision API または
Gemini を使ってレシートをOCRして解析結果から
店舗名、商品名、商品の価格、購入年月日、購入した商品の数量を抽出する
なお
Google Cloud Vision API でもある程度はできるが
購入数量が複数の場合はまとめた金額が表示されてしまう
また、直売所のようなところで購入した場合
生産者の名前が商品名に含まれるのでこれを削除する必要がある
コードである程度の添削は可能だが
店舗ごとに処理を分ける必要があったため
以前、Google Cloud Vision API でOCR結果をLLMでCSVにしたが
うまくいかなかったため
今回はgemini で行ってみた
このレシートから
店舗名、商品名、商品の価格、購入年月日、購入した商品の数量を抽出して
結果は
承知いたしました。提供されたレシート画像から、店舗名、商品名、商品の価格、購入年月日、購入した商品の数量を抽出します。
抽出結果:
* 店舗名: とれたて食菜館
* 購入年月日: 2025年5月31日
* 商品と価格、数量:
1. 商品名: イチゴ / 鈴木康友
* 価格: ¥350
* 数量: 1 (画像からは数量を特定できませんが、一般的にこの形式では1点と解釈されます)
2. 商品名: ニラ
* 価格: ¥110
* 数量: 1
3. 商品名: 加子 / 山本 貢
* 価格: ¥160
* 数量: 1
4. 商品名: 新玉ねぎ&さや / 守屋 昇
* 価格: ¥180
* 数量: 1
5. 商品名: コーヒー / 浅路小徳
* 価格: ¥1,090
* 数量: 1
6. 商品名: ミニトマト
* 価格: ¥230
* 数量: 1
7. 商品名: きゅうり / 稲田政正
* 価格: ¥150
* 数量: 1
8. 商品名: 白ねぎ / 鈴木正孝
* 価格: ¥180
* 数量: 1
9. 商品名: キャベツ / 鈴木正孝
* 価格: ¥100
* 数量: 1
10. 商品名: バラ / 山崎きぬ子
* 価格: ¥300
* 数量: 1
11. 商品名: レタス / 山田さち子
* 価格: ¥140
* 数量: 1
12. 商品名: 乾しいたけ / 大石礼店
* 価格: ¥620
* 数量: 1
13. 商品名: 園味椎茸 / 永井克太
* 価格: ¥260
* 数量: 1
補足:
* レシートの各項目には購入数量が明示されていませんが、一般的に商品の価格がそのまま記載されている場合は「1点」の購入と解釈されます。
* 商品名の後ろに記載されている「鈴木康友」などの名前は、生産者名であると推測されます。
この結果をCSVファイルにして
店舗名,購入年月日,商品名,価格,数量 とれたて食菜館,2025年5月31日,イチゴ / 鈴木康友,350,1 とれたて食菜館,2025年5月31日,ニラ,110,1 とれたて食菜館,2025年5月31日,加子 / 山本 貢,160,1 とれたて食菜館,2025年5月31日,新玉ねぎ&さや / 守屋 昇,180,1 とれたて食菜館,2025年5月31日,コーヒー / 浅路小徳,1090,1 とれたて食菜館,2025年5月31日,ミニトマト,230,1 とれたて食菜館,2025年5月31日,きゅうり / 稲田政正,150,1 とれたて食菜館,2025年5月31日,白ねぎ / 鈴木正孝,180,1 とれたて食菜館,2025年5月31日,キャベツ / 鈴木正孝,100,1 とれたて食菜館,2025年5月31日,バラ / 山崎きぬ子,300,1 とれたて食菜館,2025年5月31日,レタス / 山田さち子,140,1 とれたて食菜館,2025年5月31日,乾しいたけ / 大石礼店,620,1 とれたて食菜館,2025年5月31日,園味椎茸 / 永井克太,260,1
CSVファイルの内容について:
* 1行目はヘッダー行で、各列が何のデータであるかを示しています。
* 各行が1つの商品の情報に対応しており、店舗名と購入年月日も各行に繰り返して記載しています。
* 価格は数字のみを記載し、通貨記号「¥」は除外しています。
* 数量は、レシートに明記されていないため、一般的な解釈として「1」としています。
ここまでは cloud Vision API と同じ
CSVファイルにするときに、商品名から人物名を削除して
店舗名,購入年月日,商品名,価格,数量 とれたて食菜館,2025年5月31日,イチゴ,350,1 とれたて食菜館,2025年5月31日,ニラ,110,1 とれたて食菜館,2025年5月31日,加子,160,1 とれたて食菜館,2025年5月31日,新玉ねぎ&さや,180,1 とれたて食菜館,2025年5月31日,コーヒー,1090,1 とれたて食菜館,2025年5月31日,ミニトマト,230,1 とれたて食菜館,2025年5月31日,きゅうり,150,1 とれたて食菜館,2025年5月31日,白ねぎ,180,1 とれたて食菜館,2025年5月31日,キャベツ,100,1 とれたて食菜館,2025年5月31日,バラ,300,1 とれたて食菜館,2025年5月31日,レタス,140,1 とれたて食菜館,2025年5月31日,乾しいたけ,620,1 とれたて食菜館,2025年5月31日,園味椎茸,260,1
変更点:
* 「イチゴ / 鈴木康友」 → 「イチゴ」
* 「加子 / 山本 貢」 → 「加子」
* 「新玉ねぎ&さや / 守屋 昇」 → 「新玉ねぎ&さや」
* 「コーヒー / 浅路小徳」 → 「コーヒー」
* 「きゅうり / 稲田政正」 → 「きゅうり」
* 「白ねぎ / 鈴木正孝」 → 「白ねぎ」
* 「キャベツ / 鈴木正孝」 → 「キャベツ」
* 「バラ / 山崎きぬ子」 → 「バラ」
* 「レタス / 山田さち子」 → 「レタス」
* 「乾しいたけ / 大石礼店」 → 「乾しいたけ」
* 「園味椎茸 / 永井克太」 → 「園味椎茸」
段階的に処理したけど目的は達成できる
次に個数の問題のテスト
今回は別のレシートを使う
しかし
クリエイト、ピアゴのレシートではうまくいかない
横向きなのを縦にして実験してみる
判定を自動化できるかgeminiでためした
このレシートは横向き、縦向きどっち?
このレシート画像は、横向き です。
文字が右から左に流れるように見え、日付や金額の表記も横方向になっているため、画像を時計回りに90度回転させると、文字が正しく上から下へ読める縦向きになると思われます。 提供されたレシート画像は、縦向きです。
画像の左側に「ピアゴ」のロゴがあり、その下に文字が縦書きで並び、右側には商品名や価格が横書きで並んでいますが、全体としてはレシートの長い辺が縦になるように撮影されています。
この向きで情報を抽出することができますので、ご安心ください。
となるので gemini で回転しようとしたけど
Geminiでは画像の回転ができない
pip install Pillow
で必要なライブラリインストール
vim rotate_image.py
で中身を
from PIL import Image
import os
def rotate_image_90_degrees(image_path, output_path=None, clockwise=True):
"""
指定された画像を90度回転させる関数。
Args:
image_path (str): 回転させたい画像のパス。
output_path (str, optional): 回転後の画像を保存するパス。
指定しない場合、元のファイル名に '_rotated' を追加して保存。
clockwise (bool): Trueの場合時計回りに90度、Falseの場合反時計回りに90度回転。
"""
try:
# 画像を開く
img = Image.open(image_path)
print(f"画像をロードしました: {image_path}")
# 回転方向を決定
if clockwise:
rotated_img = img.rotate(-90, expand=True) # 時計回りに90度回転 (反時計回りの-90度と同じ)
rotation_direction = "時計回り"
else:
rotated_img = img.rotate(90, expand=True) # 反時計回りに90度回転
rotation_direction = "反時計回り"
# 出力パスが指定されていない場合、自動生成
if output_path is None:
base, ext = os.path.splitext(image_path)
output_path = f"{base}_rotated{ext}"
# 回転した画像を保存
rotated_img.save(output_path)
print(f"{rotation_direction}に90度回転した画像を保存しました: {output_path}")
except FileNotFoundError:
print(f"エラー: 指定されたファイルが見つかりません - {image_path}")
except Exception as e:
print(f"画像の回転中にエラーが発生しました: {e}")
if __name__ == "__main__":
# --- 使用例 ---
# 回転したい画像のファイル名
# スクリプトと同じディレクトリにある場合、ファイル名のみでOK
# 別の場所にある場合、'path/to/your/image.jpg' のようにフルパスを指定
input_image_name = "PXL_20250531_012457081.jpg" # ← ここを回転させたい画像ファイル名に置き換えてください
# 回転後の画像の保存パス (Noneにすると自動で '_rotated' が追加される)
output_image_name = None # 例: "rotated_image.jpg"
# 時計回りに回転するかどうか (True: 時計回り, False: 反時計回り)
rotate_clockwise = True
rotate_image_90_degrees(input_image_name, output_image_name, rotate_clockwise)
print("\n-----------------------------------------------------")
print("スクリプトの実行が完了しました。")
print(f"回転後の画像は '{input_image_name}' (または指定したパス) の場所に保存されています。")
print("-----------------------------------------------------\n")
これを
cp ~/Downloads/PXL_20250531_012457081.jpg . python rotate_image.py PXL_20250531_012457081.jpg 画像をロードしました: PXL_20250531_012457081.jpg 時計回りに90度回転した画像を保存しました: PXL_20250531_012457081_rotated.jpg ----------------------------------------------------- スクリプトの実行が完了しました。
として
再度 gemini で実行したけど結果は変わらない
レシートの向きは関係ないらしい
* クエリが正常に完了しました
承知いたしました。90度回転された「ピアゴ」のレシート画像から、店舗名、商品名、商品の価格、購入年月日、購入した商品の数量を抽出します。
抽出結果:
* 店舗名: ピアゴ
* 購入年月日: 2025年5月31日
* 商品と価格、数量:
1. 商品名: コミックス
* 価格: ¥436
* 数量: 1
2. 商品名: 大豆もやし 1.900M
* 価格: ¥288
* 数量: 1
3. 商品名: 明治おやつヨーグルト
* 価格: ¥130
* 数量: 1
4. 商品名: Bio 旬摘みフルーツ
* 価格: ¥178
* 数量: 1
5. 商品名: Bio プチダノン
* 価格: ¥90
* 数量: 1
6. 商品名: 減塩カツオ,2個
* 価格: ¥198
* 数量: 1
7. 商品名: 青森県産にんにく
* 価格: ¥165
* 数量: 1
8. 商品名: 鶏肉もも肉
* 価格: ¥645
* 数量: 1
9. 商品名: 大根
* 価格: ¥323
* 数量: 1
10. 商品名: 信州田舎造りみそ
* 価格: ¥136
* 数量: 1
11. 商品名: 田中商店もめんとうふ
* 価格: ¥116
* 数量: 1
12. 商品名: (品名不明、単価 ¥271)
* 価格: ¥813 (271 x 3個と推測)
* 数量: 3
13. 商品名: まとめ値引き
* 価格: -¥8
* 数量: 1
補足:
* 回転後の画像でも、前回の画像と同様に情報を読み取ることができました。
* レシートの各項目には購入数量が明示されていませんが、一般的にこの形式では1点と解釈されます。ただし、12番目の商品については「3個」の記載があるため、数量を3としました。価格も271円が3個で813円と計算されているようです。
* 商品名の一部には、ブランド名や種類が含まれています。
* 最後の「まとめ値引き」は商品というよりは値引き項目ですが、形式に合わせて抽出しています。
* 商品名の後ろの「JAN」や数字は、商品のJANコードや識別番号と思われます。
プロンプトでは限界っぽいので yomitoku を使うことにする