图像平滑处理(归一化块滤波、高斯滤波、中值滤波、双边滤波)
圖像平滑處理
目標
本教程教您怎樣使用各種線性濾波器對圖像進行平滑處理,相關OpenCV函數如下:
- blur
- GaussianBlur
- medianBlur
- bilateralFilter
原理
Note
???以下原理來源于Richard Szeliski 的著作?Computer Vision: Algorithms and Applications?以及?Learning OpenCV
-
平滑?也稱?模糊, 是一項簡單且使用頻率很高的圖像處理方法。
-
平滑處理的用途有很多, 但是在本教程中我們僅僅關注它減少噪聲的功用 (其他用途在以后的教程中會接觸到)。
-
平滑處理時需要用到一個?濾波器?。 最常用的濾波器是?線性?濾波器,線性濾波處理的輸出像素值 (i.e.?) 是輸入像素值 (i.e.?)的加權和 :
?稱為?核, 它僅僅是一個加權系數。
不妨把?濾波器?想象成一個包含加權系數的窗口,當使用這個濾波器平滑處理圖像時,就把這個窗口滑過圖像。
-
濾波器的種類有很多, 這里僅僅提及最常用的:
歸一化塊濾波器 (Normalized Box Filter)
-
最簡單的濾波器, 輸出像素值是核窗口內像素值的?均值?( 所有像素加權系數相等)
-
核如下:
高斯濾波器 (Gaussian Filter)
-
最有用的濾波器 (盡管不是最快的)。 高斯濾波是將輸入數組的每一個像素點與?高斯內核?卷積將卷積和當作輸出像素值。
-
還記得1維高斯函數的樣子嗎?
假設圖像是1維的,那么觀察上圖,不難發現中間像素的加權系數是最大的, 周邊像素的加權系數隨著它們遠離中間像素的距離增大而逐漸減小。
Note
???2維高斯函數可以表達為 :
其中??為均值 (峰值對應位置),??代表標準差 (變量??和 變量??各有一個均值,也各有一個標準差)
中值濾波器 (Median Filter)
中值濾波將圖像的每個像素用鄰域 (以當前像素為中心的正方形區域)像素的?中值?代替 。
雙邊濾波 (Bilateral Filter)
- 目前我們了解的濾波器都是為了?平滑?圖像, 問題是有些時候這些濾波器不僅僅削弱了噪聲, 連帶著把邊緣也給磨掉了。 為避免這樣的情形 (至少在一定程度上 ), 我們可以使用雙邊濾波。
- 類似于高斯濾波器,雙邊濾波器也給每一個鄰域像素分配一個加權系數。 這些加權系數包含兩個部分, 第一部分加權方式與高斯濾波一樣,第二部分的權重則取決于該鄰域像素與當前像素的灰度差值。
- 詳細的解釋可以查看?鏈接
源碼
-
本程序做什么?
- 裝載一張圖像
- 使用4種不同濾波器 (見原理部分) 并顯示平滑圖像
-
代碼一瞥:
解釋
下面看一看有關平滑的OpenCV函數,其余部分大家已經很熟了。
歸一化塊濾波器:
OpenCV函數?blur?執行了歸一化塊平滑操作。
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ blur( src, dst, Size( i, i ), Point(-1,-1) );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }我們輸入4個實參 (詳細的解釋請參考 Reference):
- src: 輸入圖像
- dst: 輸出圖像
- Size( w,h ): 定義內核大小(?w?像素寬度,?h?像素高度)
- Point(-1, -1): 指定錨點位置(被平滑點), 如果是負值,取核的中心為錨點。
高斯濾波器:
OpenCV函數?GaussianBlur?執行高斯平滑 :
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ GaussianBlur( src, dst, Size( i, i ), 0, 0 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }我們輸入4個實參 (詳細的解釋請參考 Reference):
- src: 輸入圖像
- dst: 輸出圖像
- Size(w, h): 定義內核的大小(需要考慮的鄰域范圍)。??和??必須是正奇數,否則將使用??和??參數來計算內核大小。
- : x 方向標準方差, 如果是??則??使用內核大小計算得到。
- : y 方向標準方差, 如果是??則??使用內核大小計算得到。.
中值濾波器:
OpenCV函數?medianBlur?執行中值濾波操作:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ medianBlur ( src, dst, i );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }我們用了3個參數:
- src: 輸入圖像
- dst: 輸出圖像, 必須與?src?相同類型
- i: 內核大小 (只需一個值,因為我們使用正方形窗口),必須為奇數。
雙邊濾波器
OpenCV函數?bilateralFilter?執行雙邊濾波操作:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ bilateralFilter ( src, dst, i, i*2, i/2 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }我們使用了5個參數:
- src: 輸入圖像
- dst: 輸出圖像
- d: 像素的鄰域直徑
- : 顏色空間的標準方差
- : 坐標空間的標準方差(像素單位)
結果
-
程序顯示了原始圖像(?lena.jpg) 和使用4種濾波器之后的效果圖。
-
這里顯示的是使用?中值濾波?之后的效果圖:
Bilateral Filtering(雙邊濾波)詳解: 轉載:http://blog.csdn.net/bugrunner/article/details/7170471
1. 簡介
圖像平滑是一個重要的操作,而且有多種成熟的算法。這里主要簡單介紹一下Bilateral方法(雙邊濾波),這主要是由于前段時間做了SSAO,需要用bilateral blur 算法進行降噪。Bilateral blur相對于傳統的高斯blur來說很重要的一個特性即可可以保持邊緣(Edge Perseving),這個特點對于一些圖像模糊來說很有用。一般的高斯模糊在進行采樣時主要考慮了像素間的空間距離關系,但是卻并沒有考慮像素值之間的相似程度,因此這樣我們得到的模糊結果通常是整張圖片一團模糊。Bilateral blur的改進就在于在采樣時不僅考慮像素在空間距離上的關系,同時加入了像素間的相似程度考慮,因而可以保持原始圖像的大體分塊進而保持邊緣。在于游戲引擎的post blur算法中,bilateral blur常常被用到,比如對SSAO的降噪。
2. 原理
濾波算法中,目標點上的像素值通常是由其所在位置上的周圍的一個小局部鄰居像素的值所決定。在2D高斯濾波中的具體實現就是對周圍的一定范圍內的像素值分別賦以不同的高斯權重值,并在加權平均后得到當前點的最終結果。而這里的高斯權重因子是利用兩個像素之間的空間距離(在圖像中為2D)關系來生成。通過高斯分布的曲線可以發現,離目標像素越近的點對最終結果的貢獻越大,反之則越小。其公式化的描述一般如下所述:
其中的c即為基于空間距離的高斯權重,而用來對結果進行單位化。
高斯濾波在低通濾波算法中有不錯的表現,但是其卻有另外一個問題,那就是只考慮了像素間的空間位置上的關系,因此濾波的結果會丟失邊緣的信息。這里的邊緣主要是指圖像中主要的不同顏色區域(比如藍色的天空,黑色的頭發等),而Bilateral就是在Gaussian blur中加入了另外的一個權重分部來解決這一問題。Bilateral濾波中對于邊緣的保持通過下述表達式來實現:
其中的s為基于像素間相似程度的高斯權重,同樣用來對結果進行單位化。對兩者進行結合即可以得到基于空間距離、相似程度綜合考量的Bilateral濾波:
上式中的單位化分部綜合了兩種高斯權重于一起而得到,其中的c與s計算可以詳細描述如下:
且有
且有
上述給出的表達式均是在空間上的無限積分,而在像素化的圖像中當然無法這么做,而且也沒必要如此做,因而在使用前需要對其進行離散化。而且也不需要對于每個局部像素從整張圖像上進行加權操作,距離超過一定程度的像素實際上對當前的目標像素影響很小,可以忽略的。限定局部子區域后的離散化公就可以簡化為如下形式:
上述理論公式就構成了Bilateral濾波實現的基礎。為了直觀地了解高斯濾波與雙邊濾波的區別,我們可以從下列圖示中看出依據。假設目標源圖像為下述左右區域分明的帶有噪聲的圖像(由程序自動生成),藍色框的中心即為目標像素所在的位置,那么當前像素處所對應的高斯權重與雙邊權重因子3D可視化后的形狀如后邊兩圖所示:????????????
左圖為原始的噪聲圖像;中間為高斯采樣的權重;右圖為Bilateral采樣的權重。從圖中可以看出Bilateral加入了相似程度分部以后可以將源圖像左側那些跟當前像素差值過大的點給濾去,這樣就很好地保持了邊緣。為了更加形象地觀察兩者間的區別,使用Matlab將該圖在兩種不同方式下的高度圖3D繪制出來,如下:
??
上述三圖從左到右依次為:雙邊濾波,原始圖像,高斯濾波。從高度圖中可以明顯看出Bilateral和Gaussian兩種方法的區別,前者較好地保持了邊緣處的梯度,而在高斯濾波中,由于其在邊緣處的變化是線性的,因而就使用連累的梯度呈現出漸變的狀態,而這表現在圖像中的話就是邊界的丟失(圖像的示例可見于后述)。??????????????????????????????????????????????
3. 代碼實現
有了上述理論以后實現Bilateral Filter就比較簡單了,其實它也與普通的Gaussian Blur沒有太大的區別。這里主要包括3部分的操作: 基于空間距離的權重因子生成;基于相似度的權重因子的生成;最終filter顏色的計算。
3.1 Spatial Weight
這就是通常的Gaussian Blur中使用的計算高斯權重的方法,其主要通過兩個pixel之間的距離并使用如下公式計算而來:
其中的就表示兩個像素間的距離,比如當前像素與其右邊緊鄰的一個像素之間的距離我們就可以用來計算,也即兩個二維向量{0 , 0}以及{0 , 1}之間的歐氏距離。直接計算一個區域上的高斯權重并單位化后就可以進行高斯模糊了。
3.2 Similarity Weight
與基于距離的高斯權重計算類似,只不過此處不再根據兩個pixel之間的空間距離,而是根據其相似程度(或者兩個pixel的值之間的距離)。
其中的表示兩個像素值之間的距離,可以直接使用其灰度值之間的差值或者RGB向量之間的歐氏距離。
3.3 Color Filtering
有了上述兩部分所必需的權重因子之后,那么具體的雙邊濾波的實現即與普通的高斯濾波無異。主要部分代碼如下述:
[cpp]?view plaincopy - UCHAR3?BBColor(int?posX?,?int?posY)??
- {??
- ????int?centerItemIndex?=?posY?*?picWidth4?+?posX?*?3?,?neighbourItemIndex;??
- ????int?weightIndex;??
- ????double?gsAccumWeight?=?0;??
- ????double?accumColor?=?0;??
- ??
- ????//?計算各個采樣點處的Gaussian權重,包括closeness,similarity??
- ????for(int?i?=?-number?;?i?<=?number?;?++i)??
- ????{??
- ????????for(int?j?=?-number?;?j?<=?number?;?++j)??
- ????????{??
- ????????????weightIndex?=?(i?+?number)?*?(number?*?2?+?1)?+?(j?+?number);??
- ????????????neighbourItemIndex?=?min(noiseImageHeight?-?1?,?max(0?,?posY?+?j?*?radius))?*?picWidth4?+??
- ?????????????????????????????min(noiseImageWidth?-?1??,?max(0?,?posX?+?i?*?radius))?*?3;??
- ??????????????
- ????????????pCSWeight[weightIndex]?=?LookupGSWeightTable(pSrcDataBuffer[neighbourItemIndex]?,?pSrcDataBuffer[centerItemIndex]);??
- ????????????pCSWeight[weightIndex]?=?pGSWeight[weightIndex]?*?pGCWeight[weightIndex];??
- ????????????gsAccumWeight?+=?pCSWeight[weightIndex];??
- ????????}??
- ????}??
- ??????
- ????//?單位化權重因子??
- ????gsAccumWeight?=?1?/?gsAccumWeight;??
- ????for(int?i?=?-number?;?i?<=?number?;?++i)??
- ????{??
- ????????for(int?j?=?-number?;?j?<=?number?;?++j)??
- ????????{??
- ????????????weightIndex?=?(i?+?number)?*?(number?*?2?+?1)?+?(j?+?number);??
- ????????????pCSWeight[weightIndex]?*=?gsAccumWeight;??
- ????????}??
- ????}??
- ??????
- ????//?計算最終的顏色并返回??
- ????for(int?i?=?-number?;?i?<=?number?;?++i)??
- ????{??
- ????????for(int?j?=?-number?;?j?<=?number?;?++j)??
- ????????{??
- ????????????weightIndex?=?(i?+?number)?*?(number?*?2?+?1)?+?(j?+?number);??
- ????????????neighbourItemIndex?=?min(noiseImageHeight?-?1?,?max(0?,?posY?+?j?*?radius))?*?picWidth4?+??
- ?????????????????????????????????min(noiseImageWidth?-?1??,?max(0?,?posX?+?i?*?radius))?*?3;??
- ????????????accumColor?+=?pSrcDataBuffer[neighbourItemIndex?+?0]?*?pCSWeight[weightIndex];??
- ????????}??
- ????}??
- ??
- ????return?UCHAR3(accumColor?,?accumColor?,?accumColor);??
- }??
?其中的相似度分部的權重s主要根據兩個Pixel之間的顏色差值計算面來。對于灰度圖而言,這個差值的范圍是可以預知的,即[-255, 255],因而為了提高計算的效率我們可以將該部分權重因子預計算生成并存表,在使用時快速查詢即可。使用上述實現的算法對幾張帶有噪聲的圖像進行濾波后的結果如下所示:
???
????
上圖從左到右分別為:雙邊濾波;原始圖像;高斯濾波。從圖片中可以較為明顯地看出兩種算法的區別,最直觀的感受差別就是使用高斯算法后整張圖片都是一團模糊的狀態;而雙邊濾波則可以較好地保持原始圖像中的區域信息,看起來仍然嘴是嘴、眼是眼(特別是在第一張美女圖像上的效果!看來PS是灰常重要啊~~^o^)。
4. 在SSAO中的使用
在上述實現中的邊緣判定函數主要是通過兩個像素值之間的差異來決定,這也是我們觀察普通圖片的一種普遍感知方式。當然,也可以根據使用的需求情況來使用其它的方式判斷其它定義下的邊緣,比如使用場景的depth或是normal。比如在對SSAO進行濾波時可以直接使用Depth值來行邊緣判斷。首先,設置一個深度的閾值,在作邊緣檢測時比較兩點間的depth差值,如果差值大于閾值,則認為其屬于不同的區域,則此處就應為邊界。使用此方法得到的效果可見于下圖所示:
高斯濾波
雙邊濾波
在得到濾波之后的SSAO圖像之后,與原始圖像進行直接的整合就可以得到最終的渲染效果,如下圖所示:
SSAO關閉
SSAO開啟
總結
以上是生活随笔為你收集整理的图像平滑处理(归一化块滤波、高斯滤波、中值滤波、双边滤波)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 更新词汇至Unigram词表进行识别
- 下一篇: 【OpenCV】5种图像滤波辨析:方框、