PaddleOCR PP-OCRv4 기반 OCR(Optical Character Recognition) API입니다.
PNG, JPEG, PDF, TIFF, BMP 형식의 이미지에서 텍스트를 인식합니다.
text 단순 텍스트와 json_structured 블록·표 구조화 출력을 지원합니다.
대용량 이미지는 Multipart 모드로 전송해 base64 오버헤드를 제거할 수 있습니다.
⚠ OCR 태스크 활성화 필요
다른 태스크와 함께 실행하면 VRAM 부족이 발생합니다. 반드시 OCR만 단독으로 활성화하세요.
# .env REDGX_GPU_RELAY_ENABLED=true REDGX_GPU_OCR_ENABLED=true # 나머지는 false
# 1. POST → 202 Accepted (request_id 반환)
POST /api/v1/ns/{ns}/gpu/ocr
Content-Type: multipart/form-data 또는 application/json (image_b64)
→ 202 { "request_id": "ocr-1710000000000-a1b2c3d4" }
# 2. GET 폴링 → 완료 시 200
GET /api/v1/ns/{ns}/gpu/ocr/{request_id}
→ 202 { "status": "queued" }
→ 200 { "status": "completed", "text": "..." }
# 데이터 흐름
Client → POST /gpu/ocr (이미지 bytes) → Redis Queue → OCRWorker
→ PaddleOCR → text / blocks / tables → Redis Outbox → Client
| 항목 | 값 |
|---|---|
| OCR 엔진 | PaddleOCR PP-OCRv4 (DB++ Detection + SVTR Recognition) |
| 모델 선정 | RTX 3060 12GB 채택 — ~500MB VRAM, 4개 언어(ko/en/zh/ja) Scene Text(간판/번호판) + Document(서류/PDF/화면캡처) 모두 지원 |
| 지원 포맷 | PNG JPEG PDF TIFF BMP (magic bytes 검증) |
| 최대 파일 크기 | 50 MB |
| 출력 형식 | text: 순수 텍스트 / json_structured: blocks (위치+신뢰도) + tables (표 구조) |
| 전송 모드 | JSON: image_b64 (base64) / Multipart: file 파트 (권장) |
| 캐시 | SHA-256(모델명 + 이미지 bytes) 기반 |
| 캐시 TTL | 24시간 (86400초) |
이미지 파일을 업로드하고 OCR 결과를 조회합니다. Multipart 모드(권장)와 JSON Base64 모드를 지원합니다. 샘플 이미지를 클릭하거나 직접 파일을 드래그하세요.
curl 명령 보기
동일한 이미지를 text와 json_structured 포맷으로 각각 요청하여
결과를 나란히 비교합니다. json_structured는 블록별 위치(bbox)와 신뢰도,
표 구조(rows × cols)를 함께 반환합니다.
curl 명령 보기
동일한 이미지를 두 번 전송하면 두 번째는 GPU 추론 없이 즉시 반환됩니다. 캐시 키는 SHA-256(모델명 + 이미지 raw bytes)로 계산됩니다.
output_format과 language는 캐시 키에 포함되지 않습니다.
동일 이미지에 포맷만 다르게 요청해도 캐시가 적중됩니다.
curl 명령 보기
결과는 outbox에 1시간(3600초) 보관됩니다.
curl 명령 보기
curl 명령 보기
curl 명령 보기
여러 request_id 상태를 한 번의 API 호출로 조회합니다.
curl 명령 보기
OCR 전용 에러(INVALID_IMAGE_FORMAT)를 포함한 다양한 오류 상황을 테스트합니다.
• JSON 모드 (
Content-Type: application/json): image_b64 필드에 Base64 인코딩 이미지• Multipart 모드 (
Content-Type: multipart/form-data): file 파트에 원본 이미지 bytes (권장)
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
image_b64 | string (JSON) | 필수 | Base64 인코딩 이미지 데이터 (JSON 모드) |
file | binary (Multipart) | 필수 | 이미지 파일 원본 bytes (Multipart 모드) |
output_format | "text" | "json_structured" | "text" | 출력 형식. json_structured는 blocks+tables 포함 |
language | string | null | null | 문서 언어 힌트 (en, ko, …). 지정 시 정확도 향상 |
model | string | null | 서버 기본값 | PaddleOCR 모델명 (null = 서버 설정값) |
{
"ok": true,
"data": {
"request_id": "ocr-1710567890123-a1b2c3d4",
"task_type": "ocr",
"hint": "text_recommended" // 4KB 미만 업로드 시에만 포함 — JSON 모드 권장 힌트
}
}
hint 필드는 multipart 업로드가 4KB 미만일 때만 응답에 포함됩니다 (요청은 거부되지 않음). 작은 페이로드에는 base64 JSON 모드가 효율적입니다.
// output_format=text (기본)
{
"ok": true,
"data": {
"request_id": "ocr-1710567890123-a1b2c3d4",
"status": "completed",
"text": "Hello World\nThis is a test document.",
"output_format": "text",
"blocks": null, // text 모드 시 null
"tables": null,
"page_count": 1,
"language": "en",
"model": "PaddleOCR/PP-OCRv4",
"cached": false,
"elapsed_ms": 843.5
}
}
// output_format=json_structured
{
"data": {
"text": "Hello World\nThis is a test...",
"output_format": "json_structured",
"blocks": [
{
"text": "Hello World",
"confidence": 0.98,
"bbox": { "x": 0.05, "y": 0.02, "width": 0.4, "height": 0.08 }
}
],
"tables": [
{
"rows": [["Name", "Age"], ["Alice", "30"]],
"num_rows": 2, "num_cols": 2, "confidence": null
}
]
}
}
| 필드 | 설명 |
|---|---|
request_id | 요청 ID |
status | completed 또는 failed |
text | 전체 인식 텍스트 (output_format 무관하게 항상 포함) |
output_format | 실제 사용된 출력 형식 |
blocks | 텍스트 블록 목록 (json_structured 시), 각 블록에 text, confidence, bbox 포함 |
tables | 표 구조 목록 (json_structured 시), rows[행][열] = 셀 텍스트 |
page_count | 페이지 수 (PDF 시 실제 페이지 수, 이미지는 1) |
language | 감지(또는 지정)된 언어 |
model | 실제 사용된 OCR 모델명 |
cached | 캐시 적중 여부 |
elapsed_ms | 전체 처리 시간 (ms) |
# Multipart 모드 (권장)
curl -X POST https://localhost:1443/api/v1/ns/HRM/gpu/ocr \
-k \
-H "X-API-Key: <your-key>" \
-F "[email protected]" \
-F "output_format=json_structured" \
-F "language=en"
# JSON 모드 (소용량)
curl -X POST https://localhost:1443/api/v1/ns/HRM/gpu/ocr \
-k \
-H "X-API-Key: <your-key>" \
-H "Content-Type: application/json" \
-d '{"image_b64":"'"$(base64 -w0 document.png)"'","output_format":"text"}'
# 결과 조회 후 자동 삭제
curl "https://localhost:1443/api/v1/ns/HRM/gpu/ocr/ocr-xxx?auto_clear=true" \
-k -H "X-API-Key: <your-key>"
| 코드 | HTTP | 설명 |
|---|---|---|
INVALID_IMAGE_FORMAT | 400 | 지원하지 않는 이미지 포맷 (magic bytes 검증 실패) |
MISSING_IMAGE | 400 | JSON 모드: image_b64 필드 누락 또는 빈 값 |
MISSING_FILE | 400 | Multipart 모드: file 파트 누락 |
EMPTY_FILE | 400 | 업로드 파일이 빈 바이트 (multipart 또는 base64 디코딩 결과 0 byte) |
INVALID_MULTIPART | 400 | multipart/form-data 파싱 실패 |
INVALID_JSON | 400 | JSON 본문 파싱 실패 |
INVALID_BASE64 | 400 | image_b64가 유효한 base64가 아님 |
UNAUTHORIZED | 401 | API Key 누락 또는 잘못됨 |
NAMESPACE_DENIED | 403 | Namespace 접근 권한 없음 |
GPU_NOT_FOUND | 404 | Request ID 없음 또는 만료 (TTL 3600s) |
GPU_PAYLOAD_TOO_LARGE | 413 | 이미지 파일 크기 초과 (max_file_size_mb, 기본 50 MB) |
GPU_TASK_DISABLED | 503 | OCR 태스크 비활성 (REDGX_GPU_OCR_ENABLED=false) |
GPU_UNAVAILABLE | 503 | CUDA 불가 또는 PaddleOCR 모델 로드 실패 |
GPU_QUEUE_FULL | 503 | 큐 용량 초과 |
GPU_PROCESSING | 409 | 취소 불가 — 이미 처리 중 |
Redis Pub/Sub으로 완료 알림을 수신 후 즉시 OCR 결과를 전송합니다.
// 인증: Sec-WebSocket-Protocol 헤더로 API 키 전달 (URL 쿼리 미지원) // 브라우저: new WebSocket(url, ["redgx_ak_crm_..."]) wss://<host>/api/v1/ns/CRM/gpu/ocr/ocr-xxx/wait?timeout=90 // timeout 기본 10, 최대 300
// 완료 — REST GET과 동일 구조
{ "ok": true, "data": { "request_id": "ocr-...", "status": "completed",
"text": "인식된 텍스트", "output_format": "text",
"blocks": null, "tables": null, "page_count": 1, "language": "en",
"model": "PaddleOCR/PP-OCRv4", "cached": false, "elapsed_ms": 420.0 } }
// 실패
{ "ok": true, "data": { "request_id": "ocr-...", "status": "failed", "error": {...} } }
// 인증 실패 → HTTP 403 (upgrade 거부, accept 전 — 4001 close 프레임 미전송)
// Not Found → { "ok": false, "error": { "code": "GPU_NOT_FOUND" } } + close(4004)
// Timeout → { "ok": false, "error": { "code": "GPU_TIMEOUT" } } + close(4008)