最近在看淺墨前輩的OpenCV教程來(lái)做一次復(fù)習(xí)烫沙,其中發(fā)現(xiàn)了一個(gè)挺有趣的之前沒(méi)見(jiàn)過(guò)的算法惨好,叫雙邊濾波算法白筹。這個(gè)算法可以對(duì)圖像進(jìn)行平滑的同時(shí)盡量對(duì)高頻信息進(jìn)行保留(比如邊緣和邊角)仰剿,而相對(duì)低頻的區(qū)域則會(huì)被平滑卒废。感覺(jué)淺墨一書(shū)中講的還是有點(diǎn)不直觀民宿,這里給出我自己的一些理解进胯。
直觀理解
雙邊濾波算法本質(zhì)是基于高斯濾波,目的是解決高斯濾波造成的邊緣模糊粉渠。那么算法的做法就是想辦法去“推斷”出當(dāng)前像素是否是邊緣點(diǎn)或者接近邊緣的點(diǎn)分冈。
我們都知道,對(duì)圖像進(jìn)行空間域?yàn)V波的方法是使用一個(gè)結(jié)構(gòu)元素(核)來(lái)對(duì)原圖像進(jìn)行卷積霸株。比如說(shuō)高斯核像是這樣的:
而這個(gè)結(jié)構(gòu)元素就會(huì)對(duì)原圖像進(jìn)行卷積操作雕沉,從而得到一個(gè)新的圖像,即輸出圖像去件。我們知道坡椒,這個(gè)結(jié)構(gòu)元素是不會(huì)變的。但是尤溜!但是倔叼!但是!在雙邊濾波算法中就不是如此了宫莱。
為了使圖像的邊緣得到保留丈攒,就要根據(jù)當(dāng)前被卷積像素的鄰域進(jìn)行觀察,“推斷”是否是邊緣點(diǎn)和接近邊緣的點(diǎn)授霸。因此巡验,結(jié)構(gòu)元素就會(huì)改變,從而保留邊緣點(diǎn)碘耳。
下面的一組圖中深碱,圖a是原圖像,圖c是輸出藏畅。而中間的圖像是什么呢敷硅?顯然,這是原圖中根據(jù)某個(gè)點(diǎn)的鄰域生成的愉阎,專屬于這個(gè)點(diǎn)的結(jié)構(gòu)元素绞蹦!
可以看到,原圖中顯然有一個(gè)灰度的突變榜旦,這就表示是邊緣幽七。灰度值高的地方不應(yīng)該和灰度低的區(qū)域進(jìn)行混合溅呢,所以澡屡,圖像中接近邊緣的一個(gè)點(diǎn)就會(huì)生成圖b這樣的結(jié)構(gòu)元素猿挚。那么這個(gè)接近邊緣的點(diǎn)在哪里呢?大概就在我標(biāo)出的這個(gè)區(qū)域驶鹉。
而生成這樣的結(jié)構(gòu)元素的方法绩蜻,是將我們?cè)镜母咚购耍c一個(gè)能“推斷”出是否在邊緣點(diǎn)的結(jié)構(gòu)元素相乘室埋,如下圖中間的結(jié)構(gòu)元素
數(shù)學(xué)定義
雙邊濾波器的輸出像素依賴于當(dāng)前被卷積像素的鄰域办绝。i和j是當(dāng)前被卷積像素的坐標(biāo)點(diǎn),k和l是鄰域像素的坐標(biāo)點(diǎn)姚淆。
加權(quán)系數(shù)ω由定義域核和值域核決定孕蝉,是它們的乘積。定義域核就是高斯核腌逢,不解釋
而值域核就是用于“推斷”是否是邊緣點(diǎn)的方法降淮,公式如下
可以看到,它取決于被卷積像素的灰度值和鄰域像素的灰度值的差搏讶。我們知道佳鳖,邊緣會(huì)有較大的灰度變化,而這個(gè)公式就會(huì)使邊緣和邊緣另一邊的區(qū)域生成比較小的權(quán)值窍蓝,與被卷積像素的灰度值類似的區(qū)域會(huì)生成比較大的權(quán)值腋颠,就像之前圖中的一個(gè)“斷崖”繁成。
相乘就得到加權(quán)系數(shù)ω
編程實(shí)現(xiàn)
這里使用滑動(dòng)條來(lái)觀察一下參數(shù)對(duì)輸出的變化吓笙。
/********************************************************************
* Created by 楊幫杰 on 1/25/2019
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: yangbangjie1998@qq.com
* Association: SCAU 華南農(nóng)業(yè)大學(xué)
********************************************************************/
#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#define IMG1 "/home/jacob/圖片/77.jpg"
using namespace cv;
using namespace std;
int g_d = 15;
int g_sigmaColor = 20;
int g_sigmaSpace = 50;
Mat image1;
Mat image2;
void on_Trackbar(int, void*)
{
bilateralFilter(image1, image2, g_d, g_sigmaColor, g_sigmaSpace);
imshow("output", image2);
}
int main()
{
image1= imread(IMG1);
//resize(image1,image1,Size(image1.cols/2, image1.rows/2));
if (!image1.data)
{
cout << "img1 沒(méi)讀到" <<endl;
return 0;
}
image2 = Mat::zeros(image1.rows, image1.cols, image1.type());
bilateralFilter(image1, image2, g_d, g_sigmaColor, g_sigmaSpace);
namedWindow("output");
createTrackbar("核直徑","output", &g_d, 50, on_Trackbar);
createTrackbar("顏色空間方差","output", &g_sigmaColor, 100, on_Trackbar);
createTrackbar("坐標(biāo)空間方差","output", &g_sigmaSpace, 100, on_Trackbar);
imshow("input", image1);
imshow("output", image2);
waitKey();
return 0;
}
明顯,粗糙的皮膚得到了平滑巾腕。這個(gè)算法對(duì)皮膚不好的妹子有奇效→_→面睛!
References:
Bilateral Filtering for Gray and Color Images
OpenCV學(xué)習(xí)筆記(七)中值、雙邊濾波