上一篇我們學習了圖像卷積處理filter2D函數,相信大家通過閱讀已經對filter2D圖像卷積有了一定的瞭解,那麼今天這節我們就來學習一下圖像金字塔的概念以及如何使用圖像金字塔對圖像進行處理。
一、原理
一個圖像金字塔是一系列圖像的集合 - 所有圖像來源於同一張原始圖像 - 通過梯次向下採樣獲得,直到達到某個終止條件才停止採樣。
有兩種類型的圖像金字塔常常出現在文獻和應用中:
- 高斯金字塔(Gaussian pyramid): 用來向下採樣
- 拉普拉斯金字塔(Laplacian pyramid): 用來從金字塔低層圖像重建上層未採樣圖像
1、高斯金字塔
高斯金字塔是在圖像處理、計算機視覺、信號處理上所使用的一項技術。高斯金字塔本質上為信號的多尺度表示法,亦即將同一信號或圖片多次的進行高斯模糊,並且向下取樣,藉以產生不同尺度下的多組信號或圖片以進行後續的處理,例如在影像辨識上,可以藉由比對不同尺度下的圖片,以防止要尋找的內容可能在圖片上有不同的大小。高斯金字塔的理論基礎為尺度空間理論,而後續也衍生出了多分辨率分析。
每一層都按從下到上的次序編號, 層級(i+1) (表示為 G_i+1尺寸小於層級i(G_i)
為了獲取層級為 (i+1)的金字塔圖像,我們採用如下方法:
將 G_i與高斯內核卷積:
將所有偶數行和列去除,顯而易見,結果圖像只有原圖的四分之一。通過對輸入圖像G_0 (原始圖像) 不停迭代以上步驟就會得到整個金字塔。
Note:我們向下採樣縮小圖像的時候, 我們實際上丟失了一些信息。
2、拉普拉斯金字塔
在高斯金字塔的運算過程中,圖像經過卷積和下采樣操作會丟失部分高頻細節信息。為描述這些高頻信息,人們定義了拉普拉斯金字塔(Laplacian Pyramid, LP)。用高斯金字塔的每一層圖像減去其上一層圖像上採樣並高斯卷積之後的預測圖像,得到一系列的差值圖像即為 LP 分解圖像。
將G_i內插方法得到放大圖像G_i,使G_i的尺寸與*G_i - 1的尺寸相同,即放大算子;
該式子實現兩個步驟:在偶數行和列插入0,然後使用下采樣中的高斯核進行濾波處理,得到和(i - 1)層一樣大小的圖像。
N為拉普拉斯金字塔頂層的層號,LP_i是拉普拉斯金字塔分解的第L層圖像;由LP0,LP1、LP2…LPN構成的金字塔即為拉普拉斯金字塔;它的每一層L_0圖像是高斯金字塔本層G_0圖像與其高一層圖像G_1經內插放大後圖像*G_1的差,此過程相當於帶通濾波,因此拉普拉斯金字塔又稱為帶通金字塔分解。
二、OpenCV中的API函數詳解
1、高斯金字塔
函數原型
void pyrDown(InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT);
函數功能
先對圖像進行平滑,再進行其下采樣!
參數詳解
- 第一個參數,InputArray src,輸入圖像;
- 第二個參數,OutputArray dst,與原圖像具有一樣尺寸、類型的目標圖像;
- 第三個參數,const Size& dstsize = Size(), 這個參數指的是降採樣之後的目標圖像的大小,我們可以看出它是有默認值的,如果我們調用函數的時候不指定第三個參數,那麼這個值是按照 Size((src.cols+1)/2, (src.rows+1)/2) 計算的。
而且不管你自己如何指定這個參數,一定必須保證滿足以下關係式:
|dstsize.width * 2 - src.cols| ≤ 2;
|dstsize.height * 2 - src.rows| ≤ 2;
也就是說降採樣的意思其實是把圖像的尺寸縮減一半,行和列同時縮減一半。所以你指定的大小,無非就是多一行少一列的區別而已。在大多數情況下使用默認值就可了,因為這個函數不是縮減圖像至任意尺寸,就只是縮減一半,所以沒必要搞得那麼複雜;
- 第四個參數,int類型的borderType,邊界模式,一般我們不用去管它。
實例
#include <opencv2>
using namespace cv;
int main()
{
//載入原始圖
Mat srcImage = imread("3.png");
if (srcImage.empty())
{
printf("image error!");
return -1;
}
Mat tmpImage, dstImage;
tmpImage = srcImage;
//顯示原始圖
imshow("【原始圖】", srcImage);
//進行向下取樣操作
pyrDown(tmpImage, dstImage,
Size(tmpImage.cols / 2,
tmpImage.rows / 2));
//顯示效果圖
imshow("【效果圖】", dstImage);
waitKey(0);
return 0;
}
實驗結果:
2、拉普拉斯金字塔
函數原型
void pyrUp(InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT);
函數功能
先對對圖像上採樣,在對圖像進行平滑處理!
參數詳解
- 第一個參數,InputArray src,輸入圖像;
- 第二個參數,OutputArray dst,與原圖像具有一樣尺寸、類型的目標圖像;
- 第三個參數,const Size&類型的dstsize,輸出圖像的大小;有默認值Size(),即默認情況下,由Size Size((src.cols+1)/2, (src.rows+1)/2)來進行計算,且一直需要滿足下列條件:
- 第四個參數,int類型的borderType,邊界模式,一般我們不用去管它。
實例
實驗結果:
三、綜合實例
實現按下u鍵對圖像進行放大(拉普拉斯金字塔),按下d鍵對圖像縮小(高斯金字塔),按下esc鍵退出!
#include <opencv2>
#include <opencv2>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// 全局變量
Mat src, dst, tmp;
char* window_name = "Pyramids Demo";
int main(int argc, char** argv)
{
/// 指示說明
printf("\n Zoom In-Out demo \n ");
printf("------------------ \n");
printf(" * [u] -> Zoom in \n");
printf(" * [d] -> Zoom out \n");
printf(" * [ESC] -> Close program \n \n");
/// 測試圖像 - 尺寸必須能被 2^{n} 整除
src = imread("lena.png");
if (!src.data)
{
printf(" No data! -- Exiting the program \n");
return -1;
}
tmp = src;
dst = tmp;
/// 創建顯示窗口
namedWindow(window_name,
WINDOW_AUTOSIZE);
imshow(window_name, dst);
/// 循環
while (true)
{
int c;
c = waitKey(10);
if ((char)c == 27) break;
// 向上採樣
if ((char)c == 'u')
{
pyrUp(tmp, dst,
Size(tmp.cols * 2, tmp.rows * 2));
printf("** Zoom In: Image x 2 \n");
}
// 向下採樣
else if ((char)c == 'd')
{
pyrDown(tmp, dst,
Size(tmp.cols / 2, tmp.rows / 2));
printf("** Zoom Out: Image / 2 \n");
}
imshow(window_name, dst);
tmp = dst;
}
return 0;
}
好了,今天的OpenCV圖像處理 --- 【圖像金字塔】學到這裡就結束了,喜歡的朋友可以給我點個贊哦!!
閱讀更多 奕雙分享 的文章