스캔에서, 사람이 읽는 순서 그대로의 텍스트 꺼내기
POST /ocr/text로 읽기순서 원문을 추출하고, 필요할 때 내용 전용 blocks와 path 기반 원본 좌표, 명시적인 검토 목록을 받는 방법입니다.
"그냥 텍스트만 주세요"는 쉬운 OCR 요청처럼 들리지만 검색 색인을 조용히 망가뜨리기 좋은 요구이기도 합니다. 비전 OCR이 올바른 단어를 찾아도 검출 순서로 내보내면 왼쪽 단 첫 줄, 오른쪽 단 첫 줄, 다시 왼쪽 단이 섞입니다. 오류는 없는데 문서는 더 이상 사람이 읽는 문서가 아닙니다.
POST /ocr/text는 이 문제를 필드 추출이나 마크다운 변환과 분리합니다. 기본값인 useLlm: true에서는 블록을 사람이 읽는 순서로 정렬하고 줄바꿈으로 끊긴 행을 다시 잇습니다. 글자와 원본 위치는 비전 관측과 대조하므로 언어 모델의 전사를 무조건 정답으로 취급하지 않습니다.
요청을 바꾸는 두 스위치
useLlm의 기본값은 true입니다. 다단 조판, 사이드바, 기울어진 스캔, 사람이 읽을 텍스트라면 그대로 두세요. false로 설정하면 LLM 단계를 건너뛴 비전 전용 전사가 raw OCR 순서로 돌아옵니다. 이 경로에서도 같은 형식의 문서 검증 객체는 제공됩니다.
includeBlocks의 기본값은 false입니다. 전문만 필요하면 data.values.text를 쓰면 됩니다. 직접 청킹하거나 원본을 하이라이트하거나 검토 UI를 만들 때 켜세요. 그러면 내용만 담은 data.values.blocks, data.cells["blocks[0]"] 같은 메타데이터, data.review.flagged의 블록 경로가 추가됩니다.
한 번 호출하기
아래 예제는 검토 대상을 원본까지 추적할 수 있도록 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는 그 위치의 비전 텍스트와 정규화 후 일치했는지를 나타내는 판정이지 정확도 퍼센트가 아닙니다.data.review.flagged는 실제 검토 작업 목록입니다. 각path로data.cells[path]를 바로 찾고review.reasons에서 검토 이유를 읽습니다.data.source는 읽기순서 패스가 전사했으면llm, 비전 경로면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 오류로 끝내지 않고 비전 전사를 돌려줍니다. 이때 data.source는 "vision"이고 이유는 data.warning에 실립니다. 어떤 블록도 가져가지 않은 비전 token은 cells[path].evidence.source: "unclaimed_tokens"인 회수 블록으로 추가될 수 있어, 누락된 문단이 조용히 버려지지 않습니다.
원문 텍스트인가, 마크다운인가
제목과 표를 별도 타입으로 보존할 필요가 없는 전문 검색, 임베딩, 문서 비교, 접근성 피드라면 원문 텍스트가 맞습니다. 문서 구조가 필요하면 POST /ocr/markdown을 쓰고 이미지를 마크다운으로 바꾸는 가이드를 참고하세요. 좌표와 검토 메타데이터는 OCR 원본 좌표 가이드에서 더 자세히 다룹니다.
- 읽기순서 경로 선택하기이미지를 POST /ocr/text로 보냅니다. 읽기순서가 중요하면 기본 useLlm:true를 유지하고, raw 비전 순서로 충분할 때만 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을 표시합니다.