目录

简介

一、了解图像投影(透视)变换

一、定义与原理

二、应用场景

三、实现方法

二、案例分析

1. 辅助函数定义

1.1.cv_show 函数

1.2.order_points 函数

1.3.four_point_transform 函数

1.4.resize 函数

2. 主程序执行流程

2.1.图像缩放处理

2.2.轮廓检测

2.3.获取最大轮廓(文档边缘)

2.4.透视变换并保存结果

3.总结


简介

        在计算机视觉的进阶学习旅程中,图像投影(透视)变换是一座连接理论与实战的关键桥梁。无论是实现证件照的智能矫正、自动驾驶中道路场景的视角转换,还是无人机航拍图像的地理坐标映射,这一技术都扮演着不可或缺的核心角色。如果你曾困惑于 “为何倾斜拍摄的文档会出现边缘变形”“如何让二维图像呈现出三维空间的立体感”,或是在项目开发中急需解决图像视角转换的技术难题,那么本系列博客将为你提供系统性的解决方案。

一、了解图像投影(透视)变换

        图像的透视变换(Perspective Transformation)是一种在图像处理中广泛使用的技术,它通过模拟人眼或相机镜头观看三维空间物体时的透视效果,来改变图像的视角和形状。以下是对图像透视变换的详细解释:


一、定义与原理


        透视变换是一种非线性变换,它可以将一个二维坐标系中的点映射到三维坐标系中的点,然后再将其投影到另一个二维坐标系中的点。这种变换基于几何学中的透视原理,通过一个3x3的变换矩阵来实现,该矩阵作用于图像的每个像素坐标,从而进行坐标的映射转换。透视变换能够模拟真实世界中的透视效果,使物体看起来更接近、更远或者从不同角度观看。

二、应用场景


透视变换在图像处理和计算机视觉领域有着广泛的应用,包括但不限于以下几个方面:

        图像校正:通过透视变换可以修正由于视角引起的图像扭曲,如将拍摄的倾斜书本或建筑物照片校正为正视图。
        图像合成:将两个图像中的物体或场景合成在一起,仿佛它们是从同一视角拍摄的。
虚拟现实(VR)和增强现实(AR):在VR和AR应用中,透视变换用于模拟真实世界的视角和深度感,提升用户体验。
        目标检测与跟踪:在目标检测和跟踪任务中,透视变换可以用于调整图像视角,以便更准确地识别和跟踪目标。
        三维重建:在三维重建过程中,透视变换是连接二维图像与三维空间的关键技术之一。


三、实现方法


在OpenCV等图像处理库中,透视变换通常通过以下步骤实现:

        选择对应点:在原始图像和目标图像上分别选择四个非共线的对应点。这些点通常是图像中的显著特征点,如纸上的角落、建筑物的边缘等。
        计算变换矩阵:使用OpenCV中的cv2.getPerspectiveTransform函数根据这些对应点计算透视变换矩阵。
        应用变换矩阵:使用cv2.warpPerspective函数将计算得到的透视变换矩阵应用于原始图像,从而得到变换后的图像。

二、案例分析

了解了枯燥的理论我们用下面这个案例来分析

实现对一个小票进行图像投影变换

1. 辅助函数定义

1.1.cv_show 函数
def cv_show(name, img):cv2.imshow(name, img)  # 显示图像,name是窗口名称,img是要显示的图像cv2.waitKey(0)  # 等待用户按键,0表示无限等待

这是一个简化图像显示操作的函数,封装了 OpenCV 的imshowwaitKey方法,方便在多个地方调用。

1.2.order_points 函数
def order_points(pts):# 初始化一个4x2的矩阵存储排序后的坐标rect = np.zeros(shape=(4, 2), dtype="float32")# 按顺序找到对应坐标:左上,右上,右下,左下s = pts.sum(axis=1)  # 对每个点的x和y坐标求和rect[0] = pts[np.argmin(s)]  # 最小的和对应左上角(x+y最小)rect[2] = pts[np.argmax(s)]  # 最大的和对应右下角(x+y最大)diff = np.diff(pts, axis=1)  # 计算每个点的y-x差值rect[1] = pts[np.argmin(diff)]  # 最小的差值对应右上角(y-x最小)rect[3] = pts[np.argmax(diff)]  # 最大的差值对应左下角(y-x最大)return rect

这个函数用于对四边形的四个顶点进行排序,确保它们按 "左上→右上→右下→左下" 的顺序排列,为后续的透视变换做准备。

1.3.four_point_transform 函数
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],       # 右上[maxWidth - 1, maxHeight - 1],  # 右下[0, maxHeight - 1]       # 左下], dtype="float32")# 计算透视变换矩阵M = cv2.getPerspectiveTransform(rect, dst)# 应用透视变换warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warped

这是核心函数,实现了图像的透视变换,将倾斜的四边形区域转换为正面矩形视图,模拟了从不同角度拍摄的文档转换为正视图的效果。

1.4.resize 函数
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

这个函数用于按比例调整图像大小,避免图像过大处理困难或过小影响精度。

2. 主程序执行流程

import cv2
import numpy as np# 读取输入图像
image = cv2.imread('fapiao.jpg')
cv_show('image', image)  # 显示原始图像
2.1.图像缩放处理
# 计算缩小比率(以高度为基准缩放到500像素)
ratio = image.shape[0] / 500.0
orig = image.copy()  # 保存原始图像副本
image = resize(orig, height=500)  # 按比例缩小图像
cv_show('1', image)  # 显示缩小后的图像

将图像按比例缩小到高度为 500 像素,便于后续处理,同时记录缩放比例,以便后期恢复原始尺寸。

2.2.轮廓检测
print("STEP 1: 轮廓检测")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
# 自动阈值二值化处理(OTSU算法)
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, color=(0, 0, 255), thickness=1)
cv_show('image_contours', image_contours)

这一步将图像转为灰度图,再通过二值化处理突出边缘,最后检测出图像中的所有轮廓。

2.3.获取最大轮廓(文档边缘)
print("STEP 2: 获取最大轮廓")
# 按轮廓面积排序,取最大的那个(通常是文档的边缘)
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
print(screenCnt.shape)  # 输出轮廓形状# 轮廓近似(将不规则轮廓近似为多边形)
peri = cv2.arcLength(screenCnt, closed=True)  # 计算轮廓周长
# 用Douglas-Peucker算法近似轮廓,0.05*peri是近似精度
screenCnt = cv2.approxPolyDP(screenCnt, 0.05 * peri, closed=True)
print(screenCnt.shape)  # 输出近似后的轮廓形状# 绘制最大轮廓并显示
image_contour = cv2.drawContours(image.copy(), [screenCnt], -1, color=(0, 255, 0), thickness=2)
cv2.imshow("image_contour", image_contour)
cv2.waitKey(0)

文档通常是图像中面积最大的矩形物体,所以这里通过面积排序找到最大轮廓,并通过轮廓近似算法将其转换为四边形。

2.4.透视变换并保存结果
# 应用四点透视变换,注意要将坐标还原到原始图像尺寸
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 保存变换后的图像
cv2.imwrite(filename='invoice_new.jpg', img=warped)
# 显示变换后的图像
cv2.namedWindow('xx', cv2.WINDOW_NORMAL)  # 创建可调整大小的窗口
cv2.imshow("xx", mat=warped)
cv2.waitKey(0)

3.总结

整个程序的核心思想是:

  1. 读取图像并适当缩放
  2. 通过图像处理技术找到文档的边缘轮廓
  3. 利用透视变换将倾斜的文档转换为正视图
  4. 保存和显示处理结果

最后我们还可以通过之前学习的旋转、阈值处理和图像形态学等让图片中的文字更加突出

import cv2
import numpy as npimg=cv2.imread('invoice_new.jpg')
img=cv2.resize(img,dsize=None,fx=0.4,fy=0.4)
rotated_image1 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
cv2.imshow('ni90',rotated_image1)
cv2.waitKey(0)ret, binary = cv2.threshold(rotated_image1,  125,   255, cv2.THRESH_BINARY)
cv2.imshow(  'binary', binary) # 偏白的变纯白,偏黑的变纯黑
cv2.waitKey(0)kernel = np.ones((2,2),np.uint8)  # 这里kernel大小,修改为5*5试试
erosion_1 = cv2.erode(binary,kernel,iterations=1)  #iterations改为5试试
cv2.imshow('erosion_1',erosion_1)
cv2.waitKey(0)

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

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

相关文章

Java面试问题记录(二)

三、系统设计与问题排查1、假设你要设计一个 “秒杀系统”,需要考虑高并发、高可用、防超卖等问题,你的整体技术方案是什么?从前端、接口层、服务层、存储层分别说说核心设计点。秒杀系统设计设计核心:瞬时高并发,库存…

k8s部署kafka三节点集群

本来认为部署kafka很简单,没想到也折腾了2-3天,这水平没治了~ kafka从3.4.0版本之后,可以不依赖zookeeper直接使用KRaft模式部署,也就是说部署kafka可以不安装zookeeper直接部署。 在官网上没有找到如何使用yaml文件…

在公用同一公网IP和端口的K8S环境中,不同域名实现不同访问需求的解决方案

目录 1. 访问需求 2. 解决方案 3. 具体配置 3.1 允许互联网访问的域名(a.lmzf.com) 3.2 需IP白名单访问的域名(b.lmzf.com) 3.3 关键参数说明 3.4 测试验证 1. 访问需求 在腾讯云TKE环境中,多个域名解析到同一…

FlinkCDC 达梦数据库实时同步

一、Flink部署 1.1、JAVA环境 vi /etc/profile export JAVA_HOME/data/flinkcdc/jdk1.8.0_181 export CLASSPATH$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar export PATH$JAVA_HOME/bin:$PATHsource /etc/profilevi ~/.bash_profileexport FLINK_HOME/data/flinkcdc/fli…

Eip开源主站EIPScanner在Linux上的调试记录(二 多生产者连接)

目录 一、背景 二、可行性验证 三、开发调试 一、背景 在一般场景下,只需一路IO连接,但稍微复杂的场景,就需要不同通讯周期的连接,这就需要有多组IO连接。 而大于一组的连接调试方法是一样的,因此主要解决2组连接的…

Oracle APEX 利用卡片实现翻转(方法二)

目录 0. 以 Oracle 的标准示例表 EMP 为例,实现卡片翻转 1. 创建卡片区域 (Cards Region) 2. 定义卡片的 HTML 结构 3. 添加 CSS 实现样式和翻转动画 4. 创建动态操作触发翻转 5. 运行效果 0. 以 Oracle 的标准示例表 EMP 为例,实现卡片翻转 目标如…

低代码拖拽实现与bpmn-js详解

低代码平台中的可视化拖拽功能是其核心魅力所在,它让构建应用变得像搭积木一样直观。下面我将为你梳理其实现原理,并详细介绍 vue-draggable 这个常用工具。 🧱 一、核心架构:三大区域与数据驱动 低代码编辑器界面通常分为三个核心…

【科研绘图系列】R语言绘制模型预测与数据可视化

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍 加载R包 数据下载 函数 导入数据 数据预处理 画图 总结 系统信息 介绍 本文介绍了一种利用R语言进行海洋微生物群落动态分析的方法,该方法通过构建多个统计模型来预测不同环境…

TODO的面试(dw三面、sqb二面、ks二面)

得物的前端三面(通常是技术终面)会深入考察你的技术深度、项目经验、解决问题的思路以及职业素养。下面我结合搜索结果,为你梳理一份得物前端三面的常问问题及详解,希望能助你一臂之力。 🧠 得物前端三面常问问题及详解…

开发 PHP 扩展新途径 通过 FrankenPHP 用 Go 语言编写 PHP 扩展

通过 FrankenPHP 用 Go 语言编写 PHP 扩展 在 PHPVerse 2025 大会上(JetBrains 为纪念 PHP 语言 30 周年而组织的会议),FrankenPHP 开发者 Kvin Dunglas 做了一个开创性的宣布:通过 FrankenPHP,可以使用 Go 语言创建 …

完美解决:应用版本更新,增加字段导致 Redis 旧数据反序列化报错

完美解决:应用版本更新,增加字段导致 Redis 旧数据反序列化报错 前言 在敏捷开发和快速迭代的今天,我们经常需要为现有的业务模型增加新的字段。但一个看似简单的操作,却可能给正在稳定运行的系统埋下“地雷”。 一个典型的场景是…

66-python中的文件操作

1. 文件的编码 UTF-8 GBK GB2312 Big5 GB18030 2. 文件读取 文件操作步骤: 打开文件 读\写文件 关闭文件 open(name,mode,encoding) name:文件名字符串 “D:/haha.txt” mode: 只读、写入、追加 r:以只读方式打开 w: 只用于写 a :用于追加 encoding:编码方式 # -*- coding: utf…

FPGA实例源代码集锦:27个实战项目

本文还有配套的精品资源,点击获取 简介:FPGA是一种可编程逻辑器件,允许用户根据需求配置硬件功能。本压缩包提供27个不同的FPGA应用实例源代码,旨在帮助初学者深入学习FPGA设计,并为专业工程师提供灵感。内容涵盖了…

基于 Vue+Mapbox 的智慧矿山可视化功能的技术拆解

01、项目背景 在全球矿业加速向 “高端化、智能化、绿色化” 转型的浪潮下,传统矿业面临的深地开采难题、效率瓶颈与安全隐患日益凸显。 在矿业转型的迫切需求与政策、技术支撑的背景下依托 GIS 技术,开展了 “中国智矿” GIS 开发项目,旨在…

进程状态(Linux)

进程状态Linux进程状态Linux进程状态进程描述R运行状态S睡眠状态D磁盘休眠状态T停止状态t被追踪状态(调试状态)X死亡状态Z僵死状态其实大致也就可以分为三种运行,阻塞,挂起。运行状态每个cpu里都有一个运行队列,进程在运行队列里,…

物联网领域中PHP框架的最佳选择有哪些?

物联网(IoT)作为近年来快速发展的技术领域,已经渗透到智能家居、工业自动化、智慧城市等方方面面。作为Web开发中广泛使用的语言,PHP凭借其易学易用、开发效率高和生态丰富的特点,也在物联网领域找到了用武之地。 本文…

java反射(详细教程)

我们平常创建类的实例并调用类中成员需要建立在一个前提下,就是已经知道类名和类中成员的信息,灵活性大大降低。甚至在一些项目中还需要修改源码来满足使用条件,大大降低了操作的灵活性。Java 反射(Reflection)是 Java…

消息队列-初识kafka

优缺点 消息队列的优点: 实现系统解耦: :::color5 系统解耦解释 有 MQ 时是 “服务 A 发消息到队列,其他服务从队列拿消息,新增服务接队列就行”;无 MQ 时是 “服务 A 直接调其他服务的接口 / 依赖,新增 / …

实践《数字图像处理》之Canny边缘检测、霍夫变换与主动二值化处理在短线段清除应用中的实践

在最近的图像处理项目中,其中一个环节:图片中大量短线(不是噪声),需要在下一步处理前进行清除。在确定具体实现时,碰到了Canny边缘检测、霍夫变换与主动二值化处理的辩证使用,相关逻辑从图片灰度…

vue3与ue5通信-工具类

工具 ue5-simple.js /*** UE5 通信工具* 两个核心方法:发送消息和接收消息*/// 确保全局对象存在 if (typeof window ! undefined) {window.ue window.ue || {};window.ue.interface window.ue.interface || {}; }/*** 生成 UUID*/ function generateUUID() {retu…