PaddleOCR Android Demo 实践指南:基于 PaddleLite 的六种 OCR 运行模式与参数调优
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
本文围绕 PaddleOCR 仓库中的 Android 端 Demo 使用文档展开,系统讲解如何导入、运行和配置该 Demo,覆盖"检测 + 分类 + 识别"等六种运行模式的组合逻辑,并结合deploy/android_demo/下的 Java 与 C++ 源码剖析预处理、CTC 解码与方向分类的实现细节,帮助你既能在真机上快速跑通移动端 OCR 推理,也能理解每个可调参数(CPU 线程数、功耗模式、det long size、Score Threshold 等)在底层代码中的作用点。
1. Demo 定位与技术栈
该 Demo 是 PaddleOCR 的 Android 端示例工程,位于 deploy/android_demo 目录,目前支持**文本检测(Det)、文本方向分类(Cls)、文本识别(Rec)**三类模型的使用,预测库采用PaddleLite v2.10。
从工程结构看,Demo 由三层组成:
| 层次 | 关键文件 | 职责 |
|---|---|---|
| Android 界面层 | MainActivity.java、SettingsActivity.java | 主界面按钮、模式下拉列表、设置项读写 |
| Java 推理封装层 | Predictor.java、OCRPredictorNative.java | 模型加载、字典加载、结果后处理与画框绘制 |
| C++ 推理层(JNI) | ocr_ppredictor.h、ocr_ppredictor.cpp | 基于 PaddleLite C++ API 的检测/识别/分类流水线 |
C++ 层的核心类OCR_PPredictor内部持有三个独立预测器实例——_det_predictor、_rec_predictor、_cls_predictor(见 ocr_ppredictor.h#L126-L129),对应三个离线量化后的.nb模型文件:det_db.nb(DB 检测模型)、rec_crnn.nb(CRNN 识别模型)与cls.nb(方向分类模型)。
2. 环境准备与项目导入
2.1 环境准备
- 在本地安装 Android Studio 开发环境;
- 准备一部 Android 手机并开启 USB 调试模式:
手机设置 -> 查找开发者选项 -> 打开开发者选项和 USB 调试模式。
注意:如果 Android Studio 尚未配置 NDK,需按官方指南预先配置 NDK。可以选择最新的 NDK 版本,也可以使用与 Paddle Lite 预测库版本一致的 NDK。
2.2 导入项目
点击File -> New -> Import Project...,跟随 Android Studio 引导完成导入。
工程中的 C++ 部分通过 CMake 构建,其依赖路径在 CMakeLists.txt 中定义,有两个需要注意的外部目录约定:
- PaddleLite 预测库目录:
set(PaddleLite_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite")(CMakeLists.txt#L13-L14),即需要把 PaddleLite v2.10 解压到工程上级目录的同级位置,构建时会链接其中的libpaddle_light_api_shared.so; - OpenCV 目录:
set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../OpenCV/sdk/native/jni")(CMakeLists.txt#L16),使用 Android 版 OpenCV SDK 的 JNI 库。
此外,CMakeLists 还开启了面向移动端体积与性能的编译选项(-ffast-math -Ofast -Os、-fvisibility=hidden、--gc-sections等,见 CMakeLists.txt#L22-L29),并在构建后自动拷贝libc++_shared.so与 PaddleLite 推理库到输出目录。
3. 运行 Demo 与主界面解读
将手机连接电脑后,点击 Android Studio 工具栏中的运行按钮即可运行 Demo,手机上弹出"允许从 USB 安装软件权限"弹窗时点击允许。安装完成后,在手机主屏最后一页可以看到 PaddleOCR 应用,启动后进入主页:
主页包含四个按钮、一个下拉列表和一个菜单按钮,功能分别为:
- 运行模型:按照已选择的模式,运行对应的模型组合;
- 拍照识别:唤起手机相机拍照并获取图像,拍照完成后需要点击"运行模型"进行识别;
- 选取图片:唤起手机相册选择图像,选择完成后需要点击"运行模型"进行识别;
- 清空绘图:清空当前显示图像上绘制的文本框,以便进行下一次识别(每次识别使用的图像都是当前显示的图像);
- 下拉列表:选择运行模式,包含 6 种模式,默认模式为检测+分类+识别;
- 菜单按钮:进入菜单界面,进行模型和内置图像相关的设置。
点击"运行模型"后,程序按所选模式执行对应模型。在检测+分类+识别模式下,运行结果如下所示:
模型运行完成后,模型和运行状态显示区的STATUS字段显示当前模型的运行状态,显示run model succeeded表明模型运行成功。模型的运行结果显示在运行结果显示区,显示格式为:
序号:Det:(x1,y1)(x2,y2)(x3,y3)(x4,y4) Rec: 识别文本,识别置信度 Cls:分类类别,分类分时这一格式正是 Java 层drawResults方法拼接生成的:依次追加Det:四点坐标、Rec:识别文本与置信度、Cls:分类类别与分值(Predictor.java#L227-L249),随后用蓝色多边形(线宽 5、填充透明度 50)在原图上绘制文本框。
4. 六种运行模式详解
Demo 共提供 6 种运行模式,选项定义在 arrays.xml#L59-L66 的run_Model数组中:
- 检测+分类+识别(默认)
- 检测+识别
- 分类+识别
- 检测
- 识别
- 分类
各模式的差异本质上由三个布尔开关run_det、run_cls、run_rec控制,入口是 C++ 层的OCR_PPredictor::infer_ocr(ocr_ppredictor.cpp#L79-L106)。从源码结构看,其调度逻辑为:
if (run_det) { infer_det(origin, max_size_len, ocr_results); // 检测:输出文本框四点坐标 } if (run_rec) { if (ocr_results.size() == 0) { /* 追加一个整图结果 */ } for (int i = 0; i < ocr_results.size(); i++) { infer_rec(origin, run_cls, ocr_results[i]); // 识别:可内嵌分类旋转 } } else if (run_cls) { ClsPredictResult cls_res = infer_cls(origin); // 分类:对整图直接分类 ... }据此可以推断出各模式的实际执行路径:
- 检测+分类+识别:先检测得到多个文本框,逐框做方向分类(必要时旋转 180°),再逐框识别;
- 检测+识别:只检测 + 逐框识别,不做方向纠正;
- 分类+识别:
run_det=0时检测不产生框,代码会追加一个"空框"结果,识别阶段直接裁剪整张图像,并在识别前执行整图方向分类; - 检测:仅输出文本框坐标(四点多边形),无文本内容;
- 识别:跳过检测,直接对整图做 CRNN 识别(适合已切好的文字条);
- 分类:走
else if (run_cls)分支,直接对整图做 0°/180° 方向分类。
这种设计使得 Demo 既能演示完整 OCR 流水线,也能单独验证某个子模型在手机端的推理效果。
5. 设置项与参数默认值
点击主页菜单按钮进入设置界面:
设置项由 settings.xml 声明,默认值定义在 strings.xml。各项说明与源码佐证如下:
5.1 普通设置
| 设置项 | 默认值 | 说明 |
|---|---|---|
| Enable custom settings | 不选中 | 选中状态下才能更改其余设置项。SettingsActivity.java 中reloadPreferenceAndUpdateUI会根据该开关调用setEnabled控制其他控件可编辑性,且选择预置模型后会自动取消该开关 |
| Model Path | models/ch_PP-OCRv2 | 所运行的模型地址,使用默认值即可。若路径以/开头则视为存储卡绝对路径,否则视为 assets 内相对路径并拷贝到应用缓存目录(Predictor.java#L66-L100) |
| Label Path | labels/ppocr_keys_v1.txt | 识别模型的字典。加载时首尾各补一个占位项(Predictor.java#L114-L136),与同名字典文件 ppocr_keys_v1.txt 对应 |
| Image Path | images/det_0.jpg | 用于识别的内置图像名,候选列表见下文 |
内置图像共 8 张,定义在 arrays.xml#L3-L22,实际文件位于 app/src/main/assets/images 目录:det_0.jpg、det_90.jpg、det_180.jpg、det_270.jpg(不同方向的检测图)以及rec_0.jpg、rec_0_180.jpg、rec_1.jpg、rec_1_180.jpg(文字条识别图)。这套 0°/90°/180°/270° 的图像组合正是用来验证方向分类器在不同角度下的表现。
5.2 模型运行态设置(更改后返回主界面会自动重新加载模型)
- CPU Thread Num(默认 4):模型运行使用的 CPU 核心数量,可选 1/2/4/8 线程(arrays.xml#L23-L34)。该值传入 C++ 配置结构
OCR_Config.thread_num(ocr_ppredictor.h#L17-L22); - CPU Power Mode(默认
LITE_POWER_HIGH):大小核设定,可选值为LITE_POWER_HIGH(仅大核)、LITE_POWER_LOW(仅小核)、LITE_POWER_FULL(全部核心)、LITE_POWER_NO_BIND(交给系统调度)、LITE_POWER_RAND_HIGH、LITE_POWER_RAND_LOW(arrays.xml#L35-L50),对应 PaddleLite 的PowerMode枚举。
5.3 输入与输出设置
- det long size(默认 960):DB 模型预处理时图像的长边长度——超过此长度则 resize 到该值、短边等比例缩放,小于此长度不处理。这一行为在 ocr_ppredictor.cpp 的
DetResizeImg中实现:长边超限按max_size_len / max(w, h)计算缩放比,且宽高最终会向下对齐到 32 的整数倍(不足 32 的取 32),这是为满足 DB 网络下采样层对齐要求; - Score Threshold(默认 0.1):DB 模型后处理 box 的阈值,低于此阈值的 box 被过滤、不显示。该值经 Java 层
Predictor的scoreThreshold字段保存;需要注意的是,C++ 层calc_filtered_boxes中概率图二值化使用的阈值为硬编码的0.3(ocr_ppredictor.cpp#L296),从源码结构看,设置界面该选项的实际过滤行为以 C++ 后处理实现为准,调整时建议结合真机效果综合观察。
6. 源码级实现剖析:一条推理请求的完整链路
结合deploy/android_demo的源码,可以从三个维度理解 Demo 的底层实现:
6.1 模型加载
Predictor.init先加载模型、再加载字典(Predictor.java#L45-L52)。C++ 侧OCR_PPredictor::init_from_file为三个预测器分别指定网络类型——检测使用NET_OCR,识别与分类使用NET_OCR_INTERNAL(ocr_ppredictor.cpp#L33-L48)。Java 层在正式推理前还会执行一次warmup(默认 1 次,runModel中warmupIterNum用完即清零,Predictor.java#L144-L148),以消除首次推理的开销。
6.2 检测预处理与后处理
infer_det(ocr_ppredictor.cpp#L147-L181)的流程为:DetResizeImg缩放对齐 → 归一化(1/255缩放 + ImageNet 均值/方差{0.485, 0.456, 0.406} / {1/0.229, 1/0.224, 1/0.225},并使用neon_mean_scale做 NEON 加速)→ 推理 →calc_filtered_boxes后处理。后者将概率图二值化(阈值 0.3)后经boxes_from_bitmap提取连通域框,再按缩放比例还原到原图坐标并过滤(ocr_ppredictor.cpp#L292-L314)。
6.3 方向分类与 180° 旋转
infer_cls(ocr_ppredictor.cpp#L248-L290)对输入做0.5均值归一化后取 argmax。关键逻辑在分类结果处理处:当label % 2 == 1 && score > thresh(默认阈值 0.9)时,用cv::rotate将图像旋转 180° 后再送入识别。Java 层相应地把类别展示为"180"或"0"(Predictor.java#L222)。由于方向分类器只区分 0°/180° 两种类别,90°/270° 的图像需要依赖 det 框的多边形形态来恢复阅读顺序,这也是内置图像按四个角度设计的原因。
6.4 识别裁剪与 CTC 解码
infer_rec(ocr_ppredictor.cpp#L183-L246)按检测框多边形从原图裁出文本条(无框时直接使用整图),crnn_resize_img保持宽高比缩放后推理,随后在 C++ 侧完成CTC 贪心解码:对每个时间步取 argmax,跳过 blank(索引 0)与连续重复索引,累计最大概率并求平均作为整句置信度(score /= count)。解码出的字符索引序列经 JNI 回到 Java 层后,由postprocess方法查字典表还原为可读文本,越界索引以×标记(Predictor.java#L210-L225)。
整个链路上,推理计时(inferenceTime)与后处理计时(postprocessTime)均在 Java 层统计并通过 Logcat 输出[stat] Inference Time日志,方便开发者评估手机端推理耗时。
7. 更多支持
- 如需实时识别(相机流连续推理),可参考 Paddle-Lite-Demo 中的
ocr/android/app/cxx/ppocr_demo模块的写法更新预测库集成方式; - 更多 Paddle-Lite 相关问题(算子支持、量化、平台后端等)可前往 Paddle-Lite 官方项目获取开发支持;
- 当前仓库中另有一套基于 Kotlin 的新版 Android 端工程 deploy/ppocr-android,若需要面向 PaddleX 模型体系的新接口参考,可一并对照阅读。
适用前提说明:本文基于仓库docs/version2.x/legacy/目录下的 Android Demo 文档撰写,对应deploy/android_demo工程与 PaddleLite v2.10 预测库;文档中"近期更新"记录为 2022.02.27 将预测库升级到 PaddleLite v2.10 并支持 6 种运行模式。实际构建前请确认本机 NDK 版本、PaddleLite 离线库与 OpenCV Android SDK 三者可正常配合编译。
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考