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.hqwc.cn/news/576635.html

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

相关文章

基于单片机的二维码LCD显示控制设计

**单片机设计介绍&#xff0c;基于单片机的二维码LCD显示控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的二维码LCD显示控制设计是一个集硬件、软件与通信于一体的综合性项目。此设计的主要目标是实现单片机…

物联网实战--入门篇之(一)物联网概述

目录 一、前言 二、知识梳理 三、项目体验 四、项目分解 一、前言 近几年很多学校开设了物联网专业&#xff0c;但是确却地讲&#xff0c;物联网属于一个领域&#xff0c;包含了很多的专业或者说技能树&#xff0c;例如计算机、电子设计、传感器、单片机、网…

Elasticsearch入门及常用命令和Spring中的常用操作

入门 官网 简介 一个分布式的、Restful风格的搜索引擎。支持对各种类型的数据的检索。搜索速度快&#xff0c;可以提供实时的搜索服务。便于水平扩展&#xff0c;每秒可以处理PB级海量数据。 常用术语 索引&#xff1a;与MySQL数据库中的Database相对应类型&#xff1a;与…

本地Navicat连接MySQL时报错1142解决办法

本地Navicat连接MySQL时报错1142解决办法&#xff1a; 1&#xff0c;使用root登录 2&#xff0c;使用下面SQL查询权限 use mysql; SELECT * FROM user WHERE usernacos;

【SpringBoot从入门到精通】01_SpringBoot概述

一、Spring与SpringBoot 1.1 Spring Spring 是一款目前主流的 Java EE 轻量级开源框架&#xff0c;是 Java 世界最为成功的框架之一。Spring 由“Spring 之父”Rod Johnson(罗宾约翰逊) 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。 广义…

Java中有哪些容器(集合类)?

Java中的集合类主要由Collection和Map这两个接口派生而出&#xff0c;其中Collection接口又派生出三个子接 口&#xff0c;分别是Set、List、Queue。所有的Java集合类&#xff0c;都是Set、List、Queue、Map这四个接口的实现 类&#xff0c;这四个接口将集合分成了四大类&#…

洛谷 P1379 八数码难题

代码如下&#xff1a; #include<bits/stdc.h> using namespace std; struct node{string s;int pos; }star,en; map<string,int>mp[2]; queue<node>q[2]; int main(){cin>>star.s;en.s"123804765";for(int i0;i<9;i){if(star.s[i]0) sta…

pygame 3d三角形沿y轴旋转后 透视投影在屏幕上

import pygame from pygame.locals import * import sys import mathpygame.init()width, height 800, 600 screen pygame.display.set_mode((width, height))vertices [(0, 100, 1), (100, 200, 0), (300, 100, 1)]angle 0 rotation_speed 2 # 可根据需要调整旋转速度 c…

工业镜头常用参数之实效F(Fno.)和像圈

Fno. 工业镜头中常用到的参数F&#xff0c;有时候用F/#&#xff0c;Fno.来表示&#xff0c;指的是镜头通光能力的参数。它可用镜头焦距及入瞳直径来表示&#xff0c;也可通过镜头数值孔径&#xff08;NA&#xff09;和光学放大倍率&#xff08;β&#xff09;来计算。有效Fno.…

Maven配置国内镜像-阿里云仓库镜像

使用自己安装maven环境时&#xff1a; 打开解压目录下conf/settings.xml文件 使用Idea自带的Maven时&#xff1a; 打开Idea安装路径\plugins\maven\lib\maven3\conf\settings.xml文件 在mirrors节点中加入如下配置&#xff1a; <!-- 加入如下mirror节点 使用国内阿里云仓…

Spring Aop 源码解析(下)

ProxyFactory选择cglib或jdk动态代理原理 ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术: config就是ProxyFactory对象,把自己传进来了,因为ProxyFactory继承了很多类,其中一个父类就是ProxyConfig // config就是ProxyFactory对象// 是不是…

销售的业绩和合同无法统一管理可以通过系统实现吗?

这个问题我们在日常管理中也遇到过&#xff0c;在没有使用软件之前&#xff0c;合同原件没有专人负责打理&#xff0c;销售人员签了合同后&#xff0c;直接把原件随手放在柜子里&#xff0c;或者把数据记录到excel表中。 但是每个人的工作习惯不一样&#xff0c;记录的表格也不…