本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。
文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/22745559
作者:毛星雲(淺墨) 郵箱: [email protected]
寫作當前博文時配套使用的OpenCV版本: 2.4.8
本篇文章中,我們一起仔細探討了OpenCV圖像處理技術中比較熱門的圖像濾波操作。圖像濾波系列文章淺墨準備花兩次更新的時間來講,此爲上篇,爲大家剖析了“方框濾波“,”均值濾波“和”高斯濾波“三種常見線性鄰域濾波操作。而作爲非線性濾波的“中值濾波”和“雙邊濾波”,留待我們下次剖析。
先上一張精彩截圖:
淺墨其實很希望把這篇文章寫得精簡和簡明扼要,發現越深入寫進去,需要講的周邊內容越多,於是文章越寫越長,最後在word中字數統計突破了一萬。。。。。。。
因爲文章很長,如果詳細啃的話,或許會消化不良。在這裏給大家一個指引,如果是單單想要掌握這篇文章中講解的OpenCV線性濾波相關的三個函數:boxFilter,blur和GaussianBlur的使用方法的話,直接看第三部分“淺出”和第四部分“實例”就行。
在以後寫的OpenCV系列文章中,淺墨暫且準備將每篇博文中知識點都分成原理、深入、淺出和實例四大部分來講解,
第一部分爲和圖像處理中線性濾波相關的理論,第二部分“深入”部分主要深入OpenCV內部,帶領大家領略OpenCV的開源魅力,進行OpenCV相關源碼的剖析,做到對OpenCV理解深刻,做一個高端大氣的OpenCV使用者。 第三部分“淺出”主要教會大家如何快速上手當前文章中介紹的相關OpenCV API函數。而在第四部分,淺墨會爲大家準備一個和本篇文章相關的詳細註釋的綜合實例程序。
這樣的話呢,文章既不失深度,也不失快速入門的良方。希望淺墨按這樣的新思路寫出來的文章,無論是新手還是高手,看了都能有所收穫。
給出本篇萬字文章的結構脈絡:
一、理論——相關圖像處理概念介紹
二、深入——OpenCV源碼講解
三、淺出——API函數講解
四、實例——詳細註釋的博文配套程序
一、理論與概念講解
OK,我們開始吧。
<1>關於平滑處理
“平滑處理“(smoothing)也稱“模糊處理”(bluring),是一項簡單且使用頻率很高的圖像處理方法。平滑處理的用途有很多,最常見的是用來減少圖像上的噪點或者失真。在涉及到降低圖像分辨率時,平滑處理是非常好用的方法。
<2>圖像濾波與濾波器
首先我們看一下圖像濾波的概念。圖像濾波,即在儘量保留圖像細節特徵的條件下對目標圖像的噪聲進行抑制,是圖像預處理中不可缺少的操作,其處理效果的好壞將直接影響到後續圖像處理和分析的有效性和可靠性。
消除圖像中的噪聲成分叫作圖像的平滑化或濾波操作。信號或圖像的能量大部分集中在幅度譜的低頻和中頻段是很常見的,而在較高頻段,感興趣的信息經常被噪聲淹沒。因此一個能降低高頻成分幅度的濾波器就能夠減弱噪聲的影響。
圖像濾波的目的有兩個:一是抽出對象的特徵作爲圖像識別的特徵模式;另一個是爲適應圖像處理的要求,消除圖像數字化時所混入的噪聲。
而對濾波處理的要求也有兩條:一是不能損壞圖像的輪廓及邊緣等重要信息;二是使圖像清晰視覺效果好。
平滑濾波是低頻增強的空間域濾波技術。它的目的有兩類:一類是模糊;另一類是消除噪音。(各種“兩",:))
空間域的平滑濾波一般採用簡單平均法進行,就是求鄰近像元點的平均亮度值。鄰域的大小與平滑的效果直接相關,鄰域越大平滑的效果越好,但鄰域過大,平滑會使邊緣信息損失的越大,從而使輸出的圖像變得模糊,因此需合理選擇鄰域的大小。
關於濾波器,一種形象的比喻法是:我們可以把濾波器想象成一個包含加權係數的窗口,當使用這個濾波器平滑處理圖像時,就把這個窗口放到圖像之上,透過這個窗口來看我們得到的圖像。
濾波器的種類有很多, 在新版本的OpenCV中,提供瞭如下五種常用的圖像平滑處理操作方法,且他們分別被封裝在單獨的函數中,使用起來非常方便:
- 方框濾波——boxblur函數
- 均值濾波(鄰域平均濾波)——blur函數
- 高斯濾波——GaussianBlur函數
- 中值濾波——medianBlur函數
- 雙邊濾波——bilateralFilter函數
今天我們要講解的是作爲線性濾波的方框濾波,均值濾波和高斯濾波。兩種非線性濾波操作——中值濾波和雙邊濾波,我們留待下次講解。
<3>對線性濾波器的簡介
線性濾波器:線性濾波器經常用於剔除輸入信號中不想要的頻率或者從許多頻率中選擇一個想要的頻率。
幾種常見的線性濾波器:
- 允許低頻率通過的低通濾波器。
- 允許高頻率通過的高通濾波器。
- 允許一定範圍頻率通過的帶通濾波器。
- 阻止一定範圍頻率通過並且允許其它頻率通過的帶阻濾波器。
- 允許所有頻率通過、僅僅改變相位關係的全通濾波器。
- 阻止一個狹窄頻率範圍通過的特殊帶阻濾波器,陷波濾波器(Band-stop filter)。
<4>關於濾波和模糊
關於濾波和模糊,大家往往在初次接觸的時候會弄混淆,“一會兒說濾波,一會兒又說模糊,什麼玩意兒啊”。
沒關係,在這裏,我們就來辨別一下,爲大家掃清障礙。
我們上文已經提到過,濾波是將信號中特定波段頻率濾除的操作,是抑制和防止干擾的一項重要措施。
爲了方便說明,就拿我們經常用的高斯濾波來作例子吧。我們知道,濾波可分低通濾波和高通濾波兩種。而高斯濾波是指用高斯函數作爲濾波函數的濾波操作,至於是不是模糊,要看是高斯低通還是高斯高通,低通就是模糊,高通就是銳化。
其實說白了是很簡單的,對吧:
高斯濾波是指用高斯函數作爲濾波函數的濾波操作。
高斯模糊就是高斯低通濾波。
<5>鄰域算子與線性鄰域濾波
鄰域算子(局部算子)是利用給定像素周圍的像素值的決定此像素的最終輸出值的一種算子。而線性鄰域濾波是一種常用的鄰域算子,像素的輸出值取決於輸入像素的加權和,具體過程如下圖。
鄰域算子除了用於局部色調調整以外,還可以用於圖像濾波,實現圖像的平滑和銳化,圖像邊緣增強或者圖像噪聲的去除。本篇文章,我們介紹的主角是線性鄰域濾波算子,即用不同的權重去結合一個小鄰域內的像素,來得到應有的處理效果。
圖注:鄰域濾波(卷積):左邊圖像與中間圖像的卷積產生右邊圖像。目標圖像中藍色標記的像素是利用原圖像中紅色標記的像素計算得到的。
線性濾波處理的輸出像素值是輸入像素值的加權和 :
其中的加權和爲 ,我們稱其爲“核”,濾波器的加權係數,即濾波器的“濾波係數”。
上面的式子可以簡單寫作:
其中f表示輸入像素值,h表示加權係數“核“,g表示輸出像素值
在新版本的OpenCV中,提供瞭如下三種常用的線性濾波操作,他們分別被封裝在單獨的函數中,使用起來非常方便:
方框濾波——boxblur函數
均值濾波——blur函數
高斯濾波——GaussianBlur函數
下面我們來對他們進行一一介紹。
<6>方框濾波(box Filter)
方框濾波(box Filter)被封裝在一個名爲boxblur的函數中,即boxblur函數的作用是使用方框濾波器(box filter)來模糊一張圖片,從src輸入,從dst輸出。
函數原型如下:
- C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
參數詳解:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該爲CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。
- 第三個參數,int類型的ddepth,輸出圖像的深度,-1代表使用原圖深度,即src.depth()。
- 第四個參數,Size類型(對Size類型稍後有講解)的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 爲像素寬度, h爲像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第五個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心爲錨點,所以默認值Point(-1,-1)表示這個錨點在覈的中心。
- 第六個參數,bool類型的normalize,默認值爲true,一個標識符,表示內核是否被其區域歸一化(normalized)了。
- 第七個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
boxFilter()函數方框濾波所用的核爲:
其中:
其中f表示原圖,h表示核,g表示目標圖,當normalize=true的時候,方框濾波就變成了我們熟悉的均值濾波。也就是說,均值濾波是方框濾波歸一化(normalized)後的特殊情況。其中,歸一化就是把要處理的量都縮放到一個範圍內,比如(0,1),以便統一處理和直觀量化。
而非歸一化(Unnormalized)的方框濾波用於計算每個像素鄰域內的積分特性,比如密集光流算法(dense optical flow algorithms)中用到的圖像倒數的協方差矩陣(covariance matrices of image derivatives)
如果我們要在可變的窗口中計算像素總和,可以使用integral()函數。
<7>均值濾波
均值濾波,是最簡單的一種濾波操作,輸出圖像的每一個像素是核窗口內輸入圖像對應像素的像素的平均值( 所有像素加權係數相等),其實說白了它就是歸一化後的方框濾波。
我們在下文進行源碼剖析時會發現,blur函數內部中其實就是調用了一下boxFilter。
下面開始講均值濾波的內容吧。
1)均值濾波的理論簡析
均值濾波是典型的線性濾波算法,主要方法爲鄰域平均法,即用一片圖像區域的各個像素的均值來代替原圖像中的各個像素值。一般需要在圖像上對目標像素給出一個模板(內核),該模板包括了其周圍的臨近像素(比如以目標像素爲中心的周圍8(3x3-1)個像素,構成一個濾波模板,即去掉目標像素本身)。再用模板中的全體像素的平均值來代替原來像素值。即對待處理的當前像素點(x,y),選擇一個模板,該模板由其近鄰的若干像素組成,求模板中所有像素的均值,再把該均值賦予當前像素點(x,y),作爲處理後圖像在該點上的灰度個g(x,y),即個g(x,y)=1/m ∑f(x,y) ,其中m爲該模板中包含當前像素在內的像素總個數。
2)均值濾波的缺陷
均值濾波本身存在着固有的缺陷,即它不能很好地保護圖像細節,在圖像去噪的同時也破壞了圖像的細節部分,從而使圖像變得模糊,不能很好地去除噪聲點。
3)在OpenCV中使用均值濾波——blur函數
blur函數的作用是,對輸入的圖像src進行均值濾波後用dst輸出。
blur函數文檔中,給出的其核是這樣的:
這個內核一看就明瞭,就是在求均值,即blur函數封裝的就是均值濾波。
blur函數的原型:
- C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該爲CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片爲模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型(對Size類型稍後有講解)的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 爲像素寬度, h爲像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第四個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心爲錨點,所以默認值Point(-1,-1)表示這個錨點在覈的中心。
- 第五個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
<8>高斯濾波
1)高斯濾波的理論簡析
高斯濾波是一種線性平滑濾波,適用於消除高斯噪聲,廣泛應用於圖像處理的減噪過程。通俗的講,高斯濾波就是對整幅圖像進行加權平均的過程,每一個像素點的值,都由其本身和鄰域內的其他像素值經過加權平均後得到。高斯濾波的具體操作是:用一個模板(或稱卷積、掩模)掃描圖像中的每一個像素,用模板確定的鄰域內像素的加權平均灰度值去替代模板中心像素點的值。
大家常常說高斯濾波最有用的濾波操作,雖然它用起來,效率往往不是最高的。
高斯模糊技術生成的圖像,其視覺效果就像是經過一個半透明屏幕在觀察圖像,這與鏡頭焦外成像效果散景以及普通照明陰影中的效果都明顯不同。高斯平滑也用於計算機視覺算法中的預先處理階段,以增強圖像在不同比例大小下的圖像效果(參見尺度空間表示以及尺度空間實現)。從數學的角度來看,圖像的高斯模糊過程就是圖像與正態分佈做卷積。由於正態分佈又叫作高斯分佈,所以這項技術就叫作高斯模糊。
圖像與圓形方框模糊做卷積將會生成更加精確的焦外成像效果。由於高斯函數的傅立葉變換是另外一個高斯函數,所以高斯模糊對於圖像來說就是一個低通濾波操作。
高斯濾波器是一類根據高斯函數的形狀來選擇權值的線性平滑濾波器。高斯平滑濾波器對於抑制服從正態分佈的噪聲非常有效。一維零均值高斯函數爲:
其中,高斯分佈參數Sigma決定了高斯函數的寬度。對於圖像處理來說,常用二維零均值離散高斯函數作平滑濾波器。
二維高斯函數爲:
二、深入——OpenCV源碼剖析
2)在OpenCV中使用高斯濾波——GaussianBlur函數
GaussianBlur函數的作用是用高斯濾波器來模糊一張圖片,對輸入的圖像src進行高斯濾波後用dst輸出。它將源圖像和指定的高斯核函數做卷積運算,並且支持就地過濾(In-placefiltering)。
- C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。它可以是單獨的任意通道數的圖片,但需要注意,圖片深度應該爲CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片爲模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型的ksize高斯內核的大小。其中ksize.width和ksize.height可以不同,但他們都必須爲正數和奇數。或者,它們可以是零的,它們都是由sigma計算而來。
- 第四個參數,double類型的sigmaX,表示高斯核函數在X方向的的標準偏差。
- 第五個參數,double類型的sigmaY,表示高斯核函數在Y方向的的標準偏差。若sigmaY爲零,就將它設爲sigmaX,如果sigmaX和sigmaY都是0,那麼就由ksize.width和ksize.height計算出來。
- 爲了結果的正確性着想,最好是把第三個參數Size,第四個參數sigmaX和第五個參數sigmaY全部指定到。
- 第六個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
嗯,第一部分的理論介紹大概就是這些了,我們接着進入第二部分,OpenCV的源碼剖析。
上一篇文章中我們已經和當前最新版的OpenCV的源碼親密接觸過。 在這一部分中,淺墨將帶領大家領略OpenCV的開源魅力,對OpenCV中本篇文章裏講解到線性濾波函數——boxFilter,blur和GaussianBlur函數以及周邊的涉及到的源碼進行適當的剖析。
三、淺出——線性濾波函數快速上手攻略
這樣,我們就可以對 OpenCV有一個更加深刻的理解,成爲一個高端大氣的OpenCV使用者。
<1>OpenCV中boxFilter函數源碼解析
我們可以在OpenCV的安裝路徑的\sources\modules\imgproc\src下的smooth.cpp源文件的第711行找到boxFilter函數的源代碼。對應於淺墨將OpenCV 2.4.8安裝在D:\Program Files\opencv的路徑下,那麼,smooth.cpp文件就在D:\ProgramFiles\opencv\sources\modules\imgproc\src路徑下。
- //-----------------------------------【boxFilter()函數中文註釋版源代碼】----------------------------
- // 代碼作用:進行box Filter濾波操作的函數
- // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼
- // OpenCV源代碼版本:2.4.8
- // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp
- // 源文件中如下代碼的起始行數:711行
- // 中文註釋by淺墨
- //--------------------------------------------------------------------------------------------------------
- void cv::boxFilter( InputArray _src,OutputArray _dst, int ddepth,
- Size ksize, Point anchor,
- bool normalize, int borderType)
- {
- Mat src = _src.getMat();//拷貝源圖的形參Mat數據到臨時變量,用於稍後的操作
- int sdepth =src.depth(), cn = src.channels();//定義int型臨時變量,代表源圖深度的sdepth,源圖通道的引用cn
- //處理ddepth小於零的情況
- if( ddepth < 0 )
- ddepth = sdepth;
- _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) );//初始化目標圖
- Mat dst =_dst.getMat();//拷貝目標圖的形參Mat數據到臨時變量,用於稍後的操作
- //處理 borderType不爲 BORDER_CONSTANT 且normalize爲真的情況
- if( borderType != BORDER_CONSTANT && normalize ) {
- if( src.rows == 1 )
- ksize.height = 1;
- if( src.cols == 1 )
- ksize.width = 1;
- }
- //若之前有過HAVE_TEGRA_OPTIMIZATION優化選項的定義,則執行宏體中的tegra優化版函數並返回
- #ifdef HAVE_TEGRA_OPTIMIZATION
- if ( tegra::box(src, dst, ksize, anchor, normalize, borderType) )
- return;
- #endif
- //調用FilterEngine濾波引擎,正式開始濾波操作
- Ptr<FilterEngine> f = createBoxFilter( src.type(), dst.type(),
- ksize, anchor,normalize, borderType );
- f->apply( src, dst );
- }
//-----------------------------------【boxFilter()函數中文註釋版源代碼】---------------------------- // 代碼作用:進行box Filter濾波操作的函數 // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼 // OpenCV源代碼版本:2.4.8 // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp // 源文件中如下代碼的起始行數:711行 // 中文註釋by淺墨 //-------------------------------------------------------------------------------------------------------- void cv::boxFilter( InputArray _src,OutputArray _dst, int ddepth, Size ksize, Point anchor, bool normalize, int borderType) { Mat src = _src.getMat();//拷貝源圖的形參Mat數據到臨時變量,用於稍後的操作 int sdepth =src.depth(), cn = src.channels();//定義int型臨時變量,代表源圖深度的sdepth,源圖通道的引用cn //處理ddepth小於零的情況 if( ddepth < 0 ) ddepth = sdepth; _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) );//初始化目標圖 Mat dst =_dst.getMat();//拷貝目標圖的形參Mat數據到臨時變量,用於稍後的操作 //處理 borderType不爲 BORDER_CONSTANT 且normalize爲真的情況 if( borderType != BORDER_CONSTANT && normalize ) { if( src.rows == 1 ) ksize.height = 1; if( src.cols == 1 ) ksize.width = 1; } //若之前有過HAVE_TEGRA_OPTIMIZATION優化選項的定義,則執行宏體中的tegra優化版函數並返回 #ifdef HAVE_TEGRA_OPTIMIZATION if ( tegra::box(src, dst, ksize, anchor, normalize, borderType) ) return; #endif //調用FilterEngine濾波引擎,正式開始濾波操作 Ptr<FilterEngine> f = createBoxFilter( src.type(), dst.type(), ksize, anchor,normalize, borderType ); f->apply( src, dst ); }
其中的Ptr是用來動態分配的對象的智能指針模板類。可以發現,函數的內部代碼思路是很清晰的,先拷貝源圖的形參Mat數據到臨時變量,定義一些臨時變量,在處理ddepth小於零的情況,接着處理 borderType不爲 BORDER_CONSTANT 且normalize爲真的情況,最終調用FilterEngine濾波引擎創建一個BoxFilter,正式開始濾波操作。
這裏的FilterEngine是OpenCV圖像濾波功能的核心引擎,我們有必要詳細剖析看其源代碼。
<2>FilterEngine類解析——OpenCV圖像濾波核心引擎
FilterEngine類是OpenCV關於圖像濾波的主力軍類,OpenCV圖像濾波功能的核心引擎。各種濾波函數比如blur, GaussianBlur,到頭來其實是就是在函數末尾處定義了一個Ptr<FilterEngine>類型的f,然後f->apply( src, dst )了一下而已。
這個類可以把幾乎是所有的濾波操作施加到圖像上。它包含了所有必要的中間緩存器。有很多和濾波相關的create系函數的返回值直接就是Ptr<FilterEngine>。比如cv::createSeparableLinearFilter(),
cv::createLinearFilter(),cv::createGaussianFilter(), cv::createDerivFilter(),
cv::createBoxFilter() 和cv::createMorphologyFilter().,這裏給出其中一個函數的原型吧:
- Ptr<FilterEngine>createLinearFilter(int srcType, int dstType, InputArray kernel, Point_anchor=Point(-1,-1), double delta=0, int rowBorderType=BORDER_DEFAULT, intcolumnBorderType=-1, const Scalar& borderValue=Scalar() )
Ptr<FilterEngine>createLinearFilter(int srcType, int dstType, InputArray kernel, Point_anchor=Point(-1,-1), double delta=0, int rowBorderType=BORDER_DEFAULT, intcolumnBorderType=-1, const Scalar& borderValue=Scalar() )
上面我們提到過了,其中的Ptr是用來動態分配的對象的智能指針模板類,而上面的尖括號裏面的模板參數就是FilterEngine。
使用FilterEngine類可以分塊處理大量的圖像,構建複雜的管線,其中就包含一些進行濾波階段。如果我們需要使用預先定義好的的濾波操作,cv::filter2D(), cv::erode(),以及cv::dilate(),可以選擇,他們不依賴於FilterEngine,自立自強,在自己函數體內部就實現了FilterEngine提供的功能。不像其他的諸如我們今天講的blur系列函數,依賴於FilterEngine引擎。
我們看下其類聲明經過淺墨詳細註釋的源碼:
- //-----------------------------------【FilterEngine類中文註釋版源代碼】----------------------------
- // 代碼作用:FilterEngine類,OpenCV圖像濾波功能的核心引擎
- // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼
- // OpenCV源代碼版本:2.4.8
- // 源碼路徑:…\opencv\sources\modules\imgproc\include\opencv2\imgproc\imgproc.hpp
- // 源文件中如下代碼的起始行數:222行
- // 中文註釋by淺墨
- //--------------------------------------------------------------------------------------------------------
- class CV_EXPORTS FilterEngine
- {
- public:
- //默認構造函數
- FilterEngine();
- //完整的構造函數。 _filter2D 、_rowFilter 和 _columnFilter之一,必須爲非空
- FilterEngine(const Ptr<BaseFilter>& _filter2D,
- constPtr<BaseRowFilter>& _rowFilter,
- constPtr<BaseColumnFilter>& _columnFilter,
- int srcType, int dstType, intbufType,
- int_rowBorderType=BORDER_REPLICATE,
- int _columnBorderType=-1,
- const Scalar&_borderValue=Scalar());
- //默認析構函數
- virtual ~FilterEngine();
- //重新初始化引擎。釋放之前濾波器申請的內存。
- void init(const Ptr<BaseFilter>& _filter2D,
- constPtr<BaseRowFilter>& _rowFilter,
- constPtr<BaseColumnFilter>& _columnFilter,
- int srcType, int dstType, intbufType,
- int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
- const Scalar&_borderValue=Scalar());
- //開始對指定了ROI區域和尺寸的圖片進行濾波操作
- virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1);
- //開始對指定了ROI區域的圖片進行濾波操作
- virtual int start(const Mat& src, const Rect&srcRoi=Rect(0,0,-1,-1),
- bool isolated=false, intmaxBufRows=-1);
- //處理圖像的下一個srcCount行(函數的第三個參數)
- virtual int proceed(const uchar* src, int srcStep, int srcCount,
- uchar* dst, intdstStep);
- //對圖像指定的ROI區域進行濾波操作,若srcRoi=(0,0,-1,-1),則對整個圖像進行濾波操作
- virtual void apply( const Mat& src, Mat& dst,
- const Rect&srcRoi=Rect(0,0,-1,-1),
- Point dstOfs=Point(0,0),
- bool isolated=false);
- //如果濾波器可分離,則返回true
- boolisSeparable() const { return (const BaseFilter*)filter2D == 0; }
- //返回輸入和輸出行數
- int remainingInputRows() const;
- intremainingOutputRows() const;
- //一些成員參數定義
- int srcType, dstType, bufType;
- Size ksize;
- Point anchor;
- int maxWidth;
- Size wholeSize;
- Rect roi;
- int dx1, dx2;
- int rowBorderType, columnBorderType;
- vector<int> borderTab;
- int borderElemSize;
- vector<uchar> ringBuf;
- vector<uchar> srcRow;
- vector<uchar> constBorderValue;
- vector<uchar> constBorderRow;
- int bufStep, startY, startY0, endY, rowCount, dstY;
- vector<uchar*> rows;
- Ptr<BaseFilter> filter2D;
- Ptr<BaseRowFilter> rowFilter;
- Ptr<BaseColumnFilter> columnFilter;
- };
//-----------------------------------【FilterEngine類中文註釋版源代碼】---------------------------- // 代碼作用:FilterEngine類,OpenCV圖像濾波功能的核心引擎 // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼 // OpenCV源代碼版本:2.4.8 // 源碼路徑:…\opencv\sources\modules\imgproc\include\opencv2\imgproc\imgproc.hpp // 源文件中如下代碼的起始行數:222行 // 中文註釋by淺墨 //-------------------------------------------------------------------------------------------------------- class CV_EXPORTS FilterEngine { public: //默認構造函數 FilterEngine(); //完整的構造函數。 _filter2D 、_rowFilter 和 _columnFilter之一,必須爲非空 FilterEngine(const Ptr<BaseFilter>& _filter2D, constPtr<BaseRowFilter>& _rowFilter, constPtr<BaseColumnFilter>& _columnFilter, int srcType, int dstType, intbufType, int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1, const Scalar&_borderValue=Scalar()); //默認析構函數 virtual ~FilterEngine(); //重新初始化引擎。釋放之前濾波器申請的內存。 void init(const Ptr<BaseFilter>& _filter2D, constPtr<BaseRowFilter>& _rowFilter, constPtr<BaseColumnFilter>& _columnFilter, int srcType, int dstType, intbufType, int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1, const Scalar&_borderValue=Scalar()); //開始對指定了ROI區域和尺寸的圖片進行濾波操作 virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1); //開始對指定了ROI區域的圖片進行濾波操作 virtual int start(const Mat& src, const Rect&srcRoi=Rect(0,0,-1,-1), bool isolated=false, intmaxBufRows=-1); //處理圖像的下一個srcCount行(函數的第三個參數) virtual int proceed(const uchar* src, int srcStep, int srcCount, uchar* dst, intdstStep); //對圖像指定的ROI區域進行濾波操作,若srcRoi=(0,0,-1,-1),則對整個圖像進行濾波操作 virtual void apply( const Mat& src, Mat& dst, const Rect&srcRoi=Rect(0,0,-1,-1), Point dstOfs=Point(0,0), bool isolated=false); //如果濾波器可分離,則返回true boolisSeparable() const { return (const BaseFilter*)filter2D == 0; } //返回輸入和輸出行數 int remainingInputRows() const; intremainingOutputRows() const; //一些成員參數定義 int srcType, dstType, bufType; Size ksize; Point anchor; int maxWidth; Size wholeSize; Rect roi; int dx1, dx2; int rowBorderType, columnBorderType; vector<int> borderTab; int borderElemSize; vector<uchar> ringBuf; vector<uchar> srcRow; vector<uchar> constBorderValue; vector<uchar> constBorderRow; int bufStep, startY, startY0, endY, rowCount, dstY; vector<uchar*> rows; Ptr<BaseFilter> filter2D; Ptr<BaseRowFilter> rowFilter; Ptr<BaseColumnFilter> columnFilter; };
<3>OpenCV中size類型剖析
size類型我們也講一下,通過轉到定義,我們可以在……\opencv\sources\modules\core\include\opencv2\core\core.hpp路徑下,對應於淺墨的OpenCV安裝路徑,就是在
D:\ProgramFiles\opencv\sources\modules\core\include\opencv2\core\core.hpp下,找到其原型聲明:
typedef Size_<int> Size2i; typedef Size2i Size;
Size_ 是個模板類,在這裏Size_<int>表示其類體內部的模板所代表的類型爲int。
那這兩句的意思,就是首先給已知的數據類型Size_<int>起個新名字,叫Size2i。
然後又給已知的數據類型Size2i起個新名字,叫Size。
所以,連起來就是,Size_<int>、Size2i、Size這三個類型名等價。
然後我們追根溯源,找到Size_模板類的定義:
- //-----------------------------------【Size_類中文註釋版源代碼】----------------------------
- // 代碼作用:作爲尺寸相關數據結構的Size_ 模板類
- // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼
- // OpenCV源代碼版本:2.4.8
- // 源碼路徑:…\opencv\sources\modules\core\include\opencv2\core\core.hpp
- // 源文件中如下代碼的起始行數:816行
- // 中文註釋by淺墨
- //--------------------------------------------------------------------------------------------------------
- template<typename _Tp> class Size_
- {
- public:
- typedef _Tp value_type;
- //不同的構造函數定義
- Size_();
- Size_(_Tp _width, _Tp _height);
- Size_(const Size_& sz);
- Size_(const CvSize& sz);
- Size_(const CvSize2D32f& sz);
- Size_(const Point_<_Tp>& pt);
- Size_& operator = (const Size_& sz);
- //區域(width*height)
- _Tp area() const;
- //轉化另一種數據類型。
- template<typename_Tp2> operator Size_<_Tp2>() const;
- //轉換爲舊式的OpenCV類型.
- operator CvSize() const;
- operator CvSize2D32f() const;
- _Tp width, height; //寬度和高度,常用屬性
- };
//-----------------------------------【Size_類中文註釋版源代碼】---------------------------- // 代碼作用:作爲尺寸相關數據結構的Size_ 模板類 // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼 // OpenCV源代碼版本:2.4.8 // 源碼路徑:…\opencv\sources\modules\core\include\opencv2\core\core.hpp // 源文件中如下代碼的起始行數:816行 // 中文註釋by淺墨 //-------------------------------------------------------------------------------------------------------- template<typename _Tp> class Size_ { public: typedef _Tp value_type; //不同的構造函數定義 Size_(); Size_(_Tp _width, _Tp _height); Size_(const Size_& sz); Size_(const CvSize& sz); Size_(const CvSize2D32f& sz); Size_(const Point_<_Tp>& pt); Size_& operator = (const Size_& sz); //區域(width*height) _Tp area() const; //轉化另一種數據類型。 template<typename_Tp2> operator Size_<_Tp2>() const; //轉換爲舊式的OpenCV類型. operator CvSize() const; operator CvSize2D32f() const; _Tp width, height; //寬度和高度,常用屬性 };
可以看到Size_模板類的內部又是重載了一些構造函數以滿足我們的需要,其中,我們用得最多的是如下這個構造函數:
Size_(_Tp _width, _Tp _height);
另外,代碼末尾定義了模板類型的寬度和高度:
_Tp width, height; //寬度和高度
於是我們可以用XXX.width和XXX.height來分別表示其寬度和高度。
給大家一個示例,方便理解:
Size(5, 5);//構造出的Size寬度和高度都爲5,即XXX.width和XXX.height都爲5
<4>OpenCV中blur函數源碼剖析
我們可以在OpenCV的安裝路徑的\sources\modules\imgproc\src下的smooth.cpp源文件中找到blur的源代碼。對應於淺墨將OpenCV 2.4.8安裝在D:\Program Files\opencv下,那麼,smooth.cpp文件就在D:\ProgramFiles\opencv\sources\modules\imgproc\src路徑下。
一起來看一下OpenCV中blur函數定義的真面目:
- //-----------------------------------【blur()函數中文註釋版源代碼】----------------------------
- // 代碼作用:進行blur均值濾波操作的函數
- // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼
- // OpenCV源代碼版本:2.4.8
- // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp
- // 源文件中如下代碼的起始行數:738行
- // 中文註釋by淺墨
- //--------------------------------------------------------------------------------------------------------
- void cv::blur(InputArray src, OutputArray dst,
- Size ksize, Point anchor, int borderType )
- {
- //調用boxFilter函數進行處理
- boxFilter( src, dst, -1, ksize, anchor, true, borderType );
- }
//-----------------------------------【blur()函數中文註釋版源代碼】---------------------------- // 代碼作用:進行blur均值濾波操作的函數 // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼 // OpenCV源代碼版本:2.4.8 // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp // 源文件中如下代碼的起始行數:738行 // 中文註釋by淺墨 //-------------------------------------------------------------------------------------------------------- void cv::blur(InputArray src, OutputArray dst, Size ksize, Point anchor, int borderType ) { //調用boxFilter函數進行處理 boxFilter( src, dst, -1, ksize, anchor, true, borderType ); }
可以看到在blur函數內部就是調用了一個boxFilter函數,且第六個參數爲true,即我們上文所說的normalize=true,即均值濾波是均一化後的方框濾波。
<5>OpenCV中GaussianBlur函數源碼剖析
最後,我們看一下OpenCV中GaussianBlur函數源代碼:
- //-----------------------------------【GaussianBlur()函數中文註釋版源代碼】-----------------------
- // 代碼作用:封裝高斯濾波的GaussianBlur()函數
- // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼
- // OpenCV源代碼版本:2.4.8
- // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp
- // 源文件中如下代碼的起始行數:832行
- // 中文註釋by淺墨
- //--------------------------------------------------------------------------------------------------------
- void cv::GaussianBlur( InputArray _src,OutputArray _dst, Size ksize,
- double sigma1, doublesigma2,
- int borderType )
- {
- //拷貝形參Mat數據到臨時變量,用於稍後的操作
- Mat src = _src.getMat();
- _dst.create( src.size(), src.type() );
- Mat dst =_dst.getMat();
- //處理邊界選項不爲BORDER_CONSTANT時的情況
- if( borderType != BORDER_CONSTANT )
- {
- if( src.rows == 1 )
- ksize.height = 1;
- if( src.cols == 1 )
- ksize.width = 1;
- }
- //若ksize長寬都爲1,將源圖拷貝給目標圖
- if( ksize.width == 1 && ksize.height == 1 )
- {
- src.copyTo(dst);
- return;
- }
- //若之前有過HAVE_TEGRA_OPTIMIZATION優化選項的定義,則執行宏體中的tegra優化版函數並返回
- #ifdef HAVE_TEGRA_OPTIMIZATION
- if(sigma1 == 0 && sigma2 == 0 && tegra::gaussian(src,dst, ksize, borderType))
- return;
- #endif
- //如果HAVE_IPP&& (IPP_VERSION_MAJOR >= 7爲真,則執行宏體中語句
- #if defined HAVE_IPP &&(IPP_VERSION_MAJOR >= 7)
- if(src.type() == CV_32FC1 && sigma1 == sigma2 &&ksize.width == ksize.height && sigma1 != 0.0 )
- {
- IppiSize roi = {src.cols, src.rows};
- int bufSize = 0;
- ippiFilterGaussGetBufferSize_32f_C1R(roi, ksize.width, &bufSize);
- AutoBuffer<uchar> buf(bufSize+128);
- if( ippiFilterGaussBorder_32f_C1R((const Ipp32f *)src.data,(int)src.step,
- (Ipp32f *)dst.data, (int)dst.step,
- roi,ksize.width, (Ipp32f)sigma1,
- (IppiBorderType)borderType, 0.0,
- alignPtr(&buf[0],32)) >= 0 )
- return;
- }
- #endif
- //調動濾波引擎,正式進行高斯濾波操作
- Ptr<FilterEngine> f = createGaussianFilter( src.type(), ksize,sigma1, sigma2, borderType );
- f->apply( src, dst );
- }
//-----------------------------------【GaussianBlur()函數中文註釋版源代碼】----------------------- // 代碼作用:封裝高斯濾波的GaussianBlur()函數 // 說明:以下代碼爲來自於計算機開源視覺庫OpenCV的官方源代碼 // OpenCV源代碼版本:2.4.8 // 源碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp // 源文件中如下代碼的起始行數:832行 // 中文註釋by淺墨 //-------------------------------------------------------------------------------------------------------- void cv::GaussianBlur( InputArray _src,OutputArray _dst, Size ksize, double sigma1, doublesigma2, int borderType ) { //拷貝形參Mat數據到臨時變量,用於稍後的操作 Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); Mat dst =_dst.getMat(); //處理邊界選項不爲BORDER_CONSTANT時的情況 if( borderType != BORDER_CONSTANT ) { if( src.rows == 1 ) ksize.height = 1; if( src.cols == 1 ) ksize.width = 1; } //若ksize長寬都爲1,將源圖拷貝給目標圖 if( ksize.width == 1 && ksize.height == 1 ) { src.copyTo(dst); return; } //若之前有過HAVE_TEGRA_OPTIMIZATION優化選項的定義,則執行宏體中的tegra優化版函數並返回 #ifdef HAVE_TEGRA_OPTIMIZATION if(sigma1 == 0 && sigma2 == 0 && tegra::gaussian(src,dst, ksize, borderType)) return; #endif //如果HAVE_IPP&& (IPP_VERSION_MAJOR >= 7爲真,則執行宏體中語句 #if defined HAVE_IPP &&(IPP_VERSION_MAJOR >= 7) if(src.type() == CV_32FC1 && sigma1 == sigma2 &&ksize.width == ksize.height && sigma1 != 0.0 ) { IppiSize roi = {src.cols, src.rows}; int bufSize = 0; ippiFilterGaussGetBufferSize_32f_C1R(roi, ksize.width, &bufSize); AutoBuffer<uchar> buf(bufSize+128); if( ippiFilterGaussBorder_32f_C1R((const Ipp32f *)src.data,(int)src.step, (Ipp32f *)dst.data, (int)dst.step, roi,ksize.width, (Ipp32f)sigma1, (IppiBorderType)borderType, 0.0, alignPtr(&buf[0],32)) >= 0 ) return; } #endif //調動濾波引擎,正式進行高斯濾波操作 Ptr<FilterEngine> f = createGaussianFilter( src.type(), ksize,sigma1, sigma2, borderType ); f->apply( src, dst ); }
嗯,今天的源碼解析就到這裏吧,原理部分學完,深入OpenCV源碼部分也學完,相信大家應該對OpenCV中的線性濾波有了比較詳細的認識,已經躍躍欲試想看這個幾個函數用起來可以得出什麼效果了。
四、圖像線性濾波綜合示例程序
這一部分的內容就是爲了大家能快速上手boxFilter、blur和GaussianBlur這三個函數所準備的。還等什麼呢,開始吧。
<1>boxFilter函數——方框濾波
boxFilter的函數作用是使用方框濾波(box filter)來模糊一張圖片,由src輸入,dst輸出。
函數原型如下:
- C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
參數詳解如下:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該爲CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。
- 第三個參數,int類型的ddepth,輸出圖像的深度,-1代表使用原圖深度,即src.depth()。
- 第四個參數,Size類型的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 爲像素寬度, h爲像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第五個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心爲錨點,所以默認值Point(-1,-1)表示這個錨點在覈的中心。
- 第六個參數,bool類型的normalize,默認值爲true,一個標識符,表示內核是否被其區域歸一化(normalized)了。
- 第七個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
調用代碼示範如下:
//載入原圖 Mat image=imread("2.jpg"); //進行均值濾波操作 Mat out; boxFilter(image, out, -1,Size(5, 5));
用上面三句核心代碼架起來的完整程序代碼:
- //-----------------------------------【頭文件包含部分】---------------------------------------
- // 描述:包含程序所依賴的頭文件
- //----------------------------------------------------------------------------------------------
- #include "opencv2/core/core.hpp"
- #include"opencv2/highgui/highgui.hpp"
- #include"opencv2/imgproc/imgproc.hpp"
- //-----------------------------------【命名空間聲明部分】---------------------------------------
- // 描述:包含程序所使用的命名空間
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函數】--------------------------------------------
- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
- //-----------------------------------------------------------------------------------------------
- int main( )
- {
- //載入原圖
- Mat image=imread("2.jpg");
- //創建窗口
- namedWindow("均值濾波【原圖】" );
- namedWindow("均值濾波【效果圖】");
- //顯示原圖
- imshow("均值濾波【原圖】", image );
- //進行濾波操作
- Mat out;
- boxFilter(image, out, -1,Size(5, 5));
- //顯示效果圖
- imshow("均值濾波【效果圖】" ,out );
- waitKey(0 );
- }
//-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include "opencv2/core/core.hpp" #include"opencv2/highgui/highgui.hpp" #include"opencv2/imgproc/imgproc.hpp" //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原圖 Mat image=imread("2.jpg"); //創建窗口 namedWindow("均值濾波【原圖】" ); namedWindow("均值濾波【效果圖】"); //顯示原圖 imshow("均值濾波【原圖】", image ); //進行濾波操作 Mat out; boxFilter(image, out, -1,Size(5, 5)); //顯示效果圖 imshow("均值濾波【效果圖】" ,out ); waitKey(0 ); }
運行效果圖(內核大小Size(5, 5)):
<2>blur函數——均值濾波
blur的作用是對輸入的圖像src進行均值濾波後用dst輸出。
函數原型如下:
- C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
參數詳解如下:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。該函數對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該爲CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片爲模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型(對Size類型稍後有講解)的ksize,內核的大小。一般這樣寫Size( w,h )來表示內核的大小( 其中,w 爲像素寬度, h爲像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- 第四個參數,Point類型的anchor,表示錨點(即被平滑的那個點),注意他有默認值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心爲錨點,所以默認值Point(-1,-1)表示這個錨點在覈的中心。
- 第五個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。有默認值BORDER_DEFAULT,我們一般不去管它。
調用代碼示範:
//載入原圖 Mat image=imread("1.jpg"); //進行均值濾波操作 Mat out; blur(image, out, Size(7, 7));
爲了大家的理解和應用方便,還是給出用上面三句核心代碼架起來完整程序的代碼:
- //-----------------------------------【頭文件包含部分】---------------------------------------
- // 描述:包含程序所依賴的頭文件
- //----------------------------------------------------------------------------------------------
- #include "opencv2/core/core.hpp"
- #include"opencv2/highgui/highgui.hpp"
- #include"opencv2/imgproc/imgproc.hpp"
- #include <stdio.h>
- //-----------------------------------【命名空間聲明部分】---------------------------------------
- // 描述:包含程序所使用的命名空間
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函數】--------------------------------------------
- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
- //-----------------------------------------------------------------------------------------------
- int main( )
- {
- //載入原圖
- Mat image=imread("1.jpg");
- //創建窗口
- namedWindow("均值濾波【原圖】" );
- namedWindow("均值濾波【效果圖】");
- //顯示原圖
- imshow("均值濾波【原圖】", image );
- //進行濾波操作
- Mat out;
- blur(image, out, Size(7, 7));
- //顯示效果圖
- imshow("均值濾波【效果圖】" ,out );
- waitKey(0 );
- }
//-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include "opencv2/core/core.hpp" #include"opencv2/highgui/highgui.hpp" #include"opencv2/imgproc/imgproc.hpp" #include <stdio.h> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原圖 Mat image=imread("1.jpg"); //創建窗口 namedWindow("均值濾波【原圖】" ); namedWindow("均值濾波【效果圖】"); //顯示原圖 imshow("均值濾波【原圖】", image ); //進行濾波操作 Mat out; blur(image, out, Size(7, 7)); //顯示效果圖 imshow("均值濾波【效果圖】" ,out ); waitKey(0 ); }
運行效果圖(內核大小Size(7, 7)):
<3>GaussianBlur函數——高斯濾波
GaussianBlur函數的作用是用高斯濾波器來模糊一張圖片,對輸入的圖像src進行高斯濾波後用dst輸出。
函數原型如下:
- C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
參數詳解如下:
- 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。它可以是單獨的任意通道數的圖片,但需要注意,圖片深度應該爲CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
- 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。比如可以用Mat::Clone,以源圖片爲模板,來初始化得到如假包換的目標圖。
- 第三個參數,Size類型的ksize高斯內核的大小。其中ksize.width和ksize.height可以不同,但他們都必須爲正數和奇數。或者,它們可以是零的,它們都是由sigma計算而來。
- 第四個參數,double類型的sigmaX,表示高斯核函數在X方向的的標準偏差。
- 第五個參數,double類型的sigmaY,表示高斯核函數在Y方向的的標準偏差。若sigmaY爲零,就將它設爲sigmaX,如果sigmaX和sigmaY都是0,那麼就由ksize.width和ksize.height計算出來。
- 爲了結果的正確性着想,最好是把第三個參數Size,第四個參數sigmaX和第五個參數sigmaY全部指定到。
- 第六個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。注意它有默認值BORDER_DEFAULT。
調用示例:
//載入原圖 Mat image=imread("1.jpg"); //進行濾波操作 Mat out; GaussianBlur( image, out, Size( 5, 5 ), 0, 0 );
用上面三句核心代碼架起來的完整程序代碼:
- //-----------------------------------【頭文件包含部分】---------------------------------------
- // 描述:包含程序所依賴的頭文件
- //----------------------------------------------------------------------------------------------
- #include "opencv2/core/core.hpp"
- #include"opencv2/highgui/highgui.hpp"
- #include"opencv2/imgproc/imgproc.hpp"
- #include <stdio.h>
- //-----------------------------------【命名空間聲明部分】---------------------------------------
- // 描述:包含程序所使用的命名空間
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函數】--------------------------------------------
- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
- //-----------------------------------------------------------------------------------------------
- int main( )
- {
- //載入原圖
- Mat image=imread("1.jpg");
- //創建窗口
- namedWindow("均值濾波【原圖】" );
- namedWindow("均值濾波【效果圖】");
- //顯示原圖
- imshow("均值濾波【原圖】", image );
- //進行均值濾波操作
- Mat out;
- GaussianBlur(image, out, Size( 3, 3 ), 0, 0 );
- //顯示效果圖
- imshow("均值濾波【效果圖】" ,out );
- waitKey(0 );
- }
//-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include "opencv2/core/core.hpp" #include"opencv2/highgui/highgui.hpp" #include"opencv2/imgproc/imgproc.hpp" #include <stdio.h> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace cv; //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始 //----------------------------------------------------------------------------------------------- int main( ) { //載入原圖 Mat image=imread("1.jpg"); //創建窗口 namedWindow("均值濾波【原圖】" ); namedWindow("均值濾波【效果圖】"); //顯示原圖 imshow("均值濾波【原圖】", image ); //進行均值濾波操作 Mat out; GaussianBlur(image, out, Size( 3, 3 ), 0, 0 ); //顯示效果圖 imshow("均值濾波【效果圖】" ,out ); waitKey(0 ); }
運行效果圖(內核大小Size(5, 5)):
依然是每篇文章都會配給大家的一個詳細註釋的博文配套示例程序,把這篇文章中介紹的知識點以代碼爲載體,展現給大家。
這個示例程序中可以用軌跡條來控制三種線性濾波的核參數值,通過滑動滾動條,就可以控制圖像在三種線性濾波下的模糊度,有一定的可玩性。廢話不多說,上代碼吧:
- //-----------------------------------【程序說明】----------------------------------------------
- // 程序名稱::【OpenCV入門教程之八】線性濾波專場:方框濾波、均值濾波與高斯濾波 配套源碼
- // 開發所用OpenCV版本:2.4.8
- // 2014年3月31 日 Create by 淺墨
- //------------------------------------------------------------------------------------------------
- //-----------------------------------【頭文件包含部分】---------------------------------------
- // 描述:包含程序所依賴的頭文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/core/core.hpp>
- #include<opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <iostream>
- //-----------------------------------【命名空間聲明部分】---------------------------------------
- // 描述:包含程序所使用的命名空間
- //-----------------------------------------------------------------------------------------------
- using namespace std;
- using namespace cv;
- //-----------------------------------【全局變量聲明部分】--------------------------------------
- // 描述:全局變量聲明
- //-----------------------------------------------------------------------------------------------
- Mat g_srcImage,g_dstImage1,g_dstImage2,g_dstImage3;//存儲圖片的Mat類型
- int g_nBoxFilterValue=3; //方框濾波參數值
- int g_nMeanBlurValue=3; //均值濾波參數值
- int g_nGaussianBlurValue=3; //高斯濾波參數值
- //-----------------------------------【全局函數聲明部分】--------------------------------------
- // 描述:全局函數聲明
- //-----------------------------------------------------------------------------------------------
- //軌跡條的回調函數
- static void on_BoxFilter(int, void *); //方框濾波
- static void on_MeanBlur(int, void *); //均值濾波
- static void on_GaussianBlur(int, void *); //高斯濾波
- //-----------------------------------【main( )函數】--------------------------------------------
- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始
- //-----------------------------------------------------------------------------------------------
- int main( )
- {
- //改變console字體顏色
- system("color5E");
- //載入原圖
- g_srcImage= imread( "1.jpg", 1 );
- if(!g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~!\n"); return false; }
- //克隆原圖到三個Mat類型中
- g_dstImage1= g_srcImage.clone( );
- g_dstImage2= g_srcImage.clone( );
- g_dstImage3= g_srcImage.clone( );
- //顯示原圖
- namedWindow("【<0>原圖窗口】", 1);
- imshow("【<0>原圖窗口】",g_srcImage);
- //=================【<1>方框濾波】==================
- //創建窗口
- namedWindow("【<1>方框濾波】", 1);
- //創建軌跡條
- createTrackbar("內核值:", "【<1>方框濾波】",&g_nBoxFilterValue, 40,on_BoxFilter );
- on_MeanBlur(g_nBoxFilterValue,0);
- imshow("【<1>方框濾波】", g_dstImage1);
- //================================================
- //=================【<2>均值濾波】==================
- //創建窗口
- namedWindow("【<2>均值濾波】", 1);
- //創建軌跡條
- createTrackbar("內核值:", "【<2>均值濾波】",&g_nMeanBlurValue, 40,on_MeanBlur );
- on_MeanBlur(g_nMeanBlurValue,0);
- //================================================
- //=================【<3>高斯濾波】=====================
- //創建窗口
- namedWindow("【<3>高斯濾波】", 1);
- //創建軌跡條
- createTrackbar("內核值:", "【<3>高斯濾波】",&g_nGaussianBlurValue, 40,on_GaussianBlur );
- on_GaussianBlur(g_nGaussianBlurValue,0);
- //================================================
- //輸出一些幫助信息
- cout<<endl<<"\t嗯。好了,請調整滾動條觀察圖像效果~\n\n"
- <<"\t按下“q”鍵時,程序退出~!\n"
- <<"\n\n\t\t\t\tby淺墨";
- //按下“q”鍵時,程序退出
- while(char(waitKey(1))!= 'q') {}
- return 0;
- }
- //-----------------------------【on_BoxFilter( )函數】------------------------------------
- // 描述:方框濾波操作的回調函數
- //-----------------------------------------------------------------------------------------------
- static void on_BoxFilter(int, void *)
- {
- //方框濾波操作
- boxFilter(g_srcImage, g_dstImage1, -1,Size( g_nBoxFilterValue+1, g_nBoxFilterValue+1));
- //顯示窗口
- imshow("【<1>方框濾波】", g_dstImage1);
- }
- //-----------------------------【on_MeanBlur( )函數】------------------------------------
- // 描述:均值濾波操作的回調函數
- //-----------------------------------------------------------------------------------------------
- static void on_MeanBlur(int, void *)
- {
- //均值濾波操作
- blur(g_srcImage, g_dstImage2, Size( g_nMeanBlurValue+1, g_nMeanBlurValue+1),Point(-1,-1));
- //顯示窗口
- imshow("【<2>均值濾波】", g_dstImage2);
- }
- //-----------------------------【on_GaussianBlur( )函數】------------------------------------
- // 描述:高斯濾波操作的回調函數
- //-----------------------------------------------------------------------------------------------
- static void on_GaussianBlur(int, void *)
- {
- //高斯濾波操作
- GaussianBlur(g_srcImage, g_dstImage3, Size( g_nGaussianBlurValue*2+1,g_nGaussianBlurValue*2+1 ), 0, 0);
- //顯示窗口
- imshow("【<3>高斯濾波】", g_dstImage3);
- }
//-----------------------------------【程序說明】---------------------------------------------- // 程序名稱::【OpenCV入門教程之八】線性濾波專場:方框濾波、均值濾波與高斯濾波 配套源碼 // 開發所用OpenCV版本:2.4.8 // 2014年3月31 日 Create by 淺墨 //------------------------------------------------------------------------------------------------ //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> //-----------------------------------【命名空間聲明部分】--------------------------------------- // 描述:包含程序所使用的命名空間 //----------------------------------------------------------------------------------------------- using namespace std; using namespace cv; //-----------------------------------【全局變量聲明部分】-------------------------------------- // 描述:全局變量聲明 //----------------------------------------------------------------------------------------------- Mat g_srcImage,g_dstImage1,g_dstImage2,g_dstImage3;//存儲圖片的Mat類型 int g_nBoxFilterValue=3; //方框濾波參數值 int g_nMeanBlurValue=3; //均值濾波參數值 int g_nGaussianBlurValue=3; //高斯濾波參數值 //-----------------------------------【全局函數聲明部分】-------------------------------------- // 描述:全局函數聲明 //----------------------------------------------------------------------------------------------- //軌跡條的回調函數 static void on_BoxFilter(int, void *); //方框濾波 static void on_MeanBlur(int, void *); //均值濾波 static void on_GaussianBlur(int, void *); //高斯濾波 //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制檯應用程序的入口函數,我們的程序從這裏開始 //----------------------------------------------------------------------------------------------- int main( ) { //改變console字體顏色 system("color5E"); //載入原圖 g_srcImage= imread( "1.jpg", 1 ); if(!g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~!\n"); return false; } //克隆原圖到三個Mat類型中 g_dstImage1= g_srcImage.clone( ); g_dstImage2= g_srcImage.clone( ); g_dstImage3= g_srcImage.clone( ); //顯示原圖 namedWindow("【<0>原圖窗口】", 1); imshow("【<0>原圖窗口】",g_srcImage); //=================【<1>方框濾波】================== //創建窗口 namedWindow("【<1>方框濾波】", 1); //創建軌跡條 createTrackbar("內核值:", "【<1>方框濾波】",&g_nBoxFilterValue, 40,on_BoxFilter ); on_MeanBlur(g_nBoxFilterValue,0); imshow("【<1>方框濾波】", g_dstImage1); //================================================ //=================【<2>均值濾波】================== //創建窗口 namedWindow("【<2>均值濾波】", 1); //創建軌跡條 createTrackbar("內核值:", "【<2>均值濾波】",&g_nMeanBlurValue, 40,on_MeanBlur ); on_MeanBlur(g_nMeanBlurValue,0); //================================================ //=================【<3>高斯濾波】===================== //創建窗口 namedWindow("【<3>高斯濾波】", 1); //創建軌跡條 createTrackbar("內核值:", "【<3>高斯濾波】",&g_nGaussianBlurValue, 40,on_GaussianBlur ); on_GaussianBlur(g_nGaussianBlurValue,0); //================================================ //輸出一些幫助信息 cout<<endl<<"\t嗯。好了,請調整滾動條觀察圖像效果~\n\n" <<"\t按下“q”鍵時,程序退出~!\n" <<"\n\n\t\t\t\tby淺墨"; //按下“q”鍵時,程序退出 while(char(waitKey(1))!= 'q') {} return 0; } //-----------------------------【on_BoxFilter( )函數】------------------------------------ // 描述:方框濾波操作的回調函數 //----------------------------------------------------------------------------------------------- static void on_BoxFilter(int, void *) { //方框濾波操作 boxFilter(g_srcImage, g_dstImage1, -1,Size( g_nBoxFilterValue+1, g_nBoxFilterValue+1)); //顯示窗口 imshow("【<1>方框濾波】", g_dstImage1); } //-----------------------------【on_MeanBlur( )函數】------------------------------------ // 描述:均值濾波操作的回調函數 //----------------------------------------------------------------------------------------------- static void on_MeanBlur(int, void *) { //均值濾波操作 blur(g_srcImage, g_dstImage2, Size( g_nMeanBlurValue+1, g_nMeanBlurValue+1),Point(-1,-1)); //顯示窗口 imshow("【<2>均值濾波】", g_dstImage2); } //-----------------------------【on_GaussianBlur( )函數】------------------------------------ // 描述:高斯濾波操作的回調函數 //----------------------------------------------------------------------------------------------- static void on_GaussianBlur(int, void *) { //高斯濾波操作 GaussianBlur(g_srcImage, g_dstImage3, Size( g_nGaussianBlurValue*2+1,g_nGaussianBlurValue*2+1 ), 0, 0); //顯示窗口 imshow("【<3>高斯濾波】", g_dstImage3); }
最後是一些運行截圖,原圖:
方框濾波:
均值濾波:
高斯濾波:
本篇文章到這裏就基本結束了,最後放出文章配套示例程序的打包下載地址(共四個程序),
本篇文章的配套源代碼請點擊這裏下載:
OK,今天的內容大概就是這些,我們下篇文章見:)