图像透视变换详解与实战

在图像处理中,透视变换(Perspective Transform) 是一种常见的几何变换,用来将图像中某个四边形区域拉伸或压缩,映射到一个矩形区域。常见应用场景包括:

  • 纠正拍照时的倾斜(如手机拍发票、拍文档)

  • 从复杂背景中提取规则区域(如车牌识别)

  • 图像投影变换、鸟瞰视图生成

本文将通过一个完整案例,带你从图像读取、轮廓检测,到透视变换、结果后处理,逐步完成发票/文件的自动矫正。

图片准备:


1. 导入依赖与辅助函数

import numpy as np
import cv2

我们首先导入 NumPyOpenCV,这是图像处理的核心库。

图像显示函数

def cv_show(name, img):"""显示图像"""cv2.imshow(name, img)cv2.waitKey(0)
  • cv2.imshow 用于显示图像窗口

  • cv2.waitKey(0) 表示等待键盘输入后才关闭窗口,便于逐步观察每一步结果


2. 坐标排序函数:order_points

def order_points(pts):rect = np.zeros((4, 2), dtype="float32")s = pts.sum(axis=1)rect[0] = pts[np.argmin(s)]  # 左上角点 (x+y最小)rect[2] = pts[np.argmax(s)]  # 右下角点 (x+y最大)diff = np.diff(pts, axis=1)rect[1] = pts[np.argmin(diff)]  # 右上角 (y-x最小)rect[3] = pts[np.argmax(diff)]  # 左下角 (y-x最大)return rect

在透视变换中,必须保证四个点的顺序一致(左上、右上、右下、左下),否则会导致映射结果错误。
该函数通过:

  • x+y 最小 → 左上角

  • x+y 最大 → 右下角

  • y-x 最小 → 右上角

  • y-x 最大 → 左下角
    来对四点坐标排序,保证后续计算稳定。


3. 核心函数:透视变换

def four_point_transform(image,pts):rect = order_points(pts)(tl, tr, br, bl) = rectwidthA = 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)warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warped

步骤解析:

  1. 排序四点:保证输入顺序一致

  2. 计算目标矩形宽高:根据两条对边长度取最大值,保证变换后不丢失内容

  3. 构建目标点集:从 (0,0) 开始构建新的矩形顶点

  4. 求取透视变换矩阵cv2.getPerspectiveTransform()

  5. 执行透视变换cv2.warpPerspective() 输出矫正后的图像


 resize 函数等比例缩放(保持宽高比)

只需指定 width 或 height 中的一个,自动计算另一个

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 resized

4. 图像预处理与轮廓检测

image = cv2.imread('order.jpg')
cv_show('image',image)# 缩小图像,便于处理
ratio = image.shape[0]/500
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)[-2]
# 绘制所有轮廓,便于可视化
image_contours = cv2.drawContours(image.copy(), cnts, -1, (0, 0, 255), 1)
cv_show('image_contours', image_contours)

这里先将图像灰度化、二值化,再使用 cv2.findContours 获取所有轮廓。然后在图像上绘制轮廓,便于确认检测效果。


5. 找到最大轮廓(目标区域)

# 找到最大轮廓并逼近为四边形
print("STEP 2: 获取最大轮廓")
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]  # 按面积排序,取最大
peri = cv2.arcLength(screenCnt, True)  # 计算周长
screenCnt = cv2.approxPolyDP(screenCnt, 0.05 * peri, True)  # 多边形逼近,保留主要拐点
image_contour = cv2.drawContours(image.copy(), [screenCnt], -1, (0, 0, 255), 2)
cv_show('image_contour', image_contour)
  • sorted(..., key=cv2.contourArea, reverse=True) 按面积排序,取最大轮廓(一般就是发票/文件区域)

  • cv2.approxPolyDP 多边形逼近,得到近似四边形轮廓


6. 透视矫正 & 后处理

warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
cv2.imwrite('invoice_new.jpg',warped)
cv_show('xx',warped)# 转灰度并二值化
gray1=cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY)
warped1=cv2.threshold(gray1,120,255,cv2.THRESH_BINARY)[1]
warped1=resize(warped1,height=500)
cv_show('warped1',warped1)# 腐蚀操作,去除噪点
kernel=np.ones((2,2),np.uint8)
erosion = cv2.erode(warped1,kernel,iterations=1)
cv_show('erosion',erosion)# 旋转图像
rotated_image1 =np.rot90(erosion, k=1)
cv_show('rotated_image1',rotated_image1)

后处理步骤:

  • 二值化:提高文字对比度,便于后续OCR识别

  • 腐蚀:去除孤立噪点,让边缘更干净

  • 旋转:如果方向不正,可手动或自动旋转至正方向


7. 效果展示与应用

通过上述步骤,我们将一张倾斜、带背景的发票图像,处理成一个平整、纯背景的图像,便于后续的文字识别(OCR)。

这种方法广泛应用于:

  • 发票/合同扫描矫正

  • 车牌识别

  • 桌面投影仪矫正

  • 透视校正后的AR场景识别


总结

本文完整实现了:

  1. 图像预处理:缩放、灰度化、二值化

  2. 轮廓检测:提取最大矩形区域

  3. 四点排序:确保透视变换稳定

  4. 透视矫正:将任意四边形映射到规则矩形

  5. 后处理:二值化、腐蚀、旋转

透视变换是图像几何处理中的关键技术,掌握它能够让你解决很多实际问题,尤其是文档扫描、图像矫正类任务。

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

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

相关文章

【飞书多维表格插件】

coze中添加飞书多维表格记录插件 添加单条记录 [{"fields":{"任务详情":"选项1","是否完成":"未完成"}}]添加多条记录 [{"fields":{"任务详情":"选项1","是否完成":"已完…

Java基础 9.14

1.Collection接口遍历对象方式2-for循环增强增强for循环,可以代替iterator选代器,特点:增强for就是简化版的iterator本质一样 只能用于遍历集合或数组package com.logic.collection_;import java.util.ArrayList; import java.util.Collectio…

数据结构(C语言篇):(十三)堆的应用

目录 前言 一、堆排序 1.1 版本一:基于已有数组建堆、取栈顶元素完成排序 1.1.1 实现逻辑 1.1.2 底层原理 1.1.3 应用示例 1.1.4 执行流程 1.2 版本二:原地排序 —— 标准堆排序 1.2.1 实现逻辑 1.2.2 底层原理 1.2.3 时间复杂度计算…

4步OpenCV-----扫秒身份证号

这段代码用 OpenCV 做了一份“数字模板字典”,然后在银行卡/身份证照片里自动找到身份证号那一行,把每个数字切出来跟模板比对,最终输出并高亮显示出完整的身份证号码,下面是代码解释:模块 1 工具箱(通用函…

冯诺依曼体系:现代计算机的基石与未来展望

冯诺依曼体系:现代计算机的基石与未来展望 引人入胜的开篇 当你用手机刷视频、用电脑办公时,是否想过这些设备背后共享的底层逻辑?从指尖轻滑切换APP,到电脑秒开文档,这种「无缝衔接」的体验,其实藏着一个改…

前端基础 —— C / JavaScript基础语法

以下是对《3.JavaScript(基础语法).pdf》的内容大纲总结:---📘 一、JavaScript 简介 - 定义:脚本语言,最初用于表单验证,现为通用编程语言。 - 应用:网页开发、游戏、服务器(Node.js&#xff09…

springboot 二手物品交易系统设计与实现

springboot 二手物品交易系统设计与实现 目录 【SpringBoot二手交易系统全解析】从0到1搭建你的专属平台! 🔍 需求确认:沟通对接 🗣 📊 系统功能结构:附思维导图 ☆开发技术: &#x1f6e…

【Android】可折叠式标题栏

在 Android 应用开发中,精美的用户界面可以显著提升应用品质和用户体验。Material Design 组件中的 CollapsingToolbarLayout 能够为应用添加动态、流畅的折叠效果,让标题栏不再是静态的元素。本文将深入探讨如何使用 CollapsingToolbarLayout 创建令人惊…

Debian13下使用 Vim + Vimspector + ST-LINK v2.1 调试 STM32F103 指南

1. 硬件准备与连接 1.1 所需硬件 STM32F103C8T6 最小系统板ST-LINK v2.1 调试器连接线(杜邦线) 1.2 硬件连接 ST-LINK v2.1 ↔ STM32F103C8T6 连接方式:ST-LINK v2.1 引脚STM32F103C8T6 引脚功能说明SWDIOPA13数据线SWCLKPA14时钟线GNDGND共地…

第21课:成本优化与资源管理

第21课:成本优化与资源管理 课程目标 掌握计算资源优化 学习成本控制策略 了解资源调度算法 实践实现成本优化系统 课程内容 21.1 成本分析框架 成本分析系统 class CostAnalysisFramework {constructor(config) {this.config

SAP HANA Scale-out 04:CalculationView优化

CV执行过程计算视图激活时,生成Stored ModelSELECT查询时:首先将Stored Model实例化为runtime Model 计算引擎执行优化,将runtime Model转换为Optimized Runtime ModelOptimized Runtime Model通过SQL Optimizer进行优化计算引擎优化特性说明…

鸿蒙审核问题——Scroll中嵌套了List/Grid时滑动问题

文章目录背景原因解决办法1、借鉴Flutter中的解决方式,如下图2、鸿蒙Next中对应的解决方式,如下图3、官方文档回访背景 来源一次审核被拒的情况。也是出于粗心导致的。之前在flutter项目中也是遇到过这种问题的。其实就是滚动视图内嵌滚动视图造成的&am…

测试电商购物车功能,设计测试case

在电商场景中,购物车是连接商品浏览与下单支付的关键环节,需要从功能、性能、兼容性、安全性等多维度进行测试。以下是购物车功能的测试用例设计: 一、功能测试 1. 商品添加到购物车 - 未登录状态下,添加商品到购物车(…

Linux --- 常见的基本指令

一. 前言本篇博客使用的 Linux 操作系统是 centos ,用来学习Linux 的 Linux 系统的内核版本和系统架构信息版本如下所示:上图的主要结构为:主版本号-次版本号 修正次数,3.10.0 是操作系统的主版本号;当我们在维护一段L…

微信小程序 -开发邮箱注册验证功能

一、前端验证:正则表达式与插件结合正则表达式设计 使用通用邮箱格式校验正则,并允许中文域名(如.中国): const emailReg /^[a-zA-Z0-9._%-][a-zA-Z0-9-](?:\.[a-zA-Z0-9-])*\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2})?$/i;…

docker 部署 code-server

docker 部署 code-servercode-serverError response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headersdocker 配置正确步骤 阿里云源permission de…

网络编程专题:从源码解析网络编程常用方法(基于6.16.3内核)

前言 本文是因为作者在研究下面这个代码时发现的问题: int main() {// 1. 创建 IPv4 专用地址结构体 sockaddr_instruct sockaddr_in ipv4_addr;memset(&ipv4_addr, 0, sizeof(ipv4_addr)); // 初始化清零// 2. 填充 IPv4 专属信息ipv4_addr.sin_family AF_IN…

2025年数字公共治理专业重点学什么内容?(详细指南)

数字公共治理作为一个新兴的跨学科领域,近年来受到越来越多高校和学生的关注。这个专业融合了多个学科的知识体系,旨在培养掌握现代治理理念和技术应用能力的复合型人才。对于在校大学生而言,了解这一专业的学习内容和发展方向,有…

一招解决 win 下 终端打印中文乱码问题

适合所有终端 cmd powershell git bash, 原理:修改电脑的区域设置,勾选使用 UTF-8 1.电脑搜索 区域, 打开区域设置2. 打开相关设置3. 点击更改 日期、时间或数字格式4. 选则管理-点击更改系统区域设置,在弹出框中勾选 …

Elasticsearch面试精讲 Day 13:索引生命周期管理ILM

【Elasticsearch面试精讲 Day 13】索引生命周期管理ILM 在“Elasticsearch面试精讲”系列的第13天,我们将深入探讨 索引生命周期管理(Index Lifecycle Management, ILM) 这一核心运维机制。作为大规模日志、监控和时序数据场景下的必备功能&…