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


是輸入像素值
的加權(quán)和 :




















