ocr之opencv配合paddleocr提高识别率

背景1:在这篇文章编写之前使用到的工具并不是opencv,而是java原有的工具BufferedImage。但因为在使用过程中会频繁切图,放大,模糊,所以导致的jvm内存使用量巨大,分秒中都在以百兆的速度累加内存空间。这种情况会让程序卡顿,频繁的发生full gc。增加了jvm宕机的不确定性,也给自己埋下了定时炸弹。在不断摸索后一直不能解决这个高内存使用率的问题。而这又关乎到程序的稳定,于是在近日发现并决定使用opencv试一试。

背景2:使用BufferedImage的这段时间里虽然通过不断调整jvm①达到了没有明显卡顿的效果,但是这个坑迟早还是会害人的。
注①:怎样调整的jvm可以看这篇博文。调整参数不复杂,只是通过较小堆大小来做的,但这不是最佳解决方案》》

注:只是通过paddleocr识别,准确度不如人意。但是经过矫正,使用放大模糊图片,就像给paddleocr带上了一副眼睛,成功的提高了识别率。美哉。应上了一首名句(不识庐山这面目,只缘身在此山中),让paddle能看到山就好。

一、识别思路

1. 切割图片

切割的位置以及尺寸大小是通过提前测量好的,也就是可以通过系统内的操作。

2. 放大图片①

放大的尺寸大小非常需要测试。首先放大倍数过小会导致图片不够清除。倍数过大导致图片的文件大小过量,这会导致各种的不方便,尤其是在通过后面要讲的paddleocr识别起来效率降低(识别时间过长)。
注①:测试后计划使用的放大倍数选择8

3. 模糊图片

模糊图片的操作会带使得paddleocr在现有模型下提高识别率。据观测,棱角分明的像素体,识别率是很低的(感觉paddleocr被训练的更容易看懂抽象一般)

4. paddleocr识别

这是最后一步。我在实际使用的场景下应用的是打包的exe程序。exe打包的具体内容可以查看我的这篇博文 》》

二、具体实现介绍

注:如何使用opencv呢?我咨询的大模型【文心一言】。说实话在变成使用方面他还是很在行的。在使用大模型方面我还解除了【抖音】的【豆包】,豆包的效果不是很好,文心还是不错。

opencv如何使用

1. 下载opencv4.6.0版本并进行配置

注:我使用的是460版本, 是在官网进行的下载,直接下载网速会特别慢,于是使用的【迅雷】(通过看广告获取快下的资格)
opencv官网下载页面》》

  • 下载后解压缩并配置环境变量

注:双击解压到你指定的目录
在这里插入图片描述
注:将你的下方路径配置到环境变量

E:\prgrames\opencv\build

在这里插入图片描述

  • 将下方文件配置到%JAVA_HOME%\bin目录下

在这里插入图片描述

  • 将下方文件配置到项目中

在这里插入图片描述
注:我配置的位置为根目录的/lib/下
在这里插入图片描述

  • 配置maven依赖
<dependency><groupId>opencv</groupId><artifactId>opencv</artifactId><version>460</version><scope>system</scope><systemPath>${project.basedir}/lib/opencv-460.jar</systemPath>
</dependency>
  • 在java代码中静态加载dll文件
static {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

由此就可以开始使用了

2. 编写放大模糊裁剪方法
import cn.hutool.core.util.StrUtil;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;import java.io.File;
import java.io.IOException;/*** opencv图片处理*/
public class OpencvPicHanldle {static {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);}/** 裁剪图片 */static String CLIP_NAME = "tmp_cp";/** 放大图片 */static String ZOOM_NAME = "tmp_zm";/** 模糊图片 */static String BLUR_NAME = "tmp_br";public static File blurPic(String inputFilePath, String picFormat, String tmpDir, int redius) {// 读取图片Mat src = Imgcodecs.imread(inputFilePath);// 创建输出Mat对象Mat dst = new Mat();// 定义高斯滤波器的大小,这里使用5x5的核Size ksize = new Size(redius, redius);// 定义高斯滤波器的标准差,这里使用0,意味着OpenCV会根据核大小自己计算double sigmaX = 0;double sigmaY = 0;// 应用高斯模糊Imgproc.GaussianBlur(src, dst, ksize, sigmaX, sigmaY);File file;try {file = File.createTempFile(BLUR_NAME, StrUtil.DOT+picFormat.toLowerCase(), mkdir(tmpDir));} catch (IOException e) {throw new RuntimeException(e);}// 保存模糊处理后的图片Imgcodecs.imwrite(file.getAbsolutePath(), dst);// 显示模糊处理后的图片(如果需要的话)// HighGui.imshow("Blurred Image", dst);// HighGui.waitKey(0);// 释放资源src.release();dst.release();return file;}public static File clipPic(String filePath, String picFormat, String tmpDir, int x, int y, int w, int h) {Mat src = Imgcodecs.imread(filePath);// 定义切割区域Rect roi = new Rect(x, y, w, h); // x, y 是起始点坐标,width, height 是切割区域的宽和高// 获取切割后的图片(子矩阵)Mat cropped = new Mat(src, roi);File file;try {file = File.createTempFile(CLIP_NAME, StrUtil.DOT+picFormat.toLowerCase(), mkdir(tmpDir));} catch (IOException e) {throw new RuntimeException(e);}// 保存切割后的图片Imgcodecs.imwrite(file.getAbsolutePath(), cropped);// 释放资源src.release();cropped.release();return file;}public static File zoomPic(String inputFilePath, String picFormat, String tmpDir, double scale) {// 读取图片Mat src = Imgcodecs.imread(inputFilePath);// 定义放大后的尺寸,这里假设放大两倍double scaleFactor = scale; // 放大倍数Size newSize = new Size(src.width() * scaleFactor, src.height() * scaleFactor);// 创建放大后的Mat对象Mat dst = new Mat();// 使用Imgproc.resize()函数放大图片Imgproc.resize(src, dst, newSize);File file;try {file = File.createTempFile(ZOOM_NAME, StrUtil.DOT+picFormat.toLowerCase(), mkdir(tmpDir));} catch (IOException e) {throw new RuntimeException(e);}// 保存放大后的图片Imgcodecs.imwrite(file.getAbsolutePath(), dst);// 释放资源src.release();dst.release();return file;}public static File mkdir(String dirPath) {File dirFile = new File(dirPath);if(!dirFile.exists()) {dirFile.mkdir();}return dirFile;}}
3. 对接paddleocr识别
python脚本识别

使用python脚本识别只是为了测试, 实际我在java中使用时用到的为打包后的exe
注:paddleocr的安装详情可查看这篇文章 》》
注:安装后可使用脚本进行测试识别图片 如下是python的识别脚本
注:使用时命令如: 标红处为识别出的内容。

# 参数1为打印识别到的内容
padocr.py C:\main\tmp_zm1295880000423201969.jpg 1

在这里插入图片描述

from paddleocr import PaddleOCR
import sys
def recognize(imgPath,printx):# 模型路径下必须含有model和params文件ocr = PaddleOCR(use_angle_cls = True, # 是否加载分类模型use_gpu = False# 是否使用gpu,show_log=False)			#img_path = 'C:/Users/Administrator/Desktop/zuoshangjiao/20240129162437.jpg'result = ocr.ocr(imgPath, cls = True)#print(f"result:{result}")for i,line in enumerate(result):#print(f"i:{i}, line:{line}")for j,item in enumerate(line):print(f"item: {item}")for k, body in enumerate(item):#if k == 1:print(f"k:{k}, point:{body[0]}, value:{body[1]}")print(printx)if printx == "1":print(f"{body[0]}, ordinary:{body[1]}")else:print(f"{body[0]}")if __name__ == "__main__":recognize(sys.argv[1],sys.argv[2])
java程序
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;/*** paddleocr识别工具类*/
@Slf4j
public class PaddleOcrUtil {/** 临时文件路径 */public static String tmpPath = System.getProperty("user.dir") + StrUtil.SLASH + "tmpFile" + StrUtil.SLASH + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_FORMAT);/*** 创建图片路径*/static {File tmpFile = new File(tmpPath);if(!tmpFile.exists()) {if(FileUtil.mkParentDirs(tmpPath).exists()) {if(tmpFile.mkdir()) {}}}}/*** 测试使用, 勿删除*/public static void testRec() {String cutPic = "D:\\...\\tmp_cp8579718493577844855.jpg";String abs = "D:\\...\\tmpFile\\20240328104742415\\acc3";File a = OpencvPicHanldle.zoomPic(cutPic, "jpg", abs, 8);File b = OpencvPicHanldle.blurPic(a.getAbsolutePath(), "jpg", abs, 5);System.out.println(b.getAbsolutePath());}/*** 识别图片* @param filePath 整图* @param picFormat 整图类型* @param x 需要切割的坐标x* @param y 需要切割的坐标y* @param w 需要切割的坐标w* @param h 需要切割的坐标h* @return*/public static String rec(String filePath, String picFormat, int x, int y, int w, int h) {File outputfile;try{outputfile = OpencvPicHanldle.clipPic(filePath, picFormat, tmpPath, x, y, w, h);}catch (Throwable e) {log.error("rec.err: ", e);return StrUtil.EMPTY;}return rec(outputfile, picFormat);}/*** 识别图片具体方法* @param outputfile 切割后的图片路径* @param formatName 图片类型* @return*/private static String rec(File outputfile, String formatName) {File zoomFile = null;File blurFile = null;try {// 放大zoomFile = OpencvPicHanldle.zoomPic(outputfile.getAbsolutePath(), formatName, tmpPath, 8);// 模糊化blurFile = OpencvPicHanldle.blurPic(zoomFile.getAbsolutePath(), formatName, tmpPath, 5);String console;try {console = ShellUtils.exec(OcrServiceRegistry.execPath, blurFile.getAbsolutePath());} catch (Exception e) {throw new RuntimeException(e);}if(StrUtil.isEmpty(console)) return StrUtil.EMPTY;return console.replaceAll("[\\s\\t\\n\\r]+", "");}catch (Throwable e) {e.printStackTrace();return "";}finally {if(outputfile != null) {if(!OcrServiceRegistry.saveClipImage) {outputfile.delete();}}if(zoomFile != null) {if(!OcrServiceRegistry.saveBlurImage) {zoomFile.delete();}}if(blurFile != null) {if(!OcrServiceRegistry.saveBlurImage) {blurFile.delete();}}}}
}

三、使用opencv注意事项

注:不要有中文路径,否则会报错

  • 不要有中文路径(java程序如jar包所在路径)
  • 不要有中文路径(要处理的图片所在路径)

总结

1. 持之以恒

对不满意的事情想办法让他变得更好。
注:心里一直装着的事终于能够落地了。因为一直装着,也就是放在心上,终归有了解决方案。

2. 换种思路

注:避免死心眼钻牛角尖。就比如死磕jvm调优,但还是于事无补。
注:多尝试新的东西,会带来不小的收获

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/777377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据开发扩展shell--尚硅谷shell笔记

大数据开发扩展shell 学习目标 1 熟悉shell脚本的原理和使用 2 熟悉shell的编程语法 第一节 Shell概述 1&#xff09;Linux提供的Shell解析器有&#xff1a; 查看系统中可用的 shell [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/t…

java日志技术——Logback日志框架安装及概述

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! 日志 什么是日志 程序中的日志&#xff0c;通常就是一个文件&#xff0c;里面记录的是程序运行过程中的各种信息&#xff0c;通过日志可以进行操作分析&#xff0c;bug定位等 记录日志的方案 程…

构建一个包含mvn命令的Java 17基础镜像

前言 官方提供的openjdk基础镜像&#xff0c;不包含mvn命令&#xff0c;无法用容器来打包代码。 在官方提供的镜像基础上安装maven。 前期准备&#xff0c;需要安装好docker。 一、安装maven 1、下载openjdk基础镜像&#xff0c;执行如下代码。 docker pull openjdk:17-j…

Linux 进程信号:产生信号

目录 一、通过终端按键产生信号 1、signal()函数 2、核心转储 3、ulmit命令 二、调用系统函数向进程发信号 1、kill()函数 2、raise()函数 3、abort()函数 三、发送信号的过程 读端关闭、写端继续写入的情况 如何理解软件条件给进程发送信号: 四、软件条件产生信…

伦敦金与纸黄金有什么区别?怎么选?

伦敦金与纸黄金都是与黄金相关的投资品种&#xff0c;近期黄金市场的上涨吸引了投资者的关注&#xff0c;那投资者想开户入场成为黄金投资者应该选择纸黄金还是伦敦金呢&#xff1f;两者有何区别呢&#xff1f;下面我们就来讨论一下。 伦敦金是一种起源于伦敦的标准化黄金交易合…

HarmonyOS实战开发-实现带有卡片的电影应用

介绍 本篇Codelab基于元服务卡片的能力&#xff0c;实现带有卡片的电影应用&#xff0c;介绍卡片的开发过程和生命周期实现。需要完成以下功能&#xff1a; 元服务卡片&#xff0c;用于在桌面上添加2x2或2x4规格元服务卡片。关系型数据库&#xff0c;用于创建、查询、添加、删…

FMEA的本质——FMEA软件

免费试用FMEA软件-免费版-SunFMEA FMEA&#xff0c;即故障模式与影响分析&#xff08;Failure Modes and Effects Analysis&#xff09;&#xff0c;是一种预防性的质量工具&#xff0c;广泛应用于各种行业和领域&#xff0c;特别是在制造业、航空航天、医疗设备、汽车工业等领…

AGV全电动无人堆高车选购时要注意的4点

AGV 随着机器人技术在中国的快速发展&#xff0c;国内企业开始推出区别于传统叉车的无人叉车&#xff0c;旨在为企业降本增效&#xff0c;降低人工成本与对人的依赖。同时&#xff0c;也将人工从危险恶劣的环境中解放出来。随着技术的持续提升&#xff0c;如今&#xff0c;无人…

DVB-S系统仿真学习

DVB-S系统用于卫星电视信号传输&#xff0c;发送端框图如下所示 扰码 实际数字通信中&#xff0c;载荷数据的码元会出现长连0或长连1的情况&#xff0c;不利于接收端提取时钟信号&#xff0c;同时会使得数据流中含有大量的低频分量&#xff0c;使得QPSK调制器的相位长时间不变…

【Frida】【Android】05_Objection实战

&#x1f6eb; 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…

线程创建方式、构造方法和线程属性

欢迎各位&#xff01;&#xff01;&#xff01;推荐PC端观看 文章重点&#xff1a;学会五种线程的创造方式 目录 1.开启线程的五种方式 2.线程的构造方法 3.线程的属性及获取方法 1.开启线程的五种方式 创造线程的基本两步&#xff1a;&#xff08;1&#xff09;使用run方法…

2024最新华为OD机试试题库全 -【二叉树计算】- C卷

1. 🌈题目详情 1.1 ⚠️题目 给出一个二叉树如下图所示: 请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。 左子树表示该节点左侧叶子节点为根节点的一颗新树;右子树表示该节点右侧叶子节点为根节点的一颗新树。 1.2 �…

智能车主控板原理图原理讲解

智能车主控板原理图原理讲解 综述&#xff1a;本篇文章对智能车主控板的一部分电路进行原理分析&#xff0c;文末附加整体原理图。 1. 电源电路 &#xff08;1&#xff09;通过外接电池供电并通过电源模块电路&#xff0c;运用稳压芯片lm2940&#xff0c;将电源电压转化为5V…

分布式处理

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;这是我作为学习笔记原理篇的最后一章&#xff0c;一台计算机在数据中心里是不够的。因为如果只有一台计算机&#xff0c;我们会遇到三个核心问题。第一个核心问题&#xff0c;叫作垂直扩展和水平扩展的选择问题&#xff0c;…

【leetcode】双“指针”

标题&#xff1a;【leetcode】双指针 水墨不写bug 我认为 讲清楚为什么要用双指针 比讲怎么用双指针更重要&#xff01; &#xff08;一&#xff09;快乐数 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数…

排序C++

题目 法1 sort升序排序&#xff0c;再逆序输出 #include<iostream> #include<algorithm> using namespace std;const int N 5e53;//注意const&#xff0c;全局 int a[N]; int main() {//错误int N5e53;//错误const int a[N];int n;cin >> n;for (int i 1;…

HBase Shell基本操作

一、进入Hbase Shell客户端 先在Linux Shell命令行终端执行start-dfs.sh脚本启动HDFS&#xff0c;再执行start-hbase.sh脚本启动HBase。如果Linux系统已配置HBase环境变量&#xff0c;可直接在任意目录下执行hbase shell脚本命令&#xff0c;就可进入HBase Shell的命令行终端环…

【容器源码篇】Set容器(HashSet,LinkedHashSet,TreeSet的特点)

文章目录 ⭐容器继承关系&#x1f339;Set容器&#x1f5d2;️HashSet源码解析构造方法public HashSet()public HashSet(Collection<? extends E> c)public HashSet(int initialCapacity, float loadFactor)HashSet(int initialCapacity, float loadFactor, boolean dum…

VLAN实验记录---对抗遗忘

sw1的接口6应该调成混杂模式&#xff0c;因为pc2,4,5,6的pvid各不相同而网段相同&#xff0c;所以往上去路由时应该剥离标记&#xff08;VLAN里面是标记而不是标签&#xff09;出去&#xff0c;这样 路由器上的物理接口用来管理不带标记的流量&#xff0c;而vlan2流量的往上打上…

记录 AI绘图 Stable Diffusion的本地安装使用,可搭建画图服务端

开头 最近刷短视频看到了很多关于AI绘图&#xff0c;Midjourney&#xff0c;gittimg.ai&#xff0c;Stable Diffusion等一些绘图AI工具&#xff0c;感受到了AI绘画的魅力。通过chatGPT生成关键词再加上绘图工具&#xff0c;真是完美&#xff0c;文末教大家如何用gpt提词 Midj…