opencv学习:通过图像透视进行发票识别完整代码流程

概念:

        使用OpenCV库实现图像的透视变换处理,以矫正图像中的透视失真。通过本实验,学习者将掌握图像处理的基本操作,包括图像的读取、显示、大小调整、灰度转换、二值化、轮廓检测、轮廓近似以及透视变换。

步骤:

1. 导入库

导入必要的Python库,包括OpenCV和NumPy。

import numpy as np
import cv2

2. 定义函数

定义几个函数来处理图像的不同步骤。

cv_show(name, img)
  • 显示图像的函数,接受窗口名称和图像作为参数。
    def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)
order_points(pts)
  • 对四个点进行排序,确保它们按照左上、右上、右下、左下的顺序。
    def order_points(pts):rect = np.zeros((4,2),dtype='float32')   # 创建一个4x2的数组,指定了数组中元素的数据类型为32位浮点数# 按顺序找到对应坐标0123分别是左上、右上、右下、左下s = pts.sum(axis=1)   # 对pts矩阵的每一行进行求和操作rect[0] = pts[np.argmin(s)]#左上角的点rect[2] = pts[np.argmax(s)]#右下角的点diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行进行差值rect[1] = pts[np.argmin(diff)]#右上角的点rect[3] = pts[np.argmax(diff)]#左下角的点return rect
four_point_transform(image, pts)
  • 根据四个点进行图像的透视变换。
    def four_point_transform(image,pts):# 获取输入坐标点rect = order_points(pts)(tl,tr,br,bl) = rect#tl 是左上角点,tr 是右上角点,br 是右下角点,bl 是左下角点。# 计算两个可能的宽度,取这两个宽度的最大值作为新图像的宽度。widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))maxWidth = max(int(widthA) , int(widthB))## 计算两个可能的高度,取这两个高度的最大值作为新图像的高度。heightA = np.sqrt(((tr[0]-br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0]-bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA),int(heightB))# 变换后对应坐标位置dst = np.array([[0,0],[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype='float32')# 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ MP获得转换之间的关系# cy2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst# #参数说明:# src:变换前图像四边形顶点坐标/第2个是原图# MP:透视变换矩阵,3行3列# dsize:输出图像的大小,二元元组(width,heiqht)M = cv2.getPerspectiveTransform(rect,dst)#计算透视变换矩阵 M,它描述了如何将原始图像中的点映射到目标坐标。warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))#透视变换# 返回变换后的结果return warped
resize(image, width=None, height=None, inter=cv2.INTER_AREA)
  • 调整图像大小的函数,接受图像、宽度、高度和插值方法作为参数。
    #用于调整图像的大小。
    def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):dim=None(h,w) = image.shape[:2]#获取输入图像的高度和宽度。if width is None and height is None:#如果宽度和高度都没有指定,则直接返回原始图像。return imageif width is None:#如果只指定了高度,计算新的宽度以保持图像的宽高比。r=height/float(h)dim=(int(w*r),height)else:#如果只指定了宽度,计算新的高度以保持图像的宽高比。r=width/float(w)dim=(width,int(h*r))#根据计算出的尺寸 dim 调整图像大小。resized=cv2.resize(image,dim,interpolation=inter)     # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。return resized

3. 读取和显示原始图像

读取名为fapiao.jpg的发票图像,并显示。

# 读取输入
image = cv2.imread('fapiao.jpg')
cv_show('image',image)

4. 图像缩小处理

将图像缩小到一定高度(例如500像素),并显示缩小后的图像,以便于处理。

# 图片过大,进行缩小处理
ratio = image.shape[0] / 500.0  # 计算缩小比率
orig = image.copy()
image = resize(orig, height=500)
cv_show('1',image)

5. 轮廓检测

将图像转换为灰度图,进行二值化处理,并检测图像轮廓。

# 轮廓检测
print('step 1 : 轮廓检测')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
#二值化处理
edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 自动寻找网值二位化
#检测二值化图像中的轮廓。
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]
#绘制轮廓
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)

6. 获取最大轮廓

找到面积最大的轮廓,并进行轮廓近似。

print('step 2 :获取最大轮廓')
#获取最大轮廓
screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 获取面积最大的轮廓
#计算轮廓周长
peri = cv2.arcLength(screenCnt,True)   # 计算轮廓周长
#轮廓近似
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 轮廓近似
#绘制近似轮廓
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)

7. 透视变换

使用近似后的轮廓点进行透视变换,矫正发票图像的透视失真。

# 透视变换
warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)

8. 显示和保存结果

显示透视变换后的图像,并将其保存为新文件。

cv2.imwrite('invoice_new.jpg',warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv2.imshow('xx',warped)
cv2.waitKey(0)

9.运用形态学变换图像

形态学闭运算使图像更加清晰

a=cv2.imread('invoice_new.jpg')
a=cv2.cvtColor(a,cv2.COLOR_BGR2GRAY)
a=cv2.threshold(a,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
a=cv2.resize(a,(800,600))
a=np.rot90(a,1)kernel = np.ones((1,1), np.uint8)c=cv2.morphologyEx(a,cv2.MORPH_CLOSE,kernel)cv2.imshow('a',c)
cv2.waitKey(0)

代码结果

完整代码

import numpy as np
import cv2
#透视变换
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)def order_points(pts):rect = np.zeros((4,2),dtype='float32')   # 创建一个4x2的数组,指定了数组中元素的数据类型为32位浮点数# 按顺序找到对应坐标0123分别是左上、右上、右下、左下s = pts.sum(axis=1)   # 对pts矩阵的每一行进行求和操作rect[0] = pts[np.argmin(s)]#左上角的点rect[2] = pts[np.argmax(s)]#右下角的点diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行进行差值rect[1] = pts[np.argmin(diff)]#右上角的点rect[3] = pts[np.argmax(diff)]#左下角的点return rectdef four_point_transform(image,pts):# 获取输入坐标点rect = order_points(pts)(tl,tr,br,bl) = rect#tl 是左上角点,tr 是右上角点,br 是右下角点,bl 是左下角点。# 计算两个可能的宽度,取这两个宽度的最大值作为新图像的宽度。widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))maxWidth = max(int(widthA) , int(widthB))## 计算两个可能的高度,取这两个高度的最大值作为新图像的高度。heightA = np.sqrt(((tr[0]-br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0]-bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA),int(heightB))# 变换后对应坐标位置dst = np.array([[0,0],[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype='float32')# 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ MP获得转换之间的关系# cy2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst# #参数说明:# src:变换前图像四边形顶点坐标/第2个是原图# MP:透视变换矩阵,3行3列# dsize:输出图像的大小,二元元组(width,heiqht)M = cv2.getPerspectiveTransform(rect,dst)#计算透视变换矩阵 M,它描述了如何将原始图像中的点映射到目标坐标。warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))#透视变换# 返回变换后的结果return warped#用于调整图像的大小。
def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):dim=None(h,w) = image.shape[:2]#获取输入图像的高度和宽度。if width is None and height is None:#如果宽度和高度都没有指定,则直接返回原始图像。return imageif width is None:#如果只指定了高度,计算新的宽度以保持图像的宽高比。r=height/float(h)dim=(int(w*r),height)else:#如果只指定了宽度,计算新的高度以保持图像的宽高比。r=width/float(w)dim=(width,int(h*r))#根据计算出的尺寸 dim 调整图像大小。resized=cv2.resize(image,dim,interpolation=inter)     # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。return resized# 读取输入
image = cv2.imread('fapiao.jpg')
cv_show('image',image)# 图片过大,进行缩小处理
ratio = image.shape[0] / 500.0  # 计算缩小比率
orig = image.copy()
image = resize(orig, height=500)
cv_show('1',image)# 轮廓检测
print('step 1 : 轮廓检测')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
#二值化处理
edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 自动寻找网值二位化
#检测二值化图像中的轮廓。
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]
#绘制轮廓
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)print('step 2 :获取最大轮廓')
#获取最大轮廓
screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 获取面积最大的轮廓
#计算轮廓周长
peri = cv2.arcLength(screenCnt,True)   # 计算轮廓周长
#轮廓近似
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 轮廓近似
#绘制近似轮廓
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)# 透视变换
warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
cv2.imwrite('invoice_new.jpg',warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv2.imshow('xx',warped)
cv2.waitKey(0)a=cv2.imread('invoice_new.jpg')
a=cv2.cvtColor(a,cv2.COLOR_BGR2GRAY)
a=cv2.threshold(a,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
a=cv2.resize(a,(800,600))
a=np.rot90(a,1)kernel = np.ones((1,1), np.uint8)c=cv2.morphologyEx(a,cv2.MORPH_CLOSE,kernel)cv2.imshow('a',c)
cv2.waitKey(0)

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

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

相关文章

vue3 通过 axios + jsonp 实现根据公网 ip, 查询天气信息

前提 安装 axios 的 jsonp 适配器。 pnpm install pingtou/axios-jsonp 简单使用说明:当与后端约定的请求 callback 参数名称不为为 callback 时,可修改。一般无需添加。 1. 获取当前电脑 ip 和城市信息 请求地址: https://whois.pconl…

【质优价廉】GAP9 AI算力处理器赋能智能可听耳机,超低功耗畅享未来音频体验!

当今世界,智能可听设备已经成为了流行趋势。随后耳机市场的不断成长起来,消费者又对AI-ANC,AI-ENC(环境噪音消除)降噪的需求逐年增加,但是,用户对于产品体验的需求也从简单的需求,升…

isilon存储node节点更换你必须知道的知识

最近一直想要写一篇文章是关于EMC Isilon 存储控制器方面的,是什么力量促使我要写这个文章呢?作为一个卖存储备件的资深搬运工,最近遇到了一些关于控制器方面的备件询价、备件更换方面的问题,每次都要花大量的时间给客户解释。解释…

【JVM】JVM执行流程和内存区域划分

文章目录 是什么JVM 执行流程内存区域划分堆栈程序计数器元数据区经典笔试题 是什么 Java 虚拟机 JDK,Java 开发工具包JRE,Java 运行时环境JVM,Java 虚拟机 JVM 就是 Java 虚拟机,解释执行 Java 字节码 JVM 执行流程 编程语言…

24年下重庆事业单位考试报名超详细流程

🎈提交报考申请 考生通过重庆市人力资源和社会保障局官网(rlsbj.cq.gov.cn)“热点服务”中“人事考试网上报名”栏进行报名。报名时间为2024年8月12日9:00—8月17日9:00。 🎈网上缴费 资格初审合格后,考生应在2024年8…

奇瑞汽车—经纬恒润 供应链技术共创交流日 成功举办

2024年9月12日,奇瑞汽车—经纬恒润技术交流日在安徽省芜湖市奇瑞总部成功举办。此次盛会标志着经纬恒润与奇瑞汽车再次携手,深入探索汽车智能化新技术的前沿趋势,共同开启面向未来的价值服务与产品新篇章。 面对全球汽车智能化浪潮与产业变革…

讯飞星火编排创建智能体学习(一)最简单的智能体构建

开篇 前段时间在华为全联接大会上看到讯飞星火企业级智能体平台的演示,对于拖放的可视化设计非常喜欢,刚开始以为是企业用户才有的,回来之后查了才知道个人用户也能使用。不过有关编排智能体的介绍特别少,也没有找到文档&#xf…

docker入门总结(附错误处理,持续更新)

安装、启动、卸载 卸载掉旧版本的 Docker yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engineDocker安装(选其一)…

Tableau|一入门

一 什么是BI工具 BI 工具即商业智能(Business Intelligence)工具,是一种用于收集、整理、分析和展示企业数据的软件系统,其主要目的是帮助企业用户更好地理解和利用数据,以支持决策制定。 主要功能: 1.数据…

LeetCode 每周算法 8(栈、堆)

LeetCode 每周算法 8(栈、堆) 栈算法: class Solution { public: // 判断括号是否有效的函数 bool isValid(string s) { int n s.size(); // 获取字符串s的长度 // 如果字符串长度为奇数,则括号无法有效匹配,直…

【Linux网络】详解TCP协议(3)

🎉博主首页: 有趣的中国人 🎉专栏首页: Linux网络 🎉其它专栏: C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好,本片文章将会讲解 TCP的流量控制和滑动窗口 的相关内容。 如果看到最后您觉得这篇…

性能测试学习1:性能测试的理论与目的,与功能测试的区别

一.什么是性能? 1)性能:就是软件质量属性中的“效率”特性 2)效率特性: ①时间特性:表示系统处理用户请求的响应时间【通俗来说,就是使用系统是否流畅】 ②资源特性:表示系统运行过程中&…

锐捷 NBR 1300G路由器 越权CLI命令执行漏洞

漏洞描述 锐捷NBR 1300G路由器 越权CLI命令执行漏洞,guest账户可以越权获取管理员账号密码 漏洞复现 FOFA title"锐捷网络 --NBR路由器--登录界面" 请求包 POST /WEB_VMS/LEVEL15/ HTTP/1.1 Host: Connection: keep-alive Content-Length: 73 Autho…

Python爬虫之requests模块(一)

Python爬虫之requests模块(一) 学完urllib之后对爬虫应该有一定的了解了,随后就来学习鼎鼎有名的requests模块吧。 一、requests简介。 1、什么是request模块? requests其实就是py原生的一个基于网络请求的模块,模拟…

封装左侧抽屉可拖拽组件【可多个】

一、案例效果 二、案例代码 封装抽屉组件 <template><div class"drag-drawer"><div class"out-box" :style"style"><mtd-tooltip:content"collapse ? 展开面板 : 收起面板"class"tool-tip":placeme…

AI周报(9.22-9.28)

AI应用-Siipet宠物沟通师 Siipet是一款由SiiPet公司推出的创新宠物行为分析相机&#xff0c;旨在通过尖端技术加深宠物与主人之间的情感联系。这款相机利用先进的AI算法&#xff0c;能够自动识别和分析家中宠物的行为&#xff0c;并提供定制化的护理建议。 SiiPet相机的核心功…

GUI-Guider LVGL 添加自定义代码

添加自定义代码时&#xff0c;分为上线两端 1.上部分可有可无 2.下部分为你触发事件时调用的语句 具体集合下方图片 示例参考

Python入门:掌握inspect模块,轻松调试你的代码!

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 什么是inspect模块?📝 inspect模块的常用方法📝 1. 获取对象的信息📝 2. 获取函数的参数📝 3. 检查对象类型📝 4. 获取文档字符串📝 5. 获取调用方法的名称📝 实际应用场景⚓️ 相关链接 ⚓️…

[大语言模型-论文精读] Diffusion Model技术-通过时间和空间组合扩散模型生成复杂的3D人物动作

​​​​​​Generation of Complex 3D Human Motion by Temporal and Spatial Composition of Diffusion Models L Mandelli, S Berretti - arXiv preprint arXiv:2409.11920, 2024 通过时间和空间组合扩散模型生成复杂的3D人物动作 摘要 本文提出了一种新的方法&#xff0…

Spring AOP - 注解方式实现

前文已经讨论了基于配置文件方式实现Spring AOP&#xff08;Spring AOP - 配置文件方式实现&#xff09;&#xff0c;本文采用注解的方式实现前文相同的功能。配置步骤如下&#xff1a; 1、项目增加aop依赖&#xff08;pom.xml) <dependency><groupId>org.springfr…