高斯模糊实现小结

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

相关文章

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动