| Crates.io | ocr-rs |
| lib.rs | ocr-rs |
| version | 2.0.2 |
| created_at | 2025-12-21 03:53:24.851577+00 |
| updated_at | 2025-12-23 12:11:51.247824+00 |
| description | A lightweight and efficient OCR library based on PaddleOCR models, using the MNN inference framework for high-performance text detection and recognition |
| homepage | |
| repository | https://github.com/zibo-chen/rust-paddle-ocr |
| max_upload_size | |
| id | 1997328 |
| size | 374,402 |
A lightweight and efficient OCR (Optical Character Recognition) Rust library based on PaddleOCR models. This library utilizes the MNN inference framework to provide high-performance text detection and recognition capabilities.
This project is a pure Rust library, focused on providing core OCR functionality. For command-line tools or HTTP services, please refer to:
This library supports three versions of PaddleOCR models:
ch_PP-OCRv4_det_infer.mnnch_PP-OCRv4_rec_infer.mnnppocr_keys_v4.txtPP-OCRv5_mobile_rec.mnn) supports Simplified Chinese, Traditional Chinese, English, Japanese, and Chinese Pinyin.PP-OCRv5_mobile_det.mnn).PP-OCRv5_mobile_det.mnn (Shared by all languages)PP-OCRv5_mobile_rec.mnn (Default, supports CN/EN/JP)ppocr_keys_v5.txt{lang}_PP-OCRv5_mobile_rec_infer.mnnppocr_keys_{lang}.txtarabic, cyrillic, devanagari, el, en, eslav, korean, latin, ta, te, th| Model Name | Supported Languages |
|---|---|
| korean_PP-OCRv5_mobile_rec | Korean, English |
| latin_PP-OCRv5_mobile_rec | French, German, Afrikaans, Italian, Spanish, Bosnian, Portuguese, Czech, Welsh, Danish, Estonian, Irish, Croatian, Uzbek, Hungarian, Serbian (Latin), Indonesian, Occitan, Icelandic, Lithuanian, Maori, Malay, Dutch, Norwegian, Polish, Slovak, Slovenian, Albanian, Swedish, Swahili, Tagalog, Turkish, Latin, Azerbaijani, Kurdish, Latvian, Maltese, Pali, Romanian, Vietnamese, Finnish, Basque, Galician, Luxembourgish, Romansh, Catalan, Quechua |
| eslav_PP-OCRv5_mobile_rec | Russian, Belarusian, Ukrainian, English |
| th_PP-OCRv5_mobile_rec | Thai, English |
| el_PP-OCRv5_mobile_rec | Greek, English |
| en_PP-OCRv5_mobile_rec | English |
| cyrillic_PP-OCRv5_mobile_rec | Russian, Belarusian, Ukrainian, Serbian (Cyrillic), Bulgarian, Mongolian, Abkhaz, Adyghe, Kabardian, Avar, Dargwa, Ingush, Chechen, Lak, Lezgian, Tabasaran, Kazakh, Kyrgyz, Tajik, Macedonian, Tatar, Chuvash, Bashkir, Mari, Moldovan, Udmurt, Komi, Ossetian, Buryat, Kalmyk, Tuvan, Yakut, Karakalpak, English |
| arabic_PP-OCRv5_mobile_rec | Arabic, Persian, Uyghur, Urdu, Pashto, Kurdish, Sindhi, Balochi, English |
| devanagari_PP-OCRv5_mobile_rec | Hindi, Marathi, Nepali, Bihari, Maithili, Angika, Bhojpuri, Magahi, Santali, Newari, Konkani, Sanskrit, Haryanvi, English |
| ta_PP-OCRv5_mobile_rec | Tamil, English |
| te_PP-OCRv5_mobile_rec | Telugu, English |
PP-OCRv5_mobile_det_fp16.mnnPP-OCRv5_mobile_rec_fp16.mnnppocr_keys_v5.txt| Feature | PP-OCRv4 | PP-OCRv5 | PP-OCRv5 FP16 |
|---|---|---|---|
| Language Support | Chinese, English | Multi-language (Default CN/EN/JP, 11+ specific models) | Multi-language (Default CN/EN/JP, 11+ specific models) |
| Text Types | Chinese, English | Simplified/Traditional CN, EN, JP, Pinyin | Simplified/Traditional CN, EN, JP, Pinyin |
| Handwriting | Basic | Significantly Enhanced | Significantly Enhanced |
| Vertical Text | Basic | Optimized | Optimized |
| Rare Characters | Limited | Enhanced | Enhanced |
| Speed (FPS) | 1.1 | 1.2 | 1.2 |
| Memory (Peak) | 422.22MB | 388.41MB | 388.41MB |
| Model Size | Standard | Standard | Halved |
| Recommended | Standard Docs | Complex Scenes & Multi-lang | High Performance & Multi-lang |
Choose the appropriate API level based on your requirements:
Use: End-to-End Recognition (OcrEngine)
Suitable for:
let engine = OcrEngine::new(det_path, rec_path, charset_path, None)?;
let results = engine.recognize(&image)?;
Use: Layered Calls (OcrEngine detect + recognize_batch)
Suitable for:
let engine = OcrEngine::new(det_path, rec_path, charset_path, None)?;
// 1. Detect
let mut boxes = engine.detect(&image)?;
// 2. Custom processing (e.g., filter small boxes)
boxes.retain(|b| b.rect.width() > 50);
// 3. Recognize
let detections = engine.det_model().detect_and_crop(&image)?;
let results = engine.recognize_batch(&images)?;
Use: DetOnlyEngine
Suitable for:
let det_engine = OcrEngine::det_only("models/det_model.mnn", None)?;
let text_boxes = det_engine.detect(&image)?;
// Use detection boxes for other processing...
Use: RecOnlyEngine
Suitable for:
let rec_engine = OcrEngine::rec_only(
"models/rec_model.mnn",
"models/ppocr_keys.txt",
None
)?;
let text = rec_engine.recognize_text(&text_line_image)?;
Use: Independent Models (DetModel + RecModel)
Suitable for:
let det_model = DetModel::from_file("models/det_model.mnn", None)?;
let rec_model = RecModel::from_file(
"models/rec_model.mnn",
"models/ppocr_keys.txt",
None
)?;
// Fully custom processing flow...
Use: Load from Bytes
Suitable for:
let det_bytes = include_bytes!("../models/det_model.mnn");
let rec_bytes = include_bytes!("../models/rec_model.mnn");
let charset_bytes = include_bytes!("../models/ppocr_keys.txt");
let engine = OcrEngine::from_bytes(det_bytes, rec_bytes, charset_bytes, None)?;
Add the following to your Cargo.toml:
[dependencies.rust-paddle-ocr]
git = "https://github.com/zibo-chen/rust-paddle-ocr.git"
You can also specify a specific branch or tag:
[dependencies.rust-paddle-ocr]
git = "https://github.com/zibo-chen/rust-paddle-ocr.git"
branch = "next"
This library requires:
This library provides a Layered Inference API, allowing you to choose the usage pattern that best fits your scenario:
┌─────────────────────────────────────────────────┐
│ OcrEngine (End-to-End Pipeline) │
│ Complete detection & recognition in one call │
├─────────────────────────────────────────────────┤
│ DetOnlyEngine │ RecOnlyEngine │ OcrEngine │
│ Detection Only │ Recognition Only │ Det + Rec │
├─────────────────────────────────────────────────┤
│ DetModel │ RecModel │
│ Text Det Model │ Text Rec Model │
├─────────────────────────────────────────────────┤
│ InferenceEngine (MNN) │
│ Low-level Inference Engine │
└─────────────────────────────────────────────────┘
Use OcrEngine to complete the full OCR process with a single call:
use ocr_rs::OcrEngine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create OCR engine (using default config)
let engine = OcrEngine::new(
"models/PP-OCRv5_mobile_det.mnn",
"models/PP-OCRv5_mobile_rec.mnn",
"models/ppocr_keys_v5.txt",
None,
)?;
// Load image
let image = image::open("test.jpg")?;
// Perform detection and recognition in one call
let results = engine.recognize(&image)?;
// Output results
for result in results {
println!("Text: {}", result.text);
println!("Confidence: {:.2}%", result.confidence * 100.0);
println!("Position: ({}, {})", result.bbox.rect.left(), result.bbox.rect.top());
}
Ok(())
}
Use OcrEngine but call detection and recognition separately. Useful for inserting custom processing:
use ocr_rs::OcrEngine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine = OcrEngine::new(det_path, rec_path, charset_path, None)?;
let image = image::open("test.jpg")?;
// 1. Detection only first
let text_boxes = engine.detect(&image)?;
println!("Detected {} text regions", text_boxes.len());
// Custom processing can be done here, e.g.:
// - Filter unwanted regions
// - Adjust box positions
// - Sort by position, etc.
// 2. Get detection model and manually crop
let det_model = engine.det_model();
let detections = det_model.detect_and_crop(&image)?;
// 3. Batch recognize cropped images
let cropped_images: Vec<_> = detections.iter()
.map(|(img, _)| img.clone())
.collect();
let rec_results = engine.recognize_batch(&cropped_images)?;
for (result, (_, bbox)) in rec_results.iter().zip(detections.iter()) {
println!("{}: {:.2}%", result.text, result.confidence * 100.0);
}
Ok(())
}
Create detection and recognition engines separately, or create a single-function engine:
use ocr_rs::{DetModel, RecModel, DetOptions, RecOptions};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Method A: Create detection and recognition models separately
let det_model = DetModel::from_file("models/det_model.mnn", None)?;
let rec_model = RecModel::from_file(
"models/rec_model.mnn",
"models/ppocr_keys.txt",
None
)?.with_options(RecOptions::new().with_min_score(0.5));
let image = image::open("test.jpg")?;
// Detect and crop
let detections = det_model.detect_and_crop(&image)?;
// Batch recognize
let images: Vec<_> = detections.iter().map(|(img, _)| img.clone()).collect();
let results = rec_model.recognize_batch(&images)?;
// Process results...
// Method B: Create detection-only engine
let det_only = OcrEngine::det_only("models/det_model.mnn", None)?;
let text_boxes = det_only.detect(&image)?;
// Method C: Create recognition-only engine
let rec_only = OcrEngine::rec_only(
"models/rec_model.mnn",
"models/ppocr_keys.txt",
None
)?;
let text = rec_only.recognize_text(&cropped_image)?;
Ok(())
}
use ocr_rs::{OcrEngine, OcrEngineConfig};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Use fast mode configuration
let config = OcrEngineConfig::fast();
let engine = OcrEngine::new(
"models/PP-OCRv5_mobile_det.mnn",
"models/PP-OCRv5_mobile_rec.mnn",
"models/ppocr_keys_v5.txt",
Some(config),
)?;
let image = image::open("test.jpg")?;
let results = engine.recognize(&image)?;
Ok(())
}
use ocr_rs::{OcrEngine, OcrEngineConfig, Backend};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Use GPU acceleration
let config = OcrEngineConfig::new()
.with_backend(Backend::Metal); // macOS: Metal
// .with_backend(Backend::OpenCL); // Cross-platform: OpenCL
// .with_backend(Backend::Vulkan); // Windows/Linux: Vulkan
let engine = OcrEngine::new(det_path, rec_path, charset_path, Some(config))?;
Ok(())
}
use ocr_rs::{OcrEngine, OcrEngineConfig, DetOptions, RecOptions};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Custom configuration
let config = OcrEngineConfig::new()
.with_threads(8)
.with_det_options(
DetOptions::new()
.with_max_side_len(1920) // Higher detection resolution
.with_box_threshold(0.6) // Stricter bounding box threshold
.with_merge_boxes(true) // Merge adjacent text boxes
)
.with_rec_options(
RecOptions::new()
.with_min_score(0.5) // Filter low confidence results
.with_batch_size(16) // Batch recognition size
);
let engine = OcrEngine::new(det_path, rec_path, charset_path, Some(config))?;
Ok(())
}
use ocr_rs::OcrEngine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Use Korean model
let engine = OcrEngine::new(
"models/PP-OCRv5_mobile_det.mnn",
"models/korean_PP-OCRv5_mobile_rec_infer.mnn",
"models/ppocr_keys_korean.txt",
None,
)?;
let image = image::open("korean_text.jpg")?;
let results = engine.recognize(&image)?;
for result in results {
println!("{}: {:.2}%", result.text, result.confidence * 100.0);
}
Ok(())
}
Suitable for embedded deployment or scenarios requiring model encryption:
use ocr_rs::OcrEngine;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read model bytes from file (or other sources)
let det_bytes = std::fs::read("models/det_model.mnn")?;
let rec_bytes = std::fs::read("models/rec_model.mnn")?;
let charset_bytes = std::fs::read("models/ppocr_keys.txt")?;
// Create engine from bytes
let engine = OcrEngine::from_bytes(
&det_bytes,
&rec_bytes,
&charset_bytes,
None,
)?;
let image = image::open("test.jpg")?;
let results = engine.recognize(&image)?;
Ok(())
}
use ocr_rs::ocr_file;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Perform OCR in one line of code
let results = ocr_file(
"test.jpg",
"models/det_model.mnn",
"models/rec_model.mnn",
"models/ppocr_keys.txt",
)?;
for result in results {
println!("{}", result.text);
}
Ok(())
}
For more complete examples, please refer to the examples directory.
// Real-time processing
let config = OcrEngineConfig::fast();
// macOS/iOS
let config = OcrEngineConfig::gpu(); // Uses Metal
// Other platforms
let config = OcrEngineConfig::new().with_backend(Backend::OpenCL);
// Batch recognizing multiple text lines is much faster than one by one
let results = rec_model.recognize_batch(&images)?;
Contributions are welcome! Please feel free to submit Issues or Pull Requests.
This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details.