ocr之opencv配合paddleocr提高识别率

news2024/11/15 10:23:45

背景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.coloradmin.cn/o/1552825.html

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

相关文章

docker可视化界面 - portainer安装

目录 一、官方安装说明 二、安装portainer 2.1拉取镜像 2.2运行portainer容器 2.3登录和使用portainer 一、官方安装说明&#xff1a; Install PortainerChoose to install Portainer Business Edition or Portainer Community Edition.https://www.portainer.io/install…

本地部署大模型的几种工具(上-相关使用)

目录 前言 为什么本地部署 目前的工具 vllm 介绍 下载模型 安装vllm 运行 存在问题 chatglm.cpp 介绍 下载 安装 运行 命令行运行 webdemo运行 GPU推理 ollama 介绍 下载 运行 运行不同参数量的模型 存在问题 lmstudio 介绍 下载 使用 下载模型文件…

OSCP靶场--plum

OSCP靶场–plum 考点(CVE-2022-25018 linux邮箱信息收集提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap -Pn -sC -sV 192.168.178.28 --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-28 05:41 EDT Nmap scan report for 192.168.178.2…

第十二章 微服务核心(一)

一、Spring Boot 1.1 SpringBoot 构建方式 1.1.1 通过官网自动生成 进入官网&#xff1a;https://spring.io/&#xff0c;点击 Projects --> Spring Framework&#xff1b; 拖动滚动条到中间位置&#xff0c;点击 Spring Initializr 或者直接通过 https://start.spring…

【项目技术介绍篇】若依管理系统功能介绍

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

RTOS线程切换的过程和原理

0 前言 RTOS中最重要的一个概念就是线程&#xff0c;线程的按需切换能够满足RTOS的实时性要求&#xff0c;同时能将复杂的需求分解成一个个线程执行减轻我们开发负担。 本文从栈的角度出发&#xff0c;详细介绍RTOS线程切换的过程和原理。 注&#xff1a;本文参考的RTOS是RT-T…

硬件项目中的turn-key 是啥意思?案例应用

在硬件项目中&#xff0c;turn-key是指一种工程项目模式&#xff0c;即交钥匙工程。这种模式通常由独立的第三方软件厂商直接与芯片厂商合作&#xff0c;基于芯片厂商的硬件方案和协议&#xff0c;集成成熟的上层软件和应用&#xff0c;并整套提供给电子产品生产厂商。这种模式…

实现DevOps需要什么?

实现DevOps需要什么&#xff1f; 硬性要求&#xff1a;工具上的准备 上文提到了工具链的打通&#xff0c;那么工具自然就需要做好准备。现将工具类型及对应的不完全列举整理如下&#xff1a; 代码管理&#xff08;SCM&#xff09;&#xff1a;GitHub、GitLab、BitBucket、SubV…

UE小:基于UE5的两种Billboard material(始终朝向相机材质)

本文档展示了两种不同的效果&#xff0c;分别是物体完全朝向相机和物体仅Z轴朝向相机。通过下面的演示和相关代码&#xff0c;您可以更加直观地理解这两种效果的差异和应用场景。 1. 完全朝向相机效果 此效果下&#xff0c;物体将完全面向相机&#xff0c;不论相机在哪个角度…

语音陪玩交友软件系统程序-app小程序H5三端源码交付,支持二开!

电竞行业的发展带动其周边产业的发展&#xff0c;绘制着游戏人物图画的抱枕、鼠标垫、海报销量极大&#xff0c;电竞游戏直播、游戏教程短视频也备受人们喜爱&#xff0c;自然&#xff0c;像游戏陪练、代练行业也随之生长起来&#xff0c;本文就来讲讲&#xff0c;从软件开发角…

阿里云服务器多少钱一年?阿里云价格表新鲜出炉4月最新报价

2024年阿里云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网aliyunfuwuqi.com整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新…

PHP开发全新29网课交单平台源码修复全开源版本,支持聚合登陆易支付

这是一套最新版本的PHP开发的网课交单平台源代码&#xff0c;已进行全开源修复&#xff0c;支持聚合登录和易支付功能。 项目 地 址 &#xff1a; runruncode.com/php/19721.html 以下是对该套代码的主要更新和修复&#xff1a; 1. 移除了论文编辑功能。 2. 移除了强国接码…

Linux(CentOS)/Windows-C++ 云备份项目(客户端文件操作类,数据管理模块设计,文件客户端类设计)

文章目录 1. 客户端文件操作类2. 客户端数据管理模块设计3. 文件客户端类设计项目代码 客户端负责的功能 指定目录的文件检测&#xff0c;获取文件夹里面的文件 判断这个文件是否需要备份&#xff0c;服务器备份过的文件则不需要进行备份&#xff0c;已经备份的文件如果修改也…

纯分享万岳外卖跑腿系统客户端源码uniapp目录结构示意图

系统买的是商业版&#xff0c;使用非常不错有三端uniapp开源代码&#xff0c;自从上次分享uniapp后有些网友让我分享下各个端的uniapp下的各个目录结构说明 我就截图说以下吧&#xff0c;

【python】网络编程socket TCP UDP

文章目录 socket常用方法TCP客户端服务器UDP客户端服务器网络编程就是实现两台计算机的通信 互联网协议族 即通用标准协议,任何私有网络只要支持这个协议,就可以接入互联网。 socket socke模块的socket()函数 import socketsock = socket.socket(Address Family, type)参…

网络套接字补充——UDP网络编程

五、UDP网络编程 ​ 1.对于服务器使用智能指针维护生命周期&#xff1b;2.创建UDP套接字&#xff1b;3.绑定端口号&#xff0c;包括设置服务器端口号和IP地址&#xff0c;端口号一般是2字节使用uint16_t&#xff0c;而IP地址用户习惯使用点分十进制格式所以传入的是string类型…

<深度学习入门学习笔记P1>——《深度学习》

一、深度学习概述 1.深度学习入门概念及介绍 注&#xff1a; &#xff08;1&#xff09;感知机是深度学习网络算法的起源&#xff0c;神经网络是深度学习算法的中心。 &#xff08;2&#xff09;损失函数和梯度下降是用来对模型优化和训练的一种方式。 &#xff08;3&#xff…

AugmentedReality之路-显示隐藏AR坐标原点(3)

本文介绍如何显示/隐藏坐标原点&#xff0c;分析AR坐标原点跟手机的位置关系 1、AR坐标原点在哪里 当我们通过AugmentedReality的StartARSession函数打开AR相机的那一刻&#xff0c;相机所在的位置就是坐标原点。 2、创建指示箭头资产 1.在Content/Arrow目录创建1个Actor类…

NanoMQ的安装与部署

本文使用docker进行安装&#xff0c;因此安装之前需要已经安装了docker 拉取镜像 docker pull emqx/nanomq:latest 相关配置及密码认证 创建目录/usr/local/nanomq/conf以及配置文件nanomq.conf、pwd.conf # # # # MQTT Broker # # mqtt {property_size 32max_packet_siz…

|行业洞察·趋势报告|《2024旅游度假市场简析报告-17页》

报告的主要内容解读&#xff1a; 居民收入提高推动旅游业发展&#xff1a;报告指出&#xff0c;随着人均GDP的提升&#xff0c;居民的消费能力增强&#xff0c;旅游需求从传统的观光游向休闲、度假游转变&#xff0c;国内人均旅游消费持续增加。 政府政策促进旅游市场复苏&…