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