我们都知道,卷积是一种好玩的东西,通过卷积可以实现很多功能,例如photoshop中的各种滤镜也可以通过对一副图像进行卷积获得。而目前大火的机器学习中的CNN算法也是利用卷积来实现的,至于CNN的算法以后再讲,今天主要是介绍卷积的原理以及利用它来实现高斯模糊。

    我们先来看下卷积是将两个矩阵相乘在求和,而具体原理可在这篇文章中看。而图像中高斯模糊则是根据二维的正态分布(高斯分布)来产生高斯模糊卷积子。

    一维的正态分布:基于opencv2利用卷积算子实现高斯模糊,其中我们的u取0。

    二维的正态分布基于opencv2利用卷积算子实现高斯模糊

        基于opencv2利用卷积算子实现高斯模糊

    利用这个二维高斯模糊公式,我们可以产生N*N的卷积核,然后利用这个核与图片中的每个像素进行卷积便是高斯模糊。

    这个过程可以通过以下几张图来理解,假如我产生一个3*3的卷积核,基于opencv2利用卷积算子实现高斯模糊取1,因此我们就可以产生以下的矩阵:

                                            基于opencv2利用卷积算子实现高斯模糊

我们取图像中的某个像素周围3*3的矩阵:

                                            基于opencv2利用卷积算子实现高斯模糊

而矩阵中的78对应的是(0,0)坐标,而这个矩阵我们是围绕着这个像素点在半径为1的范围内取得矩阵,因此我们卷积获得的值也应替代该值。

通过卷积:(0.0585*0+0.0965*0+0.0585*7+0.0965*4+0.1592*78+0.0965*8+0.0585*69+0.0965*2+0.0585*4)=18.4486

    因此78这个位置的值也就变为18.4486。高斯模糊便是通过对每个像素以半径r范围取一N*N矩阵与N*N卷积核进行卷积并改变改像素点的值以获得滤波效果。

以下便是灰度图的高斯模糊程序:

  1. #include<iostream>
  2. #include<opencv2/core/core.hpp>
  3. #include<opencv2/highgui/highgui.hpp>
  4. #include<opencv2/imgproc/imgproc.hpp>
  5. #include<math.h>
  6. #define sigma 1 //定义sigma的大小越大越模糊
  7. #define PI 3.14159
  8. #define N 5 //定义卷积核大小
  9. int main()
  10. {
  11. float liv_conv[N][N] = {0}; //定义卷积核
  12. cv::Mat lMv_src = cv::imread("E:\\temp\\3D\\pad.jpg");//打开图片
  13. cv::resize(lMv_src,lMv_src,cv::Size(lMv_src.cols/4,lMv_src.rows/4),0,0,cv::INTER_LINEAR);//因为我的图片太大,不方便显示,我就利用这个函数缩小
  14. cv::Mat lMv_gray, lMv_OutPut = cv::Mat::zeros(lMv_src.rows, lMv_src.cols, CV_8UC1); //定义灰度图,与输出图像
  15. cv::cvtColor(lMv_src,lMv_gray,CV_BGR2GRAY);//将彩色图转换灰度图
  16. int liv_n = N/2; //获取半径
  17. float all = 0.0;
  18. /**************
  19. 产生卷积核
  20. ***************/
  21. for(int i = 0; i<N; i++)
  22. {
  23. for(int j = 0; j<N; j++)
  24. {
  25. liv_conv[i][j] = exp(-((i-liv_n)*(i-liv_n)+(j-liv_n)*(j-liv_n))/(2.0*sigma*sigma))/(2*PI*sigma*sigma);//二维正态分布公式
  26. all+=liv_conv[i][j];
  27. }
  28. }
  29. /******************
  30. 逐个像素进行卷积
  31. *******************/
  32. for(int i = 0; i<lMv_gray.rows-N; i++)
  33. {
  34. for(int j = 0; j<lMv_gray.cols-N; j++)
  35. {
  36. float lfv_sum = 0.0;
  37. for(int y = 0; y<N; y++)
  38. {
  39. for(int x = 0; x<N; x++)
  40. {
  41. lfv_sum+=lMv_gray.at<uchar>(i+y,j+x)*liv_conv[y][x];//相称求和
  42. }
  43. }
  44. lMv_OutPut.at<uchar>(i,j) = lfv_sum/all;
  45. }
  46. }
  47. cv::imshow("Gaussian",lMv_OutPut);//显示高斯模糊后的图像
  48. cv::imshow("Src",lMv_gray);//显示原图
  49. cv::waitKey(0);
  50. return 0;
  51. }

原图:

基于opencv2利用卷积算子实现高斯模糊

sigma = 1:

基于opencv2利用卷积算子实现高斯模糊

sigma = 10:

基于opencv2利用卷积算子实现高斯模糊

sigma = 0.1:

基于opencv2利用卷积算子实现高斯模糊


其实彩色图片的高斯模糊的原理是一样,程序也是基本一样的:

  1. #include<iostream>
  2. #include<opencv2/core/core.hpp>
  3. #include<opencv2/highgui/highgui.hpp>
  4. #include<opencv2/imgproc/imgproc.hpp>
  5. #include<math.h>
  6. #define sigma 1 //定义sigma的大小越大越模糊
  7. #define PI 3.14159
  8. #define N 5 //定义卷积核大小
  9. int main()
  10. {
  11. float liv_conv[N][N] = {0}; //定义卷积核
  12. cv::Mat lMv_src = cv::imread("E:\\temp\\3D\\pad.jpg");//打开图片
  13. cv::resize(lMv_src,lMv_src,cv::Size(lMv_src.cols/2,lMv_src.rows/2),0,0,cv::INTER_LINEAR);//因为我的图片太大,不方便显示,我就利用这个函数缩小
  14. cv::Mat lMv_gray, lMv_OutPut = cv::Mat::zeros(lMv_src.rows, lMv_src.cols, CV_8UC3); //定义灰度图,与输出图像
  15. cv::cvtColor(lMv_src,lMv_gray,CV_BGR2GRAY);//将彩色图转换灰度图
  16. int liv_n = N/2; //获取半径
  17. float all = 0.0;
  18. /**************
  19. 产生卷积核
  20. ***************/
  21. for(int i = 0; i<N; i++)
  22. {
  23. for(int j = 0; j<N; j++)
  24. {
  25. liv_conv[i][j] = exp(-((i-liv_n)*(i-liv_n)+(j-liv_n)*(j-liv_n))/(2.0*sigma*sigma))/(2*PI*sigma*sigma);//二维正态分布公式
  26. all+=liv_conv[i][j];
  27. }
  28. }
  29. /******************
  30. 逐个像素进行卷积
  31. *******************/
  32. for(int i = 0; i<lMv_gray.rows-N; i++)
  33. {
  34. for(int j = 0; j<lMv_gray.cols-N; j++)
  35. {
  36. float lfv_sum_b = 0.0, lfv_sum_g = 0.0, lfv_sum_r = 0.0;
  37. for(int y = 0; y<N; y++)
  38. {
  39. for(int x = 0; x<N; x++)
  40. {
  41. lfv_sum_b+=lMv_src.at<cv::Vec3b>(i+y,j+x)[0]*liv_conv[y][x];//相称求和
  42. lfv_sum_g+=lMv_src.at<cv::Vec3b>(i+y,j+x)[1]*liv_conv[y][x];
  43. lfv_sum_r+=lMv_src.at<cv::Vec3b>(i+y,j+x)[2]*liv_conv[y][x];
  44. }
  45. }
  46. lMv_OutPut.at<cv::Vec3b>(i,j)[0] = lfv_sum_b/all;
  47. lMv_OutPut.at<cv::Vec3b>(i,j)[1] = lfv_sum_g/all;
  48. lMv_OutPut.at<cv::Vec3b>(i,j)[2] = lfv_sum_r/all;
  49. }
  50. }
  51. cv::imshow("Gaussian",lMv_OutPut);//显示高斯模糊后的图像
  52. cv::imshow("Src",lMv_src);//显示原图
  53. cv::waitKey(0);
  54. return 0;
  55. }

结果:

基于opencv2利用卷积算子实现高斯模糊

然后我们加入椒盐噪声测试下性能:

  1. #define sigma 3  //定义sigma的大小越大越模糊
  2. #define PI 3.14159
  3. #define N 8 //定义卷积核大小
  4. #define NN 1600
  1. /*************************
  2. 创建椒盐噪点
  3. *************************/
  4. for(int i = 0; i<NN; i++)
  5. {
  6. int x = rand()%(lMv_src.cols-1)+1; //创建随机数
  7. int y = rand()%(lMv_src.rows-1)+1;
  8. lMv_src.at<cv::Vec3b>(y,x)[0] = 255;
  9. lMv_src.at<cv::Vec3b>(y,x)[1] = 255;
  10. lMv_src.at<cv::Vec3b>(y,x)[2] = 255;
  11. }
基于opencv2利用卷积算子实现高斯模糊

由此看来效果还是可以的,接下来我会跟大家分享去模糊的算法。

欢迎喜欢图像处理或人工智能的朋友通过邮箱[email protected]来联系我,一起交流。

相关文章:

  • 2022-12-23
  • 2021-07-22
  • 2021-11-22
  • 2022-01-09
  • 2021-10-22
  • 2021-10-30
  • 2021-08-11
  • 2021-09-13
猜你喜欢
  • 2021-11-23
  • 2021-09-10
  • 2021-09-20
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-24
相关资源
相似解决方案