仿射变换 与 透视变换

仿射变换 与 透视变换

几种变换之间的关系


1、缩放 Rescale

1)变换矩阵

缩放变换矩阵,形为 : , 其中: 为 x轴 和 y轴的缩放因子,即 宽高的缩放因子

图像中的每一个像素点 (x, y),经过矩阵变换(如下公式),会落到的新的位置

2)举例

比如,一张图像尺寸为, 经过变换矩阵 变换之后 :

  • 其右下角点(,),落在新的位置 (

  • 图像中的其他点,也会经历同样的变换

  • 对于图像原点 (0, 0),缩放之后,仍为原点 (0, 0)

3)代码演示

注意: 图像默认的原点位置为图像的左上角点

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

M = np.eye(2, 3)
M[0, 0] = 0.8
M[1, 1] = 0.6

dst_img = cv2.warpAffine(src_img, M, (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()

2、旋转 Rotation

1)变换矩阵

旋转变换矩阵,形为 : , 其中: 为顺时针旋转角度

图像中的每一个像素点 (x, y),经过矩阵变换(如下公式),会落到的新的位置 ()

2)举例

比如,一张图像尺寸为 , 旋转 ,变换矩阵为: , 变换之后 :

  • 其右上角点(),落在新的位置

  • 图像中的其他点,也会经历同样的变换

  • 对于图像原点 (0, 0), 旋转之后,仍为原点 (0, 0)

3)代码演示

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

M = np.eye(2, 3)
M[0, 0] = np.cos(np.pi / 12)
M[0, 1] = -np.sin(np.pi / 12)
M[1, 0] = np.sin(np.pi / 12)
M[1, 1] = np.cos(np.pi / 12)

dst_img = cv2.warpAffine(src_img, M, (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()

3、切变 Shear

1)变换矩阵

切变(Shear):切变通过在坐标系中,斜向拉伸对象来改变其形状 ,但不会改变对象的大小或旋转它,也不会改变原点位置。

切变矩阵如下图所示:

2)举例

比如,一张图像尺寸为 ,沿x轴拉伸,沿y轴不拉伸,则变换矩阵为 ,变换之后 :

  • 其右下角点 ,落在新的位置

  • 图像中的其他点,也会经历同样的变换

  • 对于图像原点 (0, 0), 切变之后,仍为原点 (0, 0)

3)代码演示

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

M = np.eye(2, 3)
M[0, 1] = np.tan(np.pi / 12)

dst_img = cv2.warpAffine(src_img, M, (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()

总结一 : 线性变换

1、缩放、旋转、切变 都属于线性变换,他们的特点有以下:

  • 变换之前的原点(0, 0),在变换之后,仍为原点(0, 0)

  • 平直性 : 变换之前的直线,在变换之后 仍为直线

  • 平行性:变换之前的平行线,在变换之后,仍为平行线

2、在二维空间内,线性变换(缩放、旋转、切变)都可以通过 2x2 的变换矩阵,来实现相关的变换计算


4、平移 Translation

1)变换矩阵

之前我们介绍的 缩放、旋转、切变 都可以通过 2x2 的变换矩阵 直接计算得到,而平移则不行。

平移如果用公式表示的话,如下 :

齐次坐标 (Homogeneous Coordinates)

由上可知,平移变换 不能通过 2x2 的转换矩阵 直接进行矩阵乘法得到,所以,我们引入了 "齐次坐标 (Homogeneous Coordinates)"。 在二维情况下,齐次坐标通常由三个值表示,即(x,y,w),其中:

  • x 和 y 是普通的笛卡尔坐标

  • w是一个额外的参数,通常设置为 1 。 也可以理解为,增加了一个维度 z,只不过对象(图像)在这个维度上的值恒为1,也就是在 z=1 这个平面上。

这样,我们就可以将平移变换,写成如下公式表达 :

缩放、旋转、切变 的变换矩阵 也可以拓展为2x3矩阵,比如,旋转变换可以表示为 :

2)代码演示

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

M = np.eye(2, 3)
M[0, 2] = 20
M[1, 2] = 40

dst_img = cv2.warpAffine(src_img, M, (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()

5、仿射变换

1)仿射变换

仿射变换为 缩放、旋转、切变、平移 4种变换的任意组合

比如,下面就是 "缩放 + 旋转 + 平移" 的组合,因为 缩放、旋转 都是相对于 原点坐标来操作的,为了保证图像增强中,不会出现意外的结果,一般会将 平移操作放在最后, 即 先线性后平移

仿射变换有以下特点:

  • 变换之前的直线,在变换之后 仍为直线

  • 变换之前的平行线,在变换之后,仍为平行线

2)举例

我们以"缩放 + 旋转 + 平移" 的组合为例,对图像中的点 进行连续变换:

  • 先做 缩放变换,缩放变换矩阵 记为 S

  • 再做 旋转变换,旋转变换矩阵记为 R

  • 最后做 平移变换,平移变换矩阵记为 T

变换后 P点的新位置为P' ,整体的变换表达公式为 :

1)为了使 可以矩阵连乘,S、R、T 都要拓展为 3x3 的变换矩阵:

  • S 形为:

  • R 形为:

  • T形为:

2)因为矩阵乘法满足 乘法结合律 ,所以,上面的变换公式 可写为:

我们令 ,即 为整体的变换矩阵,

3)变换矩阵的位置不可更换 (因为矩阵乘法不满足交换律 $),变换矩阵的顺序决定着 变换的操作顺序

尤其是 平移变换,因为 平移变换始终都是最后做,所以,平移变换矩阵的位置始终都在第一个位置

yolov5 中相关代码示例 :(其中 C 表示将坐标原点 从图像左上角点转换到图像中心点)

2)代码演示

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

# 旋转矩阵
S = np.eye(3, 3)
S[0, 0] = 0.8
S[1, 1] = 0.6

# 旋转矩阵
R = np.eye(3, 3)
R[0, 0] = np.cos(np.pi / 12)
R[0, 1] = -np.sin(np.pi / 12)
R[1, 0] = np.sin(np.pi / 12)
R[1, 1] = np.cos(np.pi / 12)

# 平移矩阵
T = np.eye(3, 3)
T[0, 2] = 40
T[1, 2] = 20

# 仿射变换矩阵
M = T @ R @ S

# 操作变换
dst_img = cv2.warpAffine(src_img, M[:2], (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()


6、透视变换 Perspective

仿射变换可以将矩形图片映射为平行四边形 ,而 透视变换 可以将矩形图片映射为任意四边形

1)变换矩阵

透视变换矩阵,形为 :

透视变换分为 2 步 :

对于透视变换 以上2步的理解 :

对于齐次坐标 (x, y, z) ,我们是增加了一个维度 z (这时,z = 1),原始图像是在 z = 1 这个平面上的

  • 第一步,我们将图像,根据变换矩阵,投射到了三维空间中,黄色图像为结果图像,图像中像素点的坐标为

  • 第二步,再将三维空间上的点,给映射回 平面上。 这种映射是基于视觉原理的映射,这时人的视线为 z轴,将第1步得到的结果图像,往 平面上进行映射,示意图如下:

2)代码演示

复制代码
import cv2
import numpy as np

src_img = cv2.imread("img.jpg")
height, width = src_img.shape[:2]

M = np.eye(3, 3)
M[2, 0] = 0.0002
M[2, 1] = 0.0002

dst_img = cv2.warpPerspective(src_img, M, (width, height))

cv2.imshow("src_img", src_img)
cv2.imshow("dst_img", dst_img)
cv2.waitKey()
cv2.destroyAllWindows()

总结二 :变换矩阵

对于 变换矩阵 M,其中的每一个部分,都控制着不同的变换 :

操作顺序一般为 : 先做透视变换、最后做平移变换, 如 yolov5 数据增强中的代码,也是先做透视变换,最后做平移变换 (C 为将原点位置调整到图片中心)

相关推荐
牛客企业服务2 分钟前
2025年AI面试推荐榜单,数字化招聘转型优选
人工智能·python·算法·面试·职场和发展·金融·求职招聘
视觉语言导航33 分钟前
RAL-2025 | 清华大学数字孪生驱动的机器人视觉导航!VR-Robo:面向视觉机器人导航与运动的现实-模拟-现实框架
人工智能·深度学习·机器人·具身智能
糖葫芦君41 分钟前
Policy Gradient【强化学习的数学原理】
算法
**梯度已爆炸**43 分钟前
自然语言处理入门
人工智能·自然语言处理
ctrlworks1 小时前
楼宇自控核心功能:实时监控设备运行,快速诊断故障,赋能设备寿命延长
人工智能·ba系统厂商·楼宇自控系统厂家·ibms系统厂家·建筑管理系统厂家·能耗监测系统厂家
BFT白芙堂2 小时前
睿尔曼系列机器人——以创新驱动未来,重塑智能协作新生态(上)
人工智能·机器学习·机器人·协作机器人·复合机器人·睿尔曼机器人
aneasystone本尊2 小时前
使用 MCP 让 Claude Code 集成外部工具
人工智能
静心问道2 小时前
SEW:无监督预训练在语音识别中的性能-效率权衡
人工智能·语音识别
羊小猪~~2 小时前
【NLP入门系列五】中文文本分类案例
人工智能·深度学习·考研·机器学习·自然语言处理·分类·数据挖掘
xwz小王子2 小时前
从LLM到WM:大语言模型如何进化成具身世界模型?
人工智能·语言模型·自然语言处理