快速高斯滤波(用两个一维代替二维)

8 篇文章 2 订阅
8 篇文章 0 订阅

高斯函数分离特性

这里写图片描述

二维方式是根据kernel的大小以及sigma大小生成一个 size*size的卷积核,然后再做卷积。计算量是imgWidth*imgHeight*size*size,但如果用两个一维来替代,则计算量是imgWidth*imgHeight*size*2,计算量大大减少。

具体做法


假设现在是5*5的卷积核,先对整幅图像整体做x方向的卷积,如下
此时原图(3,3)位置的像素置为以它为中心的五个点分别乘上高斯权值求和的结果,遍历所有像素点做完一遍这个操作后,再在y方向做同样的操作,注意:y方向就不是在原图上取像素值做卷积了,而是在上一步x方向都做完卷积的那个结果图上做计算 。之前我一直想不通如果只在两个方向做卷积,那影响当前点的不就只有9个点吗。而二维卷积5*5应该是25个点的像素值决定中心点的像素值啊。所以上面的注意点根据x方向卷积后的图,再做y方向时,中心点上下的点已经受x方向计算过了,所以最终结果是不变的。影响中心点的还是25个点。
 

还有就是边缘点的处理,二维卷积有可能是直接将处理过的点拷贝到边缘点处,一维可以在处理时只处理一部分,如下

我们只处理在有效范围内的点即可,不用非要只处理图像(2,2)~(imgWidth-2,imgHeight-2)的点

代码:

double *getGaussianArray(int arr_size, double sigma)
{
	const double pi = 3.14159265359;
	int i;
	double *array = new double[arr_size];
	int center_i = arr_size / 2;
	double sum = 0.0f;
	double sigma_square_2 = (2.0f*sigma*sigma);
	for (i = 0; i < arr_size; i++) {
		array[i] = (1.0f/pi*sigma_square_2)*exp(-(1.0f)* (((i - center_i)*(i - center_i)) / sigma_square_2));
		sum += array[i];
	}
	// [2-2] 归一化求权值
	for (i = 0; i < arr_size; i++)
	{
		array[i] /= sum;
	}
	return array;
}

void gaussian( cv::Mat &_src, int kernelSize,double sigma)
{
	double *gaussianArray = getGaussianArray(kernelSize, sigma);
	int center = kernelSize / 2;
	cv::Mat temp = _src.clone();
	int channels = _src.channels();
	// X方向
	for (int i = 0; i < _src.rows; i++) 
	{
		for (int j = 0; j < _src.cols; j++)
		{
			for (int c = 0; c < channels; ++c)
			{
				double sum = 0.0;
				for (int k = -center; k <= center; k++)
				{
					if (j + k < 0 || j + k >= _src.cols)
						continue;
					sum += _src.ptr<uchar>(i)[(j + k)*channels + c] * gaussianArray[k + center];
				}
				// 放入中间结果
				temp.ptr<uchar>(i)[j*channels + c] = MAX(MIN(sum, 255), 0);
			}
			
		}
	}
// Y方向
	for (int i = 0; i < _src.rows; i++) {
		for (int j = 0; j < _src.cols; j++)
		{
			for (int c = 0; c < channels; ++c)
			{
				double sum = 0.0;
				for (int k = -center; k <= center; ++k)
				{
					if (i + k < 0 || i + k >= _src.rows)
						continue;
					sum += temp.ptr<uchar>(i + k)[j*channels+c] * gaussianArray[k + center];
				}
				// 此时可以修改原图像数据了
				_src.ptr<uchar>(i)[j*channels + c] = MAX(MIN(sum, 255), 0);
			}

		}
	}
	delete[] gaussianArray;
}

int main()
{
	cv::Mat src = imread("D:/lena2.jpg");
	gaussian(src,  55, 20);
	cv::imshow("dst", src);
	cv::waitKey(0);	
    system("pause");
	return 0;
}

 

 

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值