llm_aided_ocr: Tesseract の出力を LLM で後処理するパイプラインの実像
Enhances Tesseract OCR output using LLMs (local or API) for error correction, smart chunking, and markdown formatting of scanned PDFs
ひと目でわかる
- これは何?
- スキャン PDF の OCR 結果をチャンク分割し、ローカル LLM または OpenAI / Anthropic の API で誤り訂正と Markdown 整形を行う Python 製ツール。前処理ではなく後処理に賭けた設計と、その代償を読む。
- 誰に向いている?
- 採用を検討すべきなのは、すでに Tesseract で大量のスキャン文書を流しており、その誤り訂正と Markdown 化に人手を割いているチームである。逆に、OCR そのものの精度が根本的に足りない資料や、レイアウト情報を保持したまま構造化したい案件には向かない。
- 商用利用できる?
- まず確認が必要です。このリポジトリのライセンスは自動分類の対象外なので、商用利用の前に LICENSE ファイルを読んでください。
- 今もメンテナンスされている?
- されています。最後のコミットは 44 日前です。
- 何の言語で書かれている?
- 主に Python です(GitHub の言語統計による)。
回答はプロジェクトの GitHub データ(最終同期:2026年9月15日)と当サイトの分析に基づくもので、法的助言ではありません。
オープンソース詳細解説
Tesseract の誤りをどこで直すかという設計判断
このプロジェクトが解こうとしている問題は、OCR エンジンの精度そのものではない。Tesseract が返した文字列に残る誤認識を、文脈を理解するモデルに読ませて直すという発想である。README は冒頭で「raw OCR text into highly accurate, well-formatted, and readable documents」と述べており、対象はスキャンした PDF、とくに活字の品質が均一でない歴史的文書や社内資料の類だと読める。
想定読者は、OCR をすでに業務に組み込んでいるが、出力をそのまま使える状態にするために人手で校正している人である。前処理(画像の二値化やノイズ除去)である程度は改善できるが、辞書にない固有名詞や文脈依存の誤りは前処理では消えない。そこに LLM を挟むのがこのツールの立場で、Tesseract を置き換えるのではなく、Tesseract の後段に置く。
PDF から Markdown までのデータの流れ
処理は 3 段階に分かれている。最初に convert_pdf_to_images() が pdf2image を使って PDF の各ページを画像化する。max_pages と skip_first_n_pages というパラメータがあり、全ページを一度に処理せず部分的な検証ができる。
次に ocr_image() が pytesseract でテキストを抽出する。このとき preprocess_image() が入り、グレースケール化、Otsu の手法による二値化、膨張処理を行ってから OCR にかける。つまり画像側の前処理も一応は持っている。
抽出された全文は process_document() でチャンクに分割される。分割は文の境界を基準にし、チャンク間にはオーバーラップが設けられる。ここが実務上いちばん効く部分で、文の途中で切ると LLM は前後の文脈を失い、かえって誤りを混入させる。オーバーラップはその代償を小さくするための仕掛けである。各チャンクは process_chunk() に渡され、まず OCR 誤りの訂正、次に任意で Markdown 整形が行われる。重複段落の除去は Markdown 整形の工程内で実施され、完全一致またはほぼ一致する段落を落とす。
ローカル LLM と API を同じ .env で切り替える仕組み
LLM の呼び出しは 3 系統に分かれている。ローカル推論は generate_completion_from_local_llm() が担当し、llama_cpp ライブラリと GGUF 形式のモデルを前提とする。構造化出力のためにカスタムグラマーを渡せる点が README に記されている。API 側は generate_completion_from_claude() と generate_completion_from_openai() の 2 関数で、リトライ処理とトークン上限に応じたリクエストサイズの動的調整を含む。
切り替えは .env の USE_LOCAL_LLM と API_PROVIDER で行う。API_PROVIDER には OPENAI か ANTHROPIC を指定し、対応する API キーを同じファイルに置く。API 利用時は asyncio でチャンクを並行処理しつつ、最終出力の順序は元のチャンク順に保たれる。順序が崩れないことは、ページ番号の抑制や見出しの整形と組み合わせたときに効いてくる。
トークン管理は estimate_tokens() がモデル固有のトークナイザを使い、使えない場合は approximate_tokens() にフォールバックする。max_tokens はプロンプト長とモデル上限から動的に決まり、TOKEN_BUFFER と TOKEN_CUSHION という 2 つの余裕分が確保される。名前からすると、片方は API の上限に対する安全域、もう片方は応答長の見積もり誤差に対する余白だろうが、README は両者の役割の違いを明示していない。ここはコードを読むまで確定できない。
セットアップで実際に叩くコマンドと設定キー
Python は 3.12 以上が要件で、README は pyenv での導入から始まる。pyenv が無い環境ではビルド用パッケージを apt で入れ、pyenv 本体を clone してシェルの設定に 3 行を追記する手順が示されている。そのうえでプロジェクト側は次の流れになる。
git clone https://github.com/Dicklesworthstone/llm_aided_ocr cd llm_aided_ocr pyenv local 3.12 python -m venv venv source venv/bin/activate python -m pip install --upgrade pip python -m pip install wheel python -m pip install --upgrade setuptools wheel pip install -r requirements.txt
Tesseract 本体は別途入れる。Ubuntu は sudo apt-get install tesseract-ocr、macOS は brew install tesseract、Windows は UB-Mannheim の配布ページからインストーラを取得する。
.env に最低限書くのは USE_LOCAL_LLM、API_PROVIDER、OPENAI_API_KEY、ANTHROPIC_API_KEY の 4 つである。使用後は PDF をプロジェクトディレクトリに置き、input_ で始まる設定(README ではここで切れている)を更新してから実行する。出力は {base_name}__raw_ocr_output.txt と {base_name}_llm_corrected.md(または .txt)の 2 ファイルで、処理時間と品質評価を含むログも生成される。
チャンク分割とトークン上限が生む失敗の形
このツールの弱点は、LLM に文書全体を渡していない点に集約される。チャンクは文境界で切られ、オーバーラップで補われるが、章をまたぐ参照や前ページで定義された用語の一貫性は、チャンク単位の処理では原理的に保証されない。同じ人名がチャンクごとに別の表記へ「訂正」される可能性は残る。
もう一つは幻覚である。OCR 誤りを直すよう指示された LLM は、直す対象が無い場合でも何かを書き換えることがある。README は assess_output_quality() で元の OCR テキストと処理後の出力を比較し、LLM に品質スコアと説明を出させると説明しているが、これは自動的な検証であって、原本との照合ではない。原本の PDF と突き合わせる作業は結局人間が行う。
コスト面も素直ではない。API 経由ならチャンク数に比例して課金され、長い文書では無視できない額になる。ローカル LLM なら課金は無いが、GPU と GGUF モデルの準備が要り、README はこれが「optional」であると同時に GPU 加速を機能として挙げている。GPU が無ければ処理時間は実用域を外れる可能性が高い。
汎用 OCR サービスとの違いはどこにあるか
比較対象として分かりやすいのは、Tesseract を土台にせず、文書レイアウトごと読み取るクラウド OCR サービスである。たとえば Google Cloud Vision や Azure AI Document Intelligence は、表や段組みの座標情報を返し、それをそのまま構造化データとして扱える。llm_aided_ocr はページを画像化してから pytesseract に渡すため、座標情報は基本的に失われ、テキストと Markdown 構造だけが残る。表を表として復元したい案件では、この違いは決定的である。
一方で、クラウド OCR は文書を外部に送る。社内規程や契約書のように持ち出しが許されない資料では、ローカル LLM に切り替えられるこの設計の方が現実的な選択になる。llama_cpp で完結させれば、PDF はマシンから出ない。ここが、このプロジェクトがクラウド OCR に対して持つ唯一と言ってよい明確な優位点である。
もう一つの代替は、Tesseract の出力をそのまま使い、誤り訂正だけを自作の LLM スクリプトで行う方法である。llm_aided_ocr の価値は、チャンク分割、オーバーラップ、トークン上限の動的調整、重複段落除去といった周辺の作り込みにあり、訂正そのものはプロンプト次第で再現できる。自作の手間を許容できるなら、依存の少ない自前実装の方が見通しは良い。
メンテナンス負担とライセンス表記の不明瞭さ
リポジトリはアーカイブされておらず、最終更新は 2026 年 8 月である。ただしリリースは 1 件も取得されておらず、バージョン番号で固定して追従する運用はできない。requirements.txt に何が固定されているかは README からは分からないが、llama_cpp と pdf2image、pytesseract という外部依存を抱える以上、Tesseract 本体のバージョンやモデルファイルの更新に引きずられる。API 側も OpenAI と Anthropic のモデル名が .env で指定されるため、モデルの廃止告知のたびに設定を見直す必要がある。
ライセンスは NOASSERTION と記録されており、GitHub 上で標準的なライセンス識別子が付いていない。これは「ライセンスが無い」とも「独自の条件が書かれている」とも即断できない状態で、README にはライセンス条項の記載が見当たらない。社内利用や再配布を検討するなら、リポジトリ内の LICENSE ファイルの実物を確認するまで判断を保留すべきである。ここで法的な助言はできないが、少なくとも確認を飛ばして導入を決めるのは順序が逆になる。
編集部の結論
採用を検討すべきなのは、すでに Tesseract で大量のスキャン文書を流しており、その誤り訂正と Markdown 化に人手を割いているチームである。逆に、OCR そのものの精度が根本的に足りない資料や、レイアウト情報を保持したまま構造化したい案件には向かない。導入前に確認すべきは、requirements.txt に固定された llama_cpp 系の依存が手元の GPU で動くか、そして .env の TOKEN_BUFFER と TOKEN_CUSHION を自分の文書長に合わせて調整できるかである。この 2 点がクリアできないなら、Tesseract のままの方が安く済む。
コミュニティノート