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

OpenCV2.4.13中warpAffine函数理解,旋转,仿射变换,缩放,保持完整图片

2017-12-17 20:00 288 查看
本文借鉴了这里以及这里的内容。

问题:为什么写这个东西?

答:在进行模板匹配的时候,发现一个问题,对于直接从图片中抠出的模板,匹配效果较好,但是当模板发生形变的时候,效果就不理想了。

在对模板进行形变处理的时候,发现利用 warpAffine得到的结果并不是想要的结果。

因此,就对这个问题进行了搜索。

Mat img = imread(IMG_PATH);
if (img.empty())
cerr<<"can not load image"<<endl;

// 定义仿射变换的,中心,角度,尺度
center = Point2f(img.cols/2.0, img.rows/2.0);
degree = 60;
scale = 1;

// 获取变换矩阵
rot = getRotationMatrix2D(center,degree,scale);
rimg;
warpAffine(img,rimg,rot,img.size());
imshow("img",img);
imshow("rimg",rimg);


原图



直接利用warpAffine得到的结果:



问题:这个并不是想要的结果,怎么办呢?

答:看看是不是函数用错了呢?

在程序中有这样一句

getRotationMatrix2D(center,degree,scale);


那么这个“getRotationMatrix2D”是什么鬼呢?

查看手册得到:



问题:这里的公式是怎么来的呢?

答:这里给出了解释,但是好像不是很细诶,为什么可以得到那些结果呢?

自己推导了一下:







将图中的第 5 步得到的结果与手册中的公式对照,恩,是一样的。

问题:是不是这个函数:warpAffine 用错了呢?

答:搜索手册得到:



注意下面这段话:



问题:这句话什么意思呢?

答:默认输入的变换矩阵 M 是 “逆变换”矩阵。

问题:为什么呢?

答:这个要看这一页手册的最上端:



这段话的意思也就是说,在OpenCV中实现的时候,变换之后图像的点dst(x,y),是根据“逆变换”,找到在原图像中是哪一个点src(fx,fy)与之对应的。

问题:说了这么多,到底是什么意思呢?

答:别急,下面给出结论。

通过上面的分析,说明了为什么利用warpAffine 函数得到的 图像不完全了。

比如,以(0,0)为中心,逆时针旋转 45度,尺度缩放为 1.

我们看两个具体的点。



由于我们是利用“逆变换”找到原图中哪些点映射成为变换之后的点。因此,dst 只包含了一部分 “变换之后的图像”中的像素,其他的地方,都默认为黑色了。

与上面对应的具体的一个例子:

// 定义仿射变换的,中心,角度,尺度
Point2f center;
center = Point2f(0,0);
double degree = 45;
double scale = 1;

// 旋转 45 度的例子
Mat rot = getRotationMatrix2D(center,degree,scale);
Mat rimg;
warpAffine(img,rimg,rot,img.size());
imshow("45",rimg);




问题:将dst的大小变大一些可以显示出全部变换之后的图像么?

答:不能,不信你试试。

问题:那怎么办呢?

答:先给出代码:代码参考了这里的内容。

// 获取变换矩阵
rot = getRotationMatrix2D(center,degree,scale);
rimg;
warpAffine(img,rimg,rot,img.size());
imshow("img",img);
imshow("rimg",rimg);

// 获取变换之后的 区域,这个很重要,不然的话,变换之后的图像显示不全
Rect bbox;
bbox = RotatedRect(center,Size(scale*img.cols,scale*img.rows),degree).boundingRect();

// 对变换矩阵的最后一列做修改,重新定义变换的 中心

rot.at<double>(0,2) += bbox.width/2 - center.x;
rot.at<double>(1,2) += bbox.height/2 - center.y;

Mat dst;
warpAffine(img,dst,rot, bbox.size());
imshow("dst",dst);


得到的结果:



**问题:代码中

bbox = RotatedRect(center,Size(scale*img.cols,scale*img.rows),degree).boundingRect();
是什么鬼?**

答:这一句就是获取,展示变换之后图像所需图片的大小。

手册中 RotatedRect 给出的一个例子很好的展示了这个意思:



图中蓝色是利用 RotatedRect 得到的 Rect 的区域,绿色是变换之后的图像。

问题:最后boundingRect(); 表示什么意思呢?

答:直观理解相当于对这个区域“向大取整”。

**问题:代码中

rot.at<double>(0,2) += bbox.width/2 - center.x;

rot.at<double>(1,2) += bbox.height/2 - center.y;


是什么意思呢?**

答:还记得前面最后推导出来的公式么:



这两行代码的意思就相当于:


直观的意义就是:变换前的中心(x0,y0),在变换之后在(bw/2,bh/2)。也就是 dst 的中心。

**问题:为什么你代码中,将变换前的中心设置为

center = Point2f(img.cols/2.0, img.rows/2.0);


设置为其他的点不可以么?**

答:设置为其他的点,确实不可以,还是会出现显示不全的问题。

我们关心的是放射变换之后的全图是生么样的,与变换前的中心在哪里没有关系,只是相当于将变换之后的图像进行了平移。因此,我们用这种最简单粗暴的方式来得到我们想要的效果。

问题:为什么呢?

答:请允许我使用下图中的回答。



放大招:整体代码如下:

// csdn_code.cpp : 定义控制台应用程序的入口点。
//

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

using namespace cv;
using namespace std;

//#define IMG_PATH "..//figures//12.jpg"

#define IMG_PATH "..//figures//lotus.jpg"

int main()
{

Mat img = imread(IMG_PATH);
if (img.empty())
cerr<<"can not load image"<<endl;

// 定义仿射变换的,中心,角度,尺度
Point2f center;
center = Point2f(0,0);
double degree = 45;
double scale = 1;

// 旋转 45 度的例子
Mat rot = getRotationMatrix2D(center,degree,scale);
Mat rimg;
warpAffine(img,rimg,rot,img.size());
imshow("45",rimg);

// 定义仿射变换的,中心,角度,尺度
// !!! 注意,这里变换之前的中心必须为 原图的中心 !!!
center = Point2f(img.cols/2.0, img.rows/2.0);
degree = 60;
scale = 1;

// 获取变换矩阵
rot = getRotationMatrix2D(center,degree,scale);
rimg;
warpAffine(img,rimg,rot,img.size());
imshow("img",img);
imshow("rimg",rimg);

// 获取变换之后的 区域,这个很重要,不然的话,变换之后的图像显示不全
Rect bbox;
bbox = RotatedRect(center,Size(scale*img.cols,scale*img.rows),degree).boundingRect();// 对变换矩阵的最后一列做修改,重新定义变换的 中心

rot.at<double>(0,2) += bbox.width/2 - center.x;
rot.at<double>(1,2) += bbox.height/2 - center.y;

Mat dst;
warpAffine(img,dst,rot, bbox.size());
imshow("dst",dst);

waitKey();
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐