当前位置: 首页 > news >正文

基于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文件中添加:

allprojects {
    repositories {
        // ...
        maven { url 'https://jitpack.io' }
    }
}

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

dependencies {
    // ... 
    implementation 'com.alibaba:fastjson:1.1.46.android'
}

3、删除无用的Activity文件

 4、修改AndroidManifest.xml

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.tomato.ocr">
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:debuggable="true"
        android:theme="@style/AppTheme"
        tools:ignore="HardcodedDebugMode">
 
    </application>
</manifest>

5、修改Predictor文件

添加这两行文件:

 调整loadLabel代码如下:

6、修改cpp包名 

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

7、新建OCRApi接口类

package com.tomato.ocr.ec;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.media.ExifInterface;
import android.util.Log;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tomato.ocr.OCRResultModel;
import com.tomato.ocr.Predictor;
import com.tomato.ocr.Utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class OCRApi {

    private final int useOpencl = 0;
    private final int cpuThreadNum = 1;
    private final String cpuPowerMode = "LITE_POWER_HIGH";
    private final int detLongSize = 960;
    private final float scoreThreshold = 0.1f;

    // 检测
    protected int run_det = 1;
    // 分类
    protected int run_cls = 1;
    // 识别
    protected int run_rec = 1;

    private final String assetModelDirPath = "models/ch_PP-OCRv2";
    private String assetlabelFilePath = "labels/ppocr_keys_v1.txt";

    private Context mContext;
    private Predictor mPredictor;

    /**
     * 适配EC
     */
    public OCRApi(Context mContext) {
        this.mContext = mContext;
        try {
            String path = Utils.setPathForDefaultDataForEc(mContext, this.getClass());
            Log.d("OCR加载路径", path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        this.mPredictor = new Predictor();
        boolean flag = this.mPredictor.init(this.mContext, assetModelDirPath, assetlabelFilePath, useOpencl, cpuThreadNum,
                cpuPowerMode,
                detLongSize, scoreThreshold);
        if (!flag) {
            Log.d("*************", "初始化失败");
        } else {
            Log.d("*************", "初始化成功");
        }
    }

    public void end() {
        this.mPredictor.releaseModel();
    }

    public String ocrFile(final String imagePath) {
        return this.ocrFile(imagePath, -1);
    }

    public String ocrFile(final String imagePath, int type) {
        if (type == 0) {
            // 只检测
            return this.ocrFile(imagePath, 1, 0, 0).toJSONString();
        } else if (type == 1) {
            // 方向分类 + 识别
            return this.ocrFile(imagePath, 0, 1, 1).toJSONString();
        } else if (type == 2) {
            // 只识别
            return this.ocrFile(imagePath, 0, 0, 1).toJSONString();
        } else if (type == 3) {
            // 检测 + 识别
            return this.ocrFile(imagePath, 1, 0, 1).toJSONString();
        }
        // 默认 检测 + 方向分类 + 识别
        return this.ocrFile(imagePath, 1, 1, 1).toJSONString();
    }

    private JSONArray ocrFile(final String imagePath, int run_det, int run_cls, int run_rec) {
        try {
            Bitmap image;
            if (imagePath.contains(".jpg") || imagePath.contains(".JPG") || imagePath.contains(".jpeg") || imagePath.contains(".JPEG")) {
                ExifInterface exif = null;
                exif = new ExifInterface(imagePath);
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                        ExifInterface.ORIENTATION_UNDEFINED);
                image = BitmapFactory.decodeFile(imagePath);
                image = Utils.rotateBitmap(image, orientation);
            } else {
                image = BitmapFactory.decodeFile(imagePath);
            }
            this.mPredictor.setInputImage(image);
            boolean flag = runModel(run_det, run_cls, run_rec);
            if (!flag) {
                Log.d("****************", "无法运行!");
                return new JSONArray();
            }
            return transformOCRResult(this.mPredictor.outputResultList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new JSONArray();
    }

    public String ocrBitmap(final Bitmap bitmap) {
        return this.ocrBitmap(bitmap, -1);
    }

    public String ocrBitmap(final Bitmap bitmap, int type) {
        if (type == 0) {
            // 只检测
            return this.ocrBitmap(bitmap, 1, 0, 0).toJSONString();
        } else if (type == 1) {
            // 方向分类 + 识别
            return this.ocrBitmap(bitmap, 0, 1, 1).toJSONString();
        } else if (type == 2) {
            // 只识别
            return this.ocrBitmap(bitmap, 0, 0, 1).toJSONString();
        } else if (type == 3) {
            // 检测 + 识别
            return this.ocrBitmap(bitmap, 1, 0, 1).toJSONString();
        }
        // 默认 检测 + 方向分类 + 识别
        return this.ocrBitmap(bitmap, 1, 1, 1).toJSONString();
    }

    private JSONArray ocrBitmap(Bitmap bitmap, int run_det, int run_cls, int run_rec) {
        this.mPredictor.setInputImage(bitmap);
        boolean flag = runModel(run_det, run_cls, run_rec);
        if (!flag) {
            Log.d("****************", "无法运行!");
            return new JSONArray();
        }
        return transformOCRResult(this.mPredictor.outputResultList);
    }
    
    private boolean runModel(int run_det, int run_cls, int run_rec) {
        return this.mPredictor.runModel(run_det, run_cls, run_rec);
    }

    private JSONArray transformOCRResult(List<OCRResultModel> ocrResultModelList) {
        JSONArray jsonArray = new JSONArray();
        for (OCRResultModel ocrResultModel : ocrResultModelList) {
            JSONObject jsonObject = new JSONObject();

            jsonObject.put("words", ocrResultModel.getLabel());

            JSONArray objects = new JSONArray();
            for (Point point : ocrResultModel.getPoints()) {
                JSONArray points = new JSONArray();
                points.add(point.x);
                points.add(point.y);
                objects.add(points);
            }
            jsonObject.put("location", objects);
            jsonObject.put("score", ocrResultModel.getConfidence());
            jsonArray.add(jsonObject);
        }
        Log.d("OCR", jsonArray.toJSONString());
        return jsonArray;
    }

}

8、打包插件

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

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

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

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

function main() {
    loadDex("ocr.apk");
    var ocr = new com.tomato.ocr.ec.OCRApi(context);


    let type = -1;
    // type 可传可不传
    // type=0 : 只检测
    // type=1 : 方向分类 + 识别
    // type=2 : 只识别
    // type=3 : 检测 + 识别

    // 只检测文字位置:type=0
    // 全屏识别: type=3或者不传type
    // 截取单行文字识别:type=1或者type=2


    // 例子一
    let result1 = ocr.ocrFile("/storage/0.jpg",type);
    // 返回结果是json字符串,需使用JSON.parse(result1)转成json对象
    logd(result1);

    // 例子二
    let bitmap = image.readBitmap("/sdcard/0.jpg");
    let result2 = ocr.ocrBitmap(bitmap,type);
    logd(result2);

}


main();

完毕!!!

总结

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

插件下载地址:山月记分享

相关文章:

  • 建立对象模型— 如何确定类与对象?
  • VMware Workstation Pro16 的下载与安装
  • java计算机毕业设计基于安卓Android的电子废弃物回收利用APP
  • PostgreSQL LISTEN 与NOTIFY命令
  • 基于ssm的养老智慧服务平台毕业设计源码071526
  • MySQL 安装详细步骤
  • 关于 SAP ABAP CL_HTTP_CLIENT API 中的 SSL_ID 参数
  • JavaFX、贷款服务器
  • Powershell历史执行记录
  • elementui中表格组件的高度修改没效果
  • 难受啊,早饭忘记吃了
  • 标签上有什么defer和async属性?<script>
  • 放大器的稳定性分析举例
  • ipsec vxn详解
  • Redis实战:保证数据不丢失,RDBAOF持久化原理解析
  • 收藏网友的 源程序下载网
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • go append函数以及写入
  • HTTP中的ETag在移动客户端的应用
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JavaScript类型识别
  • JavaScript设计模式之工厂模式
  • Java小白进阶笔记(3)-初级面向对象
  • JS 面试题总结
  • leetcode388. Longest Absolute File Path
  • MobX
  • PAT A1120
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Sass Day-01
  • zookeeper系列(七)实战分布式命名服务
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 搞机器学习要哪些技能
  • 模型微调
  • 日剧·日综资源集合(建议收藏)
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 数据可视化之 Sankey 桑基图的实现
  • 小试R空间处理新库sf
  • 自定义函数
  • ​flutter 代码混淆
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • (C语言)fgets与fputs函数详解
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (三)docker:Dockerfile构建容器运行jar包
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)h264中avc和flv数据的解析
  • .bat文件调用java类的main方法
  • .cfg\.dat\.mak(持续补充)
  • .form文件_一篇文章学会文件上传
  • .Net Core 中间件验签
  • .NET Framework 服务实现监控可观测性最佳实践
  • .net 生成二级域名
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .net连接oracle数据库
  • .pop ----remove 删除