PP-OCR的安卓端部署

EMO了几天

我浪费了几天的生命,去研究PP-OCR的模型微调、从训练模型导出预测模型,结果一个坑接着一个坑,没有善终。

找了好多资料,得到一些负面信息,比如说飞浆的团队修复问题不及时啦,代码仓库有好多年不更新啦,为了KPI而搞开源啦等等。

今天想着还是先把安卓端部署过程记录下来吧,忘掉那些不开心吧。

安卓端部署简介

如果直接用官方提供的模型去跑,其实还是挺香的,OCR是飞浆框架下最厉害的库了。

安卓部署官方也有demo的(当然是几年前的),PaddleOCR--deploy下去找,我也放到csdn下载了。

例子中使用的PP-OCRv2的模型,想想现在都PP-OCRv4了,是不是心中着急?

PaddleLite

想要在安卓端部署,需要飞浆下的另一个重要的库,PaddleLite。

Paddle Lite 是百度飞桨(PaddlePaddle)生态中的端侧推理引擎,专注于在移动设备、嵌入式设备和边缘计算场景中实现高效的深度学习模型部署。其核心目标是通过轻量化、高性能的优化技术,降低模型运行时的资源消耗,同时支持多种硬件平台和操作系统。

Demo中使用的是2.10版本,目录结构如下:

OpenCV

OpenCV的作用自然在负责图像处理部分。在安卓平台,它的用途还真是广泛的不得了。

Demo中使用的是4.2.0,目录结构如下:

PaddleOCR的模型和字典

模型文件和字典是程序运行后下载的,如下图:

App源码简介

代码结构依然是Java层+Native层。

Java代码

上层的业务逻辑,以及通过JNI调用Native层代码。结构如下:

Java层与Native层沟通仅仅用了三个方法,它们在OCRPredictorNative类中:

java 复制代码
    protected native long init(String detModelPath, String recModelPath, String clsModelPath, int useOpencl, int threadNum, String cpuMode);

    protected native float[] forward(long pointer, Bitmap originalImage,int max_size_len, int run_det, int run_cls, int run_rec);

    protected native void release(long pointer);

Native层代码

源码结构如下图:

JNI的实现在native.cpp中,重点看forward方法的实现,OCR的处理逻辑及返回结果都在这里了。

cpp 复制代码
extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_baidu_paddle_lite_demo_ocr_OCRPredictorNative_forward(
    JNIEnv *env, jobject thiz, jlong java_pointer, jobject original_image,
    jint j_max_size_len, jint j_run_det, jint j_run_cls, jint j_run_rec) {
  LOGI("begin to run native forward");
  if (java_pointer == 0) {
    LOGE("JAVA pointer is NULL");
    return cpp_array_to_jfloatarray(env, nullptr, 0);
  }

  cv::Mat origin = bitmap_to_cv_mat(env, original_image);
  if (origin.size == 0) {
    LOGE("origin bitmap cannot convert to CV Mat");
    return cpp_array_to_jfloatarray(env, nullptr, 0);
  }

  int max_size_len = j_max_size_len;
  int run_det = j_run_det;
  int run_cls = j_run_cls;
  int run_rec = j_run_rec;

  ppredictor::OCR_PPredictor *ppredictor =
      (ppredictor::OCR_PPredictor *)java_pointer;
  std::vector<int64_t> dims_arr;
  std::vector<ppredictor::OCRPredictResult> results =
      ppredictor->infer_ocr(origin, max_size_len, run_det, run_cls, run_rec);
  LOGI("infer_ocr finished with boxes %ld", results.size());

  // 这里将std::vector<ppredictor::OCRPredictResult> 序列化成
  // float数组,传输到java层再反序列化
  std::vector<float> float_arr;
  for (const ppredictor::OCRPredictResult &r : results) {
    float_arr.push_back(r.points.size());
    float_arr.push_back(r.word_index.size());
    float_arr.push_back(r.score);
    // add det point
    for (const std::vector<int> &point : r.points) {
      float_arr.push_back(point.at(0));
      float_arr.push_back(point.at(1));
    }
    // add rec word idx
    for (int index : r.word_index) {
      float_arr.push_back(index);
    }
    // add cls result
    float_arr.push_back(r.cls_label);
    float_arr.push_back(r.cls_score);
  }
  return cpp_array_to_jfloatarray(env, float_arr.data(), float_arr.size());
}

运行结果

相关推荐
2401_836235868 小时前
财务报表识别产品:从“数据搬运”到“智能决策”的技术革命
人工智能·科技·深度学习·ocr·生活
爱装代码的小瓶子9 小时前
【C++与Linux基础】进程间通讯方式:匿名管道
android·c++·后端
兴趣使然HX9 小时前
Android绘帧流程解析
android
JMchen12310 小时前
Android UDP编程:实现高效实时通信的全面指南
android·经验分享·网络协议·udp·kotlin
黄林晴11 小时前
Android 17 再曝猛料:通知栏和快捷设置终于分家了,这操作等了十年
android
有位神秘人11 小时前
Android获取设备中本地音频
android·音视频
JMchen12311 小时前
Android网络安全实战:从HTTPS到双向认证
android·经验分享·网络协议·安全·web安全·https·kotlin
CS创新实验室11 小时前
Pandas 3 的新功能
android·ide·pandas
ujainu11 小时前
护眼又美观:Flutter + OpenHarmony 鸿蒙记事本一键切换夜间模式(四)
android·flutter·harmonyos
三少爷的鞋12 小时前
为什么我不在 Android ViewModel 中直接处理异常?
android