【机器视觉学习笔记】双边滤波算法(C++)

平台: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乙酸氧铍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值