仿射变换和透视变换

前言

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

国际惯例,参考博客:

opencv中的warpAffine

opencv中的warpPerspective

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

仿射变换与透视变换区别

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

Affine and Projective Transformations

OpenCV Transformationmatrix: affine vs. perspective warping

点乘即投影向量

【TensorFlow-windows】扩展层之STN

理论

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

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

仿射变换其实是透视变换的一种特殊形式,他俩都可以用下面这个矩阵表示
M=[a1a2b1a3a4b2c1c21]M=\begin{bmatrix} a_1&a_2&b_1\\ a_3&a_4&b_2\\ c_1&c_2&1 \end{bmatrix} M=a1a3c1a2a4c2b1b21
用此矩阵将(x,y)(x,y)(x,y)变换到新的坐标点就是
[x′y′1]=[a1a2b1a3a4b2c1c21][xy1]\begin{bmatrix} x'\\ y'\\ 1 \end{bmatrix} =\begin{bmatrix} a_1&a_2&b_1\\ a_3&a_4&b_2\\ c_1&c_2&1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=a1a3c1a2a4c2b1b21xy1
学过线性代数就知道,这里面

  • [a1a2a3a4]\begin{bmatrix} a_1&a_2\\ a_3&a_4 \end{bmatrix}[a1a3a2a4]用来处理旋转和缩放,比如有个点是[2,3][2,3][2,3],经过[2002]\begin{bmatrix} 2&0\\ 0&2 \end{bmatrix}[2002]矩阵变换后就成了[4,6][4,6][4,6]即被放大了四倍,而经过[0110]\begin{bmatrix} 0&1\\ 1&0 \end{bmatrix}[0110]就变成了[3,2][3,2][3,2]即从原来的[2,3][2,3][2,3]坐标点旋转到了[3,2][3,2][3,2]坐标点。
  • [b1b2]\begin{bmatrix} b_1\\ b_2 \end{bmatrix}[b1b2]这个就显而易见是平移
  • [c1,c2][c_1,c_2][c1,c2]是投影向量,因为点乘就是c1x+c2yc_1x+c_2yc1x+c2y,刚好代表一个向量在另一个向量的投影

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

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

注意的点:

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

在这里插入图片描述

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

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

变换公式

根据OpenCV中所述:

仿射变换的变换公式为:
dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23}) dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)
透视变换变换公式为:

dst(x,y)=src(M11x+M12y+M13M31x+M32y+M33,M21x+M22y+M23M31x+M32y+M33)\texttt{dst} (x,y) = \texttt{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}} \right )dst(x,y)=src(M31x+M32y+M33M11x+M12y+M13,M31x+M32y+M33M21x+M22y+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)

在这里插入图片描述

透视变换

使用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)

在这里插入图片描述

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

#透视变换
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)

在这里插入图片描述

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

理论扩展

上面说过仿射变换是特殊的透视变换,后者变换矩阵的第3行c1,c2c_1,c_2c1,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)

在这里插入图片描述

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

#获取透视变换矩阵
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中有用到相关理论。

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

在这里插入图片描述

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

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

相关文章

obj格式解析

前言 最近处理一些网格渲染的时候,需要解析Obj文件,从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例,参考博客: 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候,想到深度学习里面通常用的部署方法是flask做服务端,然后使用nginx做负载均衡,貌似也能做内网穿透。不过我不太懂负载均衡,只想利用本地电脑搭建一个简单的服务器,实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能,这里简单记录一下opencv中的轮廓检测算法使用方法,至于理论,后续有机会再去细品。 国际惯例: OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…

RBF神经网络理论与实现

前言 最近发现有挺多人喜欢径向基函数(Radial Basis Function,RBF)神经网络,其实它就是将RBF作为神经网络层间的一种连接方式而已。这里做一个简单的描述和找了个代码解读。 之前也写过一篇,不过排版不好看,可以戳这里跳转 国际惯例&#x…

基于python和unity交互的卡通角色肢体和表情驱动(深度学习)

前言 最近看到了好多卡通角色的肢体驱动的东东,感觉是时候发挥一下读研时候学的东西了,而且虽然现在不炼丹了,但是还是得保持吃丹的技能。这个项目找了很多很多代码进行测试,最终集成了一个3D姿态估计和人脸关键点提取的代码。 …

OpenCV学习——形态学

前言 继续学习图像里面的形态学知识——结构元、腐蚀、膨胀、开运算、闭运算、击中/不击中变换。以及部分基本形态学算法,包括边界提取、空洞填充、连通分量的提取、凸壳、细化、粗化、骨架、裁剪、形态学重建。 其实就是对冈萨雷斯的《数字图像处理》中第9章节《…

径向基函数RBF三维网格变形

前言 之前写过径向基函数(RBF)神经网络做分类或者拟合。然后挖了个坑说在《Phase-Functioned Neural Networks for Character Control》里面提到了用于做地形编辑,所以这篇博客就是解析一下如何用RBF做网格编辑系统。 参考博客: Noe’s tutorial on d…

OBJ可视化——UV还原(修正)

前言 前面写过一篇obj格式解析的博客,但是这篇文章中可视化的工作是参考PRNet的源码进行的,后来细细思考了一下,有点问题,具体看下面。 问题来源 在PRNet源码的render.py中有个函数render_texture,是作者用于将uv展…

Unity中BVH骨骼动画驱动的可视化理论与实现

前言 找了很久使用BVH到unity中驱动骨骼动画的代码,但是都不是特别好用,自己以前写过,原理很简单,这里记录一下。 理论 初始姿态 在BVH或者其它骨骼动画中,一般涉及到三种姿势:A-pose,T-pos…

卡通驱动项目ThreeDPoseTracker——模型驱动解析

前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型,公众号有兄弟私信一些问题,我刚好对这个项目实现有兴趣,就分析一波源码,顺便把问题解答一下。 这个源码其实包括很多内容:3D姿态估计,坐标平滑…

卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析,还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动,在ThreeDPoseTracker源码中有做两次平滑,一部分是卡尔曼滤波,还有一部分是低通滤波…

卡通角色表情驱动系列一

前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动,接下来在卡通驱动领域还有一个是表情驱动。对这个真的是一窍不通啊,只能慢慢看论文了。 国际惯例,参考博客/论文: 《Landmark-guided deformation transfer of template f…

opencv相机标定和人头姿态估计案例

前言 头部驱动除了之前关注的表情驱动外,还有眼球驱动和头部方向驱动。本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向。 国际惯例,参考博客: opencv:Camera Calibration and 3D Reconstruction opencv:…

卡通角色表情驱动系列二

前言 之前介绍了使用传统算法求解BS系数的表情驱动方法,其中提到过的三种方法之一是基于网格形变迁移做的,那么这篇文章就是对《Deformation Transfer for Triangle Meshes》做表情驱动的解析。 国际惯例,参考博客: 论文原文《…

UE自带重定向原理

UE自带重定向方法验证 核心源码在VS的解决方案中的位置: UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函数 跳转方法 AssetTypeActions_AnimationAsset.cpp的RetargetNonSkeletonAnimationHa…

【caffe-Windows】caffe+VS2013+Windows无GPU快速配置教程

前言 首先来一波地址: happynear大神的第三方caffe:http://blog.csdn.net/happynear/article/details/45372231 Neil Z大神的第三方caffe:https://initialneil.wordpress.com/2015/01/11/build-caffe-in-windows-with-visual-studio-2013-…

【caffe-Windows】caffe+VS2013+Windows+GPU配置+cifar使用

前言 国际惯例,先来波地址: CUDA WIN7:链接:http://pan.baidu.com/s/1nvyA3Qp 密码:h0f3 官方网址:https://developer.nvidia.com/cuda-toolkit CUDA WIN10:链接:http://pan.baidu.com/s/1…

【一些网站的收集】包含机器学习深度学习大牛主页等

数学概念部分 旋转矩阵、欧拉角、四元数的比较 欧拉角和四元数的表示 四元数与旋转 B样条曲线 非常好的概率统计学习的主页 误差方差偏差 编程语言学习 C#编程视频 OpenGL编程NeHe OpenGL官网 OpenGL“我叫MT“纯手工3D动画制作之1——基础介绍 【强大】非常好的Op…

Eureka源码分析

Eureka源码分析 Eureka server入口: Spring.factories PS: 意味着如果加载EurekaServerAutoConfiguration成功,需要 ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)需加载成功. 通过Bean注入了很多类 本质上, eureka-server包含很多事件: EurekaInstanceC…

matlab程序中,如何解决矢量长度必须相同的问题

主要原因就是画图的x和y长度不一样,我用一个例子说明。 问题代码: clear all;close all;clc;x 0 : 1: 9;y sin(x);n 2*length(x);yi interpft(y, n);xi 0 : 0.5 : 10;hold on ;plot(x, y ,ro);plot(xi, yi, b.-);plot(x, sin(x),m--);legend(原始…