图像有两种常见的几何转换:一种是基于2×3矩阵进行的变换,也叫仿射变换。另一种是基于3×3矩阵的变换,又称透视变换。
仿射变换是一种二维坐标到二维坐标之间的线性变换,保持二维图形的“平直性”(译注:straightness,即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(译注:parallelness,其实是指保二维图形间的相对位置关系不变,平行线还是平行线,相交直线的交角会改变。)
仿射变换:平移、旋转、放缩、剪切、反射;仿射变换包括如下所有变换,以及这些变换任意次序次数的组合。
平移(translation)和旋转(rotation)顾名思义,两者的组合称之为欧式变换(Euclidean transformation)或刚体变换(rigid transformation);
放缩(scaling)可进一步分为uniform scaling和non-uniform scaling,前者每个坐标轴放缩系数相同(各向同性),后者不同;如果放缩系数为负,则会叠加上反射(reflection)——reflection可以看成是特殊的scaling;
刚体变换+uniform scaling 称之为,相似变换(similarity transformation),即平移+旋转+各向同性的放缩。
剪切变换(shear mapping)将所有点沿某一指定方向成比例地平移。
可以把透视变换当作一个三维平面被一个特定观察者感知的计算方法,而该观察者也许不是垂直观测该平面;透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping);也称为单应性。在计算机视觉中,我们将平面的单应性定义为从一个平面到另一个平面的投影映射,因此,二维平面上的点到相机的成像器上的映射就是平面单应性的一个例子。
1.仿射变换
仿射变换代表是两幅图像之间的映射关系,可以表达为乘以一个矩阵再加上一个向量的形式;通常使用2×3的矩阵来表示仿射变换。
仿射变换可表达为Y=A×X+B的形式,在效果上等价于将向量X拓展成X’,并且只是将X’左乘T,即:
仿射变换可以表达成以下形式。一个平面内的任意平行四边形ABCD可以被仿射变换映射为另一个平行四边形A’B’C’D’。如果这些平行四边形的面积不等于0,这个隐含的仿射变换就被两个平行四边形唯一定义。可以把仿射变换想象成把一幅图像画到一个胶板上,在胶板的角上任意推拉改变形状得到不同类型的平行四边形。
2.透视变换
仿射变换可以将图像转换为平行四边形,透视变换提供了更大的灵活性,一个透视变换可以将矩形转变为梯形,平行四边形也是梯形,所以仿射变换是透视变换的子集。设透视变换矩阵:
则,透视变换的原图像与目标图像的映射关系为:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "Original Image"
#define WINDOW_NAME2 "Affine transformation"
#define WINDOW_NAME3 "Perspective transformation"
int main( )
{
bool Affine=true;
bool Perspective=true;
Mat srcImage, dstImage_Aff, dstImage_Per;
//加载源图像并作一些初始化
srcImage = imread( "lena.png", 1 );
if(!srcImage.data ) { printf("读取图片错误\n"); return false; }
dstImage_Aff = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
dstImage_Per = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
if(Affine){
//仿射变换三个点的映射关系
Point2f srcTriangle[3];
Point2f dstTriangle[3];
Mat AffMat( 2, 3, CV_32FC1 ); //仿射变换矩阵
//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f( 0,0 ); //原始图像的左上点
srcTriangle[1] = Point2f( (srcImage.cols), 0 ); //原始图像的右上点
srcTriangle[2] = Point2f( 0, (srcImage.rows )); //原始图像的左下点
dstTriangle[0] = Point2f( (srcImage.cols*0.0), (srcImage.rows*0.5));
dstTriangle[1] = Point2f( (srcImage.cols*0.5), (srcImage.rows*0.0));
dstTriangle[2] = Point2f( (srcImage.cols*0.5), (srcImage.rows*1.0));
//求得仿射变换矩阵并计算的仿射变换
AffMat = getAffineTransform( srcTriangle, dstTriangle );
//Output affine transformation matrix
cout<<"affine transformation matrix:\n"<<AffMat<<endl;
warpAffine(srcImage, dstImage_Aff, AffMat,dstImage_Aff.size());
imshow( WINDOW_NAME2, dstImage_Aff );
}
if(Perspective){
//投影变换四个点的映射关系
Point2f srcQuadrilateral[4];
Point2f dstQuadrilateral[4];
//定义一些Mat变量
Mat PerMat( 3, 3, CV_32FC1 ); //透视变换矩阵
//设置源图像和目标图像上的四组点以计算透视变换
srcQuadrilateral[0] = Point2f(0, 0); //原始图像的左上点
srcQuadrilateral[1] = Point2f((srcImage.cols), 0); //原始图像的右上点
srcQuadrilateral[2] = Point2f(0, (srcImage.rows )); //原始图像的左下点
srcQuadrilateral[3] = Point2f((srcImage.cols ), (srcImage.rows )); //原始图像的左下点
dstQuadrilateral[0] = Point2f( (srcImage.cols*0.0),(srcImage.rows*0.5));
dstQuadrilateral[1] = Point2f( (srcImage.cols*0.5),(srcImage.rows*0.0));
dstQuadrilateral[2] = Point2f( (srcImage.cols*0.5),(srcImage.rows*1.0));
dstQuadrilateral[3] = Point2f( (srcImage.cols*1.0),(srcImage.rows*0.5));
//求得透视变换矩阵并计算的透视变换
PerMat = getPerspectiveTransform( srcQuadrilateral, dstQuadrilateral );
//Output perspective transformation matrix
Mat PerMat2;
PerMat.convertTo(PerMat2, CV_32F);
cout<<"perspective transformation matrix:\n"<<PerMat2<<endl;
warpPerspective(srcImage, dstImage_Per, PerMat,dstImage_Per.size());
imshow( WINDOW_NAME3, dstImage_Per );
}
//显示结果
imshow( WINDOW_NAME1, srcImage );
waitKey(0);
return 0;
}