スキャンから、人が読む順序どおりのテキストを取り出す
POST /ocr/text で読み順を整えた本文を抽出し、必要に応じて内容だけの blocks、path キーの元画像座標、明示的な確認一覧を受け取る方法。
「テキストだけ欲しい」は簡単そうに見えますが、検索インデックスを静かに壊しやすい OCR 要件です。ビジョン OCR は正しい単語を見つけても、検出順で返すことがあります。左段の 1 行目、右段の 1 行目、また左段へ、と混ざれば、エラーは出ないのに文書として読めません。
POST /ocr/text はこの問題を、フィールド抽出や Markdown 変換から切り分けます。既定の useLlm: true ではブロックを人の読み順に並べ、折り返し行をつなぎ直します。一方、文字と読み取り元の位置は Vision の観測と照合されるため、言語モデルの転写を無条件に正解とは扱いません。
リクエストの二つのスイッチ
useLlm の既定値は true です。多段組み、サイドバー、傾いたスキャン、人が読む本文ではオンのままにします。false にすると LLM 処理を使わない Vision 専用転写となり、raw OCR 順で返ります。この経路でも文書単位の検証オブジェクトが返ります。
includeBlocks の既定値は false です。全文だけなら data.values.text を使います。独自チャンク、元画像ハイライト、確認 UI が必要ならオンにします。すると内容だけの data.values.blocks、data.cells["blocks[0]"] のようなメタデータ、data.review.flagged の block path が加わります。
1 回のリクエスト
以下は確認元までたどれるよう includeBlocks を有効にした例です。認証や全パラメータは API ドキュメントで確認できます。
curl -X POST https://api.space-ocr.com/ocr/text \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://example.com/page.jpg",
"imageType": "url",
"useLlm": true,
"includeBlocks": true
}'{
"status": "success",
"data": {
"values": {
"text": "株式会社サクラ商事\n請求書\n合計 1,451円",
"blocks": [
{
"text": "株式会社サクラ商事"
},
{
"text": "請求書\n合計 1,451円"
}
]
},
"cells": {
"blocks[0]": {
"box": {
"xmin": 60,
"ymin": 48,
"xmax": 470,
"ymax": 92
},
"quad": [
{
"x": 60,
"y": 48
},
{
"x": 470,
"y": 48
},
{
"x": 470,
"y": 92
},
{
"x": 60,
"y": 92
}
],
"verified": true,
"review": null,
"evidence": {
"text_match": true,
"source": "token_id"
}
},
"blocks[1]": {
"box": {
"xmin": 58,
"ymin": 190,
"xmax": 510,
"ymax": 274
},
"quad": [
{
"x": 58,
"y": 190
},
{
"x": 510,
"y": 190
},
{
"x": 510,
"y": 274
},
{
"x": 58,
"y": 274
}
],
"verified": false,
"review": {
"reasons": [
"text_mismatch"
]
},
"evidence": {
"text_match": false,
"source": "char_matcher_fallback"
}
}
},
"review": {
"unit": "block",
"total": 2,
"boxed": 2,
"verified": 1,
"flagged": [
{
"path": "blocks[1]",
"reasons": [
"text_mismatch"
]
}
],
"by_reason": {
"text_mismatch": 1
},
"coverage": {
"recovered_blocks": 0,
"vision_tokens": 8,
"tokens_claimed": 8,
"token_coverage": 1
}
},
"image": {
"width": 1654,
"height": 2339
},
"source": "llm"
}
}役割ごとにレスポンスを読む
data.values.textは全文です。blocks を有効にすると同じ内容が{ text }単位のdata.values.blocksにも入り、内容に座標は混ざりません。data.cells[path]は元画像との対応情報です。boxとquadは 0〜1000 のページ座標で、data.image.widthとheightを使って処理後画像のピクセルへ戻します。verifiedはその位置の Vision 文字列と正規化後に一致したかという判定で、精度のパーセンテージではありません。data.review.flaggedは確認作業の一覧です。pathでdata.cells[path]を直接開き、review.reasonsで理由を確認します。data.sourceは読み順パスならllm、Vision パスならvisionです。
索引処理は values.text、確認画面は review と cells を使う、と責務を分けられます。
const { data } = await response.json();
indexDocument(data.values.text);
for (const flag of data.review.flagged) {
const cell = data.cells?.[flag.path];
queueForReview({
path: flag.path,
reasons: flag.reasons,
box: cell?.box,
quad: cell?.quad,
image: data.image,
});
}フォールバックは隠されません。 LLM の読み順処理が失敗すると、OCR 全体をエラーにせず Vision 転写を返します。その場合は data.source が "vision" となり、理由は data.warning に入ります。どのブロックにも取り込まれなかった Vision token は、cells[path].evidence.source: "unclaimed_tokens" の回収ブロックとして追加されることがあり、欠落を静かに捨てません。
プレーンテキストか Markdown か
全文検索、埋め込み、差分、アクセシビリティ用フィードなど、見出しや表を型として残す必要がなければプレーンテキストが適します。構造が必要なら POST /ocr/markdown を使い、画像から Markdown へのガイドを参照してください。座標と確認情報は OCR の元画像座標でも説明しています。
- 読み順の経路を選ぶ画像を POST /ocr/text に送ります。読み順が重要なら既定の useLlm:true を保ち、raw Vision 順でよい場合だけ false にします。
- 必要なときだけ blocks を要求する元画像との対応が必要なら includeBlocks:true にし、values.blocks、path キーの cells、ブロック単位の review を受け取ります。
- 確認一覧を処理するdata.review.flagged を巡回し、data.cells[flag.path] の box または quad を data.image のフレーム上に描画します。
- 実行経路を保存する本文と一緒に data.source を保存し、自動フォールバックで vision になった場合は data.warning を表示します。