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
有了上述兩部分所必需的權重因子之后,那么具體的雙邊濾波的實現即與普通的高斯濾波無異。主要部分代碼如下述:
- 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開啟