"""
AI OCR to Markdown ツール
このスクリプトは、画像ファイルまたはクリップボードから画像をAI-OCRで処理し、結果をMarkdown形式で出力します。
対応するAIは現在Geminiのみです。
:doc:`ai_ocr2md_usage`
"""
import os
import sys
import argparse
from pathlib import Path
from PIL import Image, ImageGrab # ImageGrabを追加
# 添付ライブラリの読み込み
import tkai_lib
from get_from_ai import read_ini
[ドキュメント]
def get_image_from_clipboard():
"""
Windows 11のクリップボードから画像を安定して取得し、一時ファイルとして保存します。
`ImageGrab.grabclipboard()` を使用してクリップボードの内容を確認します。
画像データが見つかった場合、`clipboard_ocr_input.png` という名前で保存し、そのパスを返します。
:returns: str: 保存された画像ファイルのパス、または画像が見つからないかエラーが発生した場合はNone。
"""
print("--- クリップボードを確認中 ---")
try:
# Windowsのクリップボードから画像を直接取得
img = ImageGrab.grabclipboard()
if isinstance(img, Image.Image):
target_path = "clipboard_ocr_input.png"
img.save(target_path, "PNG")
print(f"✅ 画像を取得しました: {target_path} ({img.size[0]}x{img.size[1]})")
return target_path
else:
print("❌ クリップボードに画像データが見つかりませんでした。")
print(" (Win + Shift + S などで画像をコピーしてから実行してください)")
return None
except Exception as e:
print(f"❌ クリップボード取得エラー: {e}")
return None
[ドキュメント]
def call_ai_ocr(prompt, image_path, api="gemini", model=None):
"""
指定された画像とプロンプトを使用してAI OCRを実行し、結果のテキストを返します。
現在はGemini APIのみをサポートしています。APIキーとモデル名は環境変数または引数から取得されます。
:param prompt: str: AIに与えるプロンプトテキスト。
:param image_path: str: OCR対象の画像ファイルパス。
:param api: str: 使用するAI API ("gemini" のみサポート)。
:param model: str, optional: 使用するAIモデル名。指定しない場合は環境変数 'gemini_model' またはデフォルトの 'gemini-3.1-pro' が使用されます。
:returns: str: AI OCRの結果として得られたテキスト。
:raises Exception: AIの呼び出し中にエラーが発生した場合、特にモデルが見つからない場合。
"""
img = Image.open(image_path)
if api == "gemini":
# エラーが出た場合は 'gemini-3.1-pro' または 'gemini-1.5-pro' (安定版) を試してください
model_name = model or os.getenv("gemini_model", "gemini-3.1-pro")
api_key = os.getenv("GOOGLE_API_KEY")
print(f"🚀 {api.upper()} [{model_name}] へリクエスト送信中...")
tkai_lib.genai.configure(api_key=api_key)
# モデルの存在確認と呼び出し
try:
m = tkai_lib.genai.GenerativeModel(model_name)
response = m.generate_content([prompt, img])
return response.text
except Exception as e:
if "404" in str(e):
print(f"⚠️ モデル '{model_name}' が見つかりません。")
print(" 最新の有効なモデル名を確認してください(例: gemini-1.5-pro など)")
raise e
[ドキュメント]
def main():
"""
スクリプトのメインエントリポイント。AI OCRの実行フローを管理します。
1. AI設定ファイル (`ai.env`) を読み込みます。
2. コマンドライン引数をパースし、入力画像パス(ファイルまたはクリップボード)、出力ファイル名、
プロンプト設定ファイル、AI API、AIモデルを決定します。
3. プロンプト設定ファイル (`ai_ocr2md.ini`) からプロンプトを読み込みます。
4. 入力画像パスが 'clip' の場合はクリップボードから画像を読み込み、それ以外の場合は指定されたファイルパスを使用します。
5. `call_ai_ocr` 関数を呼び出してAI OCRを実行します。
6. AI OCRの結果をMarkdownファイルとして保存します。
:returns: None
"""
# 1. AI設定の読み込み (ai.env)
tkai_lib.read_ai_config("ai.env")
parser = argparse.ArgumentParser(description="AI OCR to Markdown ツール")
parser.add_argument("input", help="画像ファイルパス、または 'clip' (クリップボード)")
parser.add_argument("--output", "-o", default=None, help="出力Markdownファイル名")
parser.add_argument("--ini", "-i", default="ai_ocr2md.ini", help="プロンプト設定ファイル")
parser.add_argument("--api", "-a", choices=["gemini", "openai", "openai5"], default="gemini", help="使用するAPI")
parser.add_argument("--model", "-m", default="gemini-3.1-pro-preview", help="モデル名 (default: gemini-3.1-pro)")
args = parser.parse_args()
if args.output is None:
input_path = Path(args.input)
args.output = f"{input_path.stem}.md"
print()
print(f"入力ファイル名: {args.input}")
print(f"出力ファイル名: {args.output}")
print(f"プロンプトファイル名: {args.ini}")
print(f"AI API: {args.api}")
print(f"AI model: {args.model}")
# 2. プロンプトの準備
if os.path.exists(args.ini):
print(f"プロンプト読み込み: {args.ini}")
prompt = read_ini(args.ini).get("PROMPT", "画像をOCRしてください。")
else:
prompt = "画像のテキストを正確に抽出し、Markdown形式で整えてください。数式はLaTeXを使用してください。"
# 3. 入力画像の確定
target_image_path = ""
if args.input.lower() == "clip":
target_image_path = get_image_from_clipboard()
if not target_image_path:
sys.exit(1)
else:
if os.path.exists(args.input):
target_image_path = args.input
print(f"入力ファイルを確認: {target_image_path}")
else:
print(f"❌ ファイルが見つかりません: {args.input}")
sys.exit(1)
# 4. AI実行
try:
result_text = call_ai_ocr(prompt, target_image_path, api=args.api, model=args.model)
# 5. 結果保存
Path(args.output).write_text(result_text.strip(), encoding="utf-8")
print(f"✅ 成功: {args.output} に書き込みました。")
except Exception as e:
print(f"❌ AI呼び出し中にエラーが発生しました: {e}")
if __name__ == "__main__":
main()