《深度学习》【项目】OpenCV 发票识别 透视变换、轮廓检测解析及案例解析
目录
一、透视变换
1、什么是透视变换
2、操作步骤
1)选择透视变换的源图像和目标图像
2)确定透视变换所需的关键点
3)计算透视变换的变换矩阵
4)对源图像进行透视变换
5)对变换后的图像进行插值处理
二、轮廓检测
1、什么是轮廓检测
2、操作步骤
1)图像预处理
2)边缘检测
3)边缘连接
4)轮廓筛选
5)轮廓绘制
三、项目实施
1、定义展示图片函数
2、定义自动缩放图片大小函数
3、定义轮廓点的排序函数
4、定义透视变换函数
5、读取原图并缩放
运行结果:
6、进行轮廓检测
运行结果:
5、绘制最大轮廓
运行结果:
6、对最大轮廓进行透视变换
运行结果:
7、旋转、二值化处理
运行结果:
一、透视变换
1、什么是透视变换
透视变换是一种图像处理技术,用于将二维平面上的图像或物体映射到三维空间中。它通过改变图像的视角和投影来创建一个具有透视效果的图像。
透视变换通常用于计算机图像形态学和计算机视觉领域,用于实现图像的透视效果、立体视觉、图像校正等应用。它可以模拟人眼在观察远景时的透视效果,使得远处的物体看起来比近处的物体小,同时使得平行线在远处会相交的视觉效果。
透视变换的实现通常需要通过计算图像中各点在三维空间中的坐标,并将其映射回二维平面上,从而实现透视效果。这个过程涉及到几何变换、矩阵运算和投影变换等数学概念和算法。
2、操作步骤
1)选择透视变换的源图像和目标图像
源图像是需要进行透视变换的原始图像,目标图像是希望得到的透视变换后的图像。
2)确定透视变换所需的关键点
根据透视变换的要求,需要选择源图像中的四个关键点以及对应的目标图像中的四个关键点。这四个关键点共同决定了透视变换的变换矩阵。
3)计算透视变换的变换矩阵
通过四个关键点的对应关系,使用透视变换的数学公式计算出透视变换的变换矩阵。这个变换矩阵将源图像中的像素映射到目标图像中的像素。
4)对源图像进行透视变换
使用计算得到的变换矩阵,对源图像中的每个像素进行变换,计算其在目标图像中的对应像素位置。
5)对变换后的图像进行插值处理
由于透视变换可能会导致源图像中的像素映射到目标图像中的非整数位置,因此需要对其进行插值处理,以得到最终的目标图像。
二、轮廓检测
1、什么是轮廓检测
轮廓检测是一种图像处理技术,用于在图像中找到物体的边界。在图像处理领域中,物体的边界通常被表示为连续的曲线,这些曲线被称为轮廓。轮廓检测算法可以识别图像中的明显变化或不连续的像素,从而确定物体的形状和结构。
轮廓检测算法的基本原理是通过分析图像中的亮度、颜色或纹理等特征,找到物体与背景之间的显著边缘或变化。常用的轮廓检测算法包括Canny边缘检测、Sobel算子、拉普拉斯算子等。
通过轮廓检测,可以实现图像分割、目标识别、形状匹配等应用。在计算机视觉和图像处理领域中,轮廓检测是一项重要的技术,广泛应用于物体检测与跟踪、图像分析与理解、机器视觉等领域。
2、操作步骤
1)图像预处理
首先对输入图像进行预处理,可以包括灰度化、平滑滤波、边缘增强等操作,以减少噪声和突出边缘信息。
2)边缘检测
使用边缘检测算法(如Canny、Sobel、拉普拉斯等)来检测图像中的边缘。这些算法通过计算像素间的梯度或差异,找到亮度或颜色变化较大的区域。
3)边缘连接
将离散的边缘点连接成连续的轮廓线。常用的方法包括利用边缘点的邻域信息进行连接,或者利用轮廓线的闭合性质进行曲线追踪。
4)轮廓筛选
根据一定的准则对检测到的轮廓进行筛选,去除无关的轮廓。可以根据轮廓的长度、面积、形状等特征进行筛选。
5)轮廓绘制
最后,将筛选后的轮廓绘制在原始图像上,以便观察和分析。
具体可参考博客:
《深度学习》OpenCV 图像轮廓检测、轮廓处理及代码演示
https://ahao1004.blog.csdn.net/article/details/141830045?fromshare=blogdetail&sharetype=blogdetail&sharerId=141830045&sharerefer=PC&sharesource=qq_64603703&sharefrom=from_link
三、项目实施
1、定义展示图片函数
import numpy as np
import cv2
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)
2、定义自动缩放图片大小函数
# 调整图像高宽,保持图像宽高比不变
def resize(image,width=None,height=None ,inter=cv2.INTER_AREA): # 输入参数为图像、可选宽度、可选高度、插值方式默认为cv2.INTER_AREA,即面积插值dim = None # 存储计算后的目标尺寸w、h(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: # 此处表示为width不是None,即指定了宽度,与上述方法一致,计算比值r = width/float(w)dim = (width,int(h*r))resized = cv2.resize(image,dim,interpolation=inter) # 指定图像大小为上述的dim,inter默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。return resized
3、定义轮廓点的排序函数
def order_points(pts): # 对输入的四个点按照左上、右上、右下、左下进行排序rect = np.zeros((4,2),dtype='float32') # 创建一个4*2的数组,用来存储排序之后的坐标位置# 按顺序找到对应坐标0123分别是左上、右上、右下、左下s = pts.sum(axis=1) # 对pts矩阵的每个点的x y相加rect[0] = pts[np.argmin(s)] # np.argmin(s)表示数组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 # 返回排序好的四个点的坐标
4、定义透视变换函数
# 将透视扭曲的矩形变换成一个规则的矩阵
def four_point_transform(image,pts):# 获取输入坐标点rect = order_points(pts) # 为上述排序的四个点(tl,tr,br,bl) = rect # 分别返回给四个值,分别表示为左上、右上、右下、左下# 计算输入的w和h值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')M = cv2.getPerspectiveTransform(rect,dst) # 根据原始点和变换后的点计算透视变换矩阵Mwarped = cv2.warpPerspective(image,M,(maxWidth,maxHeight)) # 对原始图像,针推变换矩阵和输出图像大小进行透视变换,返回变换后的图片# 返回变换后的结果return warped
5、读取原图并缩放
# # 读取输入
image = cv2.imread('fapiao.jpg') # 读取原图
cv_show('image',image) # 展示原图# 图片过大,进行缩小处理
ratio = image.shape[0] / 500.0 # 计算缩小比率,[0]表示图像的高
orig = image.copy() # 对原图复制生成副本
image = resize(orig, height=500) # 更改图像尺寸,输入高度自动生成宽度
cv_show('1',image) # 展示缩放后的图片
运行结果:

6、进行轮廓检测
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 灰度图edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 进行二值化,cv2.THRESH_OTSU自动寻找最优全局阈值,255表示高于最优阈值时将其更改为255
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1] # 轮廓检测
# cv2.RETR_LIST表示检索所有轮廓,但是不建立层次关系
# cv2.CHAIN_APPROX_SIMPLE 表示只保存轮廓拐点的信息
# 总体返回处理的图像、轮廓列表、层次结构,这里返回索引为1,表示返回轮廓列表image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1) # 绘制所有轮廓
# 在原始图像的副本上绘制了轮廓
# 绘制轮廓的位置为上述获取的拐点信息,绘制线条颜色为红色BRG(0,0,255),线条粗细为1个像素cv_show('image_contours',image_contours) # 展示绘制好的图片
运行结果:

5、绘制最大轮廓
screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0] # 对上述获取的轮廓列表,排序依据是轮廓面积,reverse=True表示降序,[0]表示获取面积最大的轮廓
peri = cv2.arcLength(screenCnt,True) # 计算最大轮廓的周长
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True) # 轮廓近似,近似为一个多边形,表示新的轮廓与原来的轮廓最大距离不超过原始轮廓宽度的0.02倍,True表示轮廓为闭合的
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2) # 绘制轮廓,将上述找到的轮廓绘制到原图的副本上
cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)
运行结果:

6、对最大轮廓进行透视变换
warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio) # 输入参数原图,将最大轮廓图形状改变为4*2的格式,即四个点,然后乘以上述定义的比率来缩放轮廓
cv2.imwrite('invoice_new.jpg',warped) # 将经过透视变换处理的图片存入本地
cv2.namedWindow('xx',cv2.WINDOW_NORMAL) # 设置一个窗口,名称为xx,这个窗口大小用户可通过拖动随意调节大小
cv2.imshow('xx',warped) # 展示经过透视变换处理的图片
cv2.waitKey(0)
运行结果:

7、旋转、二值化处理
# 二值处理
warped = cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY) # 导入新的图片的灰度图
ref = cv2.threshold(warped,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1] # 对灰度图进行二值化处理kernel = np.ones((2,2),np.uint8) # 设置一个单位矩阵,大小为2*2,表示设置核kernel的大小
ref_new = cv2.morphologyEx(ref,cv2.MORPH_CLOSE,kernel) # 闭运算,先膨胀再腐蚀
ref_new = resize(ref_new.copy(),width=500) # 对闭运算处理完的图像重置大小
cv_show('yy',ref_new)
rotated_image = cv2.rotate(ref_new,cv2.ROTATE_90_COUNTERCLOCKWISE) # 对图像逆时针旋转90度
cv2.imshow('result',rotated_image)
cv2.waitKey(0)
运行结果:

相关文章:
《深度学习》【项目】OpenCV 发票识别 透视变换、轮廓检测解析及案例解析
目录 一、透视变换 1、什么是透视变换 2、操作步骤 1)选择透视变换的源图像和目标图像 2)确定透视变换所需的关键点 3)计算透视变换的变换矩阵 4)对源图像进行透视变换 5)对变换后的图像进行插值处理 二、轮廓检测…...
Linux 线程互斥
前言 对于初学线程的伙伴来讲,多线程并发访问导致的数据不一致问题,总是让人陷入怀疑,很多人只是给你说加锁!但没有人告诉你为什么?本篇博客将详解! 目录 前言 一、线程互斥 • 为什么票会出现负数的情…...
【Redis 源码】6AOF持久化
1 AOF功能说明 aof.c 文件是 Redis 中负责 AOF(Append-Only File)持久化的核心文件。AOF 持久化通过记录服务器接收到的每个写命令来实现数据的持久化。这样,在 Redis 重启时,可以通过重放这些命令来恢复数据。 2 AOF相关配置 a…...
6.MySQL基本查询
目录 表的增删查改Insert(插入)插入替换插入替换2 Retrieve(查找)SELECT 列全列查找指定列查询查询字段为表达式为查询结果指定别名结果去重 WHERE 条件order by子句筛选分页结果 Update(更新)delete&#…...
Linux字符设备驱动开发
Linux 字符设备驱动开发是内核模块开发中的一个重要部分,主要用于处理字节流数据设备(如串口、键盘、鼠标等)。字符设备驱动的核心任务是定义如何与用户空间程序交互,通常通过一组文件操作函数进行。这些函数会映射到 open、read、…...
HTML5+JavaScript绘制闪烁的网格错觉
HTML5JavaScript绘制闪烁的网格错觉 闪烁的网格错觉(scintillating grid illusion)是一种视觉错觉,通过简单的黑白方格网格和少量的精心设计,能够使人眼前出现动态变化的效果。 闪烁的栅格错觉,是一种经典的视觉错觉…...
每日OJ题_牛客_拼三角_枚举/DFS_C++_Java
目录 牛客_拼三角_枚举/DFS 题目解析 C代码1 C代码2 Java代码 牛客_拼三角_枚举/DFS 拼三角_枚举/DFS 题目解析 简单枚举,不过有很多种枚举方法,这里直接用简单粗暴的枚举方式。 C代码1 #include <iostream> #include <algorithm> …...
[uni-app]小兔鲜-01项目起步
项目介绍 效果演示 技术架构 创建项目 HBuilderX创建 下载HBuilderX编辑器 HBuilderX/创建项目: 选择模板/选择Vue版本/创建 安装插件: 工具/插件安装/uni-app(Vue3)编译器 vue代码不能直接运行在小程序环境, 编译插件帮助我们进行代码转换 绑定微信开发者工具: 指定微信开…...
安全的价值:构建现代企业的基础
物理安全对于组织来说并不是事后才考虑的问题:它是关键的基础设施。零售商、医疗保健提供商、市政当局、学校和所有其他类型的组织都依赖安全系统来保障其人员和场所的安全。 随着安全技术能力的不断发展,许多组织正在以更广泛的视角看待他们的投资&am…...
门面(外观)模式
简介 门面模式(Facade Pattern)又叫作外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构型设计模式。 通用模板 创建子系统角色类…...
kotlin flow 使用
1 创建flow 方式1 通过携程扩展函数FlowKt中的flow扩展函数可以直接构建flow,只需要传递FlowCollector收集器实现类就可以了 private fun create1(){val intFlow createFlow()println("创建int flow: $intFlow")runBlocking {println("开始收集&…...
vue3 实现文本内容超过N行折叠并显示“...展开”组件
1. 实现效果 组件内文字样式取决与外侧定义 组件大小发生变化时,文本仍可以省略到指定行数 文本不超过时, 无展开,收起按钮 传入文本发生改变后, 组件展示新的文本 2. 代码 文件名TextEllipsis.vue <template><div ref"compRef" class"wq-text-ellip…...
根据源码解析Vue2中对于对象的变化侦测
UI render(state) VUE的特点是数据驱动视图,在这里可以把数据理解为状态,而视图就是用户可以看到的页面,页面是动态变化的,而他的变化或是用户操作引起,或是后端数据变化引起,这些都可以说是数据的状态变…...
爬虫技术深潜:探究 JsonPath 与 XPath 的语法海洋与实战岛屿
Python爬虫中JSON与XML字符串的XPath和JsonPath过滤语法区别对比 在信息爆炸的互联网时代,数据抓取成为了获取宝贵信息的关键技能。对于技术爱好者,特别是Python程序员来说,熟练掌握JSON和XML数据解析方法至关重要。本文旨在深入探讨这两种格…...
纠删码参数自适应匹配问题ECP-AMP实验方案(一)
摘要 关键词:动态参数;多属性决策;critic权重法;DBSCA聚类分析 引言 云服务存储系统是一种基于互联网的数据存储服务,它可以为用户提供大规模、低成本、高可靠的数据存储空间。云服务存储系统的核心技术之一是数据容…...
五、人物持有武器攻击
一、手部添加预制体(武器) 1、骨骼(手) 由于人物模型有骨骼和动画,在添加预制体后,会抓握武器 建一个预制体在手部位置 二、添加武器拖尾 下载拖尾特效 赋值特效中的代码,直接使用 清空里面…...
mysql索引 -- 全文索引介绍(如何创建,使用),explain关键字
目录 全文索引 引入 介绍 创建 使用 表数据 简单搜索 explain关键字 使用全文索引 mysql索引结构详细介绍 -- mysql索引 -- 索引的硬件理解(磁盘,磁盘与系统),软件理解(mysql,与系统io,buffer pool),索引结构介绍和理解(page内部,page之间,为什么是b树)-CSDN博客 全文…...
Wayfair封号的常见原因及解决方案解析
近期关于Wayfair账号封禁的问题引发了广泛讨论。许多用户报告称,他们的Wayfair账户被突然封禁,这一现象不仅影响了用户的购物体验,也对Wayfair的品牌形象造成了一定的冲击。本文将深入探讨Wayfair封号的原因,并提出相应的解决方案…...
计算机视觉方面的一些模块
# __all__ 是一个可选的列表,定义在模块级别。当使用 from ... import * 语句时,如果模块中定义了 # __all__,则只有 __all__ 列表中的名称会被导入。这是模块作者控制哪些公开API被导入的一种方式。 # 使用 * 导入的行为 # 如果模块中有 __a…...
进阶美颜功能技术开发方案:探索视频美颜SDK
视频美颜SDK(SoftwareDevelopmentKit)作为提升视频质量的重要工具,越来越多地被开发者关注与应用。接下俩,笔者将深入探讨进阶美颜功能的技术开发方案,助力开发者更好地利用视频美颜SDK。 一、视频美颜SDK的核心功能 …...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
C++中vector类型的介绍和使用
文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...
深入理解 C++ 左值右值、std::move 与函数重载中的参数传递
在 C 编程中,左值和右值的概念以及std::move的使用,常常让开发者感到困惑。特别是在函数重载场景下,如何合理利用这些特性来优化代码性能、确保语义正确,更是一个值得深入探讨的话题。 在开始之前,先提出几个问题&…...
