space ocr
指南文章價格文件
developer

一個會回傳可驗證定界框的 OCR API

多數 OCR API 都會回傳定界框,但座標系統各不相同,而一個框只告訴你值的來源。這是一份開發者導向的指南:data.cells[path] 裡正規化為 0–1000 的 box、跟著傾斜走的 quad,以及點名哪些值值得再看一眼的 verified / review 約定。

8 分鐘閱讀· 2026-08-31

定界框是你驗證 OCR 的依據。一個光禿禿的字串只告訴你模型認為它讀到了什麼;而一個框會告訴你它是在頁面上的哪個位置讀到的,這樣你(或你的審核者,或你的程式碼)就能拿這個值去對照原件,而不必盲目相信。如果你要把 OCR 整合進任何會被稽核的場景——發票、費用、KYC、紀錄管理——「模型回傳了 total: 2,045」是不夠的;你得能指出 2,045 到底是從哪些像素來的。

好消息是,多數主流 OCR API 確實會回傳定界框。麻煩在於,一旦你開始動手做,它們有三個會帶來影響的差別——座標系統、你是不是也拿得到結構化欄位(而不只是純文字),以及框旁邊那個逐值訊號究竟是什麼。本指南會把這三點逐一走一遍,並示範當座標帶著一份明確的複核約定——逐值的判定,加上一份值得再看一眼的路徑清單——一起回來時,一個 OCR API 長什麼樣子。

多數 OCR API 都會回傳框——以下是它們的差別

Google Cloud Vision、Tesseract、Amazon Textract 和 Azure AI Document Intelligence 都會在回傳文字時一併附上幾何資訊。它們的分歧在於座標系統、你拿到的是結構化欄位還是只有純文字加版面,以及那個逐值數字到底回報了什麼。下表整理的是 2026 年 8 月各家的公開文件——這些產品一直在變,估算整合工作量之前,請以最新文件再核對一次。

API座標系統結構化欄位逐值訊號
Google Cloud VisionboundingPoly 頂點,以來源影像的像素為單位(部分功能回傳的是 normalizedVertices只有文字+幾何(結構化的鍵值對屬於 Google Document AI,是另一個獨立產品)每個字/符號的辨識信心(0–1)
TesseracthOCR/TSV 框,以像素為單位(本機函式庫,不是託管 API)無——只有純文字+版面每個字的辨識信心(0–100)
Amazon TextractBoundingBox,以頁面寬高正規化為 0–1(+同樣是 0–1 的 Polygon透過 AnalyzeDocument 處理表單/表格;收據透過 AnalyzeExpense每個區塊的辨識信心(%)
Azure Document Intelligence定界多邊形,以像素(影像)或英吋(PDF)為單位預建/自訂模型每個字的辨識信心
space-ocr以你宣告的欄位路徑為鍵,正規化為 0–1000box,外加跟著傾斜走的 quad由你用 fields 宣告(明細項目用 children),或交給 autoFieldsverified 判定+review.flagged 待確認清單(依據在 evidence

有兩件事值得注意。第一,座標單位沒辦法直接拿去沿用——像素框綁死在實際被讀取的那張影像上,而正規化的框則撐得過縮放。第二,逐值那一欄量的未必是同一件事辨識信心回答的是「引擎對自己的讀法有多確定」,這跟「回傳的這個值究竟有沒有在頁面上被找到」是兩個問題。

✓ Verified

框是怎麼推導出來的,跟它的格式一樣重要。 在 space-ocr 中,語言模型回傳的只是每個欄位的文字,以及它用到了哪些 word token 的提示,框本身從來不由它給出。引擎接著拿這段文字,去跟視覺 OCR 在頁面上實際偵測到的符號做字元層級的比對,於是框就落在這些字元真正被找到的像素上。凡是跑過這道比對的值,其儲存格的 evidence 會帶一個 match_ratio,代表它被定位到的程度;要是根本沒有可比對的對象,這個鍵就不會出現。那些 token 提示可能帶雜訊(有時會在重複的列之間張冠李戴),所以系統用欄一致性與列一致性檢查來驗證它們,而不是盲目相信。這就是「模型宣稱的座標」和「被拿回頁面上重新核對過的座標」之間的差別。

space-ocr 為每個值回傳什麼

業務資料留在 data.values,形狀就是你請求的那套 schema。而這個值從哪裡來、有沒有通過檢查,則集中在另一張以相同路徑為鍵的對應表 data.cells——例如 total,明細項目則是 items[0].price。每個儲存格都帶著:

  • box——一個軸對齊的矩形 { xmin, ymin, xmax, ymax },由整數組成,建立在 0–1000 正規化的網格上(0,0 = 左上,1000,1000 = 右下),與影像的像素尺寸無關。
  • quad——四個有序的點(左上、右上、右下、左下),組成一個跟著文件傾斜角度走的有方向定界框,所以歪斜的手機照片照樣框得乾淨。它一定和 box 一起回傳。
  • verified——判定,也是 review 的鏡像:只要有任何東西被標記就是 false;什麼都沒標記、而且比對確實跑過,就是 true;什麼都沒標記但根本沒有可比對的對象(例如整列的聯集框)則是 null
  • review——不是 null,就是 { reasons }:理由依排序排列,第一個是主因。代碼包括 text_mismatchlow_rationoboxmissing 等。
  • evidence——判定背後的原始訊號:text_match(字元比對本身)、sourcevision_symbol_matchtoken_id)、match_ratio,以及 printed_text——OCR 在那組座標處讀到的字形。

同一批路徑也會出現在 data.review.flagged 裡,那就是待辦清單:每個需要看一眼的值一筆,附上它的理由。data.image 給的是實際被讀取的那一頁的寬高,所有座標都以它為基準。(若你宣告了純量 type,或替 string 欄位加上 patternenum,還會多出一層 data.normalized;下面這個只有 string 的例子不會產生它。)

一個值:values 與 cells
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "data": {
    "values": { "total": "2,045" },
    "cells": {
      "total": {
        "box": { "xmin": 381, "ymin": 803, "xmax": 500, "ymax": 825 },
        "quad": [
          { "x": 380, "y": 804 }, { "x": 500, "y": 801 },
          { "x": 500, "y": 823 }, { "x": 381, "y": 826 }
        ],
        "verified": true,
        "review": null,
        "evidence": {
          "text_match": true,
          "source": "vision_symbol_match",
          "match_ratio": 1.0,
          "printed_text": "2,045"
        }
      }
    },
    "image": { "width": 1654, "height": 2339 }
  }
}

像素還是正規化?轉換一次,縮放就不再出包

OCR 整合常見的一個 bug,是像素座標綁死在你上傳的那張影像上——為了存檔把它縮放或重新壓縮、或漏掉一個 EXIF 旋轉旗標,疊上去的框就會飄移、被裁切,或落在錯的文字上。正規化座標能避開這一整類 bug:一個 0–1000 的框能對應到同一頁面的任何一種呈現方式。

先要弄清一件事:基準面是 data.image,而不是你送出的那個檔案。它是實際被讀取的那一頁——EXIF 方向已經烘進像素裡,大張照片在讀取前還會先縮小——所以它的 widthheight 有可能跟你上傳的正好對調(送 4000×3000,拿回 3000×4000)。以 data.image 為基準來換算,算式才會對得上。

要在顯示出來的影像上畫一個框,只要轉換一次:

  • SVG 疊圖——給 SVG 設 viewBox="0 0 1000 1000",然後原封不動地畫出 boxquad
  • 絕對定位的 div——leftPct = xmin / 1000 * 100topPct = ymin / 1000 * 100widthPct = (xmax - xmin) / 1000 * 100heightPct = (ymax - ymin) / 1000 * 100
  • 轉回像素——pixel_x = box.xmin / 1000 * data.image.widthpixel_y = box.ymin / 1000 * data.image.height

由於 EXIF 方向已經套用在那一頁上,一張旋轉過的手機照片(方向 6/8)也不必你這邊再做一次校正。但頁面不會被擺正(deskew):拍歪的照片依舊是歪的,這正是為什麼 quad 跟著傾斜走,而 box 在它周圍維持軸對齊。

請求欄位並拿回座標
1
2
3
4
5
6
7
8
9
10
11
curl -s https://api.space-ocr.com/ocr/fields \
  -H "Authorization: Bearer $SPACE_OCR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://example.com/receipt.jpg",
    "imageType": "url",
    "fields": [
      { "name": "vendor", "type": "string" },
      { "name": "total",  "type": "string" }
    ]
  }'

信心分數、比對命中率,以及判定

這個區別值得你記牢。多數 OCR API 記載的是辨識信心——一個反映引擎對自己讀法有多確定的數字,根據的是字型清晰度、影像品質之類的因素。它有用,但說到底是模型在替自己的作業打分數。而比對命中率量的是外部的事實:模型回傳的值裡那些字元,有多少真的在頁面層級 OCR 偵測到的符號之中被找到。一個值可能帶著相當高的辨識信心被回傳出來,卻仍然跟頁面上的任何東西對不上。

不過 API 並不會把這個比率丟給你、讓你自己挑一個門檻。match_ratio 作為判定的依據放在 evidence 裡;門檻由引擎自己施加——達到 0.85 以上才算高信心的字元命中——覆蓋不足時,那個儲存格會帶著 review 裡的 low_ratio 回來。所以你程式碼裡的閘門是 review != null,更好的做法是直接走訪 data.review.flagged,它還能觸及命中率看不到的那幾類:宣告為 required 卻根本沒回來的值(missing)、沒有座標的值(nobox)、違反了你所宣告的 pattern 或範圍的值。

有一種組合常讓人意外,其實不必:verified: falseevidence.text_match: true 同時出現。這代表這個值的字元跟頁面對得上,是被你宣告的規則攔下來的。兩者都值得複核,只是理由不同——而且哪一種都不能反過來替對方背書,因為兩個引擎也可能在同一個誤讀上取得一致。

先驗證,再查詢——不必重跑 OCR

資料留得住,座標才最有用。用 POST /upload 把影像推進一張表格,再用 GET /view 在伺服器端查詢它——wheresortselectlimitoffset——好把比方說每一列 total >= 40000 的資料抓出來,不必重跑 OCR,也不另外收費。篩選的對象是這張表格自己的欄位(外加 nameocrStatuscreatedAt),而每一列都會連同完整的 cells 對應表一起回來,所以 boxquad 都還在;想要更輕的回應,就加上 boxes=0 把它們丟掉。想深入了解這套驗證流程,請看 用定界框驗證 OCROCR 稽核軌跡

點選任一個值,它的來源區域就會在原件上亮起來——正是 API 回傳的那組座標,做成了可互動的樣子。

如何從這個 API 拿到可驗證的定界框

  1. 請求欄位
    把影像 POST 到 /ocr/fields,imageType 用「url」或「base64」,再附上你自己的 fields 陣列,或把 autoFields 設為 true。引擎讀取的是點陣影像。
  2. 讀取座標
    用欄位路徑查 data.cells。每個儲存格都帶著 0–1000 網格上的 box { xmin, ymin, xmax, ymax }、四點 quad、判定 verified、review 以及 evidence。
  3. 疊圖或轉換
    用一個 viewBox「0 0 1000 1000」的 SVG 畫出框,或用 pixel_x = box.xmin / 1000 * data.image.width 轉成像素。data.image 就是實際被讀取的那一頁,EXIF 旋轉已經套用過。
  4. 處理待確認清單
    別自己對分數設門檻,直接走訪 data.review.flagged:每一筆都把路徑和理由配成一組,依據則在 cells[path].evidence 裡,也包含 match_ratio。
  5. 儲存並查詢
    用 /upload 把影像推進一張表格,再用 GET /view(where、sort、select)查詢它——每一列都保留帶有 box 和 quad 的 cells 對應表,不必重跑 OCR,也不另外收費。
哪些 OCR API 會回傳定界框?
Google Cloud Vision、Tesseract、Amazon Textract 和 Azure AI Document Intelligence 都會在回傳文字時一併附上幾何資訊,space-ocr 也一樣。依 2026 年 8 月各家的公開文件,它們的差別在於座標系統(Vision 和 Tesseract 用像素;Azure 影像用像素、PDF 用英吋;Textract 正規化為 0–1;space-ocr 用 0–1000 網格)、在於回傳的是結構化欄位還是只有純文字加版面,以及那個逐值數字究竟回報了什麼。
定界框座標是像素還是正規化的?
space-ocr 回傳的 box 正規化到一個 0–1000 的網格,與影像的像素尺寸無關,另外還有一個跟著頁面傾斜走的四點 quad。要轉成像素,用 pixel_x = box.xmin / 1000 * data.image.width(y 也一樣)即可;或直接用一個 viewBox 為「0 0 1000 1000」的 SVG 來疊圖。基準面是 data.image,也就是經過 EXIF 方向處理與必要縮小之後、實際被讀取的那一頁,所以它的寬高可能和你送出的檔案不同。
一個 OCR 信心分數和一個比對命中率有什麼差別?
辨識信心反映的是引擎對自己讀法有多確定。而 match_ratio 量的是回傳值的文字有多少真的在頁面層級 OCR 偵測到的符號之中被定位到——是一項來自外部的檢查,而不是自我回報。它作為依據放在 cells[path].evidence 裡:達到 0.85 以上才算高信心的字元命中,覆蓋不足時引擎會在該儲存格的 review 理由裡立起 low_ratio,所以你的程式碼是對 review 設閘門,而不是對這個數字。
我能為歪斜或旋轉的照片拿到有方向的定界框嗎?
可以。每個儲存格都會在軸對齊的 box 之外,回傳一個由四個有序頂點(左上、右上、右下、左下)組成的 quad,而 quad 會跟著文件的傾斜角度走。頁面不會被擺正(deskew),而座標所依據的那一頁(data.image)已經套用過 EXIF 方向,所以一張方向為 6 或 8 的手機照片,也不必你這邊再做一次校正。
這套定界框 OCR 對日文、韓文和中文有效嗎?
有效。同一個引擎以自動語言偵測處理中日韓與拉丁文字——沒有語言參數要設——而且不論是哪種文字,包括全形字元,每個值都以同一套約定回傳:以你宣告的欄位路徑為鍵,data.cells 裡的 box、quad、verified、review 和 evidence。
相關文章