1. 程式人生 > >【OpenCV入門教程之八】線性鄰域濾波專場 方框濾波 均值濾波與高斯濾波

【OpenCV入門教程之八】線性鄰域濾波專場 方框濾波 均值濾波與高斯濾波

本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。 

寫作當前博文時配套使用的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輸出。

函式原型如下:

[cpp] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=trueint 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函式的原型:

[cpp] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. 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決定了高斯函式的寬度。對於影象處理來說,常用二維零均值離散高斯函式作平滑濾波器。

二維高斯函式為:

2)在OpenCV中使用高斯濾波——GaussianBlur函式

GaussianBlur函式的作用是用高斯濾波器來模糊一張圖片,對輸入的影象src進行高斯濾波後用dst輸出。它將源影象和指定的高斯核函式做卷積運算,並且支援就地過濾(In-placefiltering)。

[cpp] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. 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的開源魅力,對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路徑下。

[cpp] view plaincopyprint?在CODE上檢視程式碼片派生到我的程式碼片
  1. //-----------------------------------【boxFilter()函式中文註釋版原始碼】----------------------------
  2. //     程式碼作用:進行box Filter濾波操作的函式
  3. //     說明:以下程式碼為來自於計算機開源視覺庫OpenCV的官方原始碼
  4. //     OpenCV原始碼版本:2.4.8
  5. //     原始碼路徑:…\opencv\sources\modules\imgproc\src\smooth.cpp