高斯函数分离特性
二维方式是根据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;
}