[解疑]图像、矩阵的二维空间变换

article/2025/10/3 21:57:38

本文经过参考多个文章整理而成,感谢各位博主的无私分享。

综述

图像(2维平面)到图像(2维平面)的四种变换包括:等距变换,相似变换,仿射变换,投影变换。对图像的几何变换本质上是一种线性变换,其数学本质为 I n e w = T I o l d I_{new}=T{I_{old}} Inew=TIold,即通过变换矩阵 T T T将原图上的点的位置 I o l d I_{old} Iold 变换到新的位置,从而得到新的图像 I n e w I_{new} Inew

执行一般的二维空间变换包括如下三步:

  • 定义空间变换的参数;
  • 创建变换结构体TFORM,它定义了你所要执行变换的类型;
    TFORM结构体包含了执行变换需要的所有参数。你可以定义很多类型的空间变换,包括仿射变换affine transformations(如平移translation,缩放scaling,旋转rotation,剪切shearing)、投影变换projective transformations和自定义的变换custom transformations。
    创建结构体的方法有两种:使用maketform或者使用cp2tform。
  • 执行变换。
    通过将要变换的图像和TFORM结构体传递给imtransform函数即可实现变换。

在这里插入图片描述
2D平面变换示意图

  • Translation 平移
  • Euclidean(rigid, rotation) 旋转
  • Scale 缩放;图中没有画出
  • Similarity 相似变换;结合旋转,平移和缩放
  • Affine 仿射变换;想象在similarity的基础上用两只手对图像进行按压拉伸
  • Projective 投影变换;想象投影仪做的事情,将一个面投影到另外一个面的情况

eometry Transformation 几何变换
Homogeneous coordinate 齐次坐标:使用N+1维坐标来表示N维坐标,例如在2D笛卡尔坐标系中加上额外变量w来形成2D齐次坐标系。齐次坐标具有规模不变性,同一点可以被无数个齐次坐标表达. 齐次坐标转化为笛卡尔坐标可以通过同除最后一项得到。

仿射变换

原理

仿射变换其实是另外两种简单变换的叠加:一个是线性变换,一个是平移变换。统一平移变换和线性变换的一种变换我们起了个名字叫“仿射变换”。这个新的变换就不再单纯的是两个线性空间的映射了,而是变成了两个仿射空间的映射关系。为了更好地理解仿射变换,首先就要知道线性变换以及它的不足。在未说明的情况下,下面使用的是卡迪尔坐标系。
所谓线性变换是指两个线性空间的映射,一个变换 L : A → B {\mathcal{L}:\mathcal{A}\to\mathcal{B}} L:AB是线性变换,必须满足两个条件,也就是我们经常说的线性条件:

  • L ( u + v ) = L ( u ) + L ( v ) L(u+v)=L(u)+L(v) L(u+v)=L(u)+L(v) additivity
  • L ( α u ) = α L ( u ) {L({\alpha}u)={\alpha}L(u)} L(αu)=αL(u) homogeneity

举个例子说明一下。假设 L L L是一个二维绕原点旋转变换, u u u v v v是旋转角度。我们知道“一次性旋转 u + v u+v u+v度”和“先旋转 u u u度再旋转 v v v读”达到的效果是一样的;同样地,“一次性旋转 α u {\alpha}u αu度”和“旋转 α \alpha α u u u度”也是一样的。
线性变换可以用矩阵来表示。假设 p = ( x , y ) T p=(x,y)^{T} p=(x,y)T是二维空间中的点, T T T是一线性变换,那么存在一个矩阵 A A A,使得 p ′ = ( x ′ , y ′ ) T = T ( p ) = A p p'=(x',y')^{T}=T(p)=Ap p=(x,y)T=T(p)=Ap。上面的旋转变换 R R R,以及缩放 S S S变换都有相应的变换矩阵

[ x ′ y ′ ] = R ( p ) = [ c o s ( θ ) − s i n ( θ ) s i n ( θ ) c o s ( θ ) ] [ x y ] \left[ {\begin{array}{c} x'\\y'\\ \end{array}} \right]=R(p)= \left[ {\begin{array}{cc} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \\ \end{array} } \right] \left[ {\begin{array}{cc} x \\ y \\ \end{array} } \right] [xy]=R(p)=[cos(θ)sin(θ)sin(θ)cos(θ)][xy]

[ x ′ y ′ ] = S ( p ) = [ S x 0 0 S y ] [ x y ] \left[ {\begin{array}{c} x'\\y'\\ \end{array}} \right]=S(p)= \left[ {\begin{array}{cc} S_x & 0 \\ 0 & S_y \\ \end{array} } \right] \left[ {\begin{array}{c} x \\ y \\ \end{array} } \right] [xy]=S(p)=[Sx00Sy][xy]

但是在笛卡尔坐标系中,平移变换却不能用矩阵来表示。一个平移变换 T T T具有如下的形式

[ x ′ y ′ ] = T ( p ) = I [ x y ] + [ t x t y ] \left[ {\begin{array}{c} x'\\y'\\ \end{array}} \right]=T(p)= I \left[ {\begin{array}{cc} x \\ y \\ \end{array} } \right]+\left[ {\begin{array}{c} t_x \\ t_y \\ \end{array} } \right] [xy]=T(p)=I[xy]+[txty]

我们可以很容易地验证,平移变换 T T T是不能写成两个矩阵乘积形式的。使用齐次坐标系很好的解决了这个问题(可能还有其它的原因)。齐次坐标系统其实是用高维坐标来表示一个低维的点,就好比我们用 ( x , 1 ) (x,1) x,1)来表示一个长度值一样,其实用一个 x x x就可以了,但是用高一维的表示,在有的时候会带来便利。一个N维的卡迪尔坐标系中的一个点 p = ( x 1 , x 2 , . . . , x N ) p=(x_1,x_2,...,x_N) p=(x1,x2,...,xN),在齐次坐标系中有无数的 N + 1 N+1 N+1维点与之对应,这些点可以描述为 p H = ( ω x 1 , ω x 2 , . . . , ω x N , ω ) p_H=(\omega x_1,\omega x_2,...,\omega x_N,\omega) pH=(ωx1,ωx2,...,ωxN,ω) ω \omega ω取不同的值,我们变得到齐次坐标系中不同的点。当把这些点映射到 ω = 1 \omega=1 ω=1平面(不改变 x i x_i xi之间比例),我们又降维得到对应的卡迪尔坐标系中的点。在OpenGL中我们是用 ( x , y , z , 1 ) ( ω = 1 ) (x,y,z,1)(\omega=1) (x,y,z,1)(ω=1)来表示一点三维的点,显然这个点与卡迪尔坐标系中的点 ( x , y , z ) (x,y,z) (x,y,z)是一一对应的。在计算的过程中,会出现第四个分量不为 ω ≠ 1 \omega \neq 1 ω=1的情况,这时我们也总是同除以 ω \omega ω使齐次坐标正规化。
在这里插入图片描述
现在回来让我们看看使用齐次坐标时,对应的线性变换是什么形式。假设 p = ( x , y , 1 ) T p=(x,y,1)^{T} p=(x,y,1)T是二维点对应的齐次坐标,与上面使用卡迪尔坐标系类似,我们可以得到相应的线性变换如旋转变换 R R R和缩放变换 S S S的矩阵表示:

[ x ′ y ′ 1 ] = R ( p ) = [ c o s ( θ ) − s i n ( θ ) 0 s i n ( θ ) c o s ( θ ) 0 0 0 1 ] [ x y 1 ] \left[ {\begin{array}{c} x'\\y'\\1\\ \end{array}} \right]=R(p)= \left[ {\begin{array}{ccc} cos(\theta) & -sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1\\\end{array} } \right] \left[ {\begin{array}{c} x \\ y \\ 1 \\ \end{array} } \right] xy1=R(p)=cos(θ)sin(θ)0sin(θ)cos(θ)0001xy1

[ x ′ y ′ 1 ] = S ( p ) = [ S x 0 0 0 S y 0 0 0 1 ] [ x y 1 ] \left[ {\begin{array}{c} x'\\y'\\1\\ \end{array}} \right]=S(p)= \left[ {\begin{array}{ccc} S_x & 0 & 0 \\ 0 & S_y & 0 \\ 0 & 0 & 1\\ \end{array} } \right] \left[ {\begin{array}{c} x \\ y \\ 1 \\ \end{array} } \right] xy1=S(p)=Sx000Sy0001xy1

容易验证, ( x ′ , y ′ ) (x', y') (x,y)的值并没有变化。但是使用齐次坐标后,平移操作便也可以使用矩阵来表示了(如下),平移量出现在变换矩阵的最右侧。

[ x ′ y ′ 1 ] = T ( p ) = [ 1 0 t x 0 1 t y 0 0 1 ] [ x y 1 ] \left[ {\begin{array}{c} x'\\y'\\1\\ \end{array}} \right]=T(p)= \left[ {\begin{array}{ccc} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1\\ \end{array} } \right] \left[ {\begin{array}{c} x \\ y \\ 1 \\ \end{array} } \right] xy1=T(p)=100010txty1xy1

最后,我们给出仿射变换稍微正式点的定义。一个仿射变换 T T T,可以表示成一个线性变换 A A A后平移 t t t T ( p ) = A p + t T(p)=Ap+t T(p)=Ap+t,其中 p p p是待变换的点齐次坐标表示。 T T T可以表示成如下的形式:

T = [ a 11 a 12 a 13 t 1 a 21 a 22 a 23 t 2 a 31 a 32 a 33 t 3 0 0 0 1 ] \bf{T}=\left[ {\begin{array}{cccc} a_{11}&a_{12}&a_{13}&t_1\\ a_{21}&a_{22}&a_{23}&t_2\\ a_{31}&a_{32}&a_{33}&t_3\\ 0&0&0&1\\ \end{array}} \right] T=a11a21a310a12a22a320a13a23a330t1t2t31

其中, A = [ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] \bf{A}=\left[ {\begin{array}{ccc} a_{11}&a_{12}&a_{13}\\ a_{21}&a_{22}&a_{23}\\ a_{31}&a_{32}&a_{33}\\ \end{array}} \right] A=a11a21a31a12a22a32a13a23a33表示线性变换; t = [ t 1 t 2 t 3 ] \bf{t}=\left[ {\begin{array}{c} t_1\\ t_2\\ t_3\\ \end{array}} \right] t=t1t2t3表示平移变换;右下角的数字可以进行整体缩放,当为1时,表示不进行整体缩放。

可以知道:仿射变换保持了二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。

warpAffine

opencv中相应的函数是:
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
Parameters:

  • src – input image.
  • dst – output image that has the size dsize and the same type as src .
  • M – transformation matrix,本文中着重讲M的构造
  • dsize – size of the output image.ansformation ().
  • borderMode – pixel extrapolation method (see borderInterpolate()); when borderMode=BORDER_TRANSPARENT , it means that the pixels in the destination image corresponding to the “outliers” in the source image are not modified by the function.
  • borderValue – value used in case of a constant border; by default, it is 0.

典型的变换矩阵

  • 平移,将每一点移到到 ( x + t , y + t ) (x+t , y+t) (x+t,y+t),变换矩阵为
    在这里插入图片描述
  • 缩放变换 将每一点的横坐标放大或缩小 s x s_x sx倍,纵坐标放大(缩小)到 s y s_y sy倍,变换矩阵为
    [ S x 0 0 0 S y 0 0 0 1 ] \left[ {\begin{array}{ccc} S_x & 0 & 0 \\ 0 & S_y & 0 \\ 0 & 0 & 1\\ \end{array} } \right] Sx000Sy0001
  • 旋转变换原点:目标图形围绕原点顺时针旋转 θ \theta θ弧度,变换矩阵为
    在这里插入图片描述
  • 旋转变换 :目标图形以 ( x , y ) (x , y) (x,y)为轴心顺时针旋转 θ \theta θ弧度,变换矩阵为
    在这里插入图片描述
    相当于两次平移与一次原点旋转变换的复合,即先将轴心 ( x , y ) (x,y) (x,y)移到到原点,然后做旋转变换,最后将图片的左上角置为图片的原点,即
    在这里插入图片描述
    在opencv的图像处理中,所有对图像的处理都是从原点进行的,而图像的原点默认为图像的左上角,而我们对图像作旋转处理时一般以图像的中点为轴心。

getRotationMatrix2D

可以使用opencv中自带的Mat getRotationMatrix2D(Point2f center, double angle, double scale)函数获得变换矩阵M,

  • center:旋转中心
  • angle:旋转弧度,一定要将角度转换成弧度
  • scale:缩放尺度

它得到的矩阵是:
(顺时针)
在这里插入图片描述
(逆时针)
在这里插入图片描述

其中 α = s c a l e ∗ c o s ( a n g l e ) , β = s c a l e ∗ s i n ( a n g l e ) , ( c e n t e r . x , c e n t e r . y ) α = scale * cos(angle) , β = scale * sin(angle) , (center.x , center.y) α=scalecos(angle),β=scalesin(angle),(center.x,center.y) 表示旋转轴心。

getAffineTransform

opencv中还有一个函数:Mat getAffineTransform(InputArray src, InputArray dst)
它通过三组点对就可以获得它们之间的仿射变换,如果我们在一组图像变换中知道变换后的三组点,那么我们就可以利用该函数求得变换矩阵,然后对整张图片进行仿射变换。

仿射变换之所以重要,另一个重要的原因是仿射变换后不改变点的共线/共面性,而且还保持比例,这对图形系统尤其重要。例如,根据这个性质,如果我们要变换一个三角形,只需要对三个定点 v 1 v1 v1, v 2 v2 v2, v 3 v3 v3进行变换T就可以了,对于原先边 v 1 v 2 v1v2 v1v2上的点,变换后一定还在边后 T ( v 1 ) T ( v 2 ) T(v1)T(v2) T(v1)T(v2)上。

总结一下,仿射变换是线性变换后进行平移变换(其实也是齐次空间的线性变换),使用齐次坐标使得仿射变换可以以统一的矩阵形式进行表示。

透视变换

还有一种与仿射变换经常混淆的变换为透视变换,透视变换需要四组点对才能确定变换矩阵,由于仿射变换保持“平直性”与“平行性”,因此只需要三组点对,而透视变换没有这种约束,故需要四组点对。

原理

透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。通用的变换公式为:
在这里插入图片描述
u u u, v v v是原始图片左边,对应得到变换后的图片坐标 x x x, y y y,其中在这里插入图片描述

变换矩阵在这里插入图片描述可以拆成4部分,在这里插入图片描述表示线性变换,比如scaling,shearing和ratotion。在这里插入图片描述用于平移,在这里插入图片描述产生透视变换。所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。

重写之前的变换公式可以得到:
在这里插入图片描述
所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:
变换的4组对应点可以表示成:在这里插入图片描述
根据变换公式得到:在这里插入图片描述
定义几个辅助变量:在这里插入图片描述
在这里插入图片描述都为0时变换平面与原来是平行的,可以得到:在这里插入图片描述
在这里插入图片描述不为0时,得到:在这里插入图片描述
求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。
在这里插入图片描述

warpPerspective

主要作用:对图像进行透视变换,就是变形。
C++: void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
参数详解:

  • InputArray src:输入的图像
  • OutputArray dst:输出的图像
  • InputArray M:透视变换的矩阵
  • Size dsize:输出图像的大小
  • int flags=INTER_LINEAR:输出图像的插值方法,
    combination of interpolation methods (INTER_LINEAR or INTER_NEAREST) and the optional flagWARP_INVERSE_MAP, that sets M as the inverse transformation ( \texttt{dst}\rightarrow\texttt{src} )
  • int borderMode=BORDER_CONSTANT:图像边界的处理方式
  • const Scalar& borderValue=Scalar():边界的颜色设置,一般默认是0

参考

图像的变换
https://blog.csdn.net/liangjiubujiu/article/details/80424287

仿射变换与齐次坐标
https://blog.csdn.net/liangjiubujiu/article/details/80628506

仿射变换矩阵
https://blog.csdn.net/liangjiubujiu/article/details/80918428

matlab 二维空间变换
https://blog.csdn.net/liangjiubujiu/article/details/80607161

图像投影/单应性变换/直射
https://blog.csdn.net/liangjiubujiu/article/details/80412175

图像的等距变换,相似变换,仿射变换,射影变换及其matlab实现
https://blog.csdn.net/liangjiubujiu/article/details/80616870

仿射变换详解 warpAffine
https://blog.csdn.net/q123456789098/article/details/53330484



写于关雎


在这里插入图片描述


新浪微博:https://weibo.com/tianzhejia
CSDN博客:https://blog.csdn.net/qq_35605018
博客网站:http://www.zhijiadeboke.xyz
GitHub:https://github.com/ZhijiaTian
QQ邮箱:2461824656@qq.com
126邮箱:tianzhejia@126.com
Outlook邮箱:tianzhejia@outlook.com

以上均可与本人取得联系,欢迎探讨。^ v ^


http://chatgpt.dhexx.cn/article/WmfjK9HZ.shtml

相关文章

数字图像处理(入门篇)十四 透视变换

目录 一 透视变换 二 实践 (1)代码 (2)结果图 一 透视变换 现实生活中的空间是三维的,图像中的物体存在近大远小的特征,这种畸变仿射变换不能矫正。因此,我们需要使用到三维空间的变化&…

【图像理论】透视变换

透视变换: 定义:本质是将图像投影到一个新的视平面。仿射变换可以理解为透视变换的特殊形式。利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某…

Python图像处理之透视变换

1 引言 如果你想对图像进行校准,那么透视变换是非常有效的变换手段。透视变换的定义为将图像投影到一个新的视平面,通常也被称之为投影映射。 2 公式 一般来说,通用的图像变换公式如下所示: 上述公式中,u,v代表原…

维特比算法 python_维特比算法理解与实现(Python)

前言 写这篇文章就是想以通俗易懂的方式解析维特比算法,最后给出Python代码的实现。下面的公式和原理均出自《统计学习方法》。 算法的原理 算法的原理1.PNG 算法的原理2.PNG 上面写了一大堆,意思就是:每个时刻选择出概率最大的路径&#xf…

viterbi-algorithm 维特比算法的例子解析

维特比算法的目的: 寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 关于原理的讲解可以参考下面两篇文章,讲的比较清楚 小白给小白详解维特比算法1. 小白给小白详解维特比算法2. 本文通过分析维特比算法的例子&#xff0c…

维特比算法代码

维特比算法实现python语言版 本文主要写一个关于维特比算法的代码,具体理论请参考一文搞懂HMM(隐马尔可夫模型): HMM(隐马尔可夫模型)是用来描述隐含未知参数的统计模型,举一个经典的例子&…

维特比算法学习

参考文章1: 简直不要太通俗易懂,这篇文章,很值得看 参考文章2: 解释一些概念性的问题,我把他的一些内容写下来 维特比(Viterbi)算法的核心是动态规划。 对于 HMM 而言,其中一个重要的任务就是要找出最有…

5分钟理解维特比算法

安德鲁维特比老人家发明了维特比算法,用非常巧妙的方法简化了隐马尔可夫第二个问题运算过程。维特比先生后来发明了CDMA技术并与人一起创办了高通公司,高通现在是通信巨头,不生产产品却每年收取大量的专利费。 下面我们用简单的例子&#xff…

Viterbi-Algorithm(维特比算法)

Viterbi-Algorithm 维特比算法是一个特殊但应用最广的动态规划算法。利用动态规划,可以解决任何一个图中的最短路径问题。而维特比算法是针对一个特殊的图-篱笆网了(Lattice)的有向图最短路径问题而提出来的。它之所以重要,是因为…

NLP学习笔记06-维特比算法

一序 本文属于NLP学习笔记系列。 上一篇整理了前向最大匹配算法与所有组合算法缺点(时间复杂度太高了)。 二 维特比算法 log(x*y*z) log(x)log(y)log(z) 概率上为了避免小数练乘出现的超范围溢出,改用log,改用-log,使得原来求概…

HMM-维特比算法

HMM-维特比算法(viterbi) HMM回顾隐马科夫链解法:维特比算法(Viterbi) HMM回顾 最终的公式可以解释主要分为两个部分: P(xi|yi),发射概率,字面意思是从一个词性中发射/生成出某一个…

Viterbi算法(维特比算法)

维特比算法背景: 安德鲁维特比(Andrew J. Viterbi),CDMA之父,IEEE Fellow,高通公司创始人之一,高通首席科学家。他开发了卷积码编码的最大似然算法而享誉全球。1991年香农奖(Claude …

HMM+维特比算法

一、简介 Viterbi 算法 考虑到穷举方法的缺点,可以采用:Viterbi 算法: 动态搜索最优状态序列,这样每个节点保存的是到当前节点的局部最优概率;依据最后一个时刻中概率最高的状态,逆向找其路径中的上一个最大部分最优路…

维特比算法的java实现_原创:维特比算法

看了宗成庆博士的《统计自然语言处理(中文信息处理)》的第六章,对维特比算法有着非常精辟的讲解。把其中的讲解上传上来,个人感觉比较正统。 今天用Java实现了这个算法,也可以转换为C代码: package com.nlp.hmm.algorithm.viterbi…

隐马尔可夫(HMM)、前/后向算法、Viterbi算法 再次总结

本总结是是个人为防止遗忘而作,不得转载和商用。 说明:此篇是作者对“隐马尔可夫模型”的第二次总结,因此可以算作对上次总结的查漏补缺以及更进一步的理解,所以很多在第一次总结中已经整理过的内容在本篇中将不再重复&#xff0c…

维特比(Viterbi)算法

维特比算法 (Viterbi algorithm) 是机器学习中应用非常广泛的动态规划算法,在求解隐马尔科夫、条件随机场的预测以及seq2seq模型概率计算等问题中均用到了该算法。实际上,维特比算法不仅是很多自然语言处理的解码算法,也是现代数字通信中使用…

小白给小白详解维特比算法(一)

小白给小白详解维特比算法(一) 小白给小白详解维特比算法一 篱笆网络Lattice的最短路径问题 这个问题长什么样子这个问题难在哪里简化成这个模样你总能回答了吧下一步我们该干什么 别倒立了我们再从头想一下这个问题 我们是怎么走过来的来我们从A开始走这…

viterbi算法

维特比算法(Viterbi Algorithm) 标签(空格分隔): 未分类 维特比算法(Viterbi Algorithm) viterbi算法用于寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于…

维特比算法

1. 概述 维特比算法是安德鲁.维特比(Andrew Viterbi)于1967年为解决通信领域中的解码问题而提出的,它同样广泛用于解决自然语言处理中的解码问题,隐马尔可夫模型的解码是其中典型的代表。无论是通信中的解码问题还是自然语言处理中的解码问题&#xff0c…

维特比算法(Viterbi algorithm)

一、维特比算法(Viterbi)原理以及简单实现 维特比算法(Viterbi algorithm)是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 维特比算法实际是用动…