Flaps

LangChain 画像を理解できるモデルを利用したOCR

画像を理解できるモデルを利用してOCRが作れるか試しています

記事作成日:2025-11-29, 著者: Hi6

Overview

Ollamaで画像を読み取れるモデルに文字や数式が書かれた画像を与えて、その内容を文字はそのまま、数式はKATEX(Javascript 版LATEX)形式で出力するようなプログラムを組んで試します。

準備

使用するモデルはファイル名に -vlやocr がついた物であれば画像を認識できるモデルなのでどれでもよいです。ですが、システムプロンプトに従わないモデルも存在して、動作が不安定な物(OCRとして使用するには)も存在したので、ここではqwen3-vl:8bを採用しました。

import os
from langchain_ollama import ChatOllama

# 画像認識モデルの読み込み
model = ChatOllama(
    model="qwen3-vl:8b",
    temperature=0.0
    # モデルの出力のランダム性を制御します。
    # 数値が高いほど応答はより創造的になり、数値が低いほど応答はより決定論的になります。
)

# プロジェクトのルートディレクトリ(画像ファイルとpythonファイルを置くディレクトリ)
base_dir = os.path.dirname(os.path.abspath(__file__))

# システムプロンプト
system_prompt = """
コメントや要約、概要を追加せずに、画像内のテキストのみをそのまま入力してください。
数式の場合はKATEX形式を入力してください。
"""

Base64画像への変換

ローカル画像をモデルに渡すにはbase64である必要があるようなので変換関数を用意します。

import base64

def change_base64(path):
    """画像ファイルをBase64に変換"""
    with open(path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")

入力を受け取る関数

画像のファイル名を入力して、戻り値として返す関数を用意します。

def input_image_path(message="入力画像を指定してください: "):
    """画像のファイル名を受け取る"""
    while True:
        file_name = input(message).strip()
        if not file_name:
            print("ファイル名が空です。もう一度入力してください。")
            continue
        image_path = os.path.join(base_dir, file_name)
        if os.path.exists(image_path):# 入力されたファイルの存在を確認
            return image_path
        else:
            print(f"ファイルが見つかりません: {image_path}")

メイン関数

ここで、base64画像をプロンプトに含めてモデルに渡し、結果をstreamで受け取っています。 ローカル実行だと解析に時間がかかりますが、しばらくすると結果が表示され始めるはずです。

HumanMessage はユーザーからの入力を意味するクラスで、 role: “user”を入力する手間が省けます。同様にSystemMessageはシステムのロールを追記します。

from langchain_core.messages import HumanMessage, SystemMessage

if __name__ == "__main__":
    try:
        # 画像パスの取得
        image_path = input_image_path()
        # 直接モデルを呼び出す方法(Agentを使わない場合)
        base64_image = change_base64(image_path)
        messages = [
            SystemMessage(content=system_prompt),
            HumanMessage(content=[
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/png;base64,{base64_image}"
                    }
                }
            ])
        ]
        
        print("\n画像を解析中...\n=========================")
        for i, chunk in enumerate(model.stream(messages)):
            print(chunk.content, end="", flush=True)
            
    except Exception as e:
        print(f"エラーが発生しました: {e}")
        import traceback
        traceback.print_exc()

追加

Qwen2.5-VL-7B-Instructに、高度に選別された学術論文、技術ドキュメント、その他の参考資料で追加学習を行ったモデル、allenai/olmocr-2-7b が精度が良くておすすめ。


[!NOTE] 参照サイト

LangChain 公式ドキュメント