高斯模糊实现小结

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

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

flask库中sessions.py的使用小结

《flask库中sessions.py的使用小结》在Flask中Session是一种用于在不同请求之间存储用户数据的机制,Session默认是基于客户端Cookie的,但数据会经过加密签名,防止篡改,... 目录1. Flask Session 的基本使用(1) 启用 Session(2) 存储和读取 Se

Python获取浏览器Cookies的四种方式小结

《Python获取浏览器Cookies的四种方式小结》在进行Web应用程序测试和开发时,获取浏览器Cookies是一项重要任务,本文我们介绍四种用Python获取浏览器Cookies的方式,具有一定的... 目录什么是 Cookie?1.使用Selenium库获取浏览器Cookies2.使用浏览器开发者工具

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q