仿射变换 与 透视变换

仿射变换 与 透视变换

几种变换之间的关系


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 为将原点位置调整到图片中心)

相关推荐
微刻时光7 分钟前
影刀RPA网页自动化总结
运维·人工智能·python·低代码·自动化·rpa·影刀rpa
三天不学习20 分钟前
浅析AI大模型为何需要向量数据库?【入门基础】
数据库·人工智能·欧氏距离·向量数据库·余弦相似度
WenGyyyL30 分钟前
研读论文——《用于3D工业异常检测的自监督特征自适应》
人工智能·python·深度学习·机器学习·计算机视觉·3d
fydw_71535 分钟前
音频生成技术的前沿探索:从语音合成到智能Podcast
人工智能·音视频·语音识别
yzx99101335 分钟前
支持向量机的回归用法详解
算法·支持向量机·回归
选型宝36 分钟前
腾讯怎样基于DeepSeek搭建企业应用?怎样私有化部署满血版DS?直播:腾讯云X DeepSeek!
人工智能·ai·云计算·腾讯云·选型宝
多巴胺与内啡肽.1 小时前
OpenCV进阶操作:人脸检测、微笑检测
人工智能·opencv·计算机视觉
小羊在奋斗1 小时前
【LeetCode 热题 100】反转链表 / 回文链表 / 有序链表转换二叉搜索树 / LRU 缓存
算法·leetcode·链表
Wnq100721 小时前
基于 NanoDet 的工厂巡检机器人目标识别系统研究与实现
人工智能·机器学习·计算机视觉·目标跟踪·机器人·巡检机器人
yzx9910131 小时前
使用SVM进行图像分类
机器学习·支持向量机·分类