引导滤波和双边滤波
雙邊濾波
雙邊濾波很有名,使用廣泛,簡(jiǎn)單的說就是一種同時(shí)考慮了像素空間差異與強(qiáng)度差異的濾波器,因此具有保持圖像邊緣的特性。
先看看我們熟悉的高斯濾波器
其中W是權(quán)重,i和j是像素索引,K是歸一化常量。公式中可以看出,權(quán)重只和像素之間的空間距離有關(guān)系,無論圖像的內(nèi)容是什么,都有相同的濾波效果。
再來看看雙邊濾波器,它只是在原有高斯函數(shù)的基礎(chǔ)上加了一項(xiàng),如下
其中 I 是像素的強(qiáng)度值,所以在強(qiáng)度差距大的地方(邊緣),權(quán)重會(huì)減小,濾波效應(yīng)也就變小。總體而言,在像素強(qiáng)度變換不大的區(qū)域,雙邊濾波有類似于高斯濾波的效果,而在圖像邊緣等強(qiáng)度梯度較大的地方,可以保持梯度。
引導(dǎo)濾波
引導(dǎo)濾波是近三年才出現(xiàn)的濾波技術(shù),知道的人還不多。它與雙邊濾波最大的相似之處,就是同樣具有保持邊緣特性。在引導(dǎo)濾波的定義中,用到了局部線性模型,至于該模型,可以暫時(shí)用下圖簡(jiǎn)單的理解
該模型認(rèn)為,某函數(shù)上一點(diǎn)與其鄰近部分的點(diǎn)成線性關(guān)系,一個(gè)復(fù)雜的函數(shù)就可以用很多局部的線性函數(shù)來表示,當(dāng)需要求該函數(shù)上某一點(diǎn)的值時(shí),只需計(jì)算所有包含該點(diǎn)的線性函數(shù)的值并做平均即可。這種模型,在表示非解析函數(shù)上,非常有用。
同理,我們可以認(rèn)為圖像是一個(gè)二維函數(shù),而且沒法寫出解析表達(dá)式,因此我們假設(shè)該函數(shù)的輸出與輸入在一個(gè)二維窗口內(nèi)滿足線性關(guān)系,如下
其中,q是輸出像素的值,I是輸入圖像的值,i和k是像素索引,a和b是當(dāng)窗口中心位于k時(shí)該線性函數(shù)的系數(shù)。其實(shí),輸入圖像不一定是待濾波的圖像本身,也可以是其他圖像即引導(dǎo)圖像,這也是為何稱為引導(dǎo)濾波的原因。對(duì)上式兩邊取梯度,可以得到
即當(dāng)輸入圖像I有梯度時(shí),輸出q也有類似的梯度,現(xiàn)在可以解釋為什么引導(dǎo)濾波有邊緣保持特性了。
下一步是求出線性函數(shù)的系數(shù),也就是線性回歸,即希望擬合函數(shù)的輸出值與真實(shí)值p之間的差距最小,也就是讓下式最小
這里p只能是待濾波圖像,并不像I那樣可以是其他圖像。同時(shí),a之前的系數(shù)(以后都寫為e)用于防止求得的a過大,也是調(diào)節(jié)濾波器濾波效果的重要參數(shù)。通過最小二乘法,我們可以得到
其中,是I在窗口w_k中的平均值,是I在窗口w_k中的方差,是窗口w_k中像素的數(shù)量,是待濾波圖像p在窗口w_k中的均值。
在計(jì)算每個(gè)窗口的線性系數(shù)時(shí),我們可以發(fā)現(xiàn)一個(gè)像素會(huì)被多個(gè)窗口包含,也就是說,每個(gè)像素都由多個(gè)線性函數(shù)所描述。因此,如之前所說,要具體求某一點(diǎn)的輸出值時(shí),只需將所有包含該點(diǎn)的線性函數(shù)值平均即可,如下
這里,w_k是所有包含像素i的窗口,k是其中心位置。
當(dāng)把引導(dǎo)濾波用作邊緣保持濾波器時(shí),往往有 I = p ,如果e=0,顯然a=1, b=0是E(a,b)為最小值的解,從上式可以看出,這時(shí)的濾波器沒有任何作用,將輸入原封不動(dòng)的輸出。如果e>0,在像素強(qiáng)度變化小的區(qū)域(或單色區(qū)域),有a近似于(或等于)0,而b近似于(或等于),即做了一個(gè)加權(quán)均值濾波;而在變化大的區(qū)域,a近似于1,b近似于0,對(duì)圖像的濾波效果很弱,有助于保持邊緣。而e的作用就是界定什么是變化大,什么是變化小。在窗口大小不變的情況下,隨著e的增大,濾波效果越明顯。
在濾波效果上,引導(dǎo)濾波和雙邊濾波差不多,在一些細(xì)節(jié)上,引導(dǎo)濾波較好。引導(dǎo)濾波最大的優(yōu)勢(shì)在于,可以寫出時(shí)間復(fù)雜度與窗口大小無關(guān)的算法(打算在之后的文章中討論),因此在使用大窗口處理圖片時(shí),其效率更高。
關(guān)于引導(dǎo)濾波更多的討論和應(yīng)用,可以參看下面的論文
GuidedFilter_ECCV10.pdf
實(shí)現(xiàn)引導(dǎo)濾波這種算法的關(guān)鍵思想是盒式濾波(box filter),而且必須是通過積分圖來實(shí)現(xiàn)的盒式濾波,否則不可能與窗口大小無關(guān),好在OpenCV的boxFilter函數(shù)滿足這個(gè)要求。
再看看引導(dǎo)濾波的公式
先計(jì)算a_k的分子,Ip 在窗口w_k中的和,再除以窗口中像素的個(gè)數(shù),剛好就是盒式濾波,因此我們可以將輸入的引導(dǎo)圖像 I 和濾波圖像 p 相乘,并對(duì)相乘后的圖像做box filtering,即得第一項(xiàng)的結(jié)果。后面的和分別為?I 和 p 在窗口w_k中均值,因此分別對(duì) I 和 p 進(jìn)行box filtering,再將box filtering之后的結(jié)果相乘即可。實(shí)際上,a_k的分子就是 Ip 在窗口w_k中的協(xié)方差。
接下來計(jì)算a_k的分母部分。是引導(dǎo)圖 I 在窗口w_k中的方差,學(xué)過概率論與數(shù)理統(tǒng)計(jì)的朋友應(yīng)該知道,方差和期望(均值)之間是有關(guān)系的,如下式
因此在計(jì)算 I 的方差時(shí),我們可以先計(jì)算 I*I 的均值,再減去 I 均值的平方即的平方。在方法上,計(jì)算 I*I 的均值和計(jì)算 Ip 的均值是一樣的。最后,對(duì)計(jì)算出來的方差圖像,加上常量e(每個(gè)元素都加e),分母就計(jì)算完了,自然,a_k在所有窗口中的值也就得到了。b_k的計(jì)算太簡(jiǎn)單了,大家都懂的。
注意,我們的計(jì)算都是對(duì)整個(gè)圖像的,以圖像為單位進(jìn)行計(jì)算,所以最后算出的也是兩張圖,a_k的圖(左邊)和b_k的圖(右邊),如下
在圖中可以看到,在邊緣部分或變化劇烈的部分,a的值接近于1(白色),b的值接近為0(黑色),而在變化平坦的區(qū)域,a的值接近0(黑色),b的值為平坦區(qū)域像素的均值。這與上一篇文章中所說的規(guī)律是一致的。
下面看第二個(gè)公式
輸出值q又與兩個(gè)均值有關(guān),分別為a和b在窗口w_i中的均值(不是w_k),所以還是box filtering,我們將上一步得到兩個(gè)圖像都進(jìn)行盒式濾波,得到兩個(gè)新圖:a_i和b_i,然后用a_i乘以引導(dǎo)圖像 I ,再加上b_i,即得最終濾波之后的輸出,如下(左邊為原圖,右邊為濾波之后的圖像,其中濾波窗口半徑為8,e的值為500):
下面是整個(gè)算法的代碼,僅供參考
void guidedFilter(Mat& source, Mat& guided_image, Mat& output, int radius, float epsilon) {CV_Assert(radius >= 2 && epsilon > 0);CV_Assert(source.data != NULL && source.channels() == 1);CV_Assert(guided_image.channels() == 1);CV_Assert(source.rows == guided_image.rows && source.cols == guided_image.cols);Mat guided;if (guided_image.data == source.data){//make a copyguided_image.copyTo(guided);}else{guided = guided_image;}//將輸入擴(kuò)展為32位浮點(diǎn)型,以便以后做乘法Mat source_32f, guided_32f;makeDepth32f(source, source_32f);makeDepth32f(guided, guided_32f);//計(jì)算I*p和I*IMat mat_Ip, mat_I2;multiply(guided_32f, source_32f, mat_Ip);multiply(guided_32f, guided_32f, mat_I2);//計(jì)算各種均值Mat mean_p, mean_I, mean_Ip, mean_I2;Size win_size(2*radius + 1, 2*radius + 1);boxFilter(source_32f, mean_p, CV_32F, win_size);boxFilter(guided_32f, mean_I, CV_32F, win_size);boxFilter(mat_Ip, mean_Ip, CV_32F, win_size);boxFilter(mat_I2, mean_I2, CV_32F, win_size);//計(jì)算Ip的協(xié)方差和I的方差Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);Mat var_I = mean_I2 - mean_I.mul(mean_I);var_I += epsilon;//求a和bMat a, b;divide(cov_Ip, var_I, a);b = mean_p - a.mul(mean_I);//對(duì)包含像素i的所有a、b做平均Mat mean_a, mean_b;boxFilter(a, mean_a, CV_32F, win_size);boxFilter(b, mean_b, CV_32F, win_size);//計(jì)算輸出 (depth == CV_32F)output = mean_a.mul(guided_32f) + mean_b; } void makeDepth32f(Mat& source, Mat& output) {if (source.depth() != CV_32F ) > FLT_EPSILON)source.convertTo(output, CV_32F);elseoutput = source; }
總結(jié)
- 上一篇: 有趣的图像处理算法
- 下一篇: 三维重建-opencv实现sfm