1. 程式人生 > >OpenCV2.4.13中warpAffine函式理解,旋轉,仿射變換,縮放,保持完整圖片

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;
}