python一维平滑滤波_高斯滤波器的原理及其实现过程(附模板代码)
小数形式的模板,就是直接计算得到的值,没有经过任何的处理;
整数形式的,则需要进行归一化处理,将模板左上角的值归一化为1,下面会具体介绍。使用整数的模板时,需要在模板的前面加一个系数,系数为也就是模板系数和的倒数。
高斯模板的生成知道模板生成的原理,实现起来也就不困难了
void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{
static const double pi = 3.1415926;
int center = ksize / 2; // 模板的中心位置,也就是坐标的原点
double x2, y2;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - center, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - center, 2);
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
g /= 2 * pi * sigma;
window[i][j] = g;
}
}
double k = 1 / window[0][0]; // 将左上角的系数归一化为1
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
window[i][j] *= k;
}
}
}
需要一个二维数组,存放生成的系数(这里假设模板的最大尺寸不会超过11);第二个参数是模板的大小(不要超过11);第三个参数就比较重要了,是高斯分布的标准差。生成的过程,首先根据模板的大小,找到模板的中心位置ksize/2。然后就是遍历,根据高斯分布的函数,计算模板中每个系数的值。需要注意的是,最后归一化的过程,使用模板左上角的系数的倒数作为归一化的系数(左上角的系数值被归一化为1),模板中的每个系数都乘以该值(左上角系数的倒数),然后将得到的值取整,就得到了整数型的高斯滤波器模板。下面截图生成的是,大小为3×3,σ=0.83×3,σ=0.8的模板对上述解结果取整后得到如下模板:这个模板就比较熟悉了,其就是根据σ=0.8的高斯函数生成的模板。至于小数形式的生成也比较简单,去掉归一化的过程,并且在求解过程后,模板的每个系数要除以所有系数的和。具体代码如下:
void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{
static const double pi = 3.1415926;
int center = ksize / 2; // 模板的中心位置,也就是坐标的原点
double x2, y2;
double sum = 0;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - center, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - center, 2);
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
g /= 2 * pi * sigma;
sum += g;
window[i][j] = g;
}
}
//double k = 1 / window[0][0]; // 将左上角的系数归一化为1
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
window[i][j] /= sum;
}
}
}
3×3,σ=0.8的小数型模板。σσ值的意义及选取通过上述的实现过程,不难发现,高斯滤波器模板的生成最重要的参数就是高斯分布的标准差σσ。标准差代表着数据的离散程度,如果σσ较小,那么生成的模板的中心系数较大,而周围的系数较小,这样对图像的平滑效果就不是很明显;反之,σσ较大,则生成的模板的各个系数相差就不是很大,比较类似均值模板,对图像的平滑效果比较明显。来看下一维高斯分布的概率分布密度图:横轴表示可能得取值x,竖轴表示概率分布密度F(x),那么不难理解这样一个曲线与x轴围成的图形面积为1。σσ(标准差)决定了这个图形的宽度,可以得出这样的结论:σσ越大,则图形越宽,尖峰越小,图形较为平缓;σσ越小,则图形越窄,越集中,中间部分也就越尖,图形变化比较剧烈。这其实很好理解,如果sigma也就是标准差越大,则表示该密度分布一定比较分散,由于面积为1,于是尖峰部分减小,宽度越宽(分布越分散);同理,当σσ越小时,说明密度分布较为集中,于是尖峰越尖,宽度越窄!于是可以得到如下结论:σσ越大,分布越分散,各部分比重差别不大,于是生成的模板各元素值差别不大,类似于平均模板;σσ越小,分布越集中,中间部分所占比重远远高于其他部分,反映到高斯模板上就是中心元素值远远大于其他元素值,于是自然而然就相当于中间值得点运算。基于OpenCV的实现在生成高斯模板好,其简单的实现和其他的空间滤波器没有区别,具体代码如下:
void GaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
CV_Assert(src.channels() || src.channels() == 3); // 只处理单通道或者三通道图像
const static double pi = 3.1415926;
// 根据窗口大小和sigma生成高斯滤波器模板
// 申请一个二维数组,存放生成的高斯模板矩阵
double **templateMatrix = new double*[ksize];
for (int i = 0; i < ksize; i++)
templateMatrix[i] = new double[ksize];
int origin = ksize / 2; // 以模板的中心为原点
double x2, y2;
double sum = 0;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - origin, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - origin, 2);
// 高斯函数前的常数可以不用计算,会在归一化的过程中给消去
double g = exp(-(x2 + y2) / (2 * sigma * sigma));
sum += g;
templateMatrix[i][j] = g;
}
}
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
templateMatrix[i][j] /= sum;
cout << templateMatrix[i][j] << " ";
}
cout << endl;
}
// 将模板应用到图像中
int border = ksize / 2;
copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
int channels = dst.channels();
int rows = dst.rows - border;
int cols = dst.cols - border;
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int a = -border; a <= border; a++)
{
for (int b = -border; b <= border; b++)
{
if (channels == 1)
{
sum[0] += templateMatrix[border + a][border + b] * dst.at(i + a, j + b);
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i + a, j + b);
auto k = templateMatrix[border + a][border + b];
sum[0] += k * rgb[0];
sum[1] += k * rgb[1];
sum[2] += k * rgb[2];
}
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
// 释放模板数组
for (int i = 0; i < ksize; i++)
delete[] templateMatrix[i];
delete[] templateMatrix;
}
只处理单通道或者三通道图像,模板生成后,其滤波(卷积过程)就比较简单了。不过,这样的高斯滤波过程,其循环运算次数为m×n×ksize2,其中m,n为图像的尺寸;ksize为高斯滤波器的尺寸。这样其时间复杂度为O(ksize2),随滤波器的模板的尺寸呈平方增长,当高斯滤波器的尺寸较大时,其运算效率是极低的。为了,提高滤波的运算速度,可以将二维的高斯滤波过程分解开来。分离实现高斯滤波由于高斯函数的可分离性,尺寸较大的高斯滤波器可以分成两步进行:首先将图像在水平(竖直)方向与一维高斯函数进行卷积;然后将卷积后的结果在竖直(水平)方向使用相同的一维高斯函数得到的模板进行卷积运算。具体实现代码如下:
// 分离的计算
void separateGaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
CV_Assert(src.channels()==1 || src.channels() == 3); // 只处理单通道或者三通道图像
// 生成一维的高斯滤波模板
double *matrix = new double[ksize];
double sum = 0;
int origin = ksize / 2;
for (int i = 0; i < ksize; i++)
{
// 高斯函数前的常数可以不用计算,会在归一化的过程中给消去
double g = exp(-(i - origin) * (i - origin) / (2 * sigma * sigma));
sum += g;
matrix[i] = g;
}
// 归一化
for (int i = 0; i < ksize; i++)
matrix[i] /= sum;
// 将模板应用到图像中
int border = ksize / 2;
copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
int channels = dst.channels();
int rows = dst.rows - border;
int cols = dst.cols - border;
// 水平方向
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int k = -border; k <= border; k++)
{
if (channels == 1)
{
sum[0] += matrix[border + k] * dst.at(i, j + k); // 行不变,列变化;先做水平方向的卷积
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i, j + k);
sum[0] += matrix[border + k] * rgb[0];
sum[1] += matrix[border + k] * rgb[1];
sum[2] += matrix[border + k] * rgb[2];
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
// 竖直方向
for (int i = border; i < rows; i++)
{
for (int j = border; j < cols; j++)
{
double sum[3] = { 0 };
for (int k = -border; k <= border; k++)
{
if (channels == 1)
{
sum[0] += matrix[border + k] * dst.at(i + k, j); // 列不变,行变化;竖直方向的卷积
}
else if (channels == 3)
{
Vec3b rgb = dst.at(i + k, j);
sum[0] += matrix[border + k] * rgb[0];
sum[1] += matrix[border + k] * rgb[1];
sum[2] += matrix[border + k] * rgb[2];
}
}
for (int k = 0; k < channels; k++)
{
if (sum[k] < 0)
sum[k] = 0;
else if (sum[k] > 255)
sum[k] = 255;
}
if (channels == 1)
dst.at(i, j) = static_cast(sum[0]);
else if (channels == 3)
{
Vec3b rgb = { static_cast(sum[0]), static_cast(sum[1]), static_cast(sum[2]) };
dst.at(i, j) = rgb;
}
}
}
delete[] matrix;
}
代码没有重构较长,不过其实现原理是比较简单的。首先得到一维高斯函数的模板,在卷积(滤波)的过程中,保持行不变,列变化,在水平方向上做卷积运算;接着在上述得到的结果上,保持列不边,行变化,在竖直方向上做卷积运算。这样分解开来,算法的时间复杂度为O(ksize)O(ksize),运算量和滤波器的模板尺寸呈线性增长。在OpenCV也有对高斯滤波器的封装GaussianBlur,其声明如下:
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
二维高斯函数的标准差在x和y方向上应该分别有一个标准差,在上面的代码中一直设其在x和y方向的标准是相等的,在OpenCV中的高斯滤波器中,可以在x和y方向上设置不同的标准差。下图是自己实现的高斯滤波器和OpenCV中的GaussianBlur的结果对比上图是5×5,σ=0.8的高斯滤波器,可以看出两个实现得到的结果没有很大的区别。总结高斯滤波器是一种线性平滑滤波器,其滤波器的模板是对二维高斯函数离散得到。由于高斯模板的中心值最大,四周逐渐减小,其滤波后的结果相对于均值滤波器来说更好。高斯滤波器最重要的参数就是高斯分布的标准差σσ,标准差和高斯滤波器的平滑能力有很大的能力,σσ越大,高斯滤波器的频带就较宽,对图像的平滑程度就越好。通过调节σσ参数,可以平衡对图像的噪声的抑制和对图像的模糊。-END-
推荐阅读
【01】C语言十大经典排序算法(动态演示+代码,值得收藏)【02】给大神鼓掌:400行Python代码实现文语处理助手!(NXP资深系统工程师精品)【03】知名半导体MCU大厂软件开发C代码规范【04】曾经写代码的马化腾,居然热衷于伪代码?一步步教你写伪代码!【05】致初级开发者的一封信:坚持写代码!免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除
python一维平滑滤波_高斯滤波器的原理及其实现过程(附模板代码)相关推荐
- python 曲线平滑滤波
python 曲线平滑滤波 Savitzky-Golay滤波器 import numpy as np import matplotlib.pyplot as plt import scipy.sign ...
- 图像处理之平滑滤波、高斯滤波和中值滤波
图像的滤波 图像的滤波概念 平滑滤波 高斯滤波 中值滤波 图像的滤波概念 图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到 ...
- 能使曲线变平滑的一维滤波器_双边滤波器的原理及实现
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的. 双边滤波器之所以能够做到在平 ...
- 怎么进行数据平滑滤波_气相色谱数据处理方法 EWG1990仪器学习网
2018/12/29 作者/EWG1990仪器学习网 本节涉及气相色谱仪的几种常用检测器(TCD.FID.ECD.FPD.NPD等)的数据采集和数据分析,这类信号不包含质谱.光谱类的辅助信息,其关键在 ...
- python点云滤波_点云滤波去噪
关于点云滤波去噪的方法 为什么进行点云滤波处理: (1) 点云数据密度不规则需要平滑 (2) 因为遮挡等问题造成离群点需要去除 (3) 大量数据需要下采样 (4) 噪声数据需要去除 1234 点云数据 ...
- FIR滤波器工作原理及实现过程介绍
引言 在现代电子系统中,到处都可以看到数字信号处理( DSP )的应用,从MP3播放器.数码相机到手机.DSP设计人员的工具箱的支柱之一是有限脉冲响应( FIR )滤波器.FIR滤波器越长(有大量的抽 ...
- VTK修炼之道37:图像平滑_高斯滤波器
1.高斯滤波 高斯平滑的原理类似于均值滤波.均值滤波模板的系数都是一样的,而高斯平滑则是需要根据像素与模板中心的距离来定义权重.权重的计算方法是采用高斯分布,离中心越远,权重越小. 下面是一个利用Ga ...
- python指数平滑预测_指数平滑方法简介
本文链接:个人站 | 简书 | CSDN 版权声明:除特别声明外,本博客文章均采用 BY-NC-SA 许可协议.转载请注明出处. 指数平滑(Exponential smoothing)是除了 ARIM ...
- 滑动平均滤波_单片机数字滤波算法如何实现?(附代码)
关注.星标公众号,直达精彩内容ID:技术让梦想更伟大整理:李肖遥 单片机主要作用是控制外围的器件,并实现一定的通信和数据处理.但在某些特定场合,不可避免地要用到数学运算,尽管单片机并不擅长实现算法和进 ...
最新文章
- Radboud科学家探索材料「量子大脑」——没有软件,硬件也可以实现机器学习 | AI日报...
- VS2017下编译 XP运行程序
- 网络空间站是计算机,超级病毒或入侵国际空间站生命维持计算机
- 【前端开发系列】—— 文字阴影与样式
- 图形处理(三)简单拉普拉斯网格变形-Siggraph 2004
- 基于DGCNN和概率图的轻量级信息抽取模型
- boost::contract模块实现overload的测试程序
- yolov2移植到android,darknet(yolov2)移植到caffe框架
- Angular的@Output与@Input理解
- SpringMVC自定义注入controller变量
- application.yml 动态替换_微软免费AI作文打分软件升级:雅思考研四六级都能用,还能查单词给替换...
- 技术专题:厦门9月30日限制路由(网络尖冰),WAYOS或ROS解决方案
- 数据库备份、还原的处理
- Java一键多值Map 之Guava Multimap 用法简介
- 手机app性能测试简介了解
- html中文本框角度旋转,如何设置Word文本框旋转,任意角度调整文本框的方向?...
- 运营支持是干什么_运营|你们运营到底是干什么的?
- 数据分析处理之词频统计
- 寻找丢失的iexplore进程
- 公历农历显示节日节气星期等万年历
热门文章
- python函数式编程、高阶函数
- python教程:__file__、__name__、__dict__三个方法的作用
- Python find() 方法
- python递归方式和普通方式实现输出和查询斐波那契数列
- C语言socket()函数解析(创建套接字)af地址族,ip地址类型(Address Family)INET(Inetnet)PF(Protocol Family)
- WEB服务器和HTTP服务器和应用服务器的区别?(web服务器就是HTTP服务器)为什么要把Web服务器独立配置,和应用程序服务器一前一后?
- python pyusb库使用教程【在window10系统上操作USB】(操作Intel Realsense D435)
- Visual Studio 中Debug模式和Release模式的区别
- linux 760权限,Linux 文件rwx权限问题 chmod 777 XXX 任何人拥有最高权限
- 不装客户端连接mysql_C#不安装oracle客户端,如何连接到oracle数据库