📘

無料OCR Google Gemini API キーをPythonでローカル活用!画像をテキスト化

に公開

🟢 1. はじめに

  • 本記事の目的

    • Google Gemini APIを無料で利用
    • ローカルPython環境で画像をテキスト化
    • 結果をテキストファイルで保存
  • 想定読者

    • Python初級者〜中級者(Python実行環境が必要)
    • OCRを手軽に試してみたい人
  • 確認用デモWebアプリ
    同じAIモデル、方式で作成してあります。試してみてください。(本記事は、このアプリの作り方は取り扱いません。)

    ❤️ イチゲの AI OCR アプリ


🟢 注意事項(必ずお読みください)

この記事では、私自身がGoogleアカウントを使い、クレジットカードは登録せずに利用してみました。
ただし、Google CloudやGoogle AI Studioは利用条件が変更される場合があります。

私の利用環境

  • Googleアカウント:個人アカウント
  • 課金登録:クレジットカード登録なし(Google Cloudの請求先アカウントの管理で請求先アカウントが「請求が無効です」になっている)
  • Google AI Studio:登録方法は明確に記憶していません(途中で無料枠にサインアップした可能性あり)
  • 利用API:Gemini 1.5 Flash→2025/9/15非推奨になっていたのでgemini-2.0-flash-liteに変えました。

無料枠について

  • gemini-2.0-flash-liteには無料利用枠が提供されています。

  • 公式ドキュメント: Gemini API 料金体系レート制限

    • ネットで調べたりAIに聞いても今現在どうなっているのか、情報が錯綜して、よくわからなくなることがあります。ご自身でご確認ください。
    • 私は、今のところ(2025/7/4~)1日10回以上2日間しか使っていませんが、大丈夫です。

利用資格・注意点

  • 利用にはGoogleアカウントが必要です。

  • ビジネスアカウント・有料プラン・クレジットカード登録を行っている場合、

    • 無料枠が適用されない
    • 従量課金が発生する
      可能性があります。
  • 特に商用利用や大量リクエストを行う場合は必ずご自身で料金を確認してください。

⚠️ 注意点と免責

  • 本記事の検証環境では クレジットカード登録なし の状態で無料枠内のみ使用していますが、

    • Billingアカウントの有無有料Tierへのアップグレードによっては、
      • 利用状況に応じて従量課金が発生する可能性があります。
  • 無料枠の超過や誤操作により料金が発生した場合でも、本記事執筆者は一切責任を負えません。
    ご利用前に必ずご自身の料金設定画面で確認をお願いします。

実行時の注意事項

  • 無料利用にはリクエスト上限があります
  • 画像品質によって認識率が変わるため、解像度や明るさを工夫してください
  • API キーは絶対にGitHub公開リポジトリにあげない

🟢 2. Google Cloud API キーの取得手順

プロジェクト作成

  1. Google Cloud Consoleにログイン
  2. 上部の「プロジェクト選択」(2025/7/5時点では左上のGoogle Cloud の右のMy project~の部分にカーソルをもっていくと「プロジェクト選択ツールを開く」というボタンが表示される」)
  3. 「新しいプロジェクト」→名前を付けて「作成」

API キー取得

  1. Google AI Studio にアクセス
  2. 「GET API キー」→「API キーを作成」→「既存プロジェクトを選択」→先ほどのプロジェクト
  3. 「API キーを作成」→コピーして保存
  4. このキーは .env に保存推奨です。本記事でもそうします。

使用状況の確認

  1. Google Cloud Consoleにログイン
  2. API とサービス → Generative Language API
  3. 割り当てとシステム上限で確認できます。
    もちろんです!Google Cloud Consoleの「割り当てとシステム上限」ページに表示される**「名前」項目**は、各クォータ(使用上限)の識別名であり、どの操作に対する制限なのかを示すラベルのようなものです。

🏷️ 「名前」項目とは?

この欄には、例えば以下のような名前が表示されます:

  • Batch embed contents request limit per minute for a region
  • Batch embed text request limit per minute for a region

これらは、Gemini APIの特定の機能に対するリクエスト制限を表しています。


🔍 名前の構造と意味

それぞれの名前は、以下のような構造で意味が分かれています:

項目名 意味
Batch 一括処理(複数のデータを一度に送る)
embed contents 画像や音声などの埋め込み処理
embed text テキストの埋め込み処理(ベクトル化など)
request limit per minute 1分あたりのリクエスト上限
for a region 地域ごとの制限(例:asia-northeast1など)

つまり、例えば
Batch embed text request limit per minute for a region
という名前は、
「特定のリージョンにおいて、テキスト埋め込みの一括リクエストが1分間に何回まで可能か」
という制限を表しています。


🗺️ 地域(region)ごとの制限

同じ名前の項目でも、地域(region)ごとに別々に表示されます。たとえば:

  • asia-northeast1(東京)
  • us-central1(アイオワ)
  • europe-west1(ベルギー)

それぞれの地域に対して、同じ種類のリクエスト制限が個別に設定されているということです。


この「名前」項目を見れば、どの操作に対して、どの地域で、どれだけのリクエストが許されているかが一目で分かります。APIを効率よく使うためには、ここをしっかり把握しておくのが重要です。
無関係のものまで表示されますが、フィルターを使って関係するものだけを表示してみてください。


🟢 3.実施例

OCRスクリプト 新規プロジェクトセットアップマニュアル

このドキュメントは、画像から文字を抽出する ocr_receipt.py
スクリプトを、新しいプロジェクトとしてゼロからセットアップし、実行するための手順を説明します。

3-1. プロジェクトの準備

  1. 新しいフォルダを作成
    まず、このOCRプロジェクト専用の新しいフォルダを任意の場所に作成します。
mkdir ocr_project
  1. 作成したフォルダに移動
    ターミナル(コマンドプロンプトやPowerShell)で、作成したフォルダに移動します。
cd ocr_project
  1. OCRスクリプトファイルを作成
    ocr_receipt.py という名前のファイルをプロジェクトフォルダ内に作成し、以下のコードをコピーして貼り付け、保存してください。
ocr_receipt.py
import textwrap
import google.generativeai as genai
import PIL.Image
import os
import sys
import pandas as pd
from pathlib import Path
from datetime import datetime
def ocr_image(image_path: str) -> str:
    """
    Performs OCR on an image file using the Gemini Pro Vision model.

    Args:
        image_path: The path to the image file.

    Returns:
        The extracted text from the image.
    """
    from dotenv import load_dotenv
    load_dotenv()

    api_key = os.getenv("GEMINI_API_KEY")
    if not api_key:
        return "Error: GEMINI_API_KEY not found in .env file or environment variables."

    genai.configure(api_key=api_key)

    model = genai.GenerativeModel('gemini-2.0-flash-lite')

    try:
        img = PIL.Image.open(image_path)
    except FileNotFoundError:
        return f"Error: Image file not found at '{image_path}'"
    except Exception as e:
        return f"Error opening image: {e}"

    prompt = "Extract all text from this image, including any handwritten text. Preserve the original line breaks and formatting as much as possible."

    try:
        response = model.generate_content([prompt, img], stream=True)
        response.resolve()
        return response.text
    except Exception as e:
        return f"Error calling Gemini API: {e}"

if __name__ == "__main__":
    if len(sys.argv) < 2 or len(sys.argv) > 3:
        print("Usage: python ocr_receipt.py <path_to_image> [output_file]")
        sys.exit(1)

    image_file = sys.argv[1].strip("'\"")
    result = ocr_image(image_file)

    if len(sys.argv) == 3:
        output_file = sys.argv[2]
        try:
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write(result)
            print(f"Successfully saved OCR result to {output_file}")
        except IOError as e:
            print(f"Error writing to file {output_file}: {e}")
    else:
        sys.stdout.reconfigure(encoding='utf-8')
        print(result)

    # 保存用ディレクトリ
    save_dir = Path("ocr_results")
    save_dir.mkdir(parents=True, exist_ok=True)

    # 日時付きファイル名
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    file_name = f"ocr_{timestamp}.txt"
    file_path = save_dir / file_name

    # テキストファイルに保存
    with file_path.open("w", encoding="utf-8") as f:
        f.write(result)

    print(f"OCR結果を保存しました: {file_path}")
  1. ライブラリリストファイルを作成
    requirements.txt という名前のファイルをプロジェクトフォルダ内に作成し、以下の内容をコピーして貼り付け、保存してください。
requirements.txt
google-generativeai
Pillow
pandas
python-dotenv

3-2. ディレクトリ構成

この段階では、まだ以下のようになっていませんが、
すべてのファイルとフォルダの準備が整い、スクリプトを一度実行すると、プロジェクトフォルダ (ocr_project)
の中は以下のようになります。

 ocr_project/
 ├── .env                  # API キーを設定するファイル
 ├── ocr_receipt.py        # 実行するPythonスクリプト
 ├── requirements.txt      # 必要なライブラリのリスト
 ├── receipt1.jpg          # (例) OCR対象の画像ファイル
 ├── .venv/                # Pythonの仮想環境(自動生成)
 │   ├── ...
 │   └── ...
 └── ocr_results/          # OCR結果のテキストファイルが保存されるフォルダ(自動生成)
    └── ocr_20250705_140000.txt # (例) 保存された結果ファイル

(注: .venvocr_results フォルダは、それぞれ仮想環境の構築コマンドとスクリプトの初回実行時に自動的に作成されます。)

3-3. 環境構築とインストール

  1. 仮想環境の作成(推奨)
    プロジェクトフォルダ内で、以下のコマンドを実行して仮想環境を作成します。
python -m venv .venv
  1. 仮想環境のアクティベート
    作成した仮想環境を有効にします。
.venv/Scripts/activate

ターミナルのプロンプトの先頭に (.venv) と表示されれば成功です。

  1. 依存ライブラリのインストール
    pip を使って、requirements.txt に記載されたライブラリをインストールします。
pip install -r requirements.txt
  1. .envファイルにAPI キーを設定
    プロジェクトフォルダ内に .env という名前のファイルを作成します。
    作成した .env ファイルに、以下のようにご自身のGoogle API キーを記述して保存します。
.env
GEMINI_API_KEY="ここにあなたのGoogle API キーを貼り付けます"

3-4. 実行方法

  1. 画像ファイルの準備
    OCRを実行したい画像ファイル(例: receipt1.jpg, document.pngなど)を、作成した ocr_project フォルダ内にコピーしてください。

  2. スクリプトの実行
    設定が完了したら、以下のコマンドでスクリプトを実行できます。

python ocr_receipt.py receipt1.jpg

複数画像の実行と結果の保存

このスクリプトは、異なる画像ファイルで何度でも繰り返し実行することが可能です。

別の画像での実行例:プロジェクトフォルダ以外の画像を指定しても大丈夫です。

python ocr_receipt.py C:\Users\user\Downloads\document.png

実行するたびに、ocr_results フォルダ内に、実行日時を含む新しいファイル名(例: ocr_20250705_140510.txt)で結果がテキストファイル
として自動的に保存されます。そのため、以前のOCR結果が上書きされることはありません。

3-5. 仮想環境の無効化

作業をもうしないようでしたら仮想環境を以下コマンドで無効化します。

deactivate

🟢 4. まとめ

  • Google Gemini APIを使ったOCRは無料でも非常に高精度
  • Pythonで加工まで一気通貫
  • 実務や趣味でOCRを活用できる

🟢 5. 参考リンク


おまけ

さらに実用例として
Gemini CLIの実践的使い方!レシートをOCRして家計簿をつけるスクリプトをつくる
以下のコードを使用しています。必要なライブラリは追加してください。

process_all_receipts.py
import os
from pathlib import Path
from ocr_receipt import ocr_image

def process_all_images_in_directory(directory: str):
    """
    Processes all JPG and PNG images in a directory and its subdirectories,
    extracting text and saving it to a corresponding .txt file.

    Args:
        directory: The path to the directory containing the images.
    """
    image_dir = Path(directory)
    output_dir = Path("ocr_results")
    output_dir.mkdir(exist_ok=True)

    image_files = list(image_dir.glob("**/*.png")) + list(image_dir.glob("**/*.jpg"))

    if not image_files:
        print(f"No PNG or JPG images found in '{directory}'.")
        return

    print(f"Found {len(image_files)} images to process.")

    for image_path in image_files:
        print(f"Processing: {image_path.name}...")
        try:
            # Perform OCR on the image
            text_result = ocr_image(str(image_path))

            if text_result.startswith("Error:"):
                print(f"  Error processing {image_path.name}: {text_result}")
                continue

            # Create a path for the output text file
            output_filename = image_path.stem + ".txt"
            output_filepath = output_dir / output_filename

            # Save the result to the text file
            with open(output_filepath, "w", encoding="utf-8") as f:
                f.write(text_result)

            print(f"  Successfully saved result to {output_filepath}")

        except Exception as e:
            print(f"  An unexpected error occurred while processing {image_path.name}: {e}")

if __name__ == "__main__":
    receipts_directory = r"C:\Users\taka3\gemini_cli\ocr_receipt\receipts"
    process_all_images_in_directory(receipts_directory)
create_household_ledger.py
import os
import json
import re
from pathlib import Path
import pandas as pd
import google.generativeai as genai
from dotenv import load_dotenv
from tqdm import tqdm

def analyze_receipt_text(text: str) -> dict | None:
    try:
        load_dotenv()
        api_key = os.getenv("GEMINI_API_KEY")
        if not api_key:
            print("Error: GEMINI_API_KEY not found.")
            return None

        genai.configure(api_key=api_key)
        model = genai.GenerativeModel('gemini-2.0-flash-lite')

        # Constructing the prompt line-by-line to avoid syntax errors
        prompt_lines = [
            "Analyze the following receipt text and extract the store name, transaction date, a list of purchased items with their prices, and the total amount.",
            'Return the result as a JSON object with the following keys: "store", "date" (in YYYY-MM-DD format), "items" (an array of objects with "name" and "price" keys), and "total".',
            "If a value cannot be found, use null for that field.",
            "",
            "Receipt text:",
            "---",
            text,
            "---"
        ]
        prompt = "\n".join(prompt_lines)

        response = model.generate_content(prompt)
        
        json_match = re.search(r"```json\s*([\s\S]+?)\s*```", response.text)
        
        if json_match:
            json_str = json_match.group(1)
        else:
            json_str = response.text.strip()

        return json.loads(json_str)

    except json.JSONDecodeError:
        print(f"Error: Failed to decode JSON from model response.")
        return None
    except Exception as e:
        print(f"An unexpected error occurred during analysis: {e}")
        return None

def create_household_ledger():
    results_dir = Path("ocr_results")
    if not results_dir.exists():
        print(f"Error: Directory not found at '{results_dir}'")
        return

    text_files = list(results_dir.glob("*.txt"))
    if not text_files:
        print(f"No .txt files found in '{results_dir}'")
        return

    print(f"Found {len(text_files)} receipt files to process.")

    all_receipt_data = []
    for file_path in tqdm(text_files, desc="Analyzing Receipts"):
        try:
            content = file_path.read_text(encoding="utf-8", errors='replace')
            extracted_data = analyze_receipt_text(content)

            if extracted_data:
                extracted_data['source_file'] = file_path.name
                all_receipt_data.append(extracted_data)
        except Exception as e:
            print(f"Error processing file {file_path.name}: {type(e).__name__}")

    if not all_receipt_data:
        print("No data could be extracted from the receipts.")
        return

    records = []
    for receipt in all_receipt_data:
        store = receipt.get('store', 'N/A')
        date = receipt.get('date', 'N/A')
        total = receipt.get('total', 0)
        source_file = receipt.get('source_file', 'N/A')
        items = receipt.get('items', [])
        
        if items:
            for item in items:
                records.append({
                    'date': date,
                    'store': store,
                    'item_name': item.get('name', item.get('item')), # Handle both 'name' and 'item'
                    'item_price': item.get('price', 0),
                    'receipt_total': total,
                    'source_file': source_file
                })
        else:
             records.append({
                'date': date,
                'store': store,
                'item_name': 'N/A',
                'item_price': 0,
                'receipt_total': total,
                'source_file': source_file
            })

    df = pd.DataFrame(records)
    if not df.empty:
        df = df[['date', 'store', 'item_name', 'item_price', 'receipt_total', 'source_file']]

    output_path = "household_ledger.csv"
    df.to_csv(output_path, index=False, encoding="utf-8-sig")

    print(f"\nSuccessfully created household ledger: {output_path}")
    if not df.empty:
        print("--- First 5 rows ---")
        print(df.head())
    else:
        print("The DataFrame is empty, no data to display.")

if __name__ == "__main__":
    create_household_ledger()

Discussion