About this project

PolyOCR Service is a multilingual OCR, optional translation, and standalone PaddleOCR-VL service built on PaddleOCR 3.x and FastAPI. The author explicitly states this is a community project, not an official PaddleOCR component. Capabilities and Boundaries Basic OCR calls PaddleOCR 3.x's predict(), compatible with 3.x mapping/object results and legacy list results. Supports 78 languages covering Chinese, Japanese, Korean, Latin, Cyrillic, Arabic, Devanagari, Thai, Greek, and other writing systems, and accepts three categories of aliases: language codes, English names, and Chinese names (e.g., fr / french / 法文). Language validation occurs at the request boundary; unknown languages directly return 422 unsupported_language, rather than failing only when the model is loaded. Images undergo validation of byte count, decoding result, pixel count, and confidence threshold before inference; synchronous inference runs in a thread pool with concurrency limited by a semaphore. Translation input is limited by entry count and total character count, and requires the vendor to return the same number of results as input. HTTP, authentication, request validation, and domain errors use a unified error response structure. PaddleOCR-VL accepts only uploaded content, rejects URLs, requires a separate API Key, and limits upload size. The browser page renders server results using textContent, not interpreting returned content as HTML. Installation and Running Supports Python 3.10, 3.11, 3.12, checked by CI. Install via pip install -e ".[dev,ocr]", copy .env.example to .env and modify the API Key before starting. The command-line entry point listens on 127.0.0.1:8000 by default, configurable via POLYOCR_HOST / POLYOCR_PORT; the README specifically warns that --host 0.0.0.0 exposes the instance on all network interfaces, which is necessary inside containers, but running directly on a laptop or shared network would let anyone on the LAN access the instance. The web page is at the root path, and API documentation is at /docs. API Overview Health check /v1/health does not require authentication; /v1/languages returns language codes, PaddleOCR language codes, writing systems, and available aliases. OCR request /v1/ocr supports X-API-Key or Bearer Token, with form fields including file, language, score_threshold; the response echoes the canonical language code and includes items (text, score, bbox), cost_ms, request_id, and warnings. Translation endpoint /v2/translate accepts texts and target_language. Failed responses uniformly use an error object containing code, message, and request_id. Motion Blur Warning When the image Laplacian variance falls below a threshold (default 45.0, adjustable via POLYOCR_BLUR_VARIANCE_FLOOR) but the model still returns high-confidence text, the response includes a suspected_blur warning, with detail giving the Laplacian variance and threshold. The README explains the motivation: motion blur returns text with normal confidence but wrong content, which callers previously could not distinguish; this warning turns "indistinguishable" into "distinguishable." The warning is evaluated only when the model actually returns text; empty results produce no warning. Configuration Items The documentation lists authentication toggle and API Key, CORS origins, upload size limit (default 10MB), decoded pixel limit (default 25 million), concurrent inference count (default 2), OCR worker thread count, blur variance threshold, translation entry and character limits, OpenAI-compatible translation service key/base/model, and the VL service's separate key and upload limit. When authentication is enabled and POLYOCR_API_KEY is empty, the base service refuses to start; the VL service always requires POLYOCR_VL_API_KEY; CORS with credentials does not allow wildcard origins. Docker and Testing Provides docker compose up --build deployment, with the image fixed on a Python 3.10 baseline, running as a non-root user with health checks. The first OCR run downloads models, with cache stored in a Compose volume. Testing includes ruff formatting and checks, pytest non-integration tests, and python -m build; real OCR E2E requires explicitly setting POLYOCR_RUN_OCR_E2E=1 and may download models. CI runs only fast tests that do not download models. Benchmarks and Measured Conclusions The language accuracy benchmark compares annotated images per language, reporting both exact (per-line exact match ratio) and cer (character error rate), and is unaffected by detection order. The robustness benchmark covers blur, compression, rotation, downscaling, noise, brightness/contrast, and photographic degradations such as motion blur, perspective, uneven lighting, shadows, and paper texture. The README's measured conclusions: rotation, perspective, JPEG compression, brightness/contrast, uneven lighting, shadows, and paper texture barely affect recognition; combined photo scenarios even score perfectly. The only true failures are detail-loss types, namely blur beyond about σ2 and downscaling below 25%. The only thing to watch out for is motion blur: at 15px displacement, it returns wrong text with normal confidence (Hello World → Heelco Ncotec); within 9px it is completely normal. The preprocess parameter is not implemented: all five preprocessing pipelines show net negative gains across all 17 degradations, and auto-contrast harms 11 of 17 items, so preprocess=true returns 400 preprocess_unsupported rather than silently ignoring it. The real-photo benchmark uses CORD-v2 (CC-BY-4.0, with manual word-level annotations): synthetic images average 0.975 exact, real photos achieve 0.841 word recall; the README states synthetic scores are optimistic by about 13 points. Preprocessing on real photos shows positive mean for all three pipelines, but bootstrap testing shows the 95% confidence intervals for all five pipelines cross zero, with the smallest p-value at 0.371, so the conclusion remains "no statistically detectable benefit." License Uses Apache License 2.0, consistent with upstream PaddleOCR, with third-party attribution recorded in NOTICE. PaddleOCR models are downloaded from their original distributors at runtime, subject to their respective licenses and terms.