프로젝트 소개

PolyOCR Service는 PaddleOCR 3.x와 FastAPI로 구축된 다국어 OCR, 선택적 번역 및 독립적인 PaddleOCR-VL 서비스이며, 작성자는 이것이 커뮤니티 프로젝트로 PaddleOCR 공식 컴포넌트가 아님을 명시한다. 기능과 한계 기본 OCR은 PaddleOCR 3.x의 predict()를 호출하며, 3.x 매핑/객체 결과와 구 리스트 결과를 모두 호환한다. 78개 언어를 지원하여 한자, 일본어, 한국어, 라틴, 키릴, 아랍, 데바나가리, 태국어, 그리스어 등 문자 체계를 포괄하며, 언어 코드, 영어명, 중국어명 세 가지 별칭을 허용한다(예: fr / french / 法文). 언어는 요청 경계에서 검증되어 알 수 없는 언어는 422 unsupported_language를 즉시 반환하며, 모델 로드 시점까지 가서 실패하지 않는다. 이미지는 추론 전에 바이트 수, 디코딩 결과, 픽셀 수 및 신뢰도 임계값 검사를 거치며, 동기 추론은 스레드 풀에서 실행되고 세마포어로 동시성을 제한한다. 번역 입력은 항목 수와 총 문자 수를 제한하며, 공급자가 반환한 수량이 입력과 일치할 것을 요구한다. HTTP, 인증, 요청 검증 및 도메인 오류는 통일된 error 응답 구조를 사용한다. PaddleOCR-VL은 업로드 콘텐츠만 허용하고 URL을 거부하며, 독립적인 API Key를 요구하고 업로드 크기를 제한한다. 브라우저 페이지는 textContent로 서버 측 결과를 렌더링하며, 반환된 내용을 HTML로 해석하지 않는다. 설치와 실행 Python 3.10, 3.11, 3.12를 지원하며 CI로 검증된다. pip install -e ".[dev,ocr]"로 설치하고, .env.example을 .env로 복사하여 API Key를 수정한 뒤 시작한다. 명령줄 진입점은 기본적으로 127.0.0.1:8000을 수신하며, POLYOCR_HOST / POLYOCR_PORT로 설정할 수 있다. README는 --host 0.0.0.0이 모든 네트워크 인터페이스에 노출됨을 특별히 경고하며, 컨테이너 내에서는 필요하지만 노트북이나 공유 네트워크에서 직접 실행하면 LAN 내 누구나 인스턴스에 접근할 수 있다. 웹 페이지는 루트 경로에, API 문서는 /docs에 있다. API 개요 상태 확인 /v1/health는 인증을 요구하지 않으며, /v1/languages는 언어 코드, PaddleOCR 언어 코드, 문자 체계 및 사용 가능한 별칭을 반환한다. OCR 요청 /v1/ocr은 X-API-Key 또는 Bearer Token을 지원하며, 폼 필드는 file, language, score_threshold를 포함하고, 응답은 정규화된 언어 코드를 에코하고 items(text, score, bbox), cost_ms, request_id, warnings를 포함한다. 번역 인터페이스 /v2/translate는 texts와 target_language를 받는다. 실패 응답은 error 객체로 통일되며 code, message, request_id를 포함한다. 모션 블러 경고 이미지의 라플라시안 분산이 임계값(기본 45.0, POLYOCR_BLUR_VARIANCE_FLOOR로 조정 가능) 미만이지만 모델이 여전히 높은 신뢰도의 텍스트를 반환할 때, 응답에 suspected_blur 경고를 추가하고 detail에 라플라시안 분산과 임계값을 제공한다. README는 그 동기를 설명한다: 모션 블러는 신뢰도는 정상이지만 내용이 잘못된 텍스트를 반환하며, 호출자가 원래 구분할 수 없었던 것을 이 경고가 "구분 불가"에서 "구분 가능"으로 바꾼다. 경고는 모델이 실제로 텍스트를 반환할 때만 평가되며, 빈 결과는 경고를 발생시키지 않는다. 설정 항목 문서는 인증 스위치와 API Key, CORS 출처, 업로드 크기 상한(기본 10MB), 디코딩 후 픽셀 상한(기본 2,500만), 동시 추론 수(기본 2), OCR 작업 스레드 수, 블러 분산 임계값, 번역 항목 및 문자 상한, OpenAI 호환 번역 서비스의 키/베이스 주소/모델, 그리고 VL 서비스의 독립 키와 업로드 상한을 나열한다. 인증이 활성화되고 POLYOCR_API_KEY가 비어 있으면 기본 서비스는 시작을 거부하며, VL 서비스는 항상 POLYOCR_VL_API_KEY를 요구하고, 자격 증명이 있는 CORS는 와일드카드 출처를 허용하지 않는다. Docker와 테스트 docker compose up --build 배포 방식을 제공하며, 이미지는 Python 3.10 기준으로 고정되고, non-root 사용자로 실행되며 상태 확인을 제공한다. 첫 OCR 시 모델을 다운로드하고 캐시는 Compose volume에 저장된다. 테스트 절차는 ruff 포맷 및 검사, pytest 비통합 테스트, python -m build를 포함하며, 실제 OCR E2E는 POLYOCR_RUN_OCR_E2E=1을 명시적으로 설정해야 하고 모델을 다운로드할 수 있다. CI는 모델을 다운로드하지 않는 빠른 테스트만 실행한다. 벤치마크와 실측 결론 언어 정확도 벤치마크는 언어별로 라벨이 있는 이미지를 대조하여 exact(줄별 완전 일치 비율)와 cer(문자 오류율)를 동시에 보고하며, 검출 순서의 영향을 받지 않는다. 견고성 벤치마크는 블러, 압축, 회전, 축소, 노이즈, 명암 대비 및 모션 블러, 원근, 불균일 조명, 그림자, 종이 질감 등 촬영류 열화를 포괄한다. README의 실측 결론은: 회전, 원근, JPEG 압축, 명암 대비, 불균일 조명, 그림자, 종이 질감은 인식에 거의 영향을 주지 않으며, 조합 촬영 시나리오는 오히려 만점이다. 실제 실패는 디테일 손실 한 가지뿐으로, 블러가 약 σ2를 초과하거나 25% 이하로 축소된 경우이다. 유일하게 경계해야 할 것은 모션 블러이다: 15px 변위에서 신뢰도가 정상인 잘못된 텍스트를 반환하며(Hello World → Heelco Ncotec), 9px 이내에서는 완전히 정상이다. preprocess 파라미터는 미구현이다: 다섯 가지 전처리 파이프라인이 전체 17가지 열화에서 모두 순부 효과가 음수이며, 자동 대비는 17항 중 11항에 해를 끼쳤다. 따라서 preprocess=true는 400 preprocess_unsupported를 반환하고 조용히 무시하지 않는다. 실제 사진 벤치마크는 CORD-v2(CC-BY-4.0, 수동 단어별 라벨 포함)로 실측하였으며, 합성 이미지는 평균 0.975 exact, 실제 사진은 0.841 단어 재현율이고, README는 합성 이미지 점수가 약 13포인트 편향되어 있다고 밝힌다. 전처리는 실제 사진에서 세 가지 파이프라인의 평균이 양수였지만, 부트스트랩 검정에서 다섯 가지 파이프라인의 95% 신뢰구간이 모두 0을 가로지르며, 최소 p값이 0.371로 결론은 "통계적으로 검출 가능한 이득이 없다"로 유지된다. 라이선스 Apache License 2.0을 채택하여 상위 PaddleOCR과 일치하며, 제3자 저작권 표시는 NOTICE에 기록된다. PaddleOCR 모델은 런타임에 원래 배포자로부터 다운로드되며, 각각의 라이선스와 사용 약관의 적용을 받는다.