高斯函數分離特性
二維方式是根據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;
}