熱線電話(hua):0755-23712116
郵箱(xiang):contact@legoupos.cn
地址:深圳市(shi)寶安區沙井街道后(hou)亭茅洲(zhou)山工業園(yuan)(yuan)工業大廈全至科技創新園(yuan)(yuan)科創大廈2層(ceng)2A
雙邊(bian)濾(lv)波(Bilateral filter)是(shi)一種非線(xian)性(xing)的(de)(de)濾(lv)波方法,是(shi)結合(he)圖像的(de)(de)空間(jian)鄰近(jin)度(du)和(he)像素值相似度(du)的(de)(de)一種折衷(zhong)處理,同(tong)時考慮(lv)空域信(xin)息和(he)灰度(du)相似性(xing),達(da)到(dao)保(bao)邊(bian)去噪的(de)(de)目的(de)(de)。
雙邊濾波器之所以能夠做到(dao)在平滑去噪的同時還能夠很好的保存(cun)邊緣(Edge Preserve),是由(you)于其濾波器的核(he)由(you)兩(liang)個函數生成:
? 一個函數(shu)由像素歐式距離決定(ding)濾波(bo)器(qi)模板的系數(shu)
? 另一個函數(shu)由(you)像素(su)的灰(hui)度差(cha)值決定濾(lv)波器的系(xi)數(shu)
其綜合了高斯濾波器(Gaussian Filter)和截尾均值濾波器(Alpha-Trimmed mean Filter)的特點。高斯濾波器只考慮像素間的歐式距離,其使用的模板系數隨著和窗口中心的距離增大而減小;Alpha截尾均值濾波器則只考慮了像素灰度值之間的差值,去掉
雙邊濾波器使用二維高斯函數生成距離模板,使用一維高斯函數生成值域模板。
距離模板系數的生成公式如(ru)下:
其中,為模板窗口的中心坐標;
為模板窗口的其他系數的坐標;
值域模板系數的(de)生成(cheng)公式如下:
其中,函數表示要處理的圖像,
表示圖像在點
處的像素值;
為模板窗口的中心坐標;
為模板窗口的其他系數的坐標;
將上述兩個模板(ban)相乘就得(de)到了雙邊濾(lv)波器的模板(ban)
這里的實現主要參考OpenCV中的bilateralFilter
實(shi)現,其實(shi)現主要有兩個(ge)優化(hua):
? 使(shi)用(yong)查表的(de)方式計算灰度值模板系(xi)數
? 將二維的模板轉換(huan)為(wei)一維,降低算(suan)法復雜度(du)。
在濾波(bo)之前,首(shou)先將(jiang)灰度值模板(ban)系數(shu)計算出來。
double color_coeff = -0.5 / (color_sigma * color_sigma);
vector<double> _color_weight(channels * 256); // 存放差值的平方
double *color_weight = &_color_weight[0];
for (int i = 0; i < channels * 256; i++)
color_weight[i] = exp(i * i * color_coeff);
灰度值的模板系數計算公式參見上面的公式,是兩個灰度值的差值的平方。這里表的長度是channels * 256
沒有想通,應該255的長度就足夠了。在使用的時候,首先取出模板中心的灰度值val0,然后依次取出模板其他位置的灰度值val,使用abs(val -val0)
的差值從color_weight
查表(biao)得到灰度值模(mo)板的(de)系數。
距離的(de)(de)模板是二維(wei)的(de)(de),這里(li)使用的(de)(de)方法(fa)就i比(bi)較(jiao)巧(qiao)妙(miao),將其化為(wei)了一維(wei)。
vector<double> _space_weight(ksize * ksize); // 空間模板(ban)系(xi)數
vector<int> _space_ofs(ksize * ksize); // 模(mo)板窗口(kou)的(de)坐標
// 生成空間模板
int maxk = 0;
for (int i = -radius; i <= radius; i++)
{
for (int j = -radius; j <= radius; j++)
{
double r = sqrt(i*i + j * j);
if (r > radius)
continue;
space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系(xi)數
space_ofs[maxk++] = i * temp.step + j * channels; // 存(cun)放模板的位置,和(he)模板系數相對應
}
}
使用一維數組存放空間模板系數,同時使用另一個一維數組存放模板位置,和系數相對應。
整(zheng)個代碼的實(shi)現如(ru)下:
void myBilateralFilter(const Mat &src, Mat &dst, int ksize, double space_sigma, double color_sigma)
{
int channels = src.channels();
CV_Assert(channels == 1 || channels == 3);
double space_coeff = -0.5 / (space_sigma * space_sigma);
double color_coeff = -0.5 / (color_sigma * color_sigma);
int radius = ksize / 2;
Mat temp;
copyMakeBorder(src, temp, radius, radius, radius, radius, BorderTypes::BORDER_REFLECT);
vector<double> _color_weight(channels * 256); // 存放差(cha)值的(de)平(ping)方
vector<double> _space_weight(ksize * ksize); // 空間模板系數
vector<int> _space_ofs(ksize * ksize); // 模板窗(chuang)口的坐(zuo)標
double *color_weight = &_color_weight[0];
double *space_weight = &_space_weight[0];
int *space_ofs = &_space_ofs[0];
for (int i = 0; i < channels * 256; i++)
color_weight[i] = exp(i * i * color_coeff);
// 生(sheng)成空間模板
int maxk = 0;
for (int i = -radius; i <= radius; i++)
{
for (int j = -radius; j <= radius; j++)
{
double r = sqrt(i*i + j * j);
if (r > radius)
continue;
space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系數
space_ofs[maxk++] = i * temp.step + j * channels; // 存放模(mo)板(ban)的位(wei)置,和模(mo)板(ban)系(xi)數相對(dui)應
}
}
// 濾波過程
for (int i = 0; i < src.rows; i++)
{
const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;
uchar *dptr = dst.data + i * dst.step;
if (channels == 1)
{
for (int j = 0; j < src.cols; j++)
{
double sum = 0, wsum = 0;
int val0 = sptr[j]; // 模板中(zhong)心位(wei)置(zhi)的像素(su)
for (int k = 0; k < maxk; k++)
{
int val = sptr[j + space_ofs[k]];
double w = space_weight[k] * color_weight[abs(val - val0)]; // 模(mo)板系數 = 空(kong)間系數 * 灰度值系數
sum += val * w;
wsum += w;
}
dptr[j] = (uchar)cvRound(sum / wsum);
}
}
else if (channels == 3)
{
for (int j = 0; j < src.cols * 3; j+=3)
{
double sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
int b0 = sptr[j];
int g0 = sptr[j + 1];
int r0 = sptr[j + 2];
for (int k = 0; k < maxk; k++)
{
const uchar *sptr_k = sptr + j + space_ofs[k];
int b = sptr_k[0];
int g = sptr_k[1];
int r = sptr_k[2];
double w = space_weight[k] * color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];
sum_b += b * w;
sum_g += g * w;
sum_r += r * w;
wsum += w;
}
wsum = 1.0f / wsum;
b0 = cvRound(sum_b * wsum);
g0 = cvRound(sum_g * wsum);
r0 = cvRound(sum_r * wsum);
dptr[j] = (uchar)b0;
dptr[j + 1] = (uchar)g0;
dptr[j + 2] = (uchar)r0;
}
}
}
}
需要(yao)注(zhu)意圖像像素值的獲取,首先(xian)獲取到每行的坐標指針
const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;
uchar *dptr = dst.data + i * dst.step;
在濾波循環中,從space_ofs
中取出每個模板(ban)位置偏移地(di)址
int val = sptr[j + space_ofs[k]];
這種實現(xian)方法,大大的降低(di)濾波的時間復雜(za)度。
結果對比:
實(shi)現(xian)的(de)結果和OpenCV的(de)實(shi)現(xian)相(xiang)差無幾。sigma = 80,模板大(da)小為20
雙邊濾波器,在平滑圖像的同時,還能夠很好的保護圖像的邊緣信息,例如上圖中,圖像的平滑效果非常明顯了,但是頭發的發絲還是很明顯的。
雙(shuang)邊濾波(bo)器的最重(zhong)要參數仍(reng)然是標準(zhun)差sigma,其(qi)值小(xiao)于10時,平(ping)滑效果(guo)不是很明顯。
文章轉自Brook_icv //www.cnblogs.com/wangguchangqing/p/6416401.html