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

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

问题:为什么写这个东西?
答:在进行模板匹配的时候,发现一个问题,对于直接从图片中抠出的模板,匹配效果较好,但是当模板发生形变的时候,效果就不理想了。
在对模板进行形变处理的时候,发现利用 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;
}
  • 19
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值