space ocr
指南文章價格文件
developer

從掃描件裡取出人會讀的那個順序的文字

樸素 OCR 按偵測順序回傳頁面上的所有詞,而那並不是任何人閱讀的順序。這是一份關於還原閱讀順序的純文字擷取指南——附帶逐區塊座標,以及「文字有沒有被改寫」的校驗。

6 分鐘閱讀· 2026-07-28

「把文字給我就好」—— 聽起來是最簡單的訴求,卻是多數 OCR 整合悄悄做錯的一環。

視覺 OCR 引擎會把找到的詞按段落歸組,以 偵測順序 回傳:大致自上而下、自左而右掃過整頁。單欄便條恰好與閱讀順序一致;雙欄文章、帶側欄的發票、略有傾斜的掃描件就不是了。你會拿到左欄第一段、然後右欄第一段、再回到左欄。詞都沒錯,文件卻讀不通。

如果這些文字進入檢索索引、向量或差異比對,損害是安靜的:不報錯,只是結果慢慢變差,而沒人會追溯到 OCR 這一步。

即使不保留結構,純文字仍需要三件事

就算沒有結構要保留,可用的純文字擷取也必須回答三個問題:

  • 什麼順序? 區塊要按閱讀順序出來,被換行截斷的句子要重新接上,而不是留成兩截。
  • 來自哪裡? 如果你索引了一個段落,之後需要向人解釋這份文件為什麼命中,你需要那個段落的座標——頁碼不夠。
  • 是不是頁面上寫的? 轉寫頁面的模型也會順手「正規化」。悄悄的改良比錯誤更糟,因為它讀起來是對的。

一次呼叫

POST /ocr/text 接收一張影像並回傳文件文字。加上 includeBlocks,還會拿到每個區塊及其座標。

1
2
3
4
5
6
7
8
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",
    "includeBlocks": true
  }'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  "status": "success",
  "data": {
    "text": "櫻花商事株式會社\n發票\n合計 1,451",
    "source": "llm",
    "image_size": { "width": 1654, "height": 2339 },
    "review_summary": {
      "unit": "block",
      "total": 12,
      "boxed": 12,
      "text_verified": 11,
      "text_mismatch": 1,
      "needs_review": 1,
      "flagged": [{ "path": "blocks[7]", "reason": "text_mismatch" }],
      "recovered_blocks": 0,
      "token_coverage": 1.0
    },
    "blocks": [
      {
        "text": "櫻花商事株式會社",
        "bbox": { "xmin": 60, "ymin": 48, "xmax": 470, "ymax": 92 },
        "vertices": [{ "x": 60, "y": 48 }, "…"],
        "bbox_source": "token_id",
        "text_verified": true,
        "needs_review": false
      }
    ]
  }
}

text 是整份文件拼成的一個字串(區塊以換行連接)。blocks[] 是同樣的內容,按實際定位到的單位切分,每個區塊帶 0–1000 正規化的 bboxvertices

分工值得寫清楚:字元與座標以視覺 OCR 為準,模型只被問及順序與歸組。 由它決定下一個區塊是哪個、哪些片段屬於同一塊——它無法憑空造出座標,對字元也沒有最終決定權。

text_verified,以及它為 false 的時候

每個區塊都帶 text_verified。引擎取出該區塊所主張的詞元,查出視覺 OCR 在這些座標上讀到的內容,正規化後比對。true 表示兩次判讀一致;false 表示轉寫與頁面不同——文字照樣回傳,被標記出來而不是悄悄出錯。

這不是準確率百分比。它回答的是更窄也更有用的問題:這條有沒有和像素對過,並且對上了? 對索引流程來說,一個自然的策略是全部入庫但保留標記,等到最終要展示給人時,不確定的區塊被標出來,而不是當作事實呈現。

✓ Verified

兩種失敗被明確處理,而不是藏起來。 如果模型這一路徹底失敗(無金鑰、配額用盡、重試耗盡),端點不會報錯,而是降級為視覺轉寫並如實告知:source: "vision" 加一條 warning。另外,任何沒有被任何區塊主張的頁面詞元區間,都會作為回收區塊(bbox_source: "unclaimed_tokens")追加回來,於是掉了的段落會出現在輸出裡,而不是從輸出裡消失。

什麼時候該關掉模型

useLlm: false 給你純視覺轉寫:即時、無模型成本,代價是區塊按原始 OCR 順序。對於順序本就正確的單欄頁面,或只需判斷關鍵字是否存在的批次處理,這是划算的取捨。

當順序承載含義時就留著它——多欄排版、帶側欄的表單、有傾斜的掃描件,以及要給人看而不只是拿去比對的文字。

如果你要的是結構而不是行文(標題作為標題、表格作為表格),那是另一個端點:POST /ocr/markdown 會回傳保留版式的 Markdown,帶同樣的逐元素座標與同樣的 text_verified

相關文章