# ppocr_demo
**Repository Path**: pengjun1206/ppocr_demo
## Basic Information
- **Project Name**: ppocr_demo
- **Description**: 百度飞桨ocr android文字识别
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2025-08-25
- **Last Updated**: 2025-08-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# OOCR 文字识别 Demo 使用指南
在 Android APP 上,实现实时的 OCR 文字识别功能。此 Demo 有很好的的易用性和开放性,如在 Demo 中跑自己训练好的模型等。
本文主要介绍 OCR 文字识别 Demo 的运行方法和如何在更新模型/输入/输出处理下,保证 Demo 仍可继续运行。
## 如何运行 OCR 文字识别 Demo
### 环境准备
1. 在本地环境安装好 Android Studio 工具,详细安装方法请见[Android Stuido 官网](https://developer.android.com/studio)。
2. 准备一部 Android 手机,并开启 USB 调试模式。开启方法: `手机设置 -> 查找开发者选项 -> 打开开发者选项和 USB 调试模式`
**注意**:如果您的 Android Studio 尚未配置 NDK ,请根据 Android Studio 用户指南中的[安装及配置 NDK 和 CMake ](https://developer.android.com/studio/projects/install-ndk)内容,预先配置好 NDK 。您可以选择最新的 NDK 版本,或者使用
Paddle Lite 预测库版本一样的 NDK
### 部署步骤
1. OCR 文字识别 Demo 位于 `Paddle-Lite-Demo/ocr/Android/app/c++/ppocr_demo` 目录
2. cd `Paddle-Lite-Demo/libs` 目录,运行 `download.sh` 脚本,下载所需要的 Paddle Lite 预测库
3. cd `Paddle-Lite-Demo/ocr/assets` 目录,运行 `download.sh` 脚本,下载OPT 优化后模型、测试图片和标签文件
4. cd `Paddle-Lite-Demo/ocr/Android/app/c++/ppocr_demo` 目录,运行 `prepare.sh` 脚本, 将模型、测试图片等资源文件拷贝到当前工程中
5. 用 Android Studio 打开 ppocr_demo 工程
6. 手机连接电脑,打开 USB 调试和文件传输模式,并在 Android Studio 上连接自己的手机设备(手机需要开启允许从 USB 安装软件权限)
> **注意:**
>> 如果您在导入项目、编译或者运行过程中遇到 NDK 配置错误的提示,请打开 ` File > Project Structure > SDK Location`,修改 `Andriod NDK location` 为您本机配置的 NDK 所在路径。
>> 如果您是通过 Andriod Studio 的 SDK Tools 下载的 NDK (见本章节"环境准备"),可以直接点击下拉框选择默认路径。
>> 还有一种 NDK 配置方法,你可以在 `image_classifiction_demo/local.properties` 文件中手动完成 NDK 路径配置,如下图所示
>> 如果以上步骤仍旧无法解决 NDK 配置错误,请尝试根据 Andriod Studio 官方文档中的[更新 Android Gradle 插件](https://developer.android.com/studio/releases/gradle-plugin?hl=zh-cn#updating-plugin)章节,尝试更新Android Gradle plugin版本。
7. 点击 Run 按钮,自动编译 APP 并安装到手机。(该过程会自动下载 Paddle Lite 预测库和模型,需要联网)
成功后效果如下:
| APP 图标 | APP 效果 |
| --- | --- |
|  |  |
```shell
cd Paddle-Lite-Demo/libs
# 下载所需要的 Paddle Lite 预测库
sh download.sh
cd ../ocr/assets
# 下载OPT 优化后模型、测试图片、标签文件及 config 文件
sh download.sh
cd ../android/app/c++/ppocr_demo
# 将预测库、模型等资源文件拷贝到当前工程中
sh prepare.sh
# 用 Android Studio 打开 ppocr_demo 工程, 完成APP编译和运行
```
## 如何更新预测库
* Paddle Lite 项目:https://github.com/PaddlePaddle/Paddle-Lite
* 参考 [Paddle Lite 源码编译文档](https://www.paddlepaddle.org.cn/lite/develop/source_compile/compile_env.html),编译 Android 预测库
* 编译最终产物位于 `build.lite.xxx.xxx.xxx` 下的 `inference_lite_lib.xxx.xxx`
* 替换 c++ 库
* 头文件
将生成的 `build.lite.android.xxx.gcc/inference_lite_lib.android.xxx/cxx/include` 文件夹替换 Demo 中的 `ppocr_demo/app/PaddleLite/cxx/include`
* armeabi-v7a
将生成的 `build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/cxx/libs/libpaddle_lite_api_shared.so` 库替换 Demo 中的 `ppocr_demo/app/PaddleLite/cxx/libs/armeabi-v7a/libpaddle_lite_api_shared.so`
* arm64-v8a
将生成的 `build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/cxx/libs/libpaddle_lite_api_shared.so` 库替换 Demo 中的 `ppocr_demo/app/PaddleLite/cxx/libs/arm64-v8a/libpaddle_lite_api_shared.so`
**注意:**
如果预测库有版本升级,建议同步更新 OPT 优化后的模型。例如,预测库升级至 2.10—rc 版本,需要做以下操作:
```shell
# 下载 PaddleOCR V2.0 版本的中英文 inference 模型
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/slim/ch_ppocr_mobile_v2.0_det_slim_infer.tar && tar xf ch_ppocr_mobile_v2.0_det_slim_infer.tar
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/slim/ch_ppocr_mobile_v2.0_rec_slim_infer.tar && tar xf ch_ppocr_mobile_v2.0_rec_slim_infer.tar
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/slim/ch_ppocr_mobile_v2.0_cls_slim_infer.tar && tar xf ch_ppocr_mobile_v2.0_cls_slim_infer.tar
# 获取 2.10 版本的 MAC 系统的 OPT 工具
wget https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.10-rc/opt_mac
# 转换 V2.0 检测模型
./opt --model_file=./ch_ppocr_mobile_v2.0_det_slim_infer/inference.pdmodel --param_file=./ch_ppocr_mobile_v2.0_det_slim_infer/inference.pdiparams --optimize_out=./ch_ppocr_mobile_v2.0_det_slim_opt --valid_targets=arm --optimize_out_type=naive_buffer
# 转换 V2.0 识别模型
./opt --model_file=./ch_ppocr_mobile_v2.0_rec_slim_infer/inference.pdmodel --param_file=./ch_ppocr_mobile_v2.0_rec_slim_infer/inference.pdiparams --optimize_out=./ch_ppocr_mobile_v2.0_rec_slim_opt --valid_targets=arm --optimize_out_type=naive_buffer
# 转换 V2.0 方向分类器模型
./opt --model_file=./ch_ppocr_mobile_v2.0_cls_slim_infer/inference.pdmodel --param_file=./ch_ppocr_mobile_v2.0_cls_slim_infer/inference.pdiparams --optimize_out=./ch_ppocr_mobile_v2.0_cls_slim_opt --valid_targets=arm --optimize_out_type=naive_buffer
```
## Demo 代码介绍
先整体介绍下OCR 文字识别 Demo 的代码结构,然后再从 Java 和 C++ 两部分简要的介绍 Demo 每部分功能.

1. `PaddleLite/` : 存放 PaddleLite 预测库
**备注:**
如需更新预测库,例如更新 Adnroid v8 动态库 `so`,则将新的动态库 `so` 更新到 `PaddleLite/cxx/libs/armv64-v8a` 目录
2. `OpenCV/` : 存放 OpenCV 预测库
3. `assets/` : 存放 OCR demo 的模型、测试图片、标签文件及 config 文件
**备注:**
- `./assets/ppocr_keys_v1.txt` 是中文字典文件,如果使用的模型是英文数字或其他语言的模型,则需要更换为对应语言的字典.
- 其他语言的字典文件,可从 PaddleOCR 仓库下载:https://github.com/PaddlePaddle/PaddleOCR/tree/release/2.3/ppocr/utils
4. `./src/main/cpp` : 存放 C++ 预测代码
- `cls_process.cc` : 方向分类器的推理全流程,包含预处理、预测和后处理三部分
- `rec_process.cc` : 识别模型 CRNN 的推理全流程,包含预处理、预测和后处理三部分
- `det_process.cc` : 检测模型 CRNN 的推理全流程,包含预处理、预测和后处理三部分
- `det_post_process` : 检测模型 DB 的后处理文件
- `pipeline.cc` : OCR 文字识别 Demo 推理全流程代码
- `Native.cc` : 用于 C++ 和 Java 间传递信息桥梁
- `CMakeLists.txt` : 预测代码的 MakeFile 文件
### Java 端
* common Java 包
在 `app/src/java/com/baidu/paddle/lite/demo/common` 目录下,实现摄像头和框架的公共处理,一般不用修改。其中,Utils.java 用于存放一些公用的且与 Java 基类无关的功能,例如模型拷贝、字符串类型转换等
* ppocr_demo Java 包
在 `app/src/java/com/baidu/paddle/lite/demo/ppocr_demo` 目录下,实现 APP 界面消息事件和 Java/C++ 端代码互传的桥梁功能
* MainActivity
实现 APP 的创建、运行、释放功能
重点关注 `checkRun` 函数,实现 APP 界面值向 C++ 端值互传
```
public void checkRun() {
try {
Utils.copyAssets(this, labelPath);
String labelRealDir = new File(
this.getExternalFilesDir(null),
labelPath).getAbsolutePath();
Utils.copyAssets(this, configPath);
String configRealDir = new File(
this.getExternalFilesDir(null),
configPath).getAbsolutePath();
Utils.copyAssets(this, detModelPath);
String detRealModelDir = new File(
this.getExternalFilesDir(null),
detModelPath).getAbsolutePath();
Utils.copyAssets(this, clsModelPath);
String clsRealModelDir = new File(
this.getExternalFilesDir(null),
clsModelPath).getAbsolutePath();
Utils.copyAssets(this, recModelPath);
String recRealModelDir = new File(
this.getExternalFilesDir(null),
recModelPath).getAbsolutePath();
predictor.init(
this,
detRealModelDir,
clsRealModelDir,
recRealModelDir,
configRealDir,
labelRealDir,
cpuThreadNum,
cpuPowerMode);
} catch (Throwable e) {
e.printStackTrace();
}
}
```java
* Native
实现 Java 与 C++ 端代码互传的桥梁功能
**备注:**
Java 的 native 方法和 C++ 的 native 方法要一一对应
### C++ 端
* Native
实现 Java 与 C++ 端代码互传的桥梁功能,将 Java 数值转换为 c++ 数值,调用 c++ 端的完成 OCR 文字识别功能
**注意:**
Native 文件生成方法:
```
cd app/src/java/com/baidu/paddle/lite/demo/ppocr_demo
# 在当前目录会生成包含 Native 方法的头文件,用户可以将其内容拷贝至 `cpp/Native.cc` 中
javac -classpath D:\dev\android-sdk\platforms\android-29\android.jar -encoding utf8 -h . Native.java
```
* Pipeline
实现输入预处理、推理执行和输出后处理的流水线处理,支持多个模型的串行处理
* Utils
实现其他辅助功能,如 `NHWC` 格式转 `NCHW` 格式、字符串处理等
* 其他 CC 文件
* `cls_process.cc` : 方向分类器的推理全流程,包含预处理、预测和后处理三部分
* `rec_process.cc` : 识别模型 CRNN 的推理全流程,包含预处理、预测和后处理三部分
* `det_process.cc` : 检测模型 CRNN 的推理全流程,包含预处理、预测和后处理三部分
* `det_post_process.cc` : 检测模型 DB 的后处理文件
## 代码讲解 (使用 Paddle Lite `C++ API` 执行预测)
该示例基于 C++ API 开发,调用 Paddle Lite `C++s API` 包括以下五步。
更详细的 `API` 描述参考:[Paddle Lite C++ API ](https://www.paddlepaddle.org.cn/lite/develop/api_reference/cxx_api_doc.html)。
```c++
#include
// 引入 C++ API
#include "include/paddle_api.h"
#include "include/paddle_use_ops.h"
#include "include/paddle_use_kernels.h"
// 1. 设置 MobileConfig
MobileConfig config;
config.set_model_from_file(modelPath); // 设置 NaiveBuffer 格式模型路径
config.set_power_mode(LITE_POWER_NO_BIND); // 设置 CPU 运行模式
config.set_threads(4); // 设置工作线程数
// 2. 创建 PaddlePredictor
std::shared_ptr predictor = CreatePaddlePredictor(config);
// 3. 设置输入数据
std::unique_ptr input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize({1, 3, 224, 224});
auto* data = input_tensor->mutable_data();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 如果输入是图片,则可在第三步时将预处理后的图像数据赋值给输入 Tensor
// 4. 执行预测
predictor->run();
// 5. 获取输出数据
std::unique_ptr output_tensor(std::move(predictor->GetOutput(0)));
std::cout << "Output shape " << output_tensor->shape()[1] << std::endl;
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
std::cout << "Output[" << i << "]: " << output_tensor->data()[i]
<< std::endl;
}
```
## 如何更新模型、输入/输出预处理
### 更新模型
1. 将优化后的新模型存放到目录 `./assets/` 下;
2. 如果模型名字跟工程中模型名字一模一样,则不需更新代码;否则话,需要修改 `./src/main/java/com/baidu/paddle/lite/demo/ppocr_demo/MainActivity.java` 中代码;
以将检测 det 模型更新为例,则先将优化后的模型存放到 `./assetss/ssd_mv3.nb` 下,然后更新 `MainActivity.java` 中代码
```shell
# 代码文件 `./src/main/java/com/baidu/paddle/lite/demo/ppocr_demo/MainActivity.java`
Utils.copyAssets(this, detModelPath);
String detRealModelDir = new File(
this.getExternalFilesDir(null),
detModelPath).getAbsolutePath();
# updata
Utils.copyAssets(this, "ssd_mv3.nb");
String detRealModelDir = new File(
this.getExternalFilesDir(null),
detModelPath).getAbsolutePath();
```
**注意:**
- 如果更新模型中的输入 Tensor、Shape、和 Dtype 发生更新:
- 更新文字方向分类器模型,则需要更新 `ppocr_demo/src/main/cpp/cls_process.cc` 中 `ClsPredictor::Preprocss` 函数
- 更新检测模型,则需要更新 `ppocr_demo/src/main/cpp/det_process.cc` 中 `DetPredictor::Preprocss` 函数
- 更新识别器模型,则需要更新 `ppocr_demo/src/main/cpp/rec_process.cc` 中 `RecPredictor::Preprocss` 函数
- 如果更新模型中的输出 Tensor 和 Dtype 发生更新:
- 更新文字方向分类器模型,则需要更新 `ppocr_demo/src/main/cpp/cls_process.cc` 中 `ClsPredictor::Postprocss` 函数
- 更新检测模型,则需要更新 `ppocr_demo/src/main/cpp/det_process.cc` 中 `DetPredictor::Postprocss` 函数
- 更新识别器模型,则需要更新 `ppocr_demo/src/main/cpp/rec_process.cc` 中 `RecPredictor::Postprocss` 函数
- 如果需要更新 `ppocr_keys_v1.txt` 标签文件,则需要将新的标签文件存放在目录 `./assets/` 下,并更新 `./src/main/java/com/baidu/paddle/lite/demo/ppocr_demo/MainActivity.java` 中代码;
```shell
# 代码文件 `./src/main/java/com/baidu/paddle/lite/demo/ppocr_demo/MainActivity.java`
Utils.copyAssets(this, labelPath);
String labelRealDir = new File(
this.getExternalFilesDir(null),
labelPath).getAbsolutePath();
# updata
Utils.copyAssets(this, "new_label.txt");
String labelRealDir = new File(
this.getExternalFilesDir(null),
labelPath).getAbsolutePath();
```
### 更新输入/输出预处理
1. 更新输入预处理
- 更新文字方向分类器模型,则需要更新 `ppocr_demo/src/main/cpp/cls_process.cc` 中 `ClsPredictor::Preprocss` 函数
- 更新检测模型,则需要更新 `ppocr_demo/src/main/cpp/det_process.cc` 中 `DetPredictor::Preprocss` 函数
- 更新识别器模型,则需要更新 `ppocr_demo/src/main/cpp/rec_process.cc` 中 `RecPredictor::Preprocss` 函数
2. 更新输出预处理
- 更新文字方向分类器模型,则需要更新 `ppocr_demo/src/main/cpp/cls_process.cc` 中 `ClsPredictor::Postprocss` 函数
- 更新检测模型,则需要更新 `ppocr_demo/src/main/cpp/det_process.cc` 中 `DetPredictor::Postprocss` 函数
- 更新识别器模型,则需要更新 `ppocr_demo/src/main/cpp/rec_process.cc` 中 `RecPredictor::Postprocss` 函数
## OCR 文字识别 Demo 工程详解
OCR 文字识别 Demo 由三个模型一起完成 OCR 文字识别功能,对输入图片先通过 `ch_ppocr_mobile_v2.0_det_slim_opt.nb` 模型做检测处理,然后通过 `ch_ppocr_mobile_v2.0_cls_slim_opt.nb` 模型做文字方向分类处理,最后通过 `ch_ppocr_mobile_v2.0_rec_slim_opt.nb` 模型完成文字识别处理。
1. `pipeline.cc` : OCR 文字识别 Demo 预测全流程代码
该文件完成了三个模型串行推理的全流程控制处理,包含整个处理过程的调度处理。
- `Pipeline::Pipeline(...)` 方法完成调用三个模型类构造函数,完成模型加载和线程数、绑核处理及 predictor 创建处理
- `Pipeline::Process(...)` 方法用于完成这三个模型串行推理的全流程控制处理
2. `cls_process.cc` 方向分类器的预测文件
该文件完成了方向分类器的预处理、预测和后处理过程
- `ClsPredictor::ClsPredictor()` 方法用于完成模型加载和线程数、绑核处理及 predictor 创建处理
- `ClsPredictor::Preprocess()` 方法用于模型的预处理
- `ClsPredictor::Postprocess()` 方法用于模型的后处理
3. `rec_process.cc` 识别模型 CRNN 的预测文件
该文件完成了识别模型 CRNN 的预处理、预测和后处理过程
- `RecPredictor::RecPredictor()` 方法用于完成模型加载和线程数、绑核处理及 predictor 创建处理
- `RecPredictor::Preprocess()` 方法用于模型的预处理
- `RecPredictor::Postprocess()` 方法用于模型的后处理
4. `det_process.cc` 检测模型 DB 的预测文件
该文件完成了检测模型 DB 的预处理、预测和后处理过程
- `DetPredictor::DetPredictor()` 方法用于完成模型加载和线程数、绑核处理及 predictor 创建处理
- `DetPredictor::Preprocess()` 方法用于模型的预处理
- `DetPredictor::Postprocess()` 方法用于模型的后处理
5. `db_post_process` 检测模型 DB 的后处理函数,包含 clipper 库的调用
该文件完成了检测模型 DB 的第三方库调用和其他后处理方法实现
- `std::vector>> BoxesFromBitmap(...)` 方法从 Bitmap 图中获取检测框
- `std::vector>> FilterTagDetRes(...)` 方法根据识别结果获取目标框位置
## 性能优化方法
如果你觉得当前性能不符合需求,想进一步提升模型性能,可参考[首页中性能优化文档](/README.md)完成性能优化。