TesseractをAPIとして実行
またはセルフホストサービスとして。
TesseractをHTTPサービスに確実に包みます:本番向けFastAPIエンドポイント、プロセスプールによる並行処理、言語パックとヘルスチェックを備えたDockerイメージ、スケーリングとバックプレッシャー、サーバーレス展開 — マネージドOCR APIとのプライバシーとコストのトレードオフも解説。
TL;DR — 最小構成のサービス
CLIまたはpytesseractバインディングをHTTPの背後に包みます。画像がインフラの外に出ることはありません。
pip install fastapi uvicorn python-multipart pytesseract pillow
# 先にエンジン + 言語パックをインストール(インストールガイド参照)
uvicorn app:app --host 0.0.0.0 --port 8000呼び出し: curl -F "file=@invoice.png" "http://localhost:8000/ocr?lang=jpn"。Tesseract本体はインストールガイドに従って導入します。
まず設計上の判断
3つの選択がサービス全体を決めます。
| 判断 | 選択肢 | 指針 |
|---|---|---|
| 同期 vs 非同期 | ブロッキングリクエスト vs ジョブキュー | 数秒以内の単一画像は同期。大きな文書やバッチは非同期ジョブID。 |
| 入力 | multipartアップロード vs base64 JSON | バイナリ画像はmultipartを推奨。クライアントがファイルを送れない場合のみbase64(サイズが約33%増)。 |
| 出力 | プレーンテキスト、JSON、hOCR/ALTO、検索可能PDF | 既定はJSON。レイアウト用にhOCR、アーカイブ用にPDFダウンロードURLを提供。 |
1エンドポイントにつき主要な意図は1つ。/ocr はテキストを返すままにし、PDFとレイアウトデータは別エンドポイントに分けます。
本番向けPythonサービス(FastAPI)
検証、サイズ制限、タイムアウト、イベントループ外でのOCR。
import asyncio, io
from concurrent.futures import ProcessPoolExecutor
from fastapi import FastAPI, File, UploadFile, HTTPException, Query
from PIL import Image
import pytesseract
app = FastAPI(title="Tesseract OCR API")
pool = ProcessPoolExecutor(max_workers=4)
MAX_BYTES = 10 * 1024 * 1024 # 10 MB
LANGS = {"eng", "jpn"}
def _ocr(data: bytes, lang: str) -> str:
image = Image.open(io.BytesIO(data))
return pytesseract.image_to_string(image, lang=lang)
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/ocr")
async def ocr(file: UploadFile = File(...), lang: str = Query("jpn")):
if lang not in LANGS:
raise HTTPException(400, "未対応の言語")
data = await file.read()
if len(data) > MAX_BYTES:
raise HTTPException(413, "ファイルが大きすぎます")
loop = asyncio.get_running_loop()
try:
text = await asyncio.wait_for(loop.run_in_executor(pool, _ocr, data, lang), timeout=30)
except asyncio.TimeoutError:
raise HTTPException(504, "OCRタイムアウト")
return {"text": text, "lang": lang}要点:言語を許可リスト化、デコード前にバイト上限を適用、OCRはプロセスプールで実行、実時間を制限。Pythonガイドでpytesseractの全APIを参照してください。
コンテナ化する
Tesseractと言語パックをイメージに焼き込み、レプリカを同一にします。
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr tesseract-ocr-eng tesseract-ocr-jpn \
libgl1 libglib2.0-0 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
HEALTHCHECK CMD python -c "import urllib.request;urllib.request.urlopen('http://localhost:8000/health')"
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]必要な言語パックだけを追加してイメージを小さく保ちます。インストールガイドでDockerとtessdataの配置を詳しく扱っています。
並行処理とスケーリング
OCRはCPUバウンド — スレッドではなくプロセスで並列化します。
- プロセスプールまたはCLIサブプロセス: PythonのGILによりスレッドプールはOCRを高速化しません。
ProcessPoolExecutorまたはtesseractサブプロセスのプールを使います。 - ワーカー vs レプリカ: コンテナごとにuvicornワーカーを数個実行し、ロードバランサの背後でレプリカを水平スケールします。
- キュー: 急増や長い文書には、アップロードを受け付けてジョブIDを返し、キュー(Celery、RQ、ブローカー)から処理します。
- バックプレッシャー: キューを上限付きにし、飽和時はレイテンシを悪化させる代わりに
429で拒否します。 - リソース制限: ワーカーごとにCPU/メモリを固定し、OCRタイムアウトを設定して不良画像がレプリカを止めないようにします。
セキュリティとプライバシー
セルフホストはチームがTesseractを選ぶ主な理由です。
- データの外部送信なし: 画像はネットワーク内に留まります — 法務・医療・金融文書で重要です。
- 入力を検証: 拡張子だけでなくMIMEタイプとマジックバイトを確認し、try/exceptでデコードします。
- リソースを制限: 最大アップロードサイズ、リクエストごとのタイムアウト、メモリ上限で解凍爆弾に対抗します。
- 認証: APIキーやゲートウェイを前段に置きます。OCRエンドポイントは悪用コストが高いためです。
- メタデータを除去: ファイルを保存する場合はEXIFを削除し、原本を定期的に削除します。
サーバーレスとコンテナプラットフォーム
- コンテナランタイム: Cloud Run、AWS Lambdaコンテナイメージ、Fly.ioはサーバー管理なしでTesseractイメージを実行できます。
- コールドスタート: 言語パック入りのTesseractイメージは大きいためコールドスタートが遅くなります。レイテンシ重視のトラフィックには1インスタンスをウォームに保ちます。
- 制限: メモリとタイムアウトを増やします。既定の小さなタイムアウトでは大きな文書が失敗します。
- 一時ディスク: 一時ファイルは
/tmpにのみ書き込み、ファイルシステムは使い捨てとして扱います。
セルフホストTesseract vs マネージドOCR API
| 観点 | セルフホストTesseract | マネージドOCR API |
|---|---|---|
| コスト | 固定インフラ。ページ課金なし | リクエスト/ページ課金。量に比例 |
| プライバシー | 文書がネットワーク外に出ない | 文書が第三者に送信される |
| 精度 | きれいな印刷に強い。ドメインごとに調整可能 | 難しいスキャンや手書きにしばしば強い |
| 制御 | 完全:バージョン、言語、カスタムモデル | ベンダーのロードマップとクォータ |
| 運用 | 自社で運用:スケーリング、更新、監視 | 完全マネージド |
多くのチームは大量・プライバシー重視の処理にTesseractをセルフホストし、難しい少数の文書のみマネージドAPIに頼ります。
API FAQ
Tesseract OCRをAPIとして実行するには?
CLIまたはpytesseractをFastAPI/Flaskで包み、POST /ocrを公開してコンテナ化します。画像はインフラ内に留まります。
スレッドかプロセスか?
プロセスです。OCRはCPUバウンドでGILがスレッド並列を妨げるため、ProcessPoolExecutorかCLIサブプロセスを使います。
セルフホストAPIはプライベートですか?
はい。アップロード画像はローカルで処理され、第三者に送信されません。マネージドOCR APIとは異なります。
サーバーレスで実行できますか?
はい。Cloud Run、Lambdaコンテナイメージ、Fly.ioなどで可能です。コールドスタートと言語パックによるイメージサイズに注意してください。
どの出力形式を返せますか?
プレーンテキスト、信頼度付きJSON、hOCR/ALTO、ダウンロードURL経由の検索可能PDF。