首先用一个图来演示仿射变换:
来自维基百科:https://zh.wikipedia.org/wiki/%E4%BB%BF%E5%B0%84%E5%8F%98%E6%8D%A2
本文介绍仿射变换的两个应用:
旋转和缩放。
仿射变换的API函数如下:
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
参数含义参见官方手册:https://docs.opencv.org/2.4.9/modules/imgproc/doc/geometric_transformations.html#warpaffine
几点补充:
1、旋转变换的原理:原图像素坐标乘以旋转矩阵。
旋转矩阵通过如下API获得:
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
参数含义参见官方手册:https://docs.opencv.org/2.4.9/modules/imgproc/doc/geometric_transformations.html#getrotationmatrix2d
该函数返回值为一个2×3的矩阵,其中矩阵前两列代表旋转,最后一列代表平移;
旋转矩阵的求解过程可参见博文:http://blog.csdn.net/csxiaoshui/article/details/65446125
2、由于图像旋转之后,部分数据就会超出原来图像的位置,出现截断,如下所示。
为了避免上面的问题,引入类RotatedRect,采用如下的构造函数:
RotatedRect(const Point2f& center, const Size2f& size, float angle)
使用成员函数boundingRect()返回包含此旋转举行的最小up-right矩形。
下面的示例代码来自opencv官方示例代码:https://github.com/opencv/opencv/blob/master/samples/cpp/tutorial_code/ImgTrans/Geometric_Transforms_Demo.cpp
做了适当修改和注释如下:
#include "opencv2/opencv.hpp"
#include
using namespace cv;
using namespace std;
/// 全局变量
const char* source_window = "Source image";;
const char* rotate_window = "Rotate+Resize";
/**
* @主函数
*/
int main( )
{
Mat rot_mat( 2, 3, CV_32FC1 );
Mat src, rotate_dst;
/// 载入图像
src = imread("lena.bmp", IMREAD_COLOR );
/** 图像旋转 */
/// 计算关于图像中心的旋转矩阵
Point center = Point( src.cols/2, src.rows/2 );
double angle = 35.0;
//旋转的同时也可以放缩
double scale = 0.8;
/// 根据以上参数得到旋转矩阵
rot_mat = getRotationMatrix2D( center, angle, scale );
cout<<"旋转矩阵为:\n"<(0, 2) += bbox.width / 2.0 - center.x;
rot_mat.at(1, 2) += bbox.height / 2.0 - center.y;
/// 旋转图像
warpAffine( src, rotate_dst, rot_mat, bbox.size());
/// 显示原图和旋转结果
namedWindow( source_window, WINDOW_AUTOSIZE );
imshow( source_window, src );
namedWindow( rotate_window, WINDOW_AUTOSIZE );
imshow( rotate_window, rotate_dst );
/// 等待,知道用户退出
waitKey(0);
return 0;
}
另外还有两个函数,分别为:
已知两张旋转了一定角度的图片,通过上面的两个函数便可求解出旋转矩阵。
源代码如下所示:
#include "opencv2/opencv.hpp"
#include
using namespace cv;
using namespace std;
/// 全局变量
const char* source_window = "Source image";;
const char* warp_window = "warp";
/**
* @主函数
*/
int main( )
{
Point2f srcTri[3];
Point2f dstTri[3];
vector srcTri7;
vector dstTri7;
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
/// Load the image
src = imread( "1.jpg", IMREAD_COLOR );
/// Set the dst image the same type and size as src
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
/// 设置三组点,求出变换矩阵
srcTri[0] = Point2f( 300,481 );
srcTri[1] = Point2f( 387,440 );
srcTri[2] = Point2f( 293,284 );
dstTri[0] = Point2f( 98,61 );
dstTri[1] = Point2f( 70,140 );
dstTri[2] = Point2f( 253,172 );
//相关的7个点,采用拟合的方法求出变换矩阵
srcTri7.push_back(Point2f( 300,481 ));
srcTri7.push_back(Point2f( 387,440 ));
srcTri7.push_back(Point2f( 293,284 ));
srcTri7.push_back(Point2f( 497,286 ));
srcTri7.push_back(Point2f( 369,123 ));
srcTri7.push_back(Point2f( 383,35 ));
srcTri7.push_back(Point2f( 51,136 ));
dstTri7.push_back(Point2f( 98,61 ));
dstTri7.push_back(Point2f( 70,140 ));
dstTri7.push_back(Point2f( 253,172 ));
dstTri7.push_back(Point2f( 120,310 ));
dstTri7.push_back(Point2f( 326,329));
dstTri7.push_back(Point2f( 382,397));
dstTri7.push_back(Point2f( 511,92));
//计算3个二维点对之间的仿射变换矩阵(2行x3列)
warp_mat = getAffineTransform( srcTri, dstTri );
//计算多个二维点对或者图像之间的最优仿射变换矩阵(2行x3列)
//warp_mat = estimateRigidTransform(srcTri7,dstTri7,true);
//扩大显示区域,以免显示不下
Rect bbox = RotatedRect(Point(src.cols/2,src.rows/2), Size(src.cols*1.2, src.rows*1.2), 45).boundingRect();
warp_mat.at(0, 2) += bbox.width / 2.0 - src.cols/2;
warp_mat.at(1, 2) += bbox.height / 2.0 - src.rows/2;
///应用仿射变换,可以恢复出原图
warpAffine( src, warp_dst, warp_mat, bbox.size() );
//显示结果
namedWindow( source_window, WINDOW_AUTOSIZE );
imshow( source_window, src );
namedWindow( warp_window, WINDOW_AUTOSIZE );
imshow( warp_window, warp_dst );
/// 等待,知道用户退出
waitKey(0);
return 0;
}