• 基于PaddleOCR开发easy click文字识别插件


    目录

    目的

    准备工作

    插件开发

    1、项目结构对比

     2、插件SDK集成

    3、删除无用的Activity文件

     4、修改AndroidManifest.xml

    5、修改Predictor文件

    6、修改cpp包名 

    7、新建OCRApi接口类

    8、打包插件

    9、在easy click应用中编写js代码

    总结


    目的

            easy click是 Android 平台上的一款自动化工具,它通过编写 JavaScript 脚本,结合系统的「 无障碍服务 」对 App 进行自动化操作。在文字识别方面它提供的有一款OCR识别插件,但是其中有识别速度慢,插件大的缺点,所以这里将讲解一下如何集成基于PaddleOCR文字识别开发的插件,阅读本篇文字需要对PaddleOCR有个基本的了解,还需要有一点Android开发基础,文章最后有相关插件下载地址。

    准备工作

    1、android studio最新版本即可

    下载地址:Download Android Studio & App Tools - Android Developers

    2、下载PaddleOCR提供的安卓版文字识别demo

    下载地址:PaddleOCR/deploy/android_demo at release/2.6 · PaddlePaddle/PaddleOCR (github.com)

    3、导入Android studio并成功运行

    以上三步工作完成后,将开始我们的Easy click文字识别插件开发。

    插件开发

    1、项目结构对比

    修改前 VS 修改后,调整了一些文件,去除了Activity入口。

     2、插件SDK集成

    在项目的build.gradle文件中添加:

    1. allprojects {
    2. repositories {
    3. // ...
    4. maven { url 'https://jitpack.io' }
    5. }
    6. }

    在app的build.gradle文件中添加:

    1. dependencies {
    2. // ...
    3. implementation 'com.alibaba:fastjson:1.1.46.android'
    4. }

    3、删除无用的Activity文件

     4、修改AndroidManifest.xml

    两处包名替换成自己的包名,其他地方如下代码不动。

    1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. package="com.tomato.ocr">
    4. <application
    5. android:allowBackup="true"
    6. android:icon="@mipmap/ic_launcher"
    7. android:label="@string/app_name"
    8. android:roundIcon="@mipmap/ic_launcher_round"
    9. android:supportsRtl="true"
    10. android:debuggable="true"
    11. android:theme="@style/AppTheme"
    12. tools:ignore="HardcodedDebugMode">
    13. </application>
    14. </manifest>

    5、修改Predictor文件

    添加这两行文件:

     调整loadLabel代码如下:

    6、修改cpp包名 

    修改native.cpp文件,将官方的_com_baidu_paddle_lite_demo_ocr_替换成我们自己的包名,如_com_tomato_ocr_,如下截图:

    7、新建OCRApi接口类

    1. package com.tomato.ocr.ec;
    2. import android.content.Context;
    3. import android.graphics.Bitmap;
    4. import android.graphics.BitmapFactory;
    5. import android.graphics.Point;
    6. import android.media.ExifInterface;
    7. import android.util.Log;
    8. import com.alibaba.fastjson.JSONArray;
    9. import com.alibaba.fastjson.JSONObject;
    10. import com.tomato.ocr.OCRResultModel;
    11. import com.tomato.ocr.Predictor;
    12. import com.tomato.ocr.Utils;
    13. import java.io.File;
    14. import java.io.FileOutputStream;
    15. import java.io.IOException;
    16. import java.io.InputStream;
    17. import java.util.List;
    18. public class OCRApi {
    19. private final int useOpencl = 0;
    20. private final int cpuThreadNum = 1;
    21. private final String cpuPowerMode = "LITE_POWER_HIGH";
    22. private final int detLongSize = 960;
    23. private final float scoreThreshold = 0.1f;
    24. // 检测
    25. protected int run_det = 1;
    26. // 分类
    27. protected int run_cls = 1;
    28. // 识别
    29. protected int run_rec = 1;
    30. private final String assetModelDirPath = "models/ch_PP-OCRv2";
    31. private String assetlabelFilePath = "labels/ppocr_keys_v1.txt";
    32. private Context mContext;
    33. private Predictor mPredictor;
    34. /**
    35. * 适配EC
    36. */
    37. public OCRApi(Context mContext) {
    38. this.mContext = mContext;
    39. try {
    40. String path = Utils.setPathForDefaultDataForEc(mContext, this.getClass());
    41. Log.d("OCR加载路径", path);
    42. } catch (IOException e) {
    43. e.printStackTrace();
    44. }
    45. this.mPredictor = new Predictor();
    46. boolean flag = this.mPredictor.init(this.mContext, assetModelDirPath, assetlabelFilePath, useOpencl, cpuThreadNum,
    47. cpuPowerMode,
    48. detLongSize, scoreThreshold);
    49. if (!flag) {
    50. Log.d("*************", "初始化失败");
    51. } else {
    52. Log.d("*************", "初始化成功");
    53. }
    54. }
    55. public void end() {
    56. this.mPredictor.releaseModel();
    57. }
    58. public String ocrFile(final String imagePath) {
    59. return this.ocrFile(imagePath, -1);
    60. }
    61. public String ocrFile(final String imagePath, int type) {
    62. if (type == 0) {
    63. // 只检测
    64. return this.ocrFile(imagePath, 1, 0, 0).toJSONString();
    65. } else if (type == 1) {
    66. // 方向分类 + 识别
    67. return this.ocrFile(imagePath, 0, 1, 1).toJSONString();
    68. } else if (type == 2) {
    69. // 只识别
    70. return this.ocrFile(imagePath, 0, 0, 1).toJSONString();
    71. } else if (type == 3) {
    72. // 检测 + 识别
    73. return this.ocrFile(imagePath, 1, 0, 1).toJSONString();
    74. }
    75. // 默认 检测 + 方向分类 + 识别
    76. return this.ocrFile(imagePath, 1, 1, 1).toJSONString();
    77. }
    78. private JSONArray ocrFile(final String imagePath, int run_det, int run_cls, int run_rec) {
    79. try {
    80. Bitmap image;
    81. if (imagePath.contains(".jpg") || imagePath.contains(".JPG") || imagePath.contains(".jpeg") || imagePath.contains(".JPEG")) {
    82. ExifInterface exif = null;
    83. exif = new ExifInterface(imagePath);
    84. int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
    85. ExifInterface.ORIENTATION_UNDEFINED);
    86. image = BitmapFactory.decodeFile(imagePath);
    87. image = Utils.rotateBitmap(image, orientation);
    88. } else {
    89. image = BitmapFactory.decodeFile(imagePath);
    90. }
    91. this.mPredictor.setInputImage(image);
    92. boolean flag = runModel(run_det, run_cls, run_rec);
    93. if (!flag) {
    94. Log.d("****************", "无法运行!");
    95. return new JSONArray();
    96. }
    97. return transformOCRResult(this.mPredictor.outputResultList);
    98. } catch (IOException e) {
    99. e.printStackTrace();
    100. }
    101. return new JSONArray();
    102. }
    103. public String ocrBitmap(final Bitmap bitmap) {
    104. return this.ocrBitmap(bitmap, -1);
    105. }
    106. public String ocrBitmap(final Bitmap bitmap, int type) {
    107. if (type == 0) {
    108. // 只检测
    109. return this.ocrBitmap(bitmap, 1, 0, 0).toJSONString();
    110. } else if (type == 1) {
    111. // 方向分类 + 识别
    112. return this.ocrBitmap(bitmap, 0, 1, 1).toJSONString();
    113. } else if (type == 2) {
    114. // 只识别
    115. return this.ocrBitmap(bitmap, 0, 0, 1).toJSONString();
    116. } else if (type == 3) {
    117. // 检测 + 识别
    118. return this.ocrBitmap(bitmap, 1, 0, 1).toJSONString();
    119. }
    120. // 默认 检测 + 方向分类 + 识别
    121. return this.ocrBitmap(bitmap, 1, 1, 1).toJSONString();
    122. }
    123. private JSONArray ocrBitmap(Bitmap bitmap, int run_det, int run_cls, int run_rec) {
    124. this.mPredictor.setInputImage(bitmap);
    125. boolean flag = runModel(run_det, run_cls, run_rec);
    126. if (!flag) {
    127. Log.d("****************", "无法运行!");
    128. return new JSONArray();
    129. }
    130. return transformOCRResult(this.mPredictor.outputResultList);
    131. }
    132. private boolean runModel(int run_det, int run_cls, int run_rec) {
    133. return this.mPredictor.runModel(run_det, run_cls, run_rec);
    134. }
    135. private JSONArray transformOCRResult(List<OCRResultModel> ocrResultModelList) {
    136. JSONArray jsonArray = new JSONArray();
    137. for (OCRResultModel ocrResultModel : ocrResultModelList) {
    138. JSONObject jsonObject = new JSONObject();
    139. jsonObject.put("words", ocrResultModel.getLabel());
    140. JSONArray objects = new JSONArray();
    141. for (Point point : ocrResultModel.getPoints()) {
    142. JSONArray points = new JSONArray();
    143. points.add(point.x);
    144. points.add(point.y);
    145. objects.add(points);
    146. }
    147. jsonObject.put("location", objects);
    148. jsonObject.put("score", ocrResultModel.getConfidence());
    149. jsonArray.add(jsonObject);
    150. }
    151. Log.d("OCR", jsonArray.toJSONString());
    152. return jsonArray;
    153. }
    154. }

    8、打包插件

    执行:Build->Build Bundle(s)/APKS->Build APK(S)

     一个10M以下的插件就完成了,然后安装到手机中。

    9、在easy click应用中编写js代码

    首先将apk文件放到plugin目录下,然后用loadDex()加载该插件

    1. // ***************************************************************************
    2. // ********************加入群【754442166469843332】可获取最新版本!!!*********************
    3. // ***************************************************************************
    4. // 注:插件包不需要安装,只需放在工程plugins目录下即可
    5. function main() {
    6. loadDex("ocr.apk");
    7. var ocr = new com.tomato.ocr.ec.OCRApi(context);
    8. let type = -1;
    9. // type 可传可不传
    10. // type=0 : 只检测
    11. // type=1 : 方向分类 + 识别
    12. // type=2 : 只识别
    13. // type=3 : 检测 + 识别
    14. // 只检测文字位置:type=0
    15. // 全屏识别: type=3或者不传type
    16. // 截取单行文字识别:type=1或者type=2
    17. // 例子一
    18. let result1 = ocr.ocrFile("/storage/0.jpg",type);
    19. // 返回结果是json字符串,需使用JSON.parse(result1)转成json对象
    20. logd(result1);
    21. // 例子二
    22. let bitmap = image.readBitmap("/sdcard/0.jpg");
    23. let result2 = ocr.ocrBitmap(bitmap,type);
    24. logd(result2);
    25. }
    26. main();

    完毕!!!

    总结

            相对来说,在熟悉PaddleOCR和Android开发的情况下,进行easy click插件开发还是比较容易的,而且通过自己开发插件的形式可以集成更多的功能,比如只进行文本检测、其他语言识别模型、身份识别模型等等,相对来说比较自由,这是官方提供不了的。今天就分享到这里,感谢支持!

  • 相关阅读:
    css之复合选择器
    快速上手Linux核心命令(八):网络相关命令
    美国访问学者签证如何申请加急办理?
    2023年9月青少年软件编程(C 语言) 等级考试试卷(六级)
    Spark写入支持更新【源码二次开发】
    java基础类型是否引用传递问题
    Python(12)进程与线程
    Activiti,Apache camel,Netflex conductor对比,业务选型
    Linux系统编程(三):进程
    SVG: 可伸缩的矢量图形
  • 原文地址:https://blog.csdn.net/YY007H/article/details/126622075