平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3
本文所用源码修改自双边滤波(bilateral filter)以及联合双边滤波(joint bilateral filter)—— flow_specter
源码
滤波器
// 双边滤波
// @ src 待滤波的影像
// @ dst 输出的影像
void BilateralFilter(Mat& src, Mat& dst, int d, double sigmaColor, double sigmaSpace)
{
dst = src.clone();
int n_rows = dst.rows;
int n_cols = dst.cols;
int n_channels = dst.channels();
int n_cols_with_channels = n_cols * n_channels;
int half_kernel_size = d / 2;
int index;
double pixel_sum;
double weight_sum = 0;
double temp_bilateral_weight = 0;
double color_kernel[256];
// 颜色域权重确定
// @ color_kernel 颜色域核,1D,长度为256
for (int i = 0; i < 256; i++)
{
color_kernel[i] = exp(-1.0 * (i * i) / (2 * sigmaColor * sigmaColor));
}
// 空间域权重确定
// @ distance_kernel 空间域核,1D
// **************************************************************************************************************
double *distance_kernel;
distance_kernel = new double[d * d];
int k = d / 2;
//二维动态数组申请空间
double **distance_kernel_2D = new double*[d];
for (int i = 0; i < d; i++)
distance_kernel_2D[i] = new double[d];
double delta_square = 2 * sigmaSpace * sigmaSpace; //分母
for (int i = -k; i <= k; i++)
{
for (int j = -k; j <= k; j++)
{
double distance_numerator = i * i + j * j;
distance_kernel_2D[i + k][j + k] = exp(-1.0 * distance_numerator / delta_square);
}
}
// 将2D kernel 转换为 1D kernel
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
distance_kernel[d * i + j] = distance_kernel_2D[i][j];
}
}
//释放二维动态数组空间
for (int i = 0; i < d; i++)
delete[] distance_kernel_2D[i];
delete[] distance_kernel_2D;
// **************************************************************************************************************
// 边界不做处理
for (int i = half_kernel_size; i < (n_rows - half_kernel_size); i++)
{
uchar* pt_dst = dst.ptr<uchar>(i);
uchar* pt_src = src.ptr<uchar>(i);
for (int j = n_channels * half_kernel_size; j < (n_cols_with_channels - n_channels * half_kernel_size); j++)
{
index = 0;
pixel_sum = weight_sum = 0;
// 内层kx,ky循环,空间域内滤波
for (int kx = i - half_kernel_size; kx <= i + half_kernel_size; kx++)
{
uchar* pt_k_src = src.ptr<uchar>(kx);
for (int ky = j - n_channels * half_kernel_size; ky <= (j + n_channels * half_kernel_size); ky += n_channels)
{
temp_bilateral_weight = distance_kernel[index++] * color_kernel[(int)abs(pt_src[j] - pt_k_src[ky])];
weight_sum += temp_bilateral_weight;
pixel_sum += (pt_k_src[ky] * temp_bilateral_weight); // 邻域某像素与中心点的双边权重乘积
}
}
pixel_sum /= weight_sum; // 归一化
pt_dst[j] = saturate_cast<uchar>(pixel_sum); //加权赋值
}
}
delete[]distance_kernel;
}
//————————————————
//版权声明:本文为CSDN博主「flow_specter」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/flow_specter/article/details/107557303
主函数
图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\”
int main(int argc, char * argv[])
{
Mat src = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\face.jpg");
Mat dst;
BilateralFilter(src, dst, 23, 35, 10);
imshow("原图", src);
imshow("输出", dst);
waitKey(0);
return 0;
}
效果
完整源码
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 双边滤波
// @ src 待滤波的影像
// @ dst 输出的影像
void BilateralFilter(Mat& src, Mat& dst, int d, double sigmaColor, double sigmaSpace)
{
dst = src.clone();
int n_rows = dst.rows;
int n_cols = dst.cols;
int n_channels = dst.channels();
int n_cols_with_channels = n_cols * n_channels;
int half_kernel_size = d / 2;
int index;
double pixel_sum;
double weight_sum = 0;
double temp_bilateral_weight = 0;
double color_kernel[256];
// 颜色域权重确定
// @ color_kernel 颜色域核,1D,长度为256
for (int i = 0; i < 256; i++)
{
color_kernel[i] = exp(-1.0 * (i * i) / (2 * sigmaColor * sigmaColor));
}
// 空间域权重确定
// @ distance_kernel 空间域核,1D
// **************************************************************************************************************
double *distance_kernel;
distance_kernel = new double[d * d];
int k = d / 2;
//二维动态数组申请空间
double **distance_kernel_2D = new double*[d];
for (int i = 0; i < d; i++)
distance_kernel_2D[i] = new double[d];
double delta_square = 2 * sigmaSpace * sigmaSpace; //分母
for (int i = -k; i <= k; i++)
{
for (int j = -k; j <= k; j++)
{
double distance_numerator = i * i + j * j;
distance_kernel_2D[i + k][j + k] = exp(-1.0 * distance_numerator / delta_square);
}
}
// 将2D kernel 转换为 1D kernel
for (int i = 0; i < d; i++)
{
for (int j = 0; j < d; j++)
{
distance_kernel[d * i + j] = distance_kernel_2D[i][j];
}
}
//释放二维动态数组空间
for (int i = 0; i < d; i++)
delete[] distance_kernel_2D[i];
delete[] distance_kernel_2D;
// **************************************************************************************************************
// 边界不做处理
for (int i = half_kernel_size; i < (n_rows - half_kernel_size); i++)
{
uchar* pt_dst = dst.ptr<uchar>(i);
uchar* pt_src = src.ptr<uchar>(i);
for (int j = n_channels * half_kernel_size; j < (n_cols_with_channels - n_channels * half_kernel_size); j++)
{
index = 0;
pixel_sum = weight_sum = 0;
// 内层kx,ky循环,空间域内滤波
for (int kx = i - half_kernel_size; kx <= i + half_kernel_size; kx++)
{
uchar* pt_k_src = src.ptr<uchar>(kx);
for (int ky = j - n_channels * half_kernel_size; ky <= (j + n_channels * half_kernel_size); ky += n_channels)
{
temp_bilateral_weight = distance_kernel[index++] * color_kernel[(int)abs(pt_src[j] - pt_k_src[ky])];
weight_sum += temp_bilateral_weight;
pixel_sum += (pt_k_src[ky] * temp_bilateral_weight); // 邻域某像素与中心点的双边权重乘积
}
}
pixel_sum /= weight_sum; // 归一化
pt_dst[j] = saturate_cast<uchar>(pixel_sum); //加权赋值
}
}
delete[]distance_kernel;
}
//————————————————
//版权声明:本文为CSDN博主「flow_specter」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/flow_specter/article/details/107557303
int main(int argc, char * argv[])
{
Mat src = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\face.jpg");
Mat dst;
BilateralFilter(src, dst, 23, 35, 10);
imshow("原图", src);
imshow("输出", dst);
waitKey(0);
return 0;
}