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

来源:互联网 发布:上海数据港官网 编辑:程序博客网 时间:2024/05/14 23:46

本文借鉴了这里以及这里的内容。

问题:为什么写这个东西?
答:在进行模板匹配的时候,发现一个问题,对于直接从图片中抠出的模板,匹配效果较好,但是当模板发生形变的时候,效果就不理想了。
在对模板进行形变处理的时候,发现利用 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;}
阅读全文
0 0