首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

仿射变换和透视变换

  • 23-09-22 20:01
  • 2591
  • 10648
blog.csdn.net

前言

在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐,保证一张人脸贴到另一张人脸时,大小完全一致;所以有必要理解一下这两个概念的区别,由于以实用性为目的,所以所有的图像算法都会以opencv为例,去探索其用法。

国际惯例,参考博客:

opencv中的warpAffine

opencv中的warpPerspective

【opencv实践】仿射变换和透视变换

仿射变换与透视变换区别

图像处理的仿射变换与透视变换

Affine and Projective Transformations

OpenCV Transformationmatrix: affine vs. perspective warping

点乘即投影向量

【TensorFlow-windows】扩展层之STN

理论

在做数据增强的时候,图像里面有很多几何变换,比如旋转、平移、缩放、拉伸等,但是他们的本质还是通过某个矩阵,将图像每个像素点的坐标变换到另一个新的位置。这种通过某个矩阵将图像进行变换的方法通常称为线性变换,也就是说利用了向量加法和标量乘法。

【注】本文的变换仅仅针对二维矩阵,非针对三维矩阵的变换。

仿射变换其实是透视变换的一种特殊形式,他俩都可以用下面这个矩阵表示
M = [ a 1 a 2 b 1 a 3 a 4 b 2 c 1 c 2 1 ] M=[a1a2b1a3a4b2c1c21]

⎡⎣⎢a1a3c1a2a4c2b1b21⎤⎦⎥
M=⎣⎡​a1​a3​c1​​a2​a4​c2​​b1​b2​1​⎦⎤​
用此矩阵将 ( x , y ) (x,y) (x,y)变换到新的坐标点就是
[ x ′ y ′ 1 ] = [ a 1 a 2 b 1 a 3 a 4 b 2 c 1 c 2 1 ] [ x y 1 ] [x′y′1]
⎡⎣⎢x′y′1⎤⎦⎥
=[a1a2b1a3a4b2c1c21]
⎡⎣⎢a1a3c1a2a4c2b1b21⎤⎦⎥
[xy1]
⎡⎣⎢xy1⎤⎦⎥
⎣⎡​x′y′1​⎦⎤​=⎣⎡​a1​a3​c1​​a2​a4​c2​​b1​b2​1​⎦⎤​⎣⎡​xy1​⎦⎤​

学过线性代数就知道,这里面

  • [ a 1 a 2 a 3 a 4 ] [a1a2a3a4]
    [a1​a3​​a2​a4​​]
    用来处理旋转和缩放,比如有个点是 [ 2 , 3 ] [2,3] [2,3],经过 [ 2 0 0 2 ] [2002]
    [20​02​]
    矩阵变换后就成了 [ 4 , 6 ] [4,6] [4,6]即被放大了四倍,而经过 [ 0 1 1 0 ] [0110]
    [01​10​]
    就变成了 [ 3 , 2 ] [3,2] [3,2]即从原来的 [ 2 , 3 ] [2,3] [2,3]坐标点旋转到了 [ 3 , 2 ] [3,2] [3,2]坐标点。
  • [ b 1 b 2 ] [b1b2]
    [b1​b2​​]
    这个就显而易见是平移
  • [ c 1 , c 2 ] [c_1,c_2] [c1​,c2​]是投影向量,因为点乘就是 c 1 x + c 2 y c_1x+c_2y c1​x+c2​y,刚好代表一个向量在另一个向量的投影

投影变换(projective transformation)展示的是当观察者视角变化以后,观察体的变化情况,通常用于产生透视畸变(perspective distortion),有时候称为透视变换(perspective transformation)

仿射变换(affine transformation)用于缩放(scaling)、拉伸(skew)、旋转(rotation)

注意的点:

  • 两个变换都是将直线投影到直线
  • 两条平行直线通过仿射变换后依旧是两条平行的直线
  • 两条平行直线通过透视变换后可以是两条相交的直线

在这里插入图片描述

从数学上来讲,它俩的区别在变换矩阵的最后一行 [ c 1 , c 2 ] [c_1,c_2] [c1​,c2​]的值上,仿射变换是0值,而透视变换通常不是。所以这一点也能说明仿射变换是透视变换的子集。

但是有一个要求,变换矩阵一定不能是奇异矩阵,因为奇异矩阵会导致 A X = b AX=b AX=b有无穷解或者无解,也就是说会出现多个点变换到同一个点的情况。

变换公式

根据OpenCV中所述:

仿射变换的变换公式为:
dst ( x , y ) = src ( M 11 x + M 12 y + M 13 , M 21 x + M 22 y + M 23 ) exttt{dst} (x,y) = exttt{src} ( exttt{M} _{11} x + exttt{M} _{12} y + exttt{M} _{13}, exttt{M} _{21} x + exttt{M} _{22} y + exttt{M} _{23}) dst(x,y)=src(M11​x+M12​y+M13​,M21​x+M22​y+M23​)
透视变换变换公式为:

dst ( x , y ) = src ( M 11 x + M 12 y + M 13 M 31 x + M 32 y + M 33 , M 21 x + M 22 y + M 23 M 31 x + M 32 y + M 33 ) exttt{dst} (x,y) = exttt{src} left ( frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} ight ) dst(x,y)=src(M31​x+M32​y+M33​M11​x+M12​y+M13​​,M31​x+M32​y+M33​M21​x+M22​y+M23​​)

代码实践

使用opencv测试效果

仿射变换

使用warpAffine函数,将图片旋转45度,同时向右平移300像素,向下平移100像素

#仿射变换
degree=np.deg2rad(45)
M1=np.array([
    [np.cos(degree),-np.sin(degree),300],
    [np.sin(degree),np.cos(degree),100]
])
dst1 = cv2.warpAffine(img,M1,(img.shape[1]*2,img.shape[0]*2))

plt.figure(figsize=(8,8))
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(dst1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

透视变换

使用warpPerspective函数

如果将透视变换使用上面的仿射变换矩阵,补齐第三行,可以得到和仿射变换一样的结果

#透视变换
degree=np.deg2rad(45)
M2=np.array([
    [np.cos(degree),-np.sin(degree),300],
    [np.sin(degree),np.cos(degree),100],
    [0,0,1]
])
dst2 = cv2.warpPerspective(img,M2,(img.shape[1]*2,img.shape[0]*2))

plt.figure(figsize=(8,8))
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(dst2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

一旦稍微改变投影向量,也就是第三行的值,就会发生很大的变化

#透视变换
degree=np.deg2rad(45)
M2=np.array([
    [np.cos(degree),-np.sin(degree),300],
    [np.sin(degree),np.cos(degree),100],
    [0,-0.0015,1]
])
dst2 = cv2.warpPerspective(img,M2,(img.shape[1]*2,img.shape[0]*2))

plt.figure(figsize=(8,8))
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(dst2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

所以我们通常能够通过仿射变换矩阵思考出变换后的样子,但是透视变换却很难预测出变换后的样子。

理论扩展

上面说过仿射变换是特殊的透视变换,后者变换矩阵的第3行 c 1 , c 2 c_1,c_2 c1​,c2​为0的时候就变成了前者。

为了让变换可控,我们可以预先构建某些点来规定变换矩阵的映射是什么样的,依据变换矩阵能看出参数量:透视变换的矩阵为8个参数,仿射变换矩阵为6个参数。

根据线性代数,如果需要

  • 求解仿射变换矩阵:6个未知数需要6个方程,即需要3组对应点
  • 求解透视变换矩阵:8个未知数需要8个方程,即需要4组对应点

所以比如想把原图变成平行四边形时,可以平行四边形上的三个点求解仿射变换:

#获取仿射变换矩阵
src_pts = np.float32([[0,0],[0,1],[1,1]])
dst_pts = np.float32([[0,0],[1,1],[2,1]])
M = cv2.getAffineTransform(src_pts,dst_pts)
dst1 = cv2.warpAffine(img,M,(img.shape[1]*2,img.shape[0]*2))

plt.figure(figsize=(8,8))
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(dst1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

想把原图变成直角梯形时,可以使用直角梯形上的四个点求解透视变换

#获取透视变换矩阵
src_pts = np.float32([[0,0],[0,300],[400,300],[400,0]])
dst_pts = np.float32([[0,0],[0,300],[200,300],[400,0]])
M = cv2.getPerspectiveTransform(src_pts,dst_pts)
dst2 = cv2.warpPerspective(img,M,(img.shape[1]*2,img.shape[0]*2))

plt.figure(figsize=(8,8))
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(dst2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

总结

其实就是对图像处理的一些基本知识补充,在之前写过的换脸博客1和博客2中有用到相关理论。

博客和公众号致力于图像、机器学习、运动捕捉方向的理论和代码实践,注重基础和实践,有兴趣可关注一波,代码通常公布在公众号中的github网址

在这里插入图片描述

文章知识点与官方知识档案匹配,可进一步学习相关知识
OpenCV技能树几何变换和图像特征仿射变换20473 人正在系统学习中
注:本文转载自blog.csdn.net的风翼冰舟的文章"https://blog.csdn.net/zb1165048017/article/details/109089174"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top