OpenCV:图像透视变换

文章目录

  • 一、透视变换是什么?
  • 二、透视变换的核心原理
    • 1. 关键概念:透视变换矩阵
    • 2. 核心条件:4对对应点
  • 三、OpenCV实现透视变换的关键步骤
    • 步骤1:读取并预处理图像
    • 步骤2:寻找目标物体的4个顶点
    • 步骤3:计算透视变换矩阵
    • 步骤4:执行透视变换
  • 四、实战:用透视变换矫正发票图像
    • 1. 准备工作
    • 2. 完整代码解析
    • 3. 效果对比
  • 五、常见问题与解决方案
    • 1. 找不到目标物体的 4 个顶点?
    • 2. 矫正后的图像有拉伸或变形?
    • 3. 透视变换后图像边缘有黑边?
  • 六、透视变换的应用场景

在计算机视觉领域,我们经常会遇到拍摄角度不佳导致图像变形的问题,比如倾斜的发票、扭曲的文档等。这时候,图像透视变换就成了“救星”。它能将倾斜、变形的图像矫正为正视角,为后续的文字识别、信息提取等操作扫清障碍。今天,我们就从原理入手,结合实战案例,带大家全面掌握图像透视变换。


一、透视变换是什么?

透视变换(Perspective Transformation),也叫投影变换,是一种将图像从一个二维坐标系映射到另一个三维坐标系的非线性变换。简单来说,它能模拟人眼视角的变化,把倾斜拍摄的“斜视图”转换成正面拍摄的“正视图”。

比如我们拍摄一张放在桌面上的发票,由于拍摄角度不是正上方,得到的发票图像可能是梯形或不规则四边形;而通过透视变换,就能将其矫正为标准的矩形,让发票上的文字、数字恢复正常的比例和角度。


二、透视变换的核心原理

1. 关键概念:透视变换矩阵

透视变换的实现依赖于透视变换矩阵(3×3矩阵) ,通过这个矩阵,可以将图像中任意一个像素点的坐标(x,y)(x,y)(x,y)映射到新的坐标(x′,y′)(x',y')(x,y)。其数学表达式如下:
[x′y′w′]=[a00a01a02a10a11a12a20a21a22][xy1]\begin{bmatrix} x' \\ y' \\ w' \end{bmatrix}= \begin{bmatrix} a_{00} & a_{01} & a_{02} \\ a_{10} & a_{11} & a_{12} \\ a_{20} & a_{21} & a_{22} \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xyw=a00a10a20a01a11a21a02a12a22xy1
其中,w′w'w是齐次坐标的缩放因子,最终的像素坐标需要通过x′=x′/w′x'=x'/w'x=x/wy′=y′/w′y'=y'/w'y=y/w计算得到。

这个3×3矩阵包含了9个参数,但由于齐次坐标的特性,实际只需要8个独立参数就能确定变换关系——而这8个参数,恰好可以通过4对对应点(变换前图像的4个顶点和变换后图像的4个顶点)来求解。

2. 核心条件:4对对应点

透视变换的前提是找到变换前图像的4个顶点(通常是目标物体的边界角点)变换后图像的4个顶点(通常是标准的矩形顶点) 。这4对对应点必须满足:

  • 变换前的4个点不共线(比如发票的4个角,不能在同一条直线上);
  • 变换后的4个点通常构成标准矩形(方便后续处理,如文字识别)。

三、OpenCV实现透视变换的关键步骤

在OpenCV中,实现透视变换主要依赖两个核心函数,整个流程可分为4步:

步骤1:读取并预处理图像

首先读取原始图像,根据需求进行缩放(降低图像尺寸可提高后续操作的速度)、灰度化、边缘检测等预处理,为后续寻找目标物体的4个顶点做准备。

步骤2:寻找目标物体的4个顶点

通过轮廓检测找到目标物体(如发票)的轮廓,再通过轮廓近似,提取出目标物体的4个顶点。这一步是透视变换的关键——如果顶点找错,后续的矫正效果会大打折扣。

步骤3:计算透视变换矩阵

使用cv2.getPerspectiveTransform(src, dst)函数计算变换矩阵。其中:

  • src:变换前的4个顶点坐标(需按“左上、右上、右下、左下”的顺序排列);
  • dst:变换后的4个顶点坐标(通常是标准矩形的顶点,如[[0,0], [width,0], [width,height], [0,height]])。

步骤4:执行透视变换

使用cv2.warpPerspective(src, M, dsize)函数完成透视变换。其中:

  • src:原始图像;
  • M:步骤3计算得到的透视变换矩阵;
  • dsize:变换后输出图像的尺寸(通常由dst的顶点计算得到)。

四、实战:用透视变换矫正发票图像

接下来,我们以“发票矫正”为例,结合代码详细讲解透视变换的实现过程。

1. 准备工作

  • 环境:Python 3.x + OpenCV(版本3.4.18.65,安装命令:pip install opencv-python==3.4.18.65);
  • 素材:一张倾斜的发票图像(fapiao.jpg,图像内容包含发票的文字、数字和边界)。

2. 完整代码解析

import numpy as np
import cv2# 1. 辅助函数定义
def cv_show(name, img):"""显示图像,按任意键关闭窗口"""cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyWindow(name)def order_points(pts):"""将4个顶点按“左上、右上、右下、左下”的顺序排列"""rect = np.zeros((4, 2), dtype="float32")  # 存储排序后的坐标# 计算每个点的x+y之和:左上点之和最小,右下点之和最大s = pts.sum(axis=1)rect[0] = pts[np.argmin(s)]  # 左上rect[2] = pts[np.argmax(s)]  # 右下# 计算每个点的y-x之差:右上点之差最小,左下点之差最大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):"""执行透视变换,返回矫正后的图像"""# 获取排序后的4个顶点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))# 定义矫正后图像的4个顶点(标准矩形)dst = np.array([[0, 0],                  # 左上[maxWidth - 1, 0],       # 右上[maxWidth - 1, maxHeight - 1],  # 右下[0, maxHeight - 1]       # 左下], dtype="float32")# 计算透视变换矩阵M = cv2.getPerspectiveTransform(rect, dst)# 执行透视变换warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warpeddef resize(image, height=None, inter=cv2.INTER_AREA):"""按高度缩放图像,保持宽高比"""(h, w) = image.shape[:2]if height is None:return image# 计算缩放比例r = height / float(h)dim = (int(w * r), height)# 执行缩放resized = cv2.resize(image, dim, interpolation=inter)return resized# 2. 读取并预处理图像
# 读取原始发票图像
image = cv2.imread('fapiao.jpg')
cv_show('原始图像', image)# 按高度缩放为500像素(降低尺寸,提高处理速度)
ratio = image.shape[0] / 500  # 记录缩放比例(后续恢复原始尺寸用)
orig = image.copy()  # 保存原始图像
image = resize(orig, height=500)
cv_show('缩放后图像', image)# 3. 寻找发票的4个顶点
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化(OTSU自动阈值,突出边缘)
edged = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('二值化图像', edged)# 轮廓检测(寻找图像中的所有轮廓)
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
# 绘制所有轮廓(红色,线宽1)
image_contours = cv2.drawContours(image.copy(), cnts, -1, (0, 0, 255), 1)
cv_show('所有轮廓', image_contours)# 筛选出面积最大的轮廓(发票的轮廓)
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# 轮廓近似(将不规则轮廓近似为多边形,epsilon=0.05*周长,控制近似精度)
peri = cv2.arcLength(screenCnt, True)  # 计算轮廓周长(True表示闭合轮廓)
screenCnt = cv2.approxPolyDP(screenCnt, 0.05 * peri, True)# 绘制发票的轮廓(红色,线宽2)
image_invoice_contour = cv2.drawContours(image.copy(), [screenCnt], -1, (0, 0, 255), 2)
cv_show('发票轮廓', image_invoice_contour)# 4. 执行透视变换
# 恢复原始尺寸的顶点坐标(之前缩放了图像,需乘以缩放比例)
pts = screenCnt.reshape(4, 2) * ratio
# 执行透视变换
warped = four_point_transform(orig, pts)# 5. 后处理(二值化,方便后续文字识别)
warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
# 二值化(白底黑字)
ref = cv2.threshold(warped_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 按宽度缩放为900像素,方便查看
ref = resize(ref, width=900)
cv_show('矫正后的发票', ref)# 6. 保存结果
cv2.imwrite('corrected_invoice.jpg', ref)
print("矫正完成,结果已保存为 corrected_invoice.jpg")

3. 效果对比

  • 原始图像:发票倾斜,文字和数字有透视变形;
  • 矫正后图像:发票变为标准矩形,文字、数字恢复正常比例,可直接用于 OCR 文字识别(如提取发票金额、编号等信息)。

五、常见问题与解决方案

1. 找不到目标物体的 4 个顶点?

  • 原因:图像噪声过多、边缘检测不完整、轮廓筛选错误;
  • 解决方案:
    • 增加图像预处理步骤,如使用高斯滤波(cv2.GaussianBlur)去除噪声;
    • 调整二值化阈值(可手动设置阈值,而非依赖 OTSU 自动阈值);
    • 优化轮廓近似的精度(调整epsilon参数,如0.02*peri0.08*peri)。

2. 矫正后的图像有拉伸或变形?

  • 原因:4 个顶点的顺序错误、dst(变换后顶点)的尺寸计算不准确;
  • 解决方案:
    • 确保order_points函数正确排序顶点(按 “左上、右上、右下、左下”);
    • 重新计算maxWidthmaxHeight,确保取到正确的宽度和高度最大值。

3. 透视变换后图像边缘有黑边?

  • 原因:变换矩阵计算时,部分像素超出了输出图像的范围;
  • 解决方案:
    • cv2.warpPerspective中添加borderMode=cv2.BORDER_CONSTANTborderValue=(255,255,255)(白色填充黑边);
    • 示例:warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight), borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255))

六、透视变换的应用场景

除了发票矫正,透视变换在计算机视觉中还有很多实用场景:

  • 文档扫描:将倾斜的纸质文档矫正为正视图,提升扫描质量;
  • 车牌识别:矫正倾斜拍摄的车牌,提高识别准确率;
  • 图像拼接:在全景图拼接中,通过透视变换统一多幅图像的视角;
  • AR 增强现实:将虚拟物体映射到真实场景的指定平面(如将虚拟海报贴在真实墙壁上)。

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

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

相关文章

commons-csv

maven依赖<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-csv --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.14.1</version></dependency…

LeetCode 1446.连续字符

给你一个字符串 s &#xff0c;字符串的「能量」定义为&#xff1a;只包含一种字符的最长非空子字符串的长度。 请你返回字符串 s 的 能量。 示例 1&#xff1a; 输入&#xff1a;s “leetcode” 输出&#xff1a;2 解释&#xff1a;子字符串 “ee” 长度为 2 &#xff0c;只包…

CTFHub SSRF通关笔记9:302跳转 Bypass 原理详解与渗透实战

目录 一、SSRF与302跳转 1、SSRF 2、302响应 3、SSRF与302结合 &#xff08;1&#xff09;SSRF源码分析 &#xff08;2&#xff09;攻击链条&#xff08;Flow of Exploit&#xff09; 二、渗透实战 1、打开靶场 2、尝试127.0.0.1访问 3、file协议分析源码 &#xff…

Windows-Use实战:AI驱动的Windows自动化

Windows-Use实战:AI驱动的Windows自动化 前言 项目介绍与准备工作 Windows-Use是什么? 系统要求 必需环境 步骤一:安装Python和基础环境 1.1 安装Python 检查Python版本 Python安装步骤 1.2 创建项目目录 步骤二:安装Windows-Use 2.1 使用pip安装(推荐) 步骤三:运行和基…

STM32-FreeRTOS操作系统-二值信号量与计数信号量

引言在嵌入式开发领域&#xff0c;任务同步与通信是系统稳定运行的核心。STM32配合FreeRTOS操作系统&#xff0c;为开发者提供了强大的工具支持。其中&#xff0c;二值信号量和计数信号量作为FreeRTOS的关键同步机制&#xff0c;分别用于任务间的简单同步和资源计数控制。二值信…

MarTech营销技术全景解析:概念、图谱与最新实践案例

一、引言&#xff1a;为什么企业越来越依赖MarTech&#xff1f;在数字化浪潮下&#xff0c;企业营销环境正发生深刻变化&#xff1a;客户触点增加&#xff1a;从官网、社交媒体到短视频、展会&#xff0c;信息渠道呈指数级增长。决策链条复杂&#xff1a;B2B客户通常需要多轮调…

服务器 - 从一台服务器切换至另一台服务器(损失数十条访客记录)

服务器 - 从一台服务器切换至另一台服务器(损失数十条PV记录为代价) 看着四年的服务器正式到期&#xff0c;没什么轰轰烈烈的告别&#xff0c;就像目送老朋友转身走远&#xff0c;只默默记下&#xff1a;哦&#xff0c;原来它陪了我这么久啊。 前言 一台陪伴了我4年的服务器昨…

《云原生边缘与AI训练场景:2类高频隐蔽Bug的深度排查与架构修复》

在云原生技术向边缘计算与AI训练场景渗透的过程中&#xff0c;基础设施层的问题往往会被场景特性放大——边缘环境的弱网络、异构硬件&#xff0c;AI训练的高资源依赖、分布式协作&#xff0c;都可能让原本隐藏的Bug以“业务故障”的形式爆发。这些问题大多不具备直观的报错信息…

【51单片机】【protues仿真】基于51单片机数控直流稳压电源系统

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 一、主要功能 1、数码管显示输出电压值 2、滑动电阻调节输出电压 3、电压输出范围0-15V&#xff0c;步进值1 二、使用步骤 基于51单片机的数控直流稳压电源是一种通过数字控制实现电压调节的智…

xtuoj Rectangle

题目思路将矩形间的相交情况通过投影转化为x、y两个方向下的线段是否相交&#xff0c;即前面的题目&#xff0c;判断两个区间是否相交&#xff0c;x投影的每个区间的左端点是每个矩形x的min&#xff0c;右端点是每个矩形的x的max&#xff0c;y投影情况同理&#xff0c;只要x轴的…

【深度学习踩坑实录】从 Checkpoint 报错到 TrainingArguments 精通:QNLI 任务微调全流程复盘

作为一名深度学习初学者&#xff0c;最近在基于 Hugging Face Transformers 微调 BERT 模型做 QNLI 任务时&#xff0c;被Checkpoint 保存和TrainingArguments 配置这两个知识点卡了整整两天。从磁盘爆满、权重文件加载报错&#xff0c;到不知道如何控制 Checkpoint 数量&#…

Java面试小册(3)

21【Q】: 什么是Java的SPI机制&#xff1f;【A】&#xff1a;SPI 是一种插件机制&#xff0c;用于在运行时动态加载服务的实现。它通过定义接口&#xff08;服务接口&#xff09;并提供一种可扩展的方式来让服务的提供着&#xff08;实现类&#xff09;在运行时注入&#xff0c…

P1150 Peter 的烟

记录20#include <bits/stdc.h> using namespace std; int main(){int n,k;cin>>n>>k;int cnt0;while(n>k){cntk;nn-k1;}cntn;cout<<cnt;return 0; }突破口每吸完一根烟就把烟蒂保存起来&#xff0c;k&#xff08;k>1&#xff09;个烟蒂可以换一个…

Cursor和Hbuilder用5分钟开发微信小程序

分享一个5分钟搞定微信小程序开发的技能&#xff0c;需要用到两个工具&#xff1a;Cursor和Hbuilder。 第1步、下载HBuilder。Hbuilder可以实现一套代码直接生成安卓、苹果、鸿蒙各个平台APP。访问Hbuilder的官方网站&#xff0c;HBuilderX-高效极客技巧&#xff0c;选择适合…

k8s的dashboard

找一个装有docker的机器&#xff0c;在一个rocky linux的虚拟机里弄拉取一个rancher镜像建立一个目录&#xff0c;目的&#xff1a;和里面数据做持久化关联后台运行&#xff0c;让他有权限&#xff0c;8080端口和容器80端口映射&#xff0c;443和443做映射查看一下删掉&#xf…

桥接模式,打造灵活可扩展的日志系统C++

一、为什么用桥接模式在企业开发中&#xff0c;日志系统几乎是标配。常见需求&#xff1a;日志有多种类型&#xff08;Info、Warning、Error 等&#xff09;&#xff1b;日志需要支持多种输出方式&#xff08;控制台输出、写文件、远程上传、数据库存储等&#xff09;。如果把这…

kafka--基础知识点--5.3--producer事务

1 事务简介 Kafka事务是Apache Kafka在流处理场景中实现Exactly-Once语义的核心机制。它允许生产者在跨多个分区和主题的操作中&#xff0c;以原子性&#xff08;Atomicity&#xff09;的方式提交或回滚消息&#xff0c;确保数据处理的最终一致性。例如&#xff0c;在流处理中…

利用DeepSeek实现服务器客户端模式的DuckDB原型

在网上看到韩国公司开发的一款GooseDB&#xff0c;DuckDB™ 的功能扩展分支&#xff0c;具有服务器/客户端、多会话和并发写入支持&#xff0c;使用 PostgreSQL 有线协议&#xff0c;但它是Freeware而不是开源&#xff0c;所以让DeepSeek实现之。 首先把readme页面发给他翻译&a…

麦当劳APP逆向

版本 V 7.0.17.0反调试 梆梆企业加固 frida反调试部分代码 headers {"biz_scenario": "500","biz_from": "1004","User-Agent": "mcdonald_Android/7.0.17.0 (Android)","ct": "102","…

大数据毕业设计选题推荐-基于大数据的结核病数据可视化分析系统-Hadoop-Spark-数据可视化-BigData

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…