Rust PaddleOCR

August 27, 2026 · View on GitHub

English | 中文 | 日本語 | 한국어

A lightweight Rust OCR library based on PaddleOCR models and the MNN inference runtime. It provides text detection, text recognition, and end-to-end OCR with file or in-memory model loading.

Related projects:

  • CLI: newbee-ocr-cli
  • C API bindings: paddle-ocr-capi
  • HTTP service: newbee_ocr_service is local-only and is not published as a public repository.

Supported Models

All runtime model files should be placed under models/.

FamilyDetectionRecognitionNotes
PP-OCRv4ch_PP-OCRv4_det_infer.mnnch_PP-OCRv4_rec_infer.mnnLegacy CN/EN model
PP-OCRv5PP-OCRv5_mobile_det.mnn or PP-OCRv5_mobile_det_fp16.mnnPP-OCRv5_mobile_rec*.mnnDefault CN/EN/JP plus script-specific models
PP-OCRv6 tinyPP-OCRv6_tiny_det.mnnPP-OCRv6_tiny_rec.mnnLightweight v6 tier; Japanese is not supported
PP-OCRv6 smallPP-OCRv6_small_det.mnnPP-OCRv6_small_rec.mnnBalanced v6 tier
PP-OCRv6 mediumPP-OCRv6_medium_det.mnnPP-OCRv6_medium_rec.mnnAccuracy-first v6 tier

PP-OCRv6 small and medium support the official 50 v6 recognition languages: Simplified Chinese, Traditional Chinese, English, Japanese, and 46 Latin-script languages. PP-OCRv6 tiny follows the same set except Japanese. Korean, Cyrillic, Arabic, Devanagari, Thai, Greek, Tamil, and Telugu should continue using the PP-OCRv5 script-specific recognition models.

V6 charset files are tier-specific:

models/ppocr_keys_v6_tiny.txt
models/ppocr_keys_v6_small.txt
models/ppocr_keys_v6_medium.txt

Convert Paddle Models To MNN

The converter defaults to MNN FP16 to reduce model size. Use --install-dir ./models to copy converted runtime files into the expected directory and filenames.

python script/convert_paddle_to_mnn.py \
  --ocr-dir /path/to/paddle/inference/models \
  --install-dir ./models

Use --no-fp16 only when full precision MNN output is required.

Rust Usage

use ocr_rs::OcrEngine;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let engine = OcrEngine::new(
        "models/PP-OCRv6_small_det.mnn",
        "models/PP-OCRv6_small_rec.mnn",
        "models/ppocr_keys_v6_small.txt",
        None,
    )?;

    let image = image::open("test.jpg")?;
    let results = engine.recognize(&image)?;

    for item in results {
        println!("{:.2}: {}", item.confidence, item.text);
    }

    Ok(())
}

Detection-only and recognition-only engines are also available:

let det = ocr_rs::OcrEngine::det_only("models/PP-OCRv6_small_det.mnn", None)?;
let rec = ocr_rs::OcrEngine::rec_only(
    "models/PP-OCRv6_small_rec.mnn",
    "models/ppocr_keys_v6_small.txt",
    None,
)?;

Mixed horizontal and vertical text

The existing recognize method keeps the original single-pass behavior. For images that mix horizontal text with text rotated by 90 or 270 degrees, enable the opt-in robust mode for that call:

use ocr_rs::{RecognizeOptions, RotatedTextMode};

let options = RecognizeOptions::new()
    .with_rotated_text_mode(RotatedTextMode::Robust);
let results = engine.recognize_with_options(&image, &options)?;

Robust mode runs detection on 90° and 270° copies, maps recovered boxes back to the input coordinates, and recognizes only vertical candidates. This adds no work to existing recognize calls. RotatedTextMode::DetectedOnly is a lighter option when the normal detector already finds the vertical boxes.

Character alignment

Use the additive alignment API when an image UI needs character hit testing or selection geometry:

let results = engine.recognize_with_options_and_alignment(&image, &options)?;
for line in results {
    for character in line.characters {
        println!("{}: {:?}", character.character, character.points);
    }
}

The four source-image points for each character are derived from the recognition model's CTC time steps and the detected text quadrilateral. This does not run an additional inference pass. The existing RecognitionResult, OcrResult_, recognize, and recognize_with_options APIs are unchanged.

Build

cargo build --release
cargo test

Performance Checks

Run Criterion benchmarks locally:

cargo bench --bench bench_metrics

Run the CI-style performance smoke test:

OCR_RS_PERF_TESTS=1 cargo test --release --test performance_tests -- --nocapture --test-threads=1

GitHub Actions runs these release tests serially and stores the PERF_METRIC log as an artifact. The regression guard compares the direct exact-width pipeline with the legacy crop pipeline on the same runner and fails when the median ratio exceeds OCR_RS_PERF_REGRESSION_LIMIT (default 1.15), avoiding unstable absolute latency limits.

CPU prebuilts and Apple Metal prebuilts are used automatically when compatible. Enabling a GPU feature that is not present in the prebuilt package automatically builds MNN from source:

cargo build --features build-mnn-from-source
cargo build --release --features cuda
cargo build --release --features vulkan

Install the SDK and development libraries required by the selected backend before building. GPU backends are selected through OcrEngineConfig:

use ocr_rs::{Backend, OcrEngineConfig};

let config = OcrEngineConfig::new().with_backend(Backend::Metal);
assert!(Backend::Metal.is_available());

Engine creation returns MnnError::BackendUnavailable when the requested backend was not registered instead of silently falling back to CPU.

OcrEngineConfig uses OpenCL buffer memory by default so very wide text regions do not exceed the GPU's 2D image-width limit. Advanced users can override this with with_gpu_memory_mode(GpuMemoryMode::Auto) or GpuMemoryMode::Image; the setting is ignored by non-OpenCL backends.

x86_64-pc-windows-gnu builds MNN from source and requires a MinGW C/C++ toolchain. By default, applications must distribute the matching MinGW runtime DLLs. Enable static-cpp-runtime to statically link libstdc++, libgcc, and winpthreads so the resulting binary does not depend on MinGW runtime DLLs:

cargo build --release --target x86_64-pc-windows-gnu --features static-cpp-runtime

NVIDIA's Windows CUDA toolchain requires MSVC; use x86_64-pc-windows-msvc for source-built CUDA, or provide a compatible MNN library with mnn-dynamic/mnn-static.

This feature controls the runtime linked by ocr-rs; a user-supplied DLL selected with mnn-dynamic may still have its own MinGW runtime dependencies.

License

Apache-2.0