Lightweight character recognition engine based on PP-OCRv5_mobile from PaddleOCR
Project description
CharacterOCR — Lightweight Character Recognition Engine
CharacterOCR — 轻量字符识别引擎
English
A lightweight OCR character recognition library based on PP-OCRv5_mobile from PaddleOCR. Single-file wrapper, ready to use out of the box. Pre-trained models (~15MB) are bundled in the models/ directory — no extra downloads needed.
Supported scenarios: Chinese/English printed & handwritten text recognition, digit recognition, vertical/rotated/curved text.
Requirements
- Python 3.8+
- Dependencies:
paddleocr,opencv-python,numpy
# CPU version
pip install paddlepaddle==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
pip install paddleocr opencv-python numpy
# GPU version (CUDA 11.8)
pip install paddlepaddle-gpu==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/
pip install paddleocr opencv-python numpy
Project Structure
ocr_module_realese/
├── character_ocr/
│ ├── __init__.py # Package entry point
│ ├── ocr_engine.py # Core engine (the only source file)
│ └── models/
│ ├── PP-OCRv5_mobile_det/ # Text detection model
│ └── PP-OCRv5_mobile_rec/ # Text recognition model
├── pyproject.toml # Package build config
├── LICENSE-2.0.txt # Apache License 2.0
└── README.md
Quick Start
1. Recognize a Single Image
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR() # Auto-loads models on first use
img = cv2.imread("test.jpg")
results, drawn = ocr.recognize(img) # Returns (results, annotated_image)
for r in results:
print(f"Text: {r.text} Score: {r.score:.2f} BBox: {r.bbox}")
cv2.imwrite("test_result.jpg", drawn)
2. Batch Processing Multiple Images
import cv2, os
from character_ocr import CharacterOCR
ocr = CharacterOCR()
image_dir = "./images"
for filename in os.listdir(image_dir):
if not filename.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")):
continue
img = cv2.imread(os.path.join(image_dir, filename))
if img is None:
continue
results, drawn = ocr.recognize(img)
print(f"{filename}: {len(results)} text regions found")
for r in results:
print(f" [{r.score:.2f}] {r.text!r}")
3. Recognize Only (Skip Drawing — Faster for Batch)
results = ocr.recognize_only(img) # Returns list[OCRResult] only
for r in results:
print(f"[{r.score:.2f}] {r.text!r}")
4. Real-Time Camera Recognition
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
cap = cv2.VideoCapture(0) # 0 = default camera
print("Press Q to quit...")
while True:
ret, frame = cap.read()
if not ret:
break
results, drawn = ocr.recognize(frame)
cv2.putText(drawn, f"Detected: {len(results)}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)
cv2.imshow("OCR Camera", drawn)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
5. Camera with Frame Skipping (Lower CPU Usage)
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
cap = cv2.VideoCapture(0)
frame_count = 0
skip_interval = 10 # Run OCR every 10 frames
last_results = []
while True:
ret, frame = cap.read()
if not ret:
break
if frame_count % skip_interval == 0:
last_results, drawn = ocr.recognize(frame)
else:
drawn = ocr.draw(frame, last_results) # Reuse last results
cv2.imshow("OCR Camera", drawn)
frame_count += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
6. Global Singleton (Convenience API)
import cv2
from character_ocr import get_ocr, recognize
# Method A: get the global singleton
ocr = get_ocr()
results, drawn = ocr.recognize(cv2.imread("test.jpg"))
# Method B: one-liner
results, drawn = recognize(cv2.imread("test.jpg"))
7. Recognize Image from URL
import cv2, numpy as np, requests
from character_ocr import CharacterOCR
ocr = CharacterOCR()
url = "https://example.com/sample.jpg"
resp = requests.get(url)
img_array = np.frombuffer(resp.content, dtype=np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
results, drawn = ocr.recognize(img)
8. ROI-based Recognition (Crop then Recognize)
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
img = cv2.imread("receipt.jpg")
roi = img[100:300, 50:500] # Crop region (y:y+h, x:x+w)
results, drawn = ocr.recognize(roi)
for r in results:
print(f"ROI: {r.text} ({r.score:.2f})")
API Reference
OCRResult dataclass
| Attribute | Type | Description |
|---|---|---|
text |
str |
Recognized text content |
score |
float |
Confidence score (0.0 ~ 1.0) |
box |
list[list[int]] |
Four corner points [[x0,y0],[x1,y1],[x2,y2],[x3,y3]], order: top-left→top-right→bottom-right→bottom-left |
center |
tuple[float, float] |
Quadrilateral center (cx, cy), computed from box |
bbox |
tuple[int, int, int, int] |
Axis-aligned bounding box (x, y, width, height), computed from box |
to_dict() |
dict |
Convert result to dictionary |
CharacterOCR class
ocr = CharacterOCR(
score_threshold=0.3, # Drop results below this confidence
det_limit_side_len=960, # Max side length for detection
rec_batch_size=1, # Recognition batch size
)
| Method | Signature | Description |
|---|---|---|
load() |
-> None |
Explicitly load models (auto-called on first recognize) |
recognize |
(img_bgr: np.ndarray) -> tuple[list[OCRResult], np.ndarray] |
Detect + recognize, returns results & annotated image |
recognize_only |
(img_bgr: np.ndarray) -> list[OCRResult] |
Detect + recognize, returns results only (no drawing) |
draw |
(img_bgr: np.ndarray, results: list[OCRResult]) -> np.ndarray |
Manually draw detection boxes & labels on image copy |
| Property | Description |
|---|---|
loaded |
bool — whether models are loaded |
Module-level Functions
| Function | Description |
|---|---|
get_ocr(**kwargs) |
Get global singleton CharacterOCR instance |
recognize(img_bgr) |
Recognize using global singleton, equivalent to get_ocr().recognize(img_bgr) |
Command Line
python character_ocr/ocr_engine.py test.jpg
# Prints recognition results and saves annotated image as test_result.jpg
Model Info
Bundled PP-OCRv5_mobile models (~15MB total):
| Model | Directory | Purpose |
|---|---|---|
PP-OCRv5_mobile_det |
models/PP-OCRv5_mobile_det/ |
Text detection (locating text regions) |
PP-OCRv5_mobile_rec |
models/PP-OCRv5_mobile_rec/ |
Text recognition (reading text content) |
If models/ is missing or the path contains non-ASCII characters, the engine will auto-download from HuggingFace.
Notes
- Input must be OpenCV BGR color image (
np.ndarray, shape(H, W, 3), dtypeuint8) - For grayscale images, convert first:
cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - Non-ASCII characters in the model path may cause fallback to auto-download
- Adjust
score_threshold(default 0.3): increase for higher precision (e.g. 0.5), decrease for higher recall (e.g. 0.1)
中文
基于 PaddleOCR PP-OCRv5_mobile 的轻量级 OCR 字符识别库,单文件封装,开箱即用。模型文件(约 15MB)已随库附带在 models/ 目录中,无需额外下载。
支持场景: 中英文印刷体/手写体识别、数字识别、竖排文字、旋转文字、弯曲文字等。
环境要求
- Python 3.8+
- 依赖包:
paddleocr,opencv-python,numpy
# CPU 版
pip install paddlepaddle==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
pip install paddleocr opencv-python numpy
# GPU 版 (CUDA 11.8)
pip install paddlepaddle-gpu==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/
pip install paddleocr opencv-python numpy
目录结构
ocr_module_realese/
├── character_ocr/
│ ├── __init__.py # 包入口
│ ├── ocr_engine.py # 核心引擎(唯一源码文件)
│ └── models/
│ ├── PP-OCRv5_mobile_det/ # 文字检测模型
│ └── PP-OCRv5_mobile_rec/ # 文字识别模型
├── pyproject.toml # 包构建配置
├── LICENSE-2.0.txt # Apache 2.0 许可证
└── README.md
快速上手
1. 读取单张图片并识别
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR() # 首次调用自动加载模型
img = cv2.imread("test.jpg")
results, drawn = ocr.recognize(img) # 返回 (结果列表, 标注图像)
for r in results:
print(f"文字: {r.text} 置信度: {r.score:.2f} 位置: {r.bbox}")
cv2.imwrite("test_result.jpg", drawn)
2. 批量处理多张图片
import cv2, os
from character_ocr import CharacterOCR
ocr = CharacterOCR()
image_dir = "./images"
for filename in os.listdir(image_dir):
if not filename.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")):
continue
img = cv2.imread(os.path.join(image_dir, filename))
if img is None:
continue
results, drawn = ocr.recognize(img)
print(f"{filename}: 检测到 {len(results)} 个文字区域")
for r in results:
print(f" [{r.score:.2f}] {r.text!r}")
3. 只识别不绘图(提升批量处理性能)
results = ocr.recognize_only(img) # 只返回 list[OCRResult]
for r in results:
print(f"[{r.score:.2f}] {r.text!r}")
4. 摄像头实时识别
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
cap = cv2.VideoCapture(0) # 0 = 默认摄像头
print("按 Q 键退出...")
while True:
ret, frame = cap.read()
if not ret:
break
results, drawn = ocr.recognize(frame)
cv2.putText(drawn, f"Detected: {len(results)}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)
cv2.imshow("OCR Camera", drawn)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
5. 摄像头——间隔帧识别(降低 CPU 占用)
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
cap = cv2.VideoCapture(0)
frame_count = 0
skip_interval = 10 # 每 10 帧识别一次
last_results = []
while True:
ret, frame = cap.read()
if not ret:
break
if frame_count % skip_interval == 0:
last_results, drawn = ocr.recognize(frame)
else:
drawn = ocr.draw(frame, last_results) # 复用上一次结果
cv2.imshow("OCR Camera", drawn)
frame_count += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
6. 使用全局单例(快捷调用)
import cv2
from character_ocr import get_ocr, recognize
# 方式 A:获取全局单例
ocr = get_ocr()
results, drawn = ocr.recognize(cv2.imread("test.jpg"))
# 方式 B:一行调用
results, drawn = recognize(cv2.imread("test.jpg"))
7. 读取 URL 图片
import cv2, numpy as np, requests
from character_ocr import CharacterOCR
ocr = CharacterOCR()
url = "https://example.com/sample.jpg"
resp = requests.get(url)
img_array = np.frombuffer(resp.content, dtype=np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
results, drawn = ocr.recognize(img)
8. 识别特定区域(ROI 裁剪后识别)
import cv2
from character_ocr import CharacterOCR
ocr = CharacterOCR()
img = cv2.imread("receipt.jpg")
roi = img[100:300, 50:500] # 裁剪区域 (y:y+h, x:x+w)
results, drawn = ocr.recognize(roi)
for r in results:
print(f"ROI 区域: {r.text} ({r.score:.2f})")
API 参考
OCRResult 数据类
| 属性 | 类型 | 说明 |
|---|---|---|
text |
str |
识别出的文字内容 |
score |
float |
置信度 (0.0 ~ 1.0) |
box |
list[list[int]] |
四点坐标 [[x0,y0],[x1,y1],[x2,y2],[x3,y3]],顺序:左上→右上→右下→左下 |
center |
tuple[float, float] |
四边形中心点 (cx, cy),由 box 自动计算 |
bbox |
tuple[int, int, int, int] |
轴对齐包围盒 (x, y, width, height),由 box 自动计算 |
to_dict() |
dict |
将结果转为字典 |
CharacterOCR 类
ocr = CharacterOCR(
score_threshold=0.3, # 低于此置信度的结果将被丢弃
det_limit_side_len=960, # 检测阶段长边尺寸上限
rec_batch_size=1, # 识别批大小
)
| 方法 | 签名 | 说明 |
|---|---|---|
load() |
-> None |
显式加载模型(recognize 首次调用时也会自动加载) |
recognize |
(img_bgr: np.ndarray) -> tuple[list[OCRResult], np.ndarray] |
检测+识别,返回结果列表和标注图像 |
recognize_only |
(img_bgr: np.ndarray) -> list[OCRResult] |
检测+识别,只返回结果列表(不绘图) |
draw |
(img_bgr: np.ndarray, results: list[OCRResult]) -> np.ndarray |
手动在图片副本上绘制检测框和标签 |
| 属性 | 说明 |
|---|---|
loaded |
bool,模型是否已加载 |
模块级函数
| 函数 | 说明 |
|---|---|
get_ocr(**kwargs) |
获取全局单例 CharacterOCR 实例 |
recognize(img_bgr) |
使用全局单例识别一张图,等同于 get_ocr().recognize(img_bgr) |
命令行
python character_ocr/ocr_engine.py test.jpg
# 输出识别结果,并将标注图保存为 test_result.jpg
模型说明
库附带 PP-OCRv5_mobile 模型(移动端轻量版),模型总大小约 15MB:
| 模型 | 目录 | 说明 |
|---|---|---|
PP-OCRv5_mobile_det |
models/PP-OCRv5_mobile_det/ |
文字检测(定位文字区域) |
PP-OCRv5_mobile_rec |
models/PP-OCRv5_mobile_rec/ |
文字识别(识别文字内容) |
若 models/ 目录不存在或路径包含非 ASCII 字符,引擎会自动从 HuggingFace 下载模型。
注意事项
- 输入图片格式必须为 OpenCV BGR 彩色图(
np.ndarray,shape 为(H, W, 3),dtype 为uint8) - 灰度图请先用
cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)转为三通道 - 模型路径中包含中文可能导致自动回退到网络下载
score_threshold默认 0.3,可根据场景调整:对精度要求高则调高(如 0.5),对召回要求高则调低(如 0.1)
Acknowledgments / 致谢
This project is built upon the excellent work of the PaddleOCR team. The bundled models PP-OCRv5_mobile_det and PP-OCRv5_mobile_rec are part of the PaddleOCR project, licensed under Apache 2.0.
本项目基于 PaddleOCR 团队的杰出成果构建。附带的 PP-OCRv5_mobile_det 和 PP-OCRv5_mobile_rec 模型来自 PaddleOCR 项目,基于 Apache 2.0 协议。
- PaddleOCR GitHub: https://github.com/PaddlePaddle/PaddleOCR
- PaddleOCR Documentation: https://paddlepaddle.github.io/PaddleOCR/latest/en/index.html
License / 许可证
Copyright 2026 pronoobe
Licensed under the Apache License, Version 2.0. See LICENSE-2.0.txt for the full license text.
基于 Apache License 2.0 发布。详见 LICENSE-2.0.txt。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file character_ocr-0.1.0-py3-none-any.whl.
File metadata
- Download URL: character_ocr-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8d26846a0f0ad21487bd891d5411ff89d817562ea31bfff1076d5eef304b71c
|
|
| MD5 |
02d87d551fa445476fa739fe5ab9e5c7
|
|
| BLAKE2b-256 |
5ede6b2967c142c32b343f47d5129d5c30ccd25ad9da02203a918c93c199e9c5
|