這篇已經寫得很好,真心給作者點個贊。題目都是直接轉過來的,直接去看吧。
Reference Link : http://blog.csdn.net/poem_qianmo/article/details/26157633
In case:
【OpenCV入門教程之十三】OpenCV圖像金字塔:高斯金字塔、拉普拉斯金字塔與圖片尺寸縮放
這篇文章里,我們將一起探討圖像金字塔的一些基本概念,如何使用OpenCV函數 pyrUp 和 pyrDown 對圖像進行向上和向下采樣,以及了解了專門用於縮放圖像尺寸的resize函數的用法。此博文一共有四個配套的簡短的示例程序,其詳細注釋過的代碼都在文中貼出,且文章最后提供了綜合示例程序的下載。
先嘗鮮一下其中一個示例程序的運行截圖:
![]()
我們經常會將某種尺寸的圖像轉換為其他尺寸的圖像,如果放大或者縮小圖片的尺寸,籠統來說的話,可以使用OpenCV為我們提供的如下兩種方式:
<1>resize函數。這是最直接的方式,
<2>pyrUp( )、pyrDown( )函數。即圖像金字塔相關的兩個函數,對圖像進行向上采樣,向下采樣的操作。
pyrUp、pyrDown其實和專門用作放大縮小圖像尺寸的resize在功能上差不多,披着圖像金字塔的皮,說白了還是在對圖像進行放大和縮小操作。另外需要指出的是,pyrUp、pyrDown在OpenCV的imgproc模塊中的Image Filtering子模塊里。而resize在imgproc 模塊的Geometric Image Transformations子模塊里。
這篇文章中,我們將先介紹圖像金字塔的原理,接着介紹resize函數,然后是pyrUp和pyrDown函數,最后是一個綜合示例程序。
二、關於圖像金字塔
圖像金字塔是圖像中多尺度表達的一種,最主要用於圖像的分割,是一種以多分辨率來解釋圖像的有效但概念簡單的結構。
圖像金字塔最初用於機器視覺和圖像壓縮,一幅圖像的金字塔是一系列以金字塔形狀排列的分辨率逐步降低,且來源於同一張原始圖的圖像集合。其通過梯次向下采樣獲得,直到達到某個終止條件才停止采樣。
金字塔的底部是待處理圖像的高分辨率表示,而頂部是低分辨率的近似。
我們將一層一層的圖像比喻成金字塔,層級越高,則圖像越小,分辨率越低。
![]()
![]()
![]()
素材圖是帥氣的美劇《綠箭俠》里面的綠箭俠Oliver Queen。
一般情況下有兩種類型的圖像金字塔常常出現在文獻和以及實際運用中。他們分別是:
- 高斯金字塔Gaussianpyramid): 用來向下采樣,主要的圖像金字塔
- 拉普拉斯金字塔(Laplacianpyramid): 用來從金字塔低層圖像重建上層未采樣圖像,在數字圖像處理中也即是預測殘差,可以對圖像進行最大程度的還原,配合高斯金字塔一起使用。
兩者的簡要區別:高斯金字塔用來向下降采樣圖像,而拉普拉斯金字塔則用來從金字塔底層圖像中向上采樣重建一個圖像。
要從金字塔第i層生成第i+1層(我們表示第i+1層為G_i+1),我們先要用高斯核對G_1進行卷積,然后刪除所有偶數行和偶數列。當然的是,新得到圖像面積會變為源圖像的四分之一。按上述過程對輸入圖像G_0執行操作就可產生出整個金字塔。
當圖像向金字塔的上層移動時,尺寸和分辨率就降低。OpenCV中,從金字塔中上一級圖像生成下一級圖像的可以用PryDown。而通過PryUp將現有的圖像在每個維度都放大兩遍。
圖像金字塔中的向上和向下采樣分別通過OpenCV函數 pyrUp 和 pyrDown 實現。
概括起來就是:
- 對圖像向上采樣:pyrUp函數
- 對圖像向下采樣:pyrDown函數
這里的向下與向上采樣,是對圖像的尺寸而言的(和金字塔的方向相反),向上就是圖像尺寸加倍,向下就是圖像尺寸減半。而如果我們按上圖中演示的金字塔方向來理解,金字塔向上圖像其實在縮小,這樣剛好是反過來了。
但需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降采樣的逆操作。這種情況下,圖像首先在每個維度上擴大為原來的兩倍,新增的行(偶數行)以0填充。然后給指定的濾波器進行卷積(實際上是一個在每個維度都擴大為原來兩倍的過濾器)去估計“丟失”像素的近似值。
PryDown( )是一個會丟失信息的函數。為了恢復原來更高的分辨率的圖像,我們要獲得由降采樣操作丟失的信息,這些數據就和拉普拉斯金字塔有關系了。
高斯金字塔
高斯金字塔是通過高斯平滑和亞采樣獲得一些列下采樣圖像,也就是說第K層高斯金字塔通過平滑、亞采樣就可以獲得K+1層高斯圖像,高斯金字塔包含了一系列低通濾波器,其截至頻率從上一層到下一層是以因子2逐漸增加,所以高斯金字塔可以跨越很大的頻率范圍。金字塔的圖像如下:
這里增加一個注解: 在數字圖像處理教材當中是從塔尖到塔底定義為0,1,2...J 層,而opencv是相反的。
![]()
另外,每一層都按從下到上的次序編號, 層級 G_i+1 (表示為 G_i+1尺寸小於第i層G_i)。
2.1.1 對圖像的向下取樣
為了獲取層級為 G_i+1 的金字塔圖像,我們采用如下方法:
<1>對圖像G_i進行高斯內核卷積
<2>將所有偶數行和列去除
得到的圖像即為G_i+1的圖像,顯而易見,結果圖像只有原圖的四分之一。通過對輸入圖像G_i(原始圖像)不停迭代以上步驟就會得到整個金字塔。同時我們也可以看到,向下取樣會逐漸丟失圖像的信息。
以上就是對圖像的向下取樣操作,即縮小圖像。
2.1.2 對圖像的向上取樣
如果想放大圖像,則需要通過向上取樣操作得到,具體做法如下:
<1>將圖像在每個方向擴大為原來的兩倍,新增的行和列以0填充
<2>使用先前同樣的內核(乘以4)與放大后的圖像卷積,獲得 “新增像素”的近似值 此處乘以4是因為在Mask(5*5)中只有6個位置是有值的,*4是一種近似,接近25個值。
得到的圖像即為放大后的圖像,但是與原來的圖像相比會發覺比較模糊,因為在縮放的過程中已經丟失了一些信息,如果想在縮小和放大整個過程中減少信息的丟失,這些數據形成了拉普拉斯金字塔。
那么,我們接下來一起看一看拉普拉斯金字塔的概念吧。
拉普拉斯金字塔
下式是拉普拉斯金字塔第i層的數學定義:
式中的
表示第i層的圖像。而UP()操作是將源圖像中位置為(x,y)的像素映射到目標圖像的(2x+1,2y+1)位置,即在進行向上取樣。符號
表示卷積,
為5x5的高斯內核。
我們下文將要介紹的pryUp,就是在進行上面這個式子的運算。
因此,我們可以直接用OpenCV進行拉普拉斯運算:
也就是說,拉普拉斯金字塔是通過源圖像減去先縮小后再放大的圖像的一系列圖像構成的。
整個拉普拉斯金字塔運算過程可以通過下圖來概括:
![]()
所以,我們可以將拉普拉斯金字塔理解為高斯金字塔的逆形式。
另外再提一點,關於圖像金字塔非常重要的一個應用就是實現圖像分割。圖像分割的話,先要建立一個圖像金字塔,然后在G_i和G_i+1的像素直接依照對應的關系,建立起”父與子“關系。而快速初始分割可以先在金字塔高層的低分辨率圖像上完成,然后逐層對分割加以優化。
三、resize( )函數剖析
resize( )為OpenCV中專職調整圖像大小的函數。
此函數將源圖像精確地轉換為指定尺寸的目標圖像。如果源圖像中設置了ROI(Region Of Interest ,感興趣區域),那么resize( )函數會對源圖像的ROI區域進行調整圖像尺寸的操作,來輸出到目標圖像中。若目標圖像中已經設置ROI區域,不難理解resize( )將會對源圖像進行尺寸調整並填充到目標圖像的ROI中。
很多時候,我們並不用考慮第二個參數dst的初始圖像尺寸和類型(即直接定義一個Mat類型,不用對其初始化),因為其尺寸和類型可以由src,dsize,fx和fy這其他的幾個參數來確定。
看一下它的函數原型:
- resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, interpolation=INTER_LINEAR )
C++: void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。
- 第二個參數,OutputArray類型的dst,輸出圖像,當其非零時,有着dsize(第三個參數)的尺寸,或者由src.size()計算出來。
- 第三個參數,Size類型的dsize,輸出圖像的大小;如果它等於零,由下式進行計算:
其中,dsize,fx,fy都不能為0。
- 第四個參數,double類型的fx,沿水平軸的縮放系數,有默認值0,且當其等於0時,由下式進行計算:
- 第五個參數,double類型的fy,沿垂直軸的縮放系數,有默認值0,且當其等於0時,由下式進行計算:
![]()
- 第六個參數,int類型的interpolation,用於指定插值方式,默認為INTER_LINEAR(線性插值)。
可選的插值方式如下:
- INTER_NEAREST - 最近鄰插值
- INTER_LINEAR - 線性插值(默認值)
- INTER_AREA - 區域插值(利用像素區域關系的重采樣插值)
- INTER_CUBIC –三次樣條插值(超過4×4像素鄰域內的雙三次插值)
- INTER_LANCZOS4 -Lanczos插值(超過8×8像素鄰域的Lanczos插值)
若要縮小圖像,一般情況下最好用CV_INTER_AREA來插值,
而若要放大圖像,一般情況下最好用CV_INTER_CUBIC(效率不高,慢,不推薦使用)或CV_INTER_LINEAR(效率較高,速度較快,推薦使用)。
關於插值,我們看幾張圖就能更好地理解。先看原圖:
![]()
當進行6次圖像縮小接着6次圖像放大操作后,兩種不同的插值方式得到的效果圖:
效果很明顯,第一張全是一個個的像素,非常影響美觀。另外一張卻有霧化的朦朧美感,所以插值方式的選擇,對經過多次放大縮小的圖片最終得到的效果是有很大影響的。
接着我們來看兩種resize的調用范例。
方式一,調用范例: Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一張512x512尺寸的圖片 Mat src=imread(“1.jpg”); //顯式指定dsize=dst.size(),那么fx和fy會其計算出來,不用額外指定。 resize(src, dst, dst.size()); Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一張512x512尺寸的圖片 Mat src=imread(“1.jpg”); //顯式指定dsize=dst.size(),那么fx和fy會其計算出來,不用額外指定。 resize(src, dst, dst.size()); 方式二、調用范例: Mat dst; Mat src=imread(“1.jpg”) //指定fx和fy,讓函數計算出目標圖像的大小。 resize(src, dst, Size(), 0.5, 0.5); Mat dst; Mat src=imread(“1.jpg”) //指定fx和fy,讓函數計算出目標圖像的大小。 resize(src, dst, Size(), 0.5, 0.5); 接着我們看看完整的示例程序: //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- usingnamespace //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- main( ) //載入原始圖 Mat srcImage = imread("1.jpg"//工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage1,dstImage2;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行尺寸調整操作 resize(tmpImage,dstImage1,Size( tmpImage.cols/2, tmpImage.rows/2 ),(0,0),(0,0),3); resize(tmpImage,dstImage2,Size( tmpImage.cols*2, tmpImage.rows*2 ),(0,0),(0,0),3); //顯示效果圖 imshow("【效果圖】之一", dstImage1); imshow("【效果圖】之二", dstImage2); waitKey(0); return //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原始圖 Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage1,dstImage2;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行尺寸調整操作 resize(tmpImage,dstImage1,Size( tmpImage.cols/2, tmpImage.rows/2 ),(0,0),(0,0),3); resize(tmpImage,dstImage2,Size( tmpImage.cols*2, tmpImage.rows*2 ),(0,0),(0,0),3); //顯示效果圖 imshow("【效果圖】之一", dstImage1); imshow("【效果圖】之二", dstImage2); waitKey(0); return 0; }
程序運行截圖:
![]()
四、pyrUp()函數剖析
pyrUp( )函數的作用是向上采樣並模糊一張圖像,說白了就是放大一張圖片。
- pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), borderType=BORDER_DEFAULT )
C++: void pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。
- 第二個參數,OutputArray類型的dst,輸出圖像,和源圖片有一樣的尺寸和類型。
- 第三個參數,const Size&類型的dstsize,輸出圖像的大小;有默認值Size(),即默認情況下,由Size(src.cols*2,src.rows*2)來進行計算,且一直需要滿足下列條件:
![]()
- 第四個參數,int類型的borderType,又來了,邊界模式,一般我們不用去管它。
pyrUp函數執行高斯金字塔的采樣操作,其實它也可以用於拉普拉斯金字塔的。
首先,它通過插入可為零的行與列,對源圖像進行向上取樣操作,然后將結果與pyrDown()乘以4的內核做卷積,就是這樣。
直接看完整的示例程序:
//-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- usingnamespace //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- main( ) //載入原始圖 Mat srcImage = imread("1.jpg"//工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行向上取樣操作 pyrUp( tmpImage, dstImage, Size( tmpImage.cols*2, tmpImage.rows*2 ) ); //顯示效果圖 imshow("【效果圖】", dstImage); waitKey(0); return //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原始圖 Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行向上取樣操作 pyrUp( tmpImage, dstImage, Size( tmpImage.cols*2, tmpImage.rows*2 ) ); //顯示效果圖 imshow("【效果圖】", dstImage); waitKey(0); return 0; }
程序運行截圖:
![]()
![]()
五、pyrDown()函數剖析
pyrDown( )函數的作用是向下采樣並模糊一張圖片,說白了就是縮小一張圖片。
- pyrDown(InputArray src,OutputArray dst, const Size& dstsize=Size(), borderType=BORDER_DEFAULT)
C++: void pyrDown(InputArray src,OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。
- 第二個參數,OutputArray類型的dst,輸出圖像,和源圖片有一樣的尺寸和類型。
- 第三個參數,const Size&類型的dstsize,輸出圖像的大小;有默認值Size(),即默認情況下,由Size Size((src.cols+1)/2, (src.rows+1)/2)來進行計算,且一直需要滿足下列條件:
![]()
該pyrDown函數執行了高斯金字塔建造的向下采樣的步驟。首先,它將源圖像與如下內核做卷積運算:
![]()
接着,它便通過對圖像的偶數行和列做插值來進行向下采樣操作。
依然是看看完整的示例程序:
//-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- usingnamespace //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- main( ) //載入原始圖 Mat srcImage = imread("1.jpg"//工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行向下取樣操作 pyrDown( tmpImage, dstImage, Size( tmpImage.cols/2, tmpImage.rows/2 ) ); //顯示效果圖 imshow("【效果圖】", dstImage); waitKey(0); return //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原始圖 Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖 Mat tmpImage,dstImage;//臨時變量和目標圖的定義 tmpImage=srcImage;//將原始圖賦給臨時變量 //顯示原始圖 imshow("【原始圖】", srcImage); //進行向下取樣操作 pyrDown( tmpImage, dstImage, Size( tmpImage.cols/2, tmpImage.rows/2 ) ); //顯示效果圖 imshow("【效果圖】", dstImage); waitKey(0); return 0; }
程序運行截圖:
![]()
![]()
六、綜合示例篇——在實戰中熟稔
依然是每篇文章都會配給大家的一個詳細注釋的博文配套示例程序,把這篇文章中介紹的知識點以代碼為載體,展現給大家。
這個示例程序中,分別演示了用resize,pryUp,pryDown來讓源圖像進行放大縮小的操作,分別用鍵盤按鍵1、2、3、4、A、D、W、S來控制圖片的放大與縮小:
![]()
OK,上詳細注釋的代碼吧:
//-----------------------------------【程序說明】---------------------------------------------- // 程序名稱::《 【OpenCV入門教程之十三】OpenCV圖像金字塔:高斯金字塔、拉普拉斯金字塔與圖片尺寸縮放》 博文配套源碼 // 開發所用IDE版本:Visual Studio 2010 // 開發所用OpenCV版本: 2.4.9 // 2014年5月18日 Create by 淺墨 //---------------------------------------------------------------------------------------------- //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【宏定義部分】-------------------------------------------- // 描述:定義一些輔助宏 //------------------------------------------------------------------------------------------------ #define WINDOW_NAME "【程序窗口】" //為窗口標題定義的宏 //-----------------------------------【命名空間聲明部分】-------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- usingnamespace usingnamespace //-----------------------------------【全局變量聲明部分】-------------------------------------- // 描述:全局變量聲明 //----------------------------------------------------------------------------------------------- Mat g_srcImage, g_dstImage, g_tmpImage; //-----------------------------------【全局函數聲明部分】-------------------------------------- // 描述:全局函數聲明 //----------------------------------------------------------------------------------------------- static ShowHelpText(); //-----------------------------------【ShowHelpText( )函數】---------------------------------- // 描述:輸出一些幫助信息 //---------------------------------------------------------------------------------------------- static ShowHelpText() //輸出一些幫助信息 printf("\n\n\n\t歡迎來到OpenCV圖像金字塔和resize示例程序~\n\n" printf( "\n\n\t按鍵操作說明: \n\n" "\t\t鍵盤按鍵【ESC】或者【Q】- 退出程序\n" "\t\t鍵盤按鍵【1】或者【W】- 進行基於【resize】函數的圖片放大\n" "\t\t鍵盤按鍵【2】或者【S】- 進行基於【resize】函數的圖片縮小\n" "\t\t鍵盤按鍵【3】或者【A】- 進行基於【pyrUp】函數的圖片放大\n" "\t\t鍵盤按鍵【4】或者【D】- 進行基於【pyrDown】函數的圖片縮小\n" "\n\n\t\t\t\t\t\t\t\t by淺墨\n\n\n" //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- main( ) //改變console字體顏色 system("color 2F" //顯示幫助文字 ShowHelpText(); //載入原圖 g_srcImage = imread("1.jpg"//工程目錄下需要有一張名為1.jpg的測試圖像,且其尺寸需被2的N次方整除,N為可以縮放的次數 ( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"returnfalse // 創建顯示窗口 namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE ); imshow(WINDOW_NAME, g_srcImage); //參數賦值 g_tmpImage = g_srcImage; g_dstImage = g_tmpImage; key =0; //輪詢獲取按鍵信息 while key=waitKey(9) ;//讀取鍵值到key變量中 //根據key變量的值,進行不同的操作 switch(key) //======================【程序退出相關鍵值處理】======================= //按鍵ESC return break //按鍵Q return break //======================【圖片放大相關鍵值處理】======================= //按鍵A按下,調用pyrUp函數 pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ) ); printf( ">檢測到按鍵【A】被按下,開始進行基於【pyrUp】函數的圖片放大:圖片尺寸×2 \n" break //按鍵W按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【W】被按下,開始進行基於【resize】函數的圖片放大:圖片尺寸×2 \n" break //按鍵1按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【1】被按下,開始進行基於【resize】函數的圖片放大:圖片尺寸×2 \n" break //按鍵3按下,調用pyrUp函數 pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【3】被按下,開始進行基於【pyrUp】函數的圖片放大:圖片尺寸×2 \n" break //======================【圖片縮小相關鍵值處理】======================= //按鍵D按下,調用pyrDown函數 pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 )); printf( ">檢測到按鍵【D】被按下,開始進行基於【pyrDown】函數的圖片縮小:圖片尺寸/2\n" break //按鍵S按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 )); printf( ">檢測到按鍵【S】被按下,開始進行基於【resize】函數的圖片縮小:圖片尺寸/2\n" break //按鍵2按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ),(0,0),(0,0),2); printf( ">檢測到按鍵【2】被按下,開始進行基於【resize】函數的圖片縮小:圖片尺寸/2\n" break //按鍵4按下,調用pyrDown函數 pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ) ); printf( ">檢測到按鍵【4】被按下,開始進行基於【pyrDown】函數的圖片縮小:圖片尺寸/2\n" break //經過操作后,顯示變化后的圖 imshow( WINDOW_NAME, g_dstImage ); //將g_dstImage賦給g_tmpImage,方便下一次循環 g_tmpImage = g_dstImage; return //-----------------------------------【程序說明】---------------------------------------------- // 程序名稱::《 【OpenCV入門教程之十三】OpenCV圖像金字塔:高斯金字塔、拉普拉斯金字塔與圖片尺寸縮放》 博文配套源碼 // 開發所用IDE版本:Visual Studio 2010 // 開發所用OpenCV版本: 2.4.9 // 2014年5月18日 Create by 淺墨 //---------------------------------------------------------------------------------------------- //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【宏定義部分】-------------------------------------------- // 描述:定義一些輔助宏 //------------------------------------------------------------------------------------------------ #define WINDOW_NAME "【程序窗口】" //為窗口標題定義的宏 //-----------------------------------【命名空間聲明部分】-------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace std; using namespace cv; //-----------------------------------【全局變量聲明部分】-------------------------------------- // 描述:全局變量聲明 //----------------------------------------------------------------------------------------------- Mat g_srcImage, g_dstImage, g_tmpImage; //-----------------------------------【全局函數聲明部分】-------------------------------------- // 描述:全局函數聲明 //----------------------------------------------------------------------------------------------- static void ShowHelpText(); //-----------------------------------【ShowHelpText( )函數】---------------------------------- // 描述:輸出一些幫助信息 //---------------------------------------------------------------------------------------------- static void ShowHelpText() { //輸出一些幫助信息 printf("\n\n\n\t歡迎來到OpenCV圖像金字塔和resize示例程序~\n\n"); printf( "\n\n\t按鍵操作說明: \n\n" "\t\t鍵盤按鍵【ESC】或者【Q】- 退出程序\n" "\t\t鍵盤按鍵【1】或者【W】- 進行基於【resize】函數的圖片放大\n" "\t\t鍵盤按鍵【2】或者【S】- 進行基於【resize】函數的圖片縮小\n" "\t\t鍵盤按鍵【3】或者【A】- 進行基於【pyrUp】函數的圖片放大\n" "\t\t鍵盤按鍵【4】或者【D】- 進行基於【pyrDown】函數的圖片縮小\n" "\n\n\t\t\t\t\t\t\t\t by淺墨\n\n\n" ); } //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- int main( ) { //改變console字體顏色 system("color 2F"); //顯示幫助文字 ShowHelpText(); //載入原圖 g_srcImage = imread("1.jpg");//工程目錄下需要有一張名為1.jpg的測試圖像,且其尺寸需被2的N次方整除,N為可以縮放的次數 if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; } // 創建顯示窗口 namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE ); imshow(WINDOW_NAME, g_srcImage); //參數賦值 g_tmpImage = g_srcImage; g_dstImage = g_tmpImage; int key =0; //輪詢獲取按鍵信息 while(1) { key=waitKey(9) ;//讀取鍵值到key變量中 //根據key變量的值,進行不同的操作 switch(key) { //======================【程序退出相關鍵值處理】======================= case 27://按鍵ESC return 0; break; case 'q'://按鍵Q return 0; break; //======================【圖片放大相關鍵值處理】======================= case 'a'://按鍵A按下,調用pyrUp函數 pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ) ); printf( ">檢測到按鍵【A】被按下,開始進行基於【pyrUp】函數的圖片放大:圖片尺寸×2 \n" ); break; case 'w'://按鍵W按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【W】被按下,開始進行基於【resize】函數的圖片放大:圖片尺寸×2 \n" ); break; case '1'://按鍵1按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【1】被按下,開始進行基於【resize】函數的圖片放大:圖片尺寸×2 \n" ); break; case '3': //按鍵3按下,調用pyrUp函數 pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 )); printf( ">檢測到按鍵【3】被按下,開始進行基於【pyrUp】函數的圖片放大:圖片尺寸×2 \n" ); break; //======================【圖片縮小相關鍵值處理】======================= case 'd': //按鍵D按下,調用pyrDown函數 pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 )); printf( ">檢測到按鍵【D】被按下,開始進行基於【pyrDown】函數的圖片縮小:圖片尺寸/2\n" ); break; case 's' : //按鍵S按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 )); printf( ">檢測到按鍵【S】被按下,開始進行基於【resize】函數的圖片縮小:圖片尺寸/2\n" ); break; case '2'://按鍵2按下,調用resize函數 resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ),(0,0),(0,0),2); printf( ">檢測到按鍵【2】被按下,開始進行基於【resize】函數的圖片縮小:圖片尺寸/2\n" ); break; case '4': //按鍵4按下,調用pyrDown函數 pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ) ); printf( ">檢測到按鍵【4】被按下,開始進行基於【pyrDown】函數的圖片縮小:圖片尺寸/2\n" ); break; } //經過操作后,顯示變化后的圖 imshow( WINDOW_NAME, g_dstImage ); //將g_dstImage賦給g_tmpImage,方便下一次循環 g_tmpImage = g_dstImage; } return 0; }
放一些程序運行截圖。
![]()
經過多次按鍵后的效果圖:
![]()
![]()
![]()
![]()
另外,還可以放大圖像到很大的尺寸,上圖的話會很凶殘而且不美觀。所以就不放出超過原圖尺寸的截圖了。
好的,就放出這些效果圖,具體更多的運行效果大家就自己下載示例程序回去玩~
本篇文章的配套源代碼請點擊這里下載:
![]()
# -*- coding: utf-8 -*- import cv2 import numpy as np A = cv2.imread('../burn image/case3-burn-to-head-showing-healing-0.jpg',0) # generate Gaussian pyramid for A G = A.copy() gpA = [G] lpA = [] for i in xrange(3): cv2.imshow('1',G) image_o = G G = cv2.pyrDown(G) GE = cv2.pyrUp(G) L = np.subtract(image_o,GE) gpA.append(G) lpA.append(L) cv2.imshow('lpA',L ) cv2.imshow('上采樣+拉普拉斯',L+GE ) cv2.imshow('upsample',GE ) MSE(image_o,L+GE) cv2.waitKey(0)
注:拉普拉斯金子塔是每層的原圖減去該圖的下采樣后的上采樣圖(一般用高斯下采樣), 所以也叫預測殘差金字塔。 通過預測殘差金字塔可以100%還原圖像,對么?