Bilateral Filtering(雙邊濾波) for SSAO

本文轉自: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濾波:

上式中的單位化分部綜合了兩種高斯權重於一起而得到,其中的cs計算可以詳細描述如下:

且有

且有

上述給出的表達式均是在空間上的無限積分,而在像素化的圖像中當然無法這麼做,而且也沒必要如此做,因而在使用前需要對其進行離散化。而且也不需要對於每個局部像素從整張圖像上進行加權操作,距離超過一定程度的像素實際上對當前的目標像素影響很小,可以忽略的。限定局部子區域後的離散化公就可以簡化爲如下形式:

上述理論公式就構成了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

有了上述兩部分所必需的權重因子之後,那麼具體的雙邊濾波的實現即與普通的高斯濾波無異。主要部分代碼如下述:

  1. UCHAR3 BBColor(int posX , int posY)  
  2. {  
  3.     int centerItemIndex = posY * picWidth4 + posX * 3 , neighbourItemIndex;  
  4.     int weightIndex;  
  5.     double gsAccumWeight = 0;  
  6.     double accumColor = 0;  
  7.   
  8.     // 計算各個採樣點處的Gaussian權重,包括closeness,similarity  
  9.     for(int i = -number ; i <= number ; ++i)  
  10.     {  
  11.         for(int j = -number ; j <= number ; ++j)  
  12.         {  
  13.             weightIndex = (i + number) * (number * 2 + 1) + (j + number);  
  14.             neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +  
  15.                              min(noiseImageWidth - 1  , max(0 , posX + i * radius)) * 3;  
  16.               
  17.             pCSWeight[weightIndex] = LookupGSWeightTable(pSrcDataBuffer[neighbourItemIndex] , pSrcDataBuffer[centerItemIndex]);  
  18.             pCSWeight[weightIndex] = pGSWeight[weightIndex] * pGCWeight[weightIndex];  
  19.             gsAccumWeight += pCSWeight[weightIndex];  
  20.         }  
  21.     }  
  22.       
  23.     // 單位化權重因子  
  24.     gsAccumWeight = 1 / gsAccumWeight;  
  25.     for(int i = -number ; i <= number ; ++i)  
  26.     {  
  27.         for(int j = -number ; j <= number ; ++j)  
  28.         {  
  29.             weightIndex = (i + number) * (number * 2 + 1) + (j + number);  
  30.             pCSWeight[weightIndex] *= gsAccumWeight;  
  31.         }  
  32.     }  
  33.       
  34.     // 計算最終的顏色並返回  
  35.     for(int i = -number ; i <= number ; ++i)  
  36.     {  
  37.         for(int j = -number ; j <= number ; ++j)  
  38.         {  
  39.             weightIndex = (i + number) * (number * 2 + 1) + (j + number);  
  40.             neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +  
  41.                                  min(noiseImageWidth - 1  , max(0 , posX + i * radius)) * 3;  
  42.             accumColor += pSrcDataBuffer[neighbourItemIndex + 0] * pCSWeight[weightIndex];  
  43.         }  
  44.     }  
  45.   
  46.     return UCHAR3(accumColor , accumColor , accumColor);  
  47. }  

 其中的相似度分部的權重s主要根據兩個Pixel之間的顏色差值計算面來。對於灰度圖而言,這個差值的範圍是可以預知的,即[-255, 255],因而爲了提高計算的效率我們可以將該部分權重因子預計算生成並存表,在使用時快速查詢即可。使用上述實現的算法對幾張帶有噪聲的圖像進行濾波後的結果如下所示:

   

    

上圖從左到右分別爲:雙邊濾波;原始圖像;高斯濾波。從圖片中可以較爲明顯地看出兩種算法的區別,最直觀的感受差別就是使用高斯算法後整張圖片都是一團模糊的狀態;而雙邊濾波則可以較好地保持原始圖像中的區域信息,看起來仍然嘴是嘴、眼是眼(特別是在第一張美女圖像上的效果!看來PS是灰常重要啊~~^o^)。

4. 在SSAO中的使用

在上述實現中的邊緣判定函數主要是通過兩個像素值之間的差異來決定,這也是我們觀察普通圖片的一種普遍感知方式。當然,也可以根據使用的需求情況來使用其它的方式判斷其它定義下的邊緣,比如使用場景的depth或是normal。比如在對SSAO進行濾波時可以直接使用Depth值來行邊緣判斷。首先,設置一個深度的閾值,在作邊緣檢測時比較兩點間的depth差值,如果差值大於閾值,則認爲其屬於不同的區域,則此處就應爲邊界。使用此方法得到的效果可見於下圖所示:

高斯濾波

雙邊濾波

在得到濾波之後的SSAO圖像之後,與原始圖像進行直接的整合就可以得到最終的渲染效果,如下圖所示:

SSAO關閉

SSAO開啓

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章