ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

斑马设备上跑 ppocr-4:文字旋转识别 + opencv-mobile 中文显示,mnn/ncnn 双版本配置骨架

斑马设备上跑 ppocr-4:文字旋转识别 + opencv-mobile 中文显示,mnn/ncnn 双版本配置骨架 1. 斑马手持终端上跑 ppocr-4旋转文字识别与中文显示到底难在哪在斑马这类 Android 手持终端上做 OCR和服务器端完全是两码事。设备算力有限、内存紧张、屏幕小还要在工业场景里识别贴歪的标签、竖排的货架牌、旋转过的单据。ppocr-4 相比 v3 在检测和识别精度上有提升但直接把 Paddle 模型搬到端侧会遇到三个绕不开的问题模型格式要转成 mnn 或 ncnn 才能高效推理文字方向分类cls模块很多开源端侧框架默认没接识别出来的中文要用 opencv-mobile 画到图上而 opencv-mobile 默认不带 freetype 中文渲染。这篇就围绕这三个问题展开。目标很明确同一套 C 代码骨架通过配置切换 mnn 和 ncnn 两个推理后端跑通 ppocr-4 的检测、方向分类、识别三段流程并且用 opencv-mobile 把中文结果正确渲染出来。适合已经在做端侧 OCR、手里有斑马或类似 Android 工业手持设备的开发者。下面给的 config.toml 和 settings.json 骨架可以直接抄模型路径按你的实际目录改。先说清楚整体链路。ppocr-4 端侧推理分三步det 检测文本框cls 判断每个框是否旋转 180 度rec 识别文字内容。很多端侧 demo 只做了 detrec遇到倒置文字就识别成乱码cls 必须补上。推理后端 mnn 和 ncnn 各有优劣mnn 对 Android 更友好、算子覆盖全ncnn 体积小、Vulkan 加速成熟。用配置驱动切换代码里只保留一套前后处理逻辑后端差异封装在推理接口层。2. 前置准备模型转换、opencv-mobile 与 TaoToken 接入模型转换是第一步。ppocr-4 的 det、cls、rec 三个模型都要从 Paddle 格式转出来。det 和 cls 转 mnn/ncnn 比较直接rec 因为带 CRNN 结构转换时注意输入输出节点名称。转换脚本网上有现成的核心是保证输入 shape 和归一化参数一致。转完后你会得到类似 det.mnn、cls.mnn、rec.mnn 和对应的 .param/.binncnn。opencv-mobile 用会长维护的版本Android 端直接引 sdk/native/jni 即可。中文渲染这块opencv-mobile 默认的 putText 不支持中文需要自己接 freetype 或者用预渲染字库。我采用的是 freetype 方案把中文字体文件放进 assets初始化时加载渲染时按 UTF-8 逐字绘制。推理后端和模型管理这块如果你不想在每台设备上手动同步模型文件可以用 TaoToken 做统一的模型分发和 API 调用管理。它的控制台可以管理多个项目的密钥模型对话接口适合做识别结果的二次校验。接入文档在 https://taotoken.net/api API Keys 在 https://taotoken.net/console/api-keys 申请。对于需要长期跑编码和 Agent 任务的场景Coding Plan 更划算入口在 https://taotoken.net/coding-plan 。模型对话调试可以直接用 https://taotoken.net/model-chat 。注意TaoToken 在这里的角色是模型分发和 API 管理不是推理后端。端侧推理仍然在设备本地用 mnn/ncnn 完成TaoToken 负责的是模型版本同步和云端校验接口。3. 可复制配置config.toml 与 settings.json 双版本骨架配置分两层。config.toml 管推理后端和模型路径settings.json 管预处理参数和渲染选项。这样切换 mnn/ncnn 只改一个字段不用动代码。先看 config.toml[engine] # 可选值: mnn 或 ncnn backend mnn num_threads 4 use_vulkan false [model.det] path models/ppocr4/det.mnn input_shape [1, 3, 640, 640] mean [0.485, 0.456, 0.406] std [0.229, 0.224, 0.225] thresh 0.3 box_thresh 0.6 unclip_ratio 1.5 [model.cls] path models/ppocr4/cls.mnn input_shape [1, 3, 192, 48] mean [0.5, 0.5, 0.5] std [0.5, 0.5, 0.5] cls_thresh 0.9 [model.rec] path models/ppocr4/rec.mnn input_shape [1, 3, 48, 320] mean [0.5, 0.5, 0.5] std [0.5, 0.5, 0.5] char_dict models/ppocr4/ppocr_keys_v1.txt [render] font_path fonts/simhei.ttf font_size 24 text_color [0, 255, 0] box_color [255, 0, 0]ncnn 版本只需要改 backend 和模型后缀[engine] backend ncnn num_threads 4 use_vulkan true [model.det] path models/ppocr4/det.param weights models/ppocr4/det.bin # 其余参数与 mnn 版本一致再看 settings.json管的是运行时行为和自检开关{ preprocess: { det_limit_side_len: 640, rec_batch_num: 6, use_rotate_detection: true }, cls: { enable: true, rotate_threshold: 0.9, angles: [0, 180] }, render: { draw_box: true, draw_text: true, chinese_support: true, font_cache_size: 128 }, debug: { save_intermediate: false, log_level: info, verify_rotation: true } }关键字段说明use_rotate_detection控制是否启用 cls 分支angles定义分类角度ppocr-4 的 cls 模型输出 0 和 180 两类。verify_rotation打开后会在日志里打印每个框的旋转判定结果方便自检。chinese_support打开后渲染走 freetype 路径关闭则退回 opencv-mobile 原生 putText。C 侧读取配置的代码骨架#include toml.hpp #include nlohmann/json.hpp struct EngineConfig { std::string backend; int num_threads; bool use_vulkan; }; EngineConfig loadConfig(const std::string toml_path) { auto data toml::parse(toml_path); EngineConfig cfg; cfg.backend toml::findstd::string(data, engine, backend); cfg.num_threads toml::findint(data, engine, num_threads); cfg.use_vulkan toml::findbool(data, engine, use_vulkan); return cfg; }推理接口层用工厂模式根据 backend 字段返回 mnn 或 ncnn 的实现class InferenceEngine { public: virtual ~InferenceEngine() default; virtual bool loadModel(const std::string path) 0; virtual std::vectorfloat forward(const std::vectorfloat input, const std::vectorint shape) 0; }; std::unique_ptrInferenceEngine createEngine(const EngineConfig cfg) { if (cfg.backend mnn) { return std::make_uniqueMnnEngine(cfg.num_threads); } else if (cfg.backend ncnn) { return std::make_uniqueNcnnEngine(cfg.num_threads, cfg.use_vulkan); } return nullptr; }这样 det、cls、rec 三个模型都通过同一个接口加载和推理前后处理代码完全复用。4. 验证请求与成功结果旋转角度自检 中文渲染自检配置写好后先做两个自检动作确认 cls 和中文渲染都正常工作。旋转角度自检准备一张包含正置和倒置文字的测试图跑一遍完整流程打开verify_rotation后日志会输出每个检测框的 cls 得分和判定角度。预期结果是倒置文字的框被判定为 180 度并自动旋转回来识别结果正确。如果 cls 得分低于阈值检查 cls 模型的输入归一化参数是否和训练时一致。void verifyRotation(const std::vectorTextBox boxes, const std::vectorfloat cls_scores) { for (size_t i 0; i boxes.size(); i) { float score cls_scores[i]; int angle score 0.9f ? 180 : 0; LOGI(Box %zu: cls_score%.4f, angle%d, i, score, angle); if (angle 180) { rotateBox180(boxes[i]); } } }中文渲染自检加载字体后在空白图上绘制一段中文保存成 png用设备或电脑打开确认没有乱码和方框。freetype 渲染的核心是按 UTF-8 解码后逐字获取 glyphvoid drawChineseText(cv::Mat img, const std::string text, cv::Point org, cv::Scalar color, int font_size) { FT_Library ft; FT_Init_FreeType(ft); FT_Face face; FT_New_Face(ft, fonts/simhei.ttf, 0, face); FT_Set_Pixel_Sizes(face, 0, font_size); int x org.x; for (size_t i 0; i text.size();) { unsigned long code decodeUTF8(text, i); FT_Load_Char(face, code, FT_LOAD_RENDER); FT_GlyphSlot g face-glyph; for (int row 0; row g-bitmap.rows; row) { for (int col 0; col g-bitmap.width; col) { int px x g-bitmap_left col; int py org.y - g-bitmap_top row; if (px 0 px img.cols py 0 py img.rows) { img.atcv::Vec3b(py, px) cv::Vec3b(color[0], color[1], color[2]); } } } x g-advance.x 6; } FT_Done_Face(face); FT_Done_FreeType(ft); }成功结果应该是测试图上所有文字框被正确绘制倒置文字识别结果正确中文显示无乱码。如果 mnn 和 ncnn 两个后端跑出来的识别结果一致说明配置切换逻辑没问题。5. 本篇常见错排查cls 模型加载失败或输出维度不对。ppocr-4 的 cls 模型输出是 2 类有些转换脚本会输出成单类。检查转换时的输出节点确保 shape 是 [1, 2]。mnn 版本用 Netron 打开看输出层ncnn 版本看 .param 最后一层。中文渲染出现方框或乱码。三个原因字体文件没打包进 assets、UTF-8 解码函数写错、freetype 没链接。先确认字体路径在设备上可读再检查 decodeUTF8 对多字节字符的处理。opencv-mobile 本身不带 freetype需要在 CMakeLists 里显式链接。mnn 和 ncnn 结果不一致。常见于 rec 模型的输入宽度不同。mnn 版本可能默认 320ncnn 版本 640导致识别结果有差异。统一 input_shape 后重新转换。另外 ncnn 开 Vulkan 后浮点精度略有差异对识别结果影响很小但如果 cls 得分卡在阈值附近建议关掉 Vulkan 对比。旋转检测误判。cls 阈值设太低会把正置文字判成 180 度。默认 0.9 比较稳如果场景里文字方向单一可以调到 0.95。另外 det 检测框如果本身是斜的cls 只处理 180 度旋转斜框需要额外的角度回归ppocr-4 的 det 不直接输出角度这块要自己加。CMake 链接顺序问题。ncnn 和 opencv-mobile 同时链接时注意 ncnn 放在 opencv 前面否则会出现符号冲突。Android 端还要加-static-openmp否则 OpenMP 运行时可能找不到。6. 接入与后续模型分发和编码任务的分流建议端侧推理跑通后模型版本管理和结果校验可以接到 TaoToken 上。API Keys 在 https://taotoken.net/console/api-keys 申请接入文档在 https://taotoken.net/api 。识别结果需要二次校验时用模型对话接口 https://taotoken.net/model-chat 做语义纠错。如果你后续要做长期的编码和 Agent 任务Coding Plan 的入口在 https://taotoken.net/coding-plan 控制台在 https://taotoken.net/console 。整套代码骨架的核心思路就是配置驱动加接口抽象mnn 和 ncnn 的差异被隔离在引擎实现层det/cls/rec 的前后处理完全共享。cls 补上后旋转文字识别就完整了opencv-mobile 接 freetype 解决中文显示。两个自检动作建议每次换模型或换设备都跑一遍能省掉大量排查时间。
返回列表