高斯模糊实现小结

2024-09-03 12:18
文章标签 实现 模糊 小结 高斯

本文主要是介绍高斯模糊实现小结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:http://blog.csdn.net/zddblog/article/details/7450033

高斯模糊是一种图像滤波器,它使用正态分布(高斯函数)计算模糊模板,并使用该模板与原图像做卷积运算,达到模糊图像的目的。

N维空间正态分布方程为:

其中,σ是正态分布的标准差,σ值越大,图像越模糊(平滑)r为模糊半径,模糊半径是指模板元素到模板中心的距离。如二维模板大小为m*n,则模板上的元素(x,y)对应的高斯计算公式为:

在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。

理论上来讲,图像中每点的分布都不为零,这也就是说每个像素的计算都需要包含整幅图像。在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。通常,图像处理程序只需要计算(6σ+1)*(6σ+1)的矩阵就可以保证相关像素影响。

1、使用给定高斯模板平滑图像函数

σ=0.84089642的77列高斯模糊矩阵为:

现使用该模板对源图像做模糊处理,其函数如下:

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  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 plain copy
在CODE上查看代码片 派生到我的代码片
  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 <<endl;  
  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*35*5阶高斯模板如表2,3所示:

由上表可以看出,高斯模板是中心对称的。

模糊效果如图2所示。

 

对图2中边缘的处理:

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. ...  
[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  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 plain copy
在CODE上查看代码片 派生到我的代码片
  1. ...  


结果图为:

如上图所示,边缘明显变窄,但是存在黑边。

3、改进的高斯模糊函数

上述的二维高斯模糊函数GaussianSmooth2D达到了高斯模糊图像的目的,但是如图2所示,会因模板的关系而造成边缘图像缺失,σ越大,缺失像素越多,额外的边缘处理会增加计算量。并且当σ变大时,高斯模板(高斯核)和卷积运算量将大幅度提高。根据高斯函数的可分离性,可对二维高斯模糊函数进行改进。

高斯函数的可分离性是指使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。从计算的角度来看,这是一项有用的特性,因为这样只需要O(n*M*n)+O(m*M*N)次计算,而二维不可分的矩阵则需要O(m*n*M*n)次计算,其中,m,n为高斯矩阵的维数,M,N为二维图像的维数。

另外,两次一维的高斯卷积将消除二维高斯矩阵所产生的边缘。

(关于消除边缘的论述如下图2.4所示, 对用模板矩阵超出边界的部分——虚线框,将不做卷积计算。如图2.4中x方向的第一个模板1*5,将退化成1*3的模板,只在图像之内的部分做卷积。)


改进的高斯模糊函数如下:

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  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=" <<ksize<<endl;  
  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*temp.step+x*temp.channels()+0) = bmul/sum;  
  87.                 *(tempData+y*temp.step+x*temp.channels()+1) = gmul/sum;  
  88.                 *(tempData+y*temp.step+x*temp.channels()+2) = rmul/sum;  
  89.             }  
  90.         }  
  91.     }  
  92.   
  93.       
  94.     //y方向一维高斯模糊  
  95.     for(int x = 0; x < temp.cols; x++)  
  96.     {  
  97.         for(int y = 0; y < temp.rows; y++)  
  98.         {  
  99.             double mul = 0;  
  100.             sum = 0;  
  101.             double bmul = 0, gmul = 0, rmul = 0;  
  102.             for(i = -kcenter; i <= kcenter; i++)  
  103.             {  
  104.                 if((y+i) >= 0 && (y+i) < temp.rows)  
  105.                 {  
  106.                     if(temp.channels() == 1)  
  107.                     {  
  108.                         mul += *(tempData+(y+i)*temp.step+x)*(*(kernel+kcenter+i));  
  109.                     }  
  110.                     else  
  111.                     {  
  112.                         bmul += *(tempData+(y+i)*temp.step+x*temp.channels() + 0)*(*(kernel+kcenter+i));  
  113.                         gmul += *(tempData+(y+i)*temp.step+x*temp.channels() + 1)*(*(kernel+kcenter+i));  
  114.                         rmul += *(tempData+(y+i)*temp.step+x*temp.channels() + 2)*(*(kernel+kcenter+i));  
  115.                     }  
  116.                     sum += (*(kernel+kcenter+i));  
  117.                 }  
  118.             }  
  119.             if(temp.channels() == 1)  
  120.             {  
  121.                 *(dstData+y*dst.step+x) = mul/sum;  
  122.             }  
  123.             else  
  124.             {  
  125.                 *(dstData+y*dst.step+x*dst.channels()+0) = bmul/sum;  
  126.                 *(dstData+y*dst.step+x*dst.channels()+1) = gmul/sum;  
  127.                 *(dstData+y*dst.step+x*dst.channels()+2) = rmul/sum;  
  128.             }  
  129.           
  130.         }  
  131.     }  
  132.       
  133.     delete[] kernel;  
  134. }  

该函数中使用的水平方向和垂直方向的高斯矩阵为同一矩阵,实际计算时可根据需要取不同。

模糊效果如图3所示:

比较

使用GetTickCount()进行比较,GetTickCount()函数的精度为1ms。

以下表格中的数据均为作者机器上的某两次运行结果取均值,编程环境为vs2010+opencv2.2


上表中Debug版本的GaussianTemplateSmooth竟然比GaussianSmooth2D运行时间长,难道是二维数组比不上一维指针,或者是Debug版本的问题?实验结果确实如上。

将本文所写函数与opencv2.2提供的高斯模糊函数GaussianBlur一起进行比较。

结论

如上表4,5所示,对GaussianSmooth2D的改进函数GaussianSmooth,越大时,提速效果越明显,这种速度的改进在Debug模式下尤为明显。无论是在Debug,还是在Release模式下,Opencv2.2提供的GaussianBlur完胜本文所用的函数。建议在学习算法时参考本文内容,实际项目中使用GaussianBlur

 

本例代码键连接:http://download.csdn.net/detail/zddmail/4217704


这篇关于高斯模糊实现小结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132935

相关文章

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Flutter打包APK的几种方式小结

《Flutter打包APK的几种方式小结》Flutter打包不同于RN,Flutter可以在AndroidStudio里编写Flutter代码并最终打包为APK,本篇主要阐述涉及到的几种打包方式,通... 目录前言1. android原生打包APK方式2. Flutter通过原生工程打包方式3. Futte

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.