快速高斯濾波(用兩個一維代替二維)

高斯函數分離特性

這裏寫圖片描述

二維方式是根據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;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章