最近在看圖像風格化的論文的時候,頻繁遇到 Bilateral Filter。google 一波后,發現並不是什么不得了的東西,但它的思想卻很有借鑒意義。
簡介
Bilateral Filter,中文又稱「雙邊濾波器」。相比以往那些僅僅使用位置信息進行濾波的 filter,Bilateral Filter 還考慮了顏色信息,可以保證邊緣部分不會被過濾。
簡單來說,一般的 filter 都是基於這樣的公式進行濾波的:
其中,\(k_{d}^{-1}{(x)}\) 是權重之和,\(f(\zeta)\) 可以理解為單個像素,\(c(\zeta, x)\) 可以理解為位置權重。
翻譯成程序員可以理解的語言,大概是這樣:
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= +r; j++) {
newpixel += pixel[row+i][col+j] * c[i][j];
k += c[i][j];
}
}
pixel[row][col] = newPixel / k;
高斯函數也屬於這類 filter。
但這種 filter 有一個缺點:各向同性(不知道這個理解對不對)。用這種濾波器,每個點受鄰居的影響是一樣的,即使它跟鄰居像素可能差得比較多,也會被鄰居「同化」(舉個例子:邊緣被「和諧」掉了)。因此,有人提出了 Bilateral Filter。
Bilateral Filter 采用這樣的公式:
對比之前的式子,最大的變化無非是權值中增加了一個 \(s(f(\zeta), f(x))\),這個東西也是權值,不過它不是采用位置信息,而是顏色信息 \(f(\zeta)\)。不管是哪種信息,形勢上來看都是一樣的,但由於增加了顏色權值,卻使濾波的結果有了明顯不同,后面會給出效果圖。
再次翻譯成程序語言:
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= +r; j++) {
newpixel += pixel[row+i][col+j] * c[i][j] * s(pixel[row][col], pixel[row+i][col+j]);
k += c[i][j]*s(pixel[row][col], pixel[row+i][col+j]);
}
}
pixel[row][col] = newPixel / k;
s
函數可以借鑒位置權值的思路。例如,可以采用這種方式定義(當然這個是我自己構造的):
function s(p1, p2) {
return (255-abs(p1-p2)) / 255
}
這樣,差的越多的顏色,所占權值越小。
如果要追求科學嚴謹一點,也不妨仿照高斯核函數的定義:
<br>
代碼實現
理解原理后,實現其實也很簡單,上面給出的偽代碼基本是核心算法了。另外需要注意的是,如果是彩色圖的話,需要對每個通道的顏色值進行濾波。
具體實現可以參考這篇博客:圖像處理之雙邊濾波效果(Bilateral Filtering for Gray and Color Image),或者參考我自己的 demo,當然,我也只是將上面博客的 java 版改成 c++ 而已^0^。
給出幾幅結果圖:
原圖:
高斯模糊:
僅僅用顏色信息濾波:
雙邊濾波:
仔細對比一下,雙邊濾波對邊緣的保留效果比高斯濾波好太多了,這一點從第三幅圖就可以知曉緣由了。
另外!!如果使用高斯核函數來實現雙邊濾波,顏色卷積和的 \(\sigma\) 要取大一點的值,比如:50。否則,由於不同顏色的差值往往比位置差值大出許多(舉個例子:50 和 60 兩種像素值肉眼上看很接近,但卻差出 10,平方一下就是 100),可能導致很相近的像素點權值很小,最后跟沒濾波的效果一樣。
<br>
啟發
Bilateral Filter 的思想是:在位置信息的基礎上加上顏色信息,相當於考慮兩個權值。如果還要考慮其他重要因素,是不是可以再加進一個權值,構成一個三邊濾波器呢?答案當然是可以的,由此,我們可以把很多簡單的濾波器綜合起來形成一個更強大的濾波器。
<br>