今天遇到一个问题是关于仿射变换的,但是由于没有将仿射变换的具体原理型明白,看别人的代码看的很费解,最后终于在师兄的帮助下将原理弄明白了,我觉得最重要的是理解仿射变换可以看成是几种简单变换的复合实现,
具体实现形式即将几种简单变换的变换矩阵M相乘,这样就很容易理解啦
定义:仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。
这类变换可以用一个3*3的矩阵M来表示,其最后一行为(0,0,1)。该变换矩阵将原坐标为(x,y)变换为新坐标(x',y'),
即
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.
- flags – combination of interpolation methods (see resize() ) and the optional flag WARP_INVERSE_MAP that means that M is the inverse transformation ( ).
- 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.
下面介绍一些典型的仿射变换:
(1)平移,将每一点移到到(x+t , y+t),变换矩阵为
(2)缩放变换 将每一点的横坐标放大或缩小sx倍,纵坐标放大(缩小)到sy倍,变换矩阵为
(3)旋转变换原点:目标图形围绕原点顺时针旋转Θ 弧度,变换矩阵为
(4) 旋转变换 :目标图形以(x , y )为轴心顺时针旋转θ弧度,变换矩阵为
相当于两次平移与一次原点旋转变换的复合,即先将轴心(x,y)移到到原点,然后做旋转变换,最后将图片的左上角置为图片的原点,即
有的人可能会说为什么这么复杂呢,那是因为在opencv的图像处理中,所有对图像的处理都是从原点进行的,而图像的原点默认为图像的左上角,而我们对图像作旋转处理时一般以图像的中点为轴心,因此就需要做如下处理
如果你觉得这样很麻烦,可以使用opencv中自带的Mat getRotationMatrix2D(Point2f center, double angle, double scale)函数获得变换矩阵M,
center:旋转中心
angle:旋转弧度,一定要将角度转换成弧度
scale:缩放尺度
它得到的矩阵是:
其中α = scale * cos( angle ) , β = scale * sing( angle ) , ( center.x , center.y ) 表示旋转轴心
但是不得不说opencv的文档以及相关书籍中都把这个矩阵写错了,如下:
建议大家自己通过下式验证一下,即首先将轴心(x,y)移到原点,然后做旋转平绽放变换,最后再将图像的左上角转换为原点