OpenCv高阶(4.0)——案例:海报的透视变换
文章目录
- 前言
- 一、工具函数模块
- 1.1 图像显示函数
- 1.2 保持宽高比的缩放函数
- 1.3 坐标点排序函数
- 二、透视变换核心模块
- 2.1 四点透视变换实现
- 三、主流程技术分解
- 3.1 图像预处理
- 3.2 轮廓检测流程
- 3.3 最大轮廓处理
- 四、后处理技术
- 4.1 透视变换
- 4.2 形态学处理
- 五、完整代码
- 总结
前言
构建智能文档视觉矫正体系:通过 AI 图像识别技术自动分析文档倾斜特征,结合自适应透视校正模型动态调整校正参数,不仅消除物理歪斜(如扫描歪斜、拍摄俯仰角偏差),还能修复因纸张褶皱、扫描设备误差导致的 “隐性扭曲”,最终呈现视觉上完全平整、几何上严格对齐的标准文档版面,为专业打印奠定基础。
一、工具函数模块
1.1 图像显示函数
def cv_show(name, value):cv2.imshow(name, value) # 创建命名窗口显示图像cv2.waitKey(0) # 等待任意键输入(单位:毫秒,0表示无限等待)cv2.destroyAllWindows() # 关闭所有OpenCV窗口(实际代码中未显式调用,需注意内存释放)
cv2.waitKey(0) 的返回值为按键ASCII码,可用于交互控制
调试建议:添加 cv2.destroyWindow(name) 关闭指定窗口,避免内存泄漏
1.2 保持宽高比的缩放函数
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 image # 无缩放直接返回if width is None:r = height / float(h) # 计算高度缩放比例dim = (int(w * r), height) # 新尺寸元组(宽度, 高度)else:r = width / float(w) # 计算宽度缩放比例dim = (width, int(h * r))# 执行缩放操作resized = cv2.resize(image, dim, interpolation=inter)return resized
插值算法选择:
cv2.INTER_AREA:基于像素区域关系重采样,适合缩小图像
cv2.INTER_CUBIC:4x4像素邻域的双三次插值,适合放大图像
cv2.INTER_LINEAR:双线性插值(默认)
1.3 坐标点排序函数
def order_points(pts):rect = np.zeros((4, 2), dtype="float32") # 初始化4x2矩阵#按顺序找到对应的坐标0123,分别是左上右上右下、左下# 计算坐标点x+y的和s = pts.sum(axis=1) # 形状:(4,) ,对矩阵的每一行进行求和操作rect[0] = pts[np.argmin(s)] # 左上角:x+y最小rect[2] = pts[np.argmax(s)] # 右下角:x+y最大# 计算坐标点x-y的差diff = np.diff(pts, axis=1) # 形状:(4,1)rect[1] = pts[np.argmin(diff)] # 右上角:x-y最小(即y相对较大)rect[3] = pts[np.argmax(diff)] # 左下角:x-y最大(即y相对较小)return rect # 返回有序坐标:[左上, 右上, 右下, 左下]
二、透视变换核心模块
2.1 四点透视变换实现
def four_point_transform(image, pts):# 坐标排序(关键步骤!)rect = order_points(pts)(tl, tr, br, bl) = rect # 解构赋值四个顶点# 计算输出图像的宽度(取两组对边最大值)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], # 宽度方向预留1像素边界[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype="float32")# 计算透视变换矩阵(核心数学操作)M = cv2.getPerspectiveTransform(rect, dst)# 执行透视变换warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warped
1.尺寸计算
公式推导:基于欧氏距离公式计算对边长度
distance = sqrt((x2-x1)^2 + (y2-y1)^2)取最大值的原因:确保输出图像能完整包含原始四边形区域
2.cv2.getPerspectiveTransform
输入:源四边形(rect)和目标四边形(dst)的4个点输出:3x3透视变换矩阵数学原理:求解投影变换方程
透视变换矩阵公式
通过4对点建立8个方程,求解8个未知参数
3.cv2.warpPerspective
参数 M:3x3变换矩阵参数 (maxWidth, maxHeight):输出图像尺寸插值方式:默认双线性插值(可指定cv2.INTER_LINEAR等)
三、主流程技术分解
3.1 图像预处理
# 读取原始图像
img = cv2.imread('../data/hb.png') # 默认BGR格式# 计算缩放比例(基于原始高度)
ratio = img.shape[0] / 500.0 # 假设将高度缩放到500px
orig = img.copy() # 深拷贝保留原始图像
img = resize(orig, height=500) # 执行缩放
缩放目的:加速后续轮廓处理(复杂度与图像尺寸成平方关系)
ratio的作用:后续将检测到的坐标还原到原始尺寸
3.2 轮廓检测流程
# 灰度化处理
gray = cv2.cvtColor(img, 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)[-2]
img_contours=cv2.drawContours(img.copy(),cnts,-1,(0,0,255),1)
cv_show('img_contours',img_contours)
cv2.findContours参数解析:
edged.copy():避免修改原始边缘图像
cv2.RETR_LIST:检索所有轮廓,不建立层级关系
cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角方向的冗余点
[-2]:兼容不同OpenCV版本(返回格式可能为 [contours, hierarchy] 或 [image, contours, hierarchy])
3.3 最大轮廓处理
# 按轮廓面积排序(降序)
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
#获取面积最大的轮廓
# 计算轮廓周长
peri = cv2.arcLength(screenCnt, True) # True表示轮廓闭合# 多边形近似(Douglas-Peucker算法)
screenCnt = cv2.approxPolyDP(screenCnt, 0.01 * peri, True)
print(screenCnt.shape)
img_contours=cv2.drawContours(img.copy(),[screenCnt],-1,(0,255,0),2)cv_show('img_contours1',img_contours)
1、cv2.contourArea
计算方式:格林公式积分对非闭合轮廓可能返回错误值
2、cv2.approxPolyDP
参数 0.01*peri:近似精度阈值(周长比例)原理:迭代拟合多边形,删除偏离当前线段超过阈值的点效果:将曲线近似为折线,减少顶点数量
四、后处理技术
4.1 透视变换
# 执行变换(注意坐标还原)
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)# 保存结果
cv2.imwrite('../data/invoice_new.jpg', warped)
screenCnt.reshape(4,2):将轮廓点从 (N,1,2) 转换为 (4,2)
ratio:将缩放后的坐标还原到原始图像尺寸
4.2 形态学处理
# 转换为灰度图
warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)# 二值化处理
_, warped_binary = cv2.threshold(warped_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)# 逆时针旋转90度,在对自己的文档进行识别时,可以根据需求调整转动角度
warped_rotated = cv2.rotate(warped_binary, cv2.ROTATE_90_COUNTERCLOCKWISE)# 定义形态学核
kernel = np.ones((11,11),np.uint8)
#做一个闭运算,先膨胀再腐蚀
warped_rotated_closed=cv2.morphologyEx(warped_rotated,cv2.MORPH_CLOSE,kernel)
cv2.namedWindow('warped_rotated_closed',cv2.WINDOW_NORMAL)
cv_show("warped_rotated_closed",warped_rotated)
腐蚀(Erode):用核的最小值替换锚点像素,消除小物体
膨胀(Dilate):用核的最大值替换锚点像素,填充空洞
开运算 = 先腐蚀后膨胀,用于去噪
核大小选择:3x3平衡去噪效果与细节保留
目的是为了使海报上的图案和文字显示的更清晰,字迹不会很模糊。
最终效果展示:
将歪歪扭扭的图片扶正,并将图片上的文字变得更清晰,上边的效果仍没有达到预计的效果,可以通过图像形态学的处理来达到更好的效果。
五、完整代码
import numpy as np
import cv2
def cv_show(name,value):cv2.imshow(name,value)cv2.waitKey(0)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))resized=cv2.resize(image,dim,interpolation=inter)return resizeddef order_points(pts):#一共四个坐标点rect=np.zeros((4,2),dtype='float32')#按顺序找到对应的坐标0123,分别是左上右上右下、左下s=pts.sum(axis=1) #对矩阵的每一行进行求和操作rect[0]=pts[np.argmin(s)]rect[2]=pts[np.argmax(s)]diff=np.diff(pts,axis=1)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#计算输入的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,0],[maxwidth,maxheight],[0,maxheight]],dtype='float32')M=cv2.getPerspectiveTransform(rect,dst)warped=cv2.warpPerspective(image,M,(maxwidth,maxheight))return warpedimg=cv2.imread('../data/hb.png')
cv_show('fp',img)# 图片多大调整大小
ratio=img.shape[0]/500.0 #计算缩小比例
orig=img.copy()
img=resize(orig,height=500)
# cv_show('1',img)#轮廓检测
print("STEP 1:轮廓检测")
gray=cv2.cvtColor(img,cv2.COLOR_BGRA2GRAY) #获取灰度图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)[-2]
img_contours=cv2.drawContours(img.copy(),cnts,-1,(0,0,255),1)
cv_show('img_contours',img_contours)print("STEP 2:获取最大轮廓")
screenCnt=sorted(cnts,key=cv2.contourArea,reverse=True)[0] #获取面积最大的轮廓peri=cv2.arcLength(screenCnt,True)screenCnt=cv2.approxPolyDP(screenCnt,0.01*peri,True)
print(screenCnt.shape)
img_contours=cv2.drawContours(img.copy(),[screenCnt],-1,(0,255,0),2)cv_show('img_contours1',img_contours)# 透视变换
warped=four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
cv2.imwrite('../data/invoice_new.jpg',warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL) #设置这个参数可以使imshow()显示出来的窗口被拉大缩小
cv2.imshow('xx',warped)
cv2.waitKey(0)warped_gray=cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY)
warped_binary=cv2.threshold(warped_gray.copy(),0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv2.namedWindow('warped_binary',cv2.WINDOW_NORMAL)
cv_show("warped_binary",warped_binary)warped_rotated=cv2.rotate(warped_binary,cv2.ROTATE_90_COUNTERCLOCKWISE)
cv2.namedWindow('warped_rotated',cv2.WINDOW_NORMAL)
cv_show("warped_rotated",warped_rotated)#做一个闭运算,先膨胀再腐蚀
kernel = np.ones((3,3),np.uint8)
warped_rotated_closed=cv2.morphologyEx(warped_rotated,cv2.MORPH_OPEN,kernel)
cv2.namedWindow('warped_rotated_closed',cv2.WINDOW_NORMAL)
cv_show("warped_rotated_closed",warped_rotated)
总结
通过对计算机视觉技术的综合使用,可以实现很多种对文档的处理任务,还可以将将功能与摄像头结合实现实时处理任务。
相关文章:

OpenCv高阶(4.0)——案例:海报的透视变换
文章目录 前言一、工具函数模块1.1 图像显示函数1.2 保持宽高比的缩放函数1.3 坐标点排序函数 二、透视变换核心模块2.1 四点透视变换实现 三、主流程技术分解3.1 图像预处理3.2 轮廓检测流程3.3 最大轮廓处理 四、后处理技术4.1 透视变换4.2 形态学处理 五、完整代码总结 前言…...

光谱相机的图像预处理技术
光谱相机的图像预处理技术旨在消除噪声、增强有效信息,为后续分析提供高质量数据。 一、预处理流程与技术要点 辐射校正 辐射定标:将图像灰度值转换为绝对辐射亮度,常用反射率法、辐亮度法和辐照度法消除传感器响应差异࿰…...
CSS 溢出内容处理、可见性控制与盒类型设置深度解析
CSS溢出内容处理、可见性控制与盒类型设置深度解析 一、溢出内容处理(Overflow) 在网页设计中,内容超出容器边界是常见问题。CSS提供了overflow属性及其变体来控制这种情况。 1.1 溢出基本属性 核心属性: overflow: visible&…...

k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持
k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持 文章目录 k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持一、Metrics Server简介二、Metrics Server实战部署…...
从代码学习深度学习 - 实战 Kaggle 比赛:图像分类 (CIFAR-10 PyTorch版)
文章目录 前言1. 读取并整理数据集1.1 读取标签文件1.2 划分训练集和验证集1.3 整理测试集1.4 执行数据整理2. 图像增广2.1 训练集图像变换2.2 测试集(和验证集)图像变换3. 读取数据集3.1 创建 Dataset 对象3.2 创建 DataLoader 对象4. 定义模型4.1 获取 ResNet-18 模型4.2 损…...
【数据结构】二分查找5.12
Basic 需求:在有序数组A内,查找值target 如果找到返回索引 如果找不到返回-1 算法描述: 前提:给定一个内含n个元素的有序数组A(升序),一个待查找值 设置两个索引:i0;jn-1; 如果…...
深入探索向量数据库:构建智能应用的新基础
📌 友情提示: 本文内容由银河易创AI(https://ai.eaigx.com)创作平台的gpt-4-turbo模型辅助生成,旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证,建议读者通过官方文档或实践进一步确认…...
Swagger go中文版本手册
Swaggo(github.com/swaggo/swag)的注解语法是基于 OpenAPI 2.0 (以前称为 Swagger 2.0) 规范的,并添加了一些自己的约定。 主要官方文档: swaggo/swag GitHub 仓库: 这是最权威的来源。 链接: https://github.com/swaggo/swag重点关注: README.md: 包含了基本的安装、使用…...
Cloudera CDP 7.1.3 主机异常关机导致元数据丢失,node不能与CM通信
问题描述 plaintext ERROR Could not load post-deployment data from /var/run/cloudera-scm-agent/process/ccdeploy_hadoop-conf_etchadoopconf.cloudera.yarn_-8903374259073700469 IOError: [Errno 2] No such file or directory: /var/run/cloudera-scm-agent/proce…...
Redis特性与应用
1、分布式缓存与redis 2、redis数据结构和客户端集成 3、缓存读写模式与数据一致性 本地缓存:Hash Map、Ehcache、Caffeine、Google Guava 分布式缓存:Memcached、redis、Hazelcast、Apache ignite redis:基于键值对内存数据库,支…...

嵌入式调试新宠!J-Scope:免费+实时数据可视化,让MCU调试效率飙升!
📌 痛点直击:调试还在用“断点打印”? 嵌入式开发中,你是否也经历过这些崩溃瞬间? 想实时观察变量变化,代码里插满printf,结果拖垮系统性能? 断点调试打断程序运行,时序…...

微信小程序学习之搜索框
1、第一步,我们在index.json中引入vant中的搜索框控件: {"usingComponents": {"van-search": "vant/weapp/search/index"} } 2、第二步,直接在index.wxml中添加布局: <view class"index…...

Altium Designer AD如何输出PIN带网络名的PDF装配图
Altium Designer AD如何输出PIN带网络名的PDF装配图 文描述在Altium Designer版本中设置焊盘网络名时遇到的问题,网络名大小不一致,部分PAD的网络名称未显示,可能涉及字符大小设置和版本差异。 参考 1.AD导出PCB装配图 https://blog.csd…...

VMware虚拟机 安装 CentOS 7
原文链接: VMware虚拟机 安装 CentOS 7 安装准备 软件: VMware Workstation Pro 17.6.3 镜像: CentOS-7.0-1406-x86_64-DVD.iso 我打包好放这了,VMware 和 CentOS7 ,下载即可。 关于VMware Workstation Pro 17.6.3,傻瓜式安装即可。 CentO…...
关于高并发GIS数据处理的一点经验分享
1、背景介绍 笔者过去几年在参与某个大型央企的项目开发过程中,遇到了十分棘手的难题。其与我们平常接触的项目性质完全不同。在一般的项目中,客户一般只要求我们能够通过桌面软件对原始数据进行加工处理,将各类地理信息数据加工处理成地图/场景和工作空间,然后再将工作空…...
Python训练打卡Day22
复习日: 1.标准化数据(聚类前通常需要标准化) scaler StandardScaler() X_scaled scaler.fit_transform(X) StandardScaler() :这部分代码调用了 StandardScaler 类的构造函数。在Python中,当你在类名后面加上括号…...

Cold Diffusion: Inverting Arbitrary Image Transforms Without Noise论文阅读
冷扩散:无需噪声的任意图像变换反转 摘要 标准扩散模型通常涉及两个核心步骤:图像降质 (添加高斯噪声)和图像恢复 (去噪操作)。本文发现,扩散模型的生成能力并不强烈依赖于噪声的选择…...
2025认证杯数学建模第二阶段C题:化工厂生产流程的预测和控制,思路+模型+代码
2025认证杯数学建模第二阶段思路模型代码,详细内容见文末名片 一、探秘化工世界:问题背景大揭秘 在 2025 年 “认证杯”数学中国数学建模网络挑战赛第二阶段 C 题中,我们一头扎进了神秘又复杂的化工厂生产流程预测与控制领域。想象一下&…...
物联网驱动的共享充电站系统:智能充电的实现原理与技术解析!
随着新能源汽车的快速普及,共享充电站系统作为其核心基础设施,正通过物联网技术的深度赋能,实现从“传统充电”到“智能充电”的跨越式升级。本文将从系统架构、核心技术、优化策略及实际案例等角度,解析物联网如何驱动共享充电站…...

嵌软面试每日一阅----通信协议篇(二)之TCP
一. TCP和UDP的区别 可靠性 TCP:✅ 可靠传输(三次握手 重传机制) UDP:❌ 不可靠(可能丢包) 连接方式 TCP:面向连接(需建立/断开连接) UDP:无连接࿰…...

机器学习 --- 模型选择与调优
机器学习 — 模型选择与调优 文章目录 机器学习 --- 模型选择与调优一,交叉验证1.1 保留交叉验证HoldOut1.2 K-折交叉验证(K-fold)1.3 分层k-折交叉验证Stratified k-fold 二,超参数搜索三,鸢尾花数据集示例四,现实世界数据集示例…...
《Python星球日记》 第58天:Transformer 与 BERT
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、引言一、Transformer 架构简介1. 自注意力机制(Self-Attention)工作原理2. 多头注意力与位置编码多头注意力机制位置编码二、BERT 的结构…...
2900. 最长相邻不相等子序列 I
2900. 最长相邻不相等子序列 I class Solution:def getLongestSubsequence(self, words: List[str], groups: List[int]) -> List[str]:n len(groups) # 获取 groups 列表的长度ans [] # 初始化一个空列表,用于存储结果for i, g in enumerate(groups): # 遍…...

AGI大模型(15):向量检索之调用ollama向量数据库
这里介绍将向量模型下载到本地,这里使用ollama,现在本地安装ollama,这里就不过多结束了。直接从下载开始。 1 下载模型 首先搜索模型,这里使用bge-large模型,你可以根据自己的需要修改。 点击进入,复制命令到命令行工具中执行。 安装后查看: 2 代码实现 先下载ollama…...
Python网络请求利器:urllib库深度解析
一、urllib库概述 urllib是Python内置的HTTP请求库,无需额外安装即可使用。它由四个核心模块构成: urllib.request:发起HTTP请求的核心模块urllib.error:处理请求异常(如404、超时等)…...

什么是Agentic AI(代理型人工智能)?
什么是Agentic AI(代理型人工智能)? 一、概述 Agentic AI(代理型人工智能)是一类具备自主决策、目标导向性与持续行动能力的人工智能系统。与传统AI系统依赖外部输入和显式命令不同,Agentic AI在设定目标…...

day 17 无监督学习之聚类算法
一、聚类流程 1. 利用聚类发现数据模式 无监督算法中的聚类,目的就是将数据点划分成不同的组或 “簇”,使得同一簇内的数据点相似度较高,而不同簇的数据点相似度较低,从而发现数据中隐藏的模式。 2. 对聚类后的类别特征进行可视…...
《Python星球日记》 第68天:BERT 与预训练模型
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、BERT模型基础1. 什么是BERT?2. BERT 的结构3.预训练和微调对比二、BERT 的预训练任务1. 掩码语言模型 (MLM)2. 下一句预测 (NSP)三、微调 …...
Spring 集成 SM4(国密对称加密)
Spring 集成 SM4(国密对称加密)算法 主要用于保护敏感数据,如身份证、手机号、密码等。 下面是完整集成步骤(含工具类 使用示例),采用 Java 实现(可用于 Spring Boot)。 一、依赖引…...

时源芯微| KY键盘接口静电浪涌防护方案
KY键盘接口静电浪涌防护方案通过集成ESD保护元件、电阻和连接键,形成了一道有效的防护屏障。当键盘接口受到静电放电或其他浪涌冲击时,该方案能够迅速将过电压和过电流引导至地,从而保护后续电路免受损害。 ESD保护元件是方案中的核心部分&a…...