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

基于PaddleOCR开发懒人精灵文字识别插件

目的

       懒人精灵是 Android 平台上的一款自动化工具,它通过编写 lua 脚本,结合系统的「 无障碍服务 」对 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.5 · PaddlePaddle/PaddleOCR · GitHub

3、导入Android studio并成功运行

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

插件开发

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"><applicationandroid: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;private static OCRApi ocrApi;public static OCRApi init(Context mContext) {if (ocrApi == null) {ocrApi = new OCRApi(mContext);}return ocrApi;}public OCRApi(Context mContext) {this.mContext = mContext;try {String path = Utils.setPathForDefaultDataForLr(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 release() {if (mPredictor != null) {mPredictor.releaseModel();}if (ocrApi != null) {ocrApi = null;}}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、在懒人精灵应用中编写lua代码

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

import('java.io.File')
import('java.lang.*')
import('java.util.Arrays')
import('android.content.Context')
import('android.hardware.Sensor')
import('android.hardware.SensorEvent')
import('android.hardware.SensorEventListener')
import('android.hardware.SensorManager')
import('com.nx.assist.lua.LuaEngine')local loader = LuaEngine.loadApk("TomatoOCR.apk")local OCR = loader.loadClass("com.tomato.ocr.lr.OCRApi")local ocr = OCR.init(LuaEngine.getContext())local type = -1;
-- type 可传可不传
-- type=0 : 只检测
-- type=1 : 方向分类 + 识别
-- type=2 : 只识别
-- type=3 : 检测 + 识别-- 只检测文字位置:type=0
-- 全屏识别: type=3或者不传type
-- 截取单行文字识别:type=1或者type=2-- 例子一
local result1 = ocr.ocrFile("/storage/emulated/0/0.jpg", type)
-- local result1 = ocr.ocrFiles(["/storage/emulated/0/0.jpg","/storage/emulated/0/0.jpg",...],type)
printEx(result1);-- 例子二
local result2 = ocr.ocrBitmap("bitmap对象", type)
-- local result2 = ocr.ocrBitmaps(["bitmap对象","bitmap对象",...],type)
printEx(result2);-- 例子三
local result3 = ocr.ocrBase64("图片base64字符串", type)
-- local result3 = ocr.ocrBase64s(["图片base64字符串","图片base64字符串",...],type)
printEx(result3);-- 释放
ocr.release()

完毕!!!

总结

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

插件下载地址:​​​​​​​地址​​​​​​​

相关文章:

基于PaddleOCR开发懒人精灵文字识别插件

目的 懒人精灵是 Android 平台上的一款自动化工具&#xff0c;它通过编写 lua 脚本&#xff0c;结合系统的「 无障碍服务 」对 App 进行自动化操作。在文字识别方面它提供的有一款OCR识别插件&#xff0c;但是其中有识别速度慢&#xff0c;插件大的缺点&#xff0c;所以这里将讲…...

PyTorch 深度学习实战 | DIEN 模拟兴趣演化的序列网络

01、实例&#xff1a;DIEN 模拟兴趣演化的序列网络深度兴趣演化网络(Deep Interest Evolution Network,DIEN)是阿里巴巴团队在2018年推出的另一力作,比DIN 多了一个Evolution,即演化的概念。在DIEN 模型结构上比DIN 复杂许多,但大家丝毫不用担心,我们将DIEN 拆解开来详细地说明…...

pyspark null类型 在 json.dumps(null) 之后,会变为字符串‘null‘

在将 hive 数仓数据写入 MySQL 时候&#xff0c;有时我们需将数据转为 json 字符串&#xff0c;然后再存入 MySQL。但 hive 数仓中的 null 类型遇到 json 函数之后会变为 ‘null’ 字符串&#xff0c;这时我们只需在使用 json 函数之前对值进行判断即可&#xff0c;当值为 null…...

LeetCode - 两数相加

题目信息 源地址&#xff1a;两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字…...

Office 2021专业版安装包及激活教程

[软件名称]: Office 2021 [软件大小]: 4.33GB [安装环境]: Win11/Win 10 [软件安装包下载]:https://pan.quark.cn/s/169ed49988b2 “Microsoft Office 2021是Microsoft推出的办公软件。2021年10月5日&#xff0c;Office 2021 for Mac发布&#xff0c;其中包含许多新功能 Micro…...

git版本规范-前端

前言 本文档适用于前端的小伙伴。针对目前前端只有测试环境和生产环境&#xff0c;为更好管理前端代码和适用于自动化部署&#xff0c;编写次文档&#xff0c;有不同意见的小伙伴可以进行讨论。 分支 由于没有目前没有预发环境&#xff0c;简化开发、测试、部署和发布流程&a…...

UEFI Device Path (1): 重新认识Device Path

从事UEFI开发的人员&#xff0c;对UEFI Device Path的概念都有一定了解&#xff0c;但未必都建立了比较系统而深刻的认识。UEFI Device Path的认知仅限于: 1)它是用来表示系统中设备的路径&#xff1b;2) 在UEFI SPEC中定义了它的数据结构和若干操作它的UEFI Protocol。除此以外…...

合成孔径成像的应用及发展

一、引言 合成孔径成像自20世纪50年代提出&#xff0c;应用于雷达成像&#xff0c;历经70年的研发&#xff0c;已经日趋成熟&#xff0c;成功地用于环境资源监测、灾害监测、海事管理及军事等领域。受物理环境制约&#xff0c;合成孔径在声呐成像中的研发与应用起步稍迟&#…...

MyBatis-Plus的基本操作

目录 1、配置文件 1、添加依赖 2、启动类 3、实体类 4、添加Mapper类 5、测试Mapper接口 2、CRUD测试 1、insert添加 2、修改操作 3、删除操作 3、MyBatis-Plus条件构造器 4、knife4j 1、Swagger介绍 2、集成knife4j 3.添加依赖 4 添加knife4j配置类 5、 Cont…...

HTTPAPI使用

1、使用浏览器 1.1、获取当前IP(限制 1200次 /小时) 用浏览器访问 http://ip.hahado.cn/current-ip 输入用户名和密码 [{"ip": "180.102.181.64","ttl": 262.87515091896057} ] "ip"&#xff1a; 字段是当前的外网IP ("ip&qu…...

Windos下设置java项目开机自启动

这里是将java项目注册为Windows服务实现开机自启动。 查看.NET framework版本 因为使用winsw工具运行时需要使用.NET framework,基本上现在的win10系统带自带有.NET framework4.0&#xff0c;为了选择合适的版本&#xff0c;我们可以查看本机.NET Framework版本&#xff0c;根…...

(链表)移除链表元素(双指针法)

文章目录前言&#xff1a;问题描述&#xff1a;解题思路&#xff08;双指针法&#xff09;&#xff1a;代码实现&#xff1a;总结&#xff1a;前言&#xff1a; 此篇是针对链表的经典练习题。 问题描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请…...

Raft协议

文章目录一、目的&#xff08;与Paxos相同&#xff09;二、名字来源三、服务器状态四、基本实现1、任期2、RPC调用3、领导者选举4、日志复制5.领导者更替三、Raft与Paxos的区别1.表现形式2.简单性3.领导选举算法一、目的&#xff08;与Paxos相同&#xff09; 保证日志完全相同…...

动态规划概述

动态规划概述动态规划的两个要求&#xff1a; 1.最优子结构 例&#xff1a;现有一座10级台阶的楼梯&#xff0c;我们要从下往上走&#xff0c;每次只能跨一步&#xff0c;一步可以往上走1级或者2级台阶&#xff0c;请问一共有多少种解法呢&#xff1f; 台阶数12345678910走法数…...

CPU缓存架构+Disruptor内存队列

文章目录CPU缓存架构Disruptor内存队列CPU缓存架构介绍缓存一致性问题缓存一致性协议MESI协议伪共享问题高性能内存队列DisruptorCPU缓存架构Disruptor内存队列 CPU缓存架构 介绍 cpu与内存的交互数据之间&#xff0c;有一个高速缓存层。有些处理器有3层缓冲&#xff0c;有些…...

Spark SQL join操作详解

一、 数据准备 本文主要介绍 Spark SQL 的多表连接&#xff0c;需要预先准备测试数据。分别创建员工和部门的 Datafame&#xff0c;并注册为临时视图&#xff0c;代码如下&#xff1a; val spark SparkSession.builder().appName("aggregations").master("lo…...

设计模式-day04

5&#xff0c;结构型模式 5.6 组合模式 5.6.1 概述 对于这个图片肯定会非常熟悉&#xff0c;上图我们可以看做是一个文件系统&#xff0c;对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树&#xff0c;当我们找到某个叶子节点后&#xff0c;…...

线段树的学习(2023.4.5)

今天我来学习线段树 首先它是树有着树的结构,线段树由于本身是专门用来处理区间问题的 它的作用可以处理区间的问题拥有更快的速度. 对于每一个子节点而言&#xff0c;都表示整个序列中的一段子区间&#xff1b;对于每个叶子节点而言&#xff0c;都表示序列中的单个元素信息…...

Java 实现excel、word、txt、ppt等办公文件在线预览功能

相信大家在开发的过程中都会遇到在线预览功能&#xff0c;有没有想过如何通过java来实现excel、word、txt、ppt等办公文件在线预览功能&#xff1f;今天我们就来解决这一疑问&#xff01; 其实&#xff0c;网上还是有些公司对这一功能提供了收费服务。那么&#xff0c;如何实现…...

《Vue3实战》 第九章 路由

1、安装路由 cnpm install vue-router42、router-link应用 2.1、创建views/OrderList.vue组件 <template> <h1>订单列表页面......</h1> </template> <script> export default{name: OrderList,data(){return{arr:[4,2,5]} } …...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...