仿射变换和透射变换

来源:互联网 发布:云南师范大学网络平台 编辑:程序博客网 时间:2024/04/30 02:03

仿射变换

定义:仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。

这类变换可以用一个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.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.

 下面介绍一些典型的仿射变换:

(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)移到原点,然后做旋转平绽放变换,最后再将图像的左上角转换为原点

没有去研究该函数的源码,不晓得源码中到底怎么写的,但是在别人的博客中看到这个函数貌似需要修正

 


opencv中还有一个函数:Mat getAffineTransform(InputArray src, InputArray dst)

 它通过三组点对就可以获得它们之间的仿射变换,如果我们在一组图像变换中知道变换后的三组点,那么我们就可以利用该函数求得变换矩阵,然后对整张图片进行仿射变换

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

 


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


函数原理:

透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。通用的变换公式为:


u,v是原始图片左边,对应得到变换后的图片坐标x,y,其中
变换矩阵可以拆成4部分,表示线性变换,比如scaling,shearing和ratotion。用于平移,产生透视变换。所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。

重写之前的变换公式可以得到:


所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:
变换的4组对应点可以表示成:

根据变换公式得到:


定义几个辅助变量:


都为0时变换平面与原来是平行的,可以得到:


不为0时,得到:


求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。

 opencv代码:

  1. #include<cv.h>  
  2. #include<highgui.h>  
  3.   
  4. #pragma comment(lib, "cv.lib")  
  5. #pragma comment(lib, "cxcore.lib")  
  6. #pragma comment(lib, "highgui.lib")  
  7.   
  8. int main()  
  9. {  
  10.     CvPoint2D32f srcTri[4], dstTri[4];  
  11.     CvMat*       warp_mat = cvCreateMat (3, 3, CV_32FC1);  
  12.     IplImage*    src = NULL;  
  13.     IplImage*    dst = NULL;  
  14.   
  15.     src = cvLoadImage ("test.png", 1);  
  16.     dst = cvCloneImage (src);  
  17.     dst->origin = src->origin;  
  18.     cvZero (dst);  
  19.   
  20.     srcTri[0].x = 0;  
  21.     srcTri[0].y = 0;  
  22.     srcTri[1].x = src->width - 1;  
  23.     srcTri[1].y = 0;  
  24.     srcTri[2].x = 0;  
  25.     srcTri[2].y = src->height - 1;  
  26.     srcTri[3].x = src->width - 1;  
  27.     srcTri[3].y = src->height - 1;  
  28.   
  29.     dstTri[0].x = src->width * 0.05;  
  30.     dstTri[0].y = src->height * 0.33;  
  31.     dstTri[1].x = src->width * 0.9;  
  32.     dstTri[1].y = src->height * 0.25;  
  33.     dstTri[2].x = src->width * 0.2;  
  34.     dstTri[2].y = src->height * 0.7;  
  35.     dstTri[3].x = src->width * 0.8;  
  36.     dstTri[3].y = src->height * 0.9;  
  37.   
  38.     cvGetPerspectiveTransform (srcTri, dstTri, warp_mat);  
  39.     cvWarpPerspective (src, dst, warp_mat);  
  40.   
  41.     cvNamedWindow("src", 1);  
  42.     cvShowImage("src", src);  
  43.     cvNamedWindow ("Affine_Transform", 1);  
  44.     cvShowImage ("Affine_Transform", dst);  
  45.   
  46.     cvWaitKey (0);  
  47.   
  48.     cvReleaseImage (&src);  
  49.     cvReleaseImage (&dst);  
  50.     cvReleaseMat (&warp_mat);  
  51.   
  52.     return 0;  
  53. }  

1.仿射变换

仿射变换代表是两幅图像之间的映射关系,可以表达为乘以一个矩阵再加上一个向量的形式;通常使用2×3的矩阵来表示仿射变换。


仿射变换可表达为Y=A×X+B的形式,在效果上等价于将向量X拓展成X,并且只是将X左乘T,即:


   仿射变换可以表达成以下形式。一个平面内的任意平行四边形ABCD可以被仿射变换映射为另一个平行四边形ABCD。如果这些平行四边形的面积不等于0,这个隐含的仿射变换就被两个平行四边形唯一定义。可以把仿射变换想象成把一幅图像画到一个胶板上,在胶板的角上任意推拉改变形状得到不同类型的平行四边形。

2.透视变换

仿射变换可以将图像转换为平行四边形,透视变换提供了更大的灵活性,一个透视变换可以将矩形转变为梯形,平行四边形也是梯形,所以仿射变换是透视变换的子集。设透视变换矩阵:


则,透视变换的原图像与目标图像的映射关系为:

 \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 )

[cpp] view plain copy
  1. #include "opencv2/highgui/highgui.hpp"  
  2. #include "opencv2/imgproc/imgproc.hpp"  
  3. #include <iostream>  
  4. using namespace cv;  
  5. using namespace std;  
  6.   
  7. #define WINDOW_NAME1 "Original Image"                      
  8. #define WINDOW_NAME2 "Affine transformation"               
  9. #define WINDOW_NAME3 "Perspective transformation"       
  10.   
  11. int main(  )  
  12. {  
  13.     bool  Affine=true;     
  14.     bool  Perspective=true;  
  15.   
  16.     Mat srcImage, dstImage_Aff, dstImage_Per;  
  17.   
  18.     //加载源图像并作一些初始化  
  19.     srcImage = imread( "lena.png", 1 );  
  20.     if(!srcImage.data ) { printf("读取图片错误\n"); return false; }   
  21.     dstImage_Aff = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());  
  22.     dstImage_Per = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());  
  23.   
  24.     if(Affine){  
  25.         //仿射变换三个点的映射关系  
  26.         Point2f srcTriangle[3];  
  27.         Point2f dstTriangle[3];  
  28.         Mat AffMat( 2, 3, CV_32FC1 );  //仿射变换矩阵  
  29.         //设置源图像和目标图像上的三组点以计算仿射变换  
  30.         srcTriangle[0] = Point2f( 0,0 );  //原始图像的左上点  
  31.         srcTriangle[1] = Point2f( (srcImage.cols), 0 ); //原始图像的右上点  
  32.         srcTriangle[2] = Point2f( 0, (srcImage.rows )); //原始图像的左下点  
  33.   
  34.         dstTriangle[0] = Point2f( (srcImage.cols*0.0), (srcImage.rows*0.5));  
  35.         dstTriangle[1] = Point2f( (srcImage.cols*0.5), (srcImage.rows*0.0));  
  36.         dstTriangle[2] = Point2f( (srcImage.cols*0.5), (srcImage.rows*1.0));  
  37.         //求得仿射变换矩阵并计算的仿射变换  
  38.         AffMat = getAffineTransform( srcTriangle, dstTriangle );  
  39.         //Output affine transformation matrix  
  40.         cout<<"affine transformation matrix:\n"<<AffMat<<endl;  
  41.         warpAffine(srcImage, dstImage_Aff, AffMat,dstImage_Aff.size());  
  42.   
  43.         imshow( WINDOW_NAME2, dstImage_Aff );  
  44.     }  
  45.     if(Perspective){  
  46.         //投影变换四个点的映射关系  
  47.         Point2f srcQuadrilateral[4];  
  48.         Point2f dstQuadrilateral[4];  
  49.         //定义一些Mat变量  
  50.         Mat PerMat( 3, 3, CV_32FC1 );  //透视变换矩阵  
  51.         //设置源图像和目标图像上的四组点以计算透视变换  
  52.         srcQuadrilateral[0] = Point2f(0,               0);  //原始图像的左上点  
  53.         srcQuadrilateral[1] = Point2f((srcImage.cols), 0); //原始图像的右上点  
  54.         srcQuadrilateral[2] = Point2f(0,                (srcImage.rows )); //原始图像的左下点  
  55.         srcQuadrilateral[3] = Point2f((srcImage.cols ), (srcImage.rows )); //原始图像的左下点  
  56.   
  57.         dstQuadrilateral[0] = Point2f( (srcImage.cols*0.0),(srcImage.rows*0.5));  
  58.         dstQuadrilateral[1] = Point2f( (srcImage.cols*0.5),(srcImage.rows*0.0));  
  59.         dstQuadrilateral[2] = Point2f( (srcImage.cols*0.5),(srcImage.rows*1.0));  
  60.         dstQuadrilateral[3] = Point2f( (srcImage.cols*1.0),(srcImage.rows*0.5));  
  61.   
  62.         //求得透视变换矩阵并计算的透视变换  
  63.         PerMat = getPerspectiveTransform( srcQuadrilateral, dstQuadrilateral );  
  64.         //Output perspective transformation matrix  
  65.         Mat PerMat2;  
  66.         PerMat.convertTo(PerMat2, CV_32F);  
  67.         cout<<"perspective transformation matrix:\n"<<PerMat2<<endl;  
  68.         warpPerspective(srcImage, dstImage_Per, PerMat,dstImage_Per.size());  
  69.   
  70.         imshow( WINDOW_NAME3, dstImage_Per );  
  71.     }  
  72.   
  73.     //显示结果  
  74.     imshow( WINDOW_NAME1, srcImage );  
  75.     waitKey(0);  
  76.   
  77.     return 0;  
  78. }  
更多资源:http://blog.csdn.net/xiaowei_cqu/article/details/26471527
      http://m.blog.csdn.net/article/details?id=51355600
      http://blog.csdn.net/u012380663/article/details/43273527