高斯模糊实现小结.docx
《高斯模糊实现小结.docx》由会员分享,可在线阅读,更多相关《高斯模糊实现小结.docx(19页珍藏版)》请在冰豆网上搜索。
高斯模糊实现小结
高斯模糊实现小结
高斯模糊是一种图像滤波器,它使用正态分布(高斯函数)计算模糊模板,并使用该模板与原图像做卷积运算,达到模糊图像的目的。
N维空间正态分布方程为:
其中,σ是正态分布的标准差,σ值越大,图像越模糊(平滑)。
r为模糊半径,模糊半径是指模板元素到模板中心的距离。
如二维模板大小为m*n,则模板上的元素(x,y)对应的高斯计算公式为:
在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。
分布不为零的像素组成的卷积矩阵与原始图像做变换。
每个像素的值都是周围相邻像素值的加权平均。
原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。
这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。
理论上来讲,图像中每点的分布都不为零,这也就是说每个像素的计算都需要包含整幅图像。
在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。
通常,图像处理程序只需要计算(6σ+1)*(6σ+1)的矩阵就可以保证相关像素影响。
1、使用给定高斯模板平滑图像函数
σ=0.84089642的7行7列高斯模糊矩阵为:
现使用该模板对源图像做模糊处理,其函数如下:
[cpp] view
plaincopy
1.//高斯平滑
2.//未使用sigma,边缘无处理
3.void GaussianTemplateSmooth(const Mat &src, Mat &dst, double sigma)
4.{
5. //高斯模板(7*7),sigma = 0.84089642,归一化后得到
6. static const double gaussianTemplate[7][7] =
7. {
8. {0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067},
9. {0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292},
10. {0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117},
11. {0.00038771, 0.01330373, 0.11098164, 0.22508352, 0.11098164, 0.01330373, 0.00038771},
12. {0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117},
13. {0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292},
14. {0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067}
15. };
16.
17. dst.create(src.size(), src.type());
18. uchar* srcData = src.data;
19. uchar* dstData = dst.data;
20.
21. for(int j = 0; j < src.cols-7; j++)
22. {
23. for(int i = 0; i < src.rows-7; i++)
24. {
25. double acc = 0;
26. double accb = 0, accg = 0, accr = 0;
27. for(int m = 0; m < 7; m++)
28. {
29. for(int n = 0; n < 7; n++)
30. {
31. if(src.channels() == 1)
32. acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * gaussianTemplate[m][n];
33. else
34. {
35. accb += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 0) * gaussianTemplate[m][n];
36. accg += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 1) * gaussianTemplate[m][n];
37. accr += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 2) * gaussianTemplate[m][n];
38. }
39. }
40. }
41. if(src.channels() == 1)
42. *(dstData + dst.step * (i+3) + dst.channels() * (j+3))=(int)acc;
43. else
44. {
45. *(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 0)=(int)accb;
46. *(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 1)=(int)accg;
47. *(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 2)=(int)accr;
48. }
49. }
50. }
51.
52.}
其效果如图1所示,7*7的高斯模板与源图像做卷积运算时,会产生半径为3的边缘,在不精确的图像处理中,可用源图像像素填充,或者去掉边缘。
2、二维高斯模糊函数
上述的例子中,如何求得高斯模糊矩阵是高斯模糊的关键。
根据高斯函数的性质,图像处理程序只需要计算(6σ+1)*(6σ+1)的矩阵就可以保证相关像素影响。
因此,可根据σ的值确定高斯模糊矩阵的大小。
高斯矩阵可利用公式(1-2)计算,并归一化得到。
归一化是保证高斯矩阵的值在[0,1]之间,
其处理函数如下:
[cpp] view
plaincopy
1.void GaussianSmooth2D(const Mat &src, Mat &dst, double sigma)
2.{
3. if(src.channels() !
= 1)
4. return;
5.
6. //确保sigma为正数
7. sigma = sigma > 0 ?
sigma :
0;
8. //高斯核矩阵的大小为(6*sigma+1)*(6*sigma+1)
9. //ksize为奇数
10. int ksize = cvRound(sigma * 3) * 2 + 1;
11.
12.// dst.create(src.size(), src.type());
13. if(ksize == 1)
14. {
15. src.copyTo(dst);
16. return;
17. }
18.
19. dst.create(src.size(), src.type());
20.
21. //计算高斯核矩阵
22. double *kernel = new double[ksize*ksize];
23.
24. double scale = -0.5/(sigma*sigma);
25. const double PI = 3.141592653;
26. double cons = -scale/PI;
27.
28. double sum = 0;
29.
30. for(int i = 0; i < ksize; i++)
31. {
32. for(int j = 0; j < ksize; j++)
33. {
34. int x = i-(ksize-1)/2;
35. int y = j-(ksize-1)/2;
36. kernel[i*ksize + j] = cons * exp(scale * (x*x + y*y));
37.
38. sum += kernel[i*ksize+j];
39.// cout << " " << kernel[i*ksize + j];
40. }
41.// cout <42. }
43. //归一化
44. for(int i = ksize*ksize-1; i >=0; i--)
45. {
46. *(kernel+i) /= sum;
47. }
48. //图像卷积运算,无边缘处理
49. for(int j = 0; j < src.cols-ksize; j++)
50. {
51. for(int i = 0; i < src.rows-ksize; i++)
52. {
53. double acc = 0;
54.
55. for(int m = 0; m < ksize; m++)
56. {
57. for(int n = 0; n < ksize; n++)
58. {
59. acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * kernel[m*ksize+n];
60. }
61. }
62.
63.
64. *(dstData + dst.step * (i + (ksize - 1)/2) + (j + (ksize -1)/2)) = (int)acc;
65. }
66. }
67. delete []kernel;
68.}
利用该函数,取σ=0.84089642即可得到上例中7*7的模板,该模板数据存在kernel中。
利用该函数计算的归一化后的3*3,5*5阶高斯模板如表2,3所示:
由上表可以看出,高斯模板是中心对称的。
模糊效果如图2所示。
对图2中边缘的处理:
[cpp] view
plaincopy
1....
[cpp] view
plaincopy
1.int center = (ksize-1) /2;
2.//图像卷积运算,处理边缘
3.for(int j = 0; j < src.cols; j++)
4.{
5. for(int i = 0; i < src.rows; i++)
6. {
7. double acc = 0;
8.
9. for(int m = -center, c = 0; m <= center; m++, c++)
10. {
11. for(int n = -center, r = 0; n <= center; n++, r++)
12. {
13. if((i+n) >=0 && (i+n) < src.rows && (j+m) >=0 && (j+m) < src.cols)
14. {
15.
16. acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * kernel[r*ksize+c];
17.
18. }
19. }
20. }
21.
22.
23. *(dstData + dst.step * (i) + (j)) = (int)acc;
24. }
25.}
[cpp] view
plaincopy
1....
结果图为:
如上图所示,边缘明显变窄,但是存在黑边。
3、改进的高斯模糊函数
上述的二维高斯模糊函数GaussianSmooth2D达到了高斯模糊图像的目的,但是如图2所示,会因模板的关系而造成边缘图像缺失,σ越大,缺失像素越多,额外的边缘处理会增加计算量。
并且当σ变大时,高斯模板(高斯核)和卷积运算量将大幅度提高。
根据高斯函数的可分离性,可对二维高斯模糊函数进行改进。
高斯函数的可分离性是指使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。
从计算的角度来看,这是一项有用的特性,因为这样只需要O(n*M*n)+O(m*M*N)次计算,而二维不可分的矩阵则需要O(m*n*M*n)次计算,其中,m,n为高斯矩阵的维数,M,N为二维图像的维数。
另外,两次一维的高斯卷积将消除二维高斯矩阵所产生的边缘。
改进的高斯模糊函数如下:
[cpp] view
plaincopy
1.void GaussianSmooth(const Mat &src, Mat &dst, double sigma)
2.{
3. if(src.channels() !
= 1 && src.channels() !
= 3)
4. return;
5.
6. //
7. sigma = sigma > 0 ?
sigma :
-sigma;
8. //高斯核矩阵的大小为(6*sigma+1)*(6*sigma+1)
9. //ksize为奇数
10. int ksize = ceil(sigma * 3) * 2 + 1;
11.
12. //cout << "ksize=" <13. // dst.create(src.size(), src.type());
14. if(ksize == 1)
15. {
16. src.copyTo(dst);
17. return;
18. }
19.
20. //计算一维高斯核
21. double *kernel = new double[ksize];
22.
23. double scale = -0.5/(sigma*sigma);
24. const double PI = 3.141592653;
25. double cons = 1/sqrt(-scale / PI);
26.
27. double sum = 0;
28. int kcenter = ksize/2;
29. int i = 0, j = 0;
30. for(i = 0; i < ksize; i++)
31. {
32. int x = i - kcenter;
33. *(kernel+i) = cons * exp(x * x * scale);//一维高斯函数
34. sum += *(kernel+i);
35.
36.// cout << " " << *(kernel+i);
37. }
38.// cout << endl;
39. //归一化,确保高斯权值在[0,1]之间
40. for(i = 0; i < ksize; i++)
41. {
42. *(kernel+i) /= sum;
43.// cout << " " << *(kernel+i);
44. }
45.// cout << endl;
46.
47. dst.create(src.size(), src.type());
48. Mat temp;
49. temp.create(src.size(), src.type());
50.
51. uchar* srcData = src.data;
52. uchar* dstData = dst.data;
53. uchar* tempData = temp.data;
54.
55. //x方向一维高斯模糊
56. for(int y = 0; y < src.rows; y++)
57. {
58. for(int x = 0; x < src.cols; x++)
59. {
60. double mul = 0;
61. sum = 0;
62. double bmul = 0, gmul = 0, rmul = 0;
63. for(i = -kcenter; i <= kcenter; i++)
64. {
65. if((x+i) >= 0 && (x+i) < src.cols)
66. {
67. if(src.channels() == 1)
68. {
69. mul += *(srcData+y*src.step+(x+i))*(*(kernel+kcenter+i));
70. }
71. else
72. {
73. bmul += *(srcData+y*src.step+(x+i)*src.channels() + 0)*(*(kernel+kcenter+i));
74. gmul += *(srcData+y*src.step+(x+i)*src.channels() + 1)*(*(kernel+kcenter+i));
75. rmul += *(srcData+y*src.step+(x+i)*src.channels() + 2)*(*(kernel+kcenter+i));
76. }
77. sum += (*(kernel+kcenter+i));
78. }
79. }
80. if(src.channels() == 1)
81. {
82. *(tempData+y*temp.step+x) = mul/sum;
83. }
84. else
85. {
86. *(tempData+y*te