您的位置:首页 > 运维架构

OpenCV—仿射变换warpAffine--旋转和缩放

2017-10-22 21:17 489 查看
首先用一个图来演示仿射变换:

来自维基百科: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 <iostream>

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"<<rot_mat;

//计算旋转后的画布大小,并将旋转中心平移到新的旋转中心
Rect bbox = RotatedRect(center, Size(src.cols*scale, src.rows*scale), angle).boundingRect();
rot_mat.at<double>(0, 2) += bbox.width / 2.0 - center.x;
rot_mat.at<double>(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;
}


运行结果如下:



另外还有两个函数,分别为:

getAffineTransform():计算3个二维点对之间的仿射变换矩阵H(2行x3列),自由度为6.

详细参数参见官方文档:https://docs.opencv.org/2.4.9/modules/imgproc/doc/geometric_transformations.html#getaffinetransform



estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变。

详细参数参见官方文档:https://docs.opencv.org/2.4.9/modules/video/doc/motion_analysis_and_object_tracking.html#estimaterigidtransform


已知两张旋转了一定角度的图片,通过上面的两个函数便可求解出旋转矩阵。



源代码如下所示:

#include "opencv2/opencv.hpp"
#include <iostream>

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<Point2f> srcTri7;
vector<Point2f> 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.
4000
cols*1.2, src.rows*1.2), 45).boundingRect();
warp_mat.at<double>(0, 2) += bbox.width / 2.0 - src.cols/2;
warp_mat.at<double>(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;
}


变换结果如下图所示:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: