实验内容
实验5.1 双边滤波
实现一个双边滤波。
- 并与高斯滤波比较保持图像边缘的效果。
- 与cv::bilateralFilter比较效果和速度。
实验过程
1,计算位置空间权重。
for (int i = 0; i < f_size; i++) {
for (int j = 0; j < f_size; j++) {
space_weight[i][j] = exp(-(1.0) * (((i*1ll - center_i) * (i*1ll - center_i) + (j*1ll - center_j) * (j*1ll - center_j)) / (2.0 * sigmas * sigmas)));
}
}
2,计算颜色空间权重。
for (int i = 0; i < 256 * channels; i++) {
color_weight[i] = exp((-1.0 * (i*1ll * i)) / (2.0 * sigma_r * sigma_r));
}
3,双边滤波计算。
先将像素之间的颜色差距算出来,得到颜色权重,再根据位置得到空间权重。像素的最终权重为颜色权重*位置权重。
for (int k = 0; k < f_size; k++) {
for (int l = 0; l < f_size; l++) {
x = i - k + (f_size / 2);
y = j - l + (f_size / 2);
values = abs(input.at<Vec3b>(i, j)[0] + input.at<Vec3b>(i, j)[1] + input.at<Vec3b>(i, j)[2] - input.at<Vec3b>(x, y)[0] - input.at<Vec3b>(x, y)[1] - input.at<Vec3b>(x, y)[2]);
for (int c = 0; c < 3; c++) {
pix[c] += (input.at<Vec3b>(x, y)[c] * color_weight[values] * space_weight[k][l]);
}
sum += (color_weight[values] * space_weight[k][l]); //方便归一化
}
}
4,进行归一化。
for (int c = 0; c < 3; c++) {
output.at<Vec3b>(i, j)[c] = pix[c]/sum; //归一化
}
5,效果。
可以看到背景的灰色噪点消除了,线条更加清晰。
6,高斯滤波。
可以看出整体变模糊了。
7,与 cv::bilateralFilter 比较。
看出自己实现的双边滤波更慢,大约是 opencv自带函数的两倍。效果相差不大。
附源代码
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
//计算空间权重
double** get_space_weight(int f_size, int channels, double sigmas)
{
double** space_weight = new double* [(long long)f_size];
for (int i = 0; i < f_size + 1; i++) {
space_weight[i] = new double[(long long)f_size];
}
// [1-2] 高斯分布计算
int center_i, center_j;
center_i = center_j = f_size / 2;
for (int i = 0; i < f_size; i++) {
for (int j = 0; j < f_size; j++) {
space_weight[i][j] =
exp(-(1.0) * (((i*1ll - center_i) * (i*1ll - center_i) + (j*1ll - center_j) * (j*1ll - center_j)) /
(2.0 * sigmas * sigmas)));
}
}
return space_weight;
}
//计算相似度权重
double* get_color_weight(int f_size, int channels, double sigma_r)
{
double* color_weight = new double[256ll * channels];
for (int i = 0; i < 256 * channels; i++) {
color_weight[i] = exp((-1.0 * (i*1ll * i)) / (2.0 * sigma_r * sigma_r));
}
return color_weight;
}
//双边扫描计算
void doBialteral(const Mat& input, Mat& output, int f_size, double* color_weight, double** space_weight)
{
output = input.clone();
for (int i = (f_size / 2); i < input.rows - (f_size / 2); i++) {
for (int j = (f_size / 2); j < input.cols - (f_size / 2); j++) {
double pix[3] = { 0.0,0.0,0.0 };
int x, y, values;
double sum = 0.0;
for (int k = 0; k < f_size; k++) {
for (int l = 0; l < f_size; l++) {
x = i - k + (f_size / 2);
y = j - l + (f_size / 2);
values = abs(input.at<Vec3b>(i, j)[0] + input.at<Vec3b>(i, j)[1] + input.at<Vec3b>(i, j)[2]
- input.at<Vec3b>(x, y)[0] - input.at<Vec3b>(x, y)[1] - input.at<Vec3b>(x, y)[2]);
for (int c = 0; c < 3; c++) {
pix[c] += (input.at<Vec3b>(x, y)[c]
* color_weight[values]
* space_weight[k][l]);
}
sum += (color_weight[values] * space_weight[k][l]);
}
}
for (int c = 0; c < 3; c++) {
output.at<Vec3b>(i, j)[c] = pix[c]/sum; //归一化
}
}
}
return;
}
// 双边滤波函数
void myBialteralFilter(const Mat& input, Mat& output, const int f_size, const double sigma_d, const double sigma_r)
{
output = Mat(input.size(), input.type());
double* color_weight = get_color_weight(f_size, input.channels(), sigma_r);
double** space_weight = get_space_weight(f_size, input.channels(), sigma_d);
doBialteral(input, output, f_size, color_weight, space_weight);
return;
}
int main(void)
{
clock_t start_time, end_time;
Mat imput = imread("02.webp");
Mat output;
int half_size = 12;
// half_size,sigma_d,sigmar越大越模糊;
start_time = clock();
myBialteralFilter(imput, output, 2 * half_size + 1, 12, 20);
end_time = clock(); //获取结束时间
double Times = (double)(end_time - start_time) / CLOCKS_PER_SEC;
printf("%f s\n", Times);
imshow("原图", imput);
imshow("双边滤波", output);
Mat img = imread("02.webp");
Mat dstb, dstg;
//GaussianBlur(img, dstg, cv::Size(3, 3), 0.8);
//imshow("高斯滤波", dstg);
start_time = clock();
bilateralFilter(img, dstb, 25, 20, 12);
imshow("双边滤波2", dstb);
end_time = clock(); //获取结束时间
Times = (double)(end_time - start_time) / CLOCKS_PER_SEC;
printf("%f s\n", Times);
waitKey(0);
destroyAllWindows();
return 0;
}