PaddleOCR
LibraryPaddlePaddle/PaddleOCR
Turn PDF or image documents into structured data for AI, supporting 100+ languages.
Overview
PaddleOCR is a powerful OCR toolkit that converts any PDF or image into structured data (JSON/Markdown) for LLMs. It supports 100+ languages, features state-of-the-art document parsing with PP-StructureV3 and PaddleOCR-VL models, and integrates seamlessly with RAG and agentic applications.
README Preview
\n\n \n \n \n\n\n\nGlobal Leading OCR Toolkit & Document AI Engine\n\nEnglish | [简体中文](./readme/README_cn.md) | [繁體中文](./readme/README_tcn.md) | [日本語](./readme/README_ja.md) | [한국어](./readme/README_ko.md) | [Français](./readme/README_fr.md) | [Русский](./readme/README_ru.md) | [Español](./readme/README_es.md) | [العربية](./readme/README_ar.md)\n\n\n\n[](https://pepy.tech/projects/paddleocr)\n[](https://github.com/PaddlePaddle/PaddleOCR/network/dependents)\n\n\n\n\n[](https://www.paddleocr.com)\n[](https://deepwiki.com/PaddlePaddle/PaddleOCR)\n[](../LICENSE)\n\n\n\n\n\n\n\n\n**PaddleOCR converts PDF documents and images into structured, LLM-ready data (JSON/Markdown) with industry-leading accuracy. With 70k+ Stars and trusted by top-tier projects like Dify, RAGFlow, and Cherry Studio, PaddleOCR is the bedrock for building intelligent RAG and Agentic applications.**\n\n\n## 🚀 Key Features\n\n### 📄 Intelligent Document Parsing (LLM-Ready)\n> *Transforming messy visuals into structured data for the LLM era.*\n\n* **SOTA Document VLM**: Featuring **PaddleOCR-VL-1.6 (0.9B)**, the industry's leading lightweight vision-language model for document parsing. It achieves 96.3% accuracy on OmniDocBench v1.6, leads in text, formula, and table recognition, and shows significantly enhanced capabilities in ancient documents, rare characters, seals, and charts, with structured outputs in **Markdown** and **JSON** formats.\n* **Structure-Aware Conversion**: Powered by **PP-StructureV3**, seamlessly convert complex PDFs and images into **Markdown** or **JSON**. Unlike the PaddleOCR-VL series models, it provides more fine-grained coordinate information, including table cell coordinates, text coordinates, and more.\n* **Production-Ready Efficiency**: Achieve commercial-grade accuracy with an ultra-small footprint. Outperforms numerous closed-source solutions in public benchmarks while remaining resource-efficient for edge/cloud deployment.\n\n### 🔍 Universal Text Recognition (S
FAQ (3)
TroubleshootingWhy does PaddleOCR SealRecognition return empty seal results (seal_res_list: []) and how to fix it?
Root cause: SealRecognition enables layout detection by default (use_layout_detection=True), and the built-in PP-DocLayout-L layout model classifies red circular seal stamps as invalid regions, truncating the downstream seal detection pipeline. Fix: disable layout detection when initializing the pipeline:
from paddleocr import SealRecognition
ocr = SealRecognition(device="gpu", use_layout_detection=False)
result = ocr.predict(input="testpic.png")This restores seal detection. Note: circular/arc text on ring-shaped seals is still not fully supported by the official pipeline (rectangular cropping cannot handle curved text, and detection boxes may be offset). As of PaddleOCR 3.7.0 there is no official fix; the suggested community approach is to extract the seal polygon and apply polar-coordinate unwrapping to straighten the arc text before recognition, or fine-tune a custom seal detection model.
TroubleshootingHow to fix PaddleOCRVL llama-cpp-server HTTP 500 "Failed to parse input" on images with smart quotes?
This occurs with llama.cpp b9637 when PaddleOCR-VL output containing curly/smart quotes (U+201C/U+201D) is decoded into invalid UTF-8 or U+FFFD bytes, causing llama-server's chat-template parser to return HTTP 500. PaddleOCR 3.7.0 then cancels every pending VLM block and fails the whole predict() call.
Preferred fix: update or rebuild llama-server to a build containing the upstream fix titled "chat: sanitize invalid UTF-8 before peg-native parsing", then restart the server and rerun predict().
Temporary PaddleOCR workaround: patch paddlex/inference/models/doc_vlm/predictor.py in DocVLMGenAIClientPredictor._genai_client_process() so one failed block returns empty content instead of cancelling all futures and re-raising:
import logging
contents = [""] * len(futures)
for idx, future in enumerate(futures):
try:
contents[idx] = future.result().choices[0].message.content or ""
except Exception as exc:
logging.warning("Skipping failed VLM block %s: %s", idx, exc)
contents[idx] = ""
Remove or replace the global handler that cancels pending futures and calls raise. The failed paragraph will be blank, but the remaining blocks and page output will be generated. Locate the installed file with:
python -c "import paddlex, os; print(os.path.dirname(paddlex.__file__))"
Restart Python after patching. Updating llama.cpp is preferable because it prevents the parser failure itself.
TroubleshootingHow to fix PaddleOCR PP-OCRv3 detection validation GPU OOM when memory keeps increasing and throws BadAlloc/ResourceExhaustedError?
This usually happens because validation images have variable H/W sizes after DetResizeForTest, so PaddlePaddle's caching allocator keeps blocks for new shapes; training/optimizer memory also remains resident on the T4. Reduce or fix the evaluation input size in your PP-OCRv3 det config:
Eval:
loader:
batch_size_per_card: 1
dataset:
transforms:
- DetResizeForTest:
limit_type: max
limit_side_len: 736 # try 640 if it still OOMs
The default is often 960. To make memory reuse deterministic, replace it with a fixed 32-divisible shape, for example:
- DetResizeForTest:
image_shape: [3, 640, 960]
If you modified the evaluation loop, make sure inference runs without autograd:
model.eval()
with paddle.no_grad():
for batch in eval_loader:
preds = model(images)
As a temporary workaround, enable CUDA managed memory before training—use true, not the false shown in the old error message:
export FLAGS_use_cuda_managed_memory=true
python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_distill_train.yml
If validation during training still fails, run evaluation separately so optimizer states and training cache are not resident:
python tools/eval.py \
-c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_distill_train.yml \
-o Global.checkpoints=<output_dir>/best_accuracy
Reducing Train.loader.batch_size_per_card to 1 or using a larger GPU are further options.