图像处理基础(4):高斯滤波器详解

news/2025/1/24 4:52:10/

本文主要介绍了高斯滤波器的原理及其实现过程

高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像。其作用原理和均值滤波器类似,都是取滤波器窗口内的像素的均值作为输出。其窗口模板的系数和均值滤波器不同,均值滤波器的模板系数都是相同的为1;而高斯滤波器的模板系数,则随着距离模板中心的增大而系数减小。所以,高斯滤波器相比于均值滤波器对图像个模糊程度较小。

什么是高斯滤波器

既然名称为高斯滤波器,那么其和高斯分布(正态分布)是有一定的关系的。一个二维的高斯函数如下:

其中

为点坐标,在图像处理中可认为是整数;

是标准差。要想得到一个高斯滤波器的模板,可以对高斯函数进行离散化,得到的高斯函数值作为模板的系数。例如:要产生一个

的高斯滤波器模板,以模板的中心位置为坐标原点进行取样。模板在各个位置的坐标,如下所示(x轴水平向右,y轴竖直向下)

这样,将各个位置的坐标带入到高斯函数中,得到的值就是模板的系数。
对于窗口模板的大小为 

,模板中各个元素值的计算公式如下:

这样计算出来的模板有两种形式:小数和整数。

·        小数形式的模板,就是直接计算得到的值,没有经过任何的处理;

·        整数形式的,则需要进行归一化处理,将模板左上角的值归一化为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]; // 将左上角的系数归一化为1for (int i = 0; i < ksize; i++){for (int j = 0; j < ksize; j++){window[i][j] *= k;}}
}

需要一个二维数组,存放生成的系数(这里假设模板的最大尺寸不会超过11);第二个参数是模板的大小(不要超过11);第三个参数就比较重要了,是高斯分布的标准差。
生成的过程,首先根据模板的大小,找到模板的中心位置ksize/2。 然后就是遍历,根据高斯分布的函数,计算模板中每个系数的值。
需要注意的是,最后归一化的过程,使用模板左上角的系数的倒数作为归一化的系数(左上角的系数值被归一化为1),模板中的每个系数都乘以该值(左上角系数的倒数),然后将得到的值取整,就得到了整数型的高斯滤波器模板。
下面截图生成的是,大小为

的模板

对上述解结果取整后得到如下模板:

这个模板就比较熟悉了,其就是根据

的高斯函数生成的模板。

至于小数形式的生成也比较简单,去掉归一化的过程,并且在求解过程后,模板的每个系数要除以所有系数的和。具体代码如下:

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]; // 将左上角的系数归一化为1for (int i = 0; i < ksize; i++){for (int j = 0; j < ksize; j++){window[i][j] /= sum;}}
}

的小数型模板。

值的意义及选取

通过上述的实现过程,不难发现,高斯滤波器模板的生成最重要的参数就是高斯分布的标准差

。标准差代表着数据的离散程度,如果

较小,那么生成的模板的中心系数较大,而周围的系数较小,这样对图像的平滑效果就不是很明显;反之,

较大,则生成的模板的各个系数相差就不是很大,比较类似均值模板,对图像的平滑效果比较明显。

来看下一维高斯分布的概率分布密度图:

横轴表示可能得取值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为图像的尺寸;ksize为高斯滤波器的尺寸。这样其时间复杂度为

,随滤波器的模板的尺寸呈平方增长,当高斯滤波器的尺寸较大时,其运算效率是极低的。为了,提高滤波的运算速度,可以将二维的高斯滤波过程分解开来。

分离实现高斯滤波

由于高斯函数的可分离性,尺寸较大的高斯滤波器可以分成两步进行:首先将图像在水平(竖直)方向与一维高斯函数进行卷积;然后将卷积后的结果在竖直(水平)方向使用相同的一维高斯函数得到的模板进行卷积运算。具体实现代码如下:

// 分离的计算
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;
}

代码没有重构较长,不过其实现原理是比较简单的。首先得到一维高斯函数的模板,在卷积(滤波)的过程中,保持行不变,列变化,在水平方向上做卷积运算;接着在上述得到的结果上,保持列不边,行变化,在竖直方向上做卷积运算。这样分解开来,算法的时间复杂度为

,运算量和滤波器的模板尺寸呈线性增长。

在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的结果对比

上图是

的高斯滤波器,可以看出两个实现得到的结果没有很大的区别。

总结

高斯滤波器是一种线性平滑滤波器,其滤波器的模板是对二维高斯函数离散得到。由于高斯模板的中心值最大,四周逐渐减小,其滤波后的结果相对于均值滤波器来说更好。
高斯滤波器最重要的参数就是高斯分布的标准差

,标准差和高斯滤波器的平滑能力有很大的能力,

越大,高斯滤波器的频带就较宽,对图像的平滑程度就越好。通过调节

参数,可以平衡对图像的噪声的抑制和对图像的模糊。


http://www.ppmy.cn/news/1565683.html

相关文章

HarmonyOS快速入门

HarmonyOS快速入门 1、基本概念 UI框架&#xff1a; HarmonyOS提供了一套UI开发框架&#xff0c;即方舟开发框架&#xff08;ArkUI框架&#xff09;。方舟开发框架可为开发者提供应用UI开发所必需的能力&#xff0c;比如多种组件、布局计算、动画能力、UI交互、绘制等。 方…

关于扫雷的自动补空实现C语言

关于扫雷的自动补空实现C语言 相信很多朋友在用C语言实现扫雷功能时&#xff0c;都想实现扫雷里面的的自动补足功能&#xff0c;但总是难以实现&#xff0c;在这里我将分享一种方法——递归思想 先看代码&#xff01; 位置在game.c //判断附近雷区并过滤空白区&#xff08;…

第6章:Python TDD实例变量私有化探索

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…

TCP协议与TCP SYN Flood攻击

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网络安全从菜鸟到飞鸟的逆袭 目录 一&#xff0c; TCP握手二&#xf…

Harmonyos之多目标构建产物实践

Harmonyos之多目标构建产物实践 概述基本概念实现原理多目标产物定制项定制HAP多目标产物定义产物的HAP包名定义产物的deviceType定义产物的资源定义产物的icon、label、launchType 配置APP多目标构建产物定义产物的APP包名和供应商名称定义product的bundleType定义Product的签…

Navicat 导出表结构后运行查询失败ERROR 1064 (42000): You have an error in your SQL syntax;

本文主要介绍了在使用 Navicat 导出 MySQL 表后新建查询时出现报错的问题及解决方案。 一、问题描述 Navicat导出MySql中的表&#xff0c;在新建数据库新建查询时通常会报错You have an error in your SQL syntax; check the manual that corresponds to your MySQL server …

C++通过输入3D相机像素点集{u、v、z}和机械手世界坐标点集{X、Y、Z}求得变换矩阵RT(眼在手外)

👑主页:吾名招财 👓简介:工科学硕,研究方向机器视觉,爱好较广泛… ​💫签名:面朝大海,春暖花开! C++通过输入3D相机像素点集{u、v、z}和机械手世界坐标点集{X、Y、Z}求得变换矩阵RT(眼在手外) 引言原理简介点集数据(含像素坐标、世界坐标及求解后的变换矩阵)配…

wireshark工具简介

目录 1 wireshark介绍 2 wireshark抓包流程 2.1 选择网卡 2.2 停止抓包 2.3 保存数据 3 wireshark过滤器设置 3.1 显示过滤器的设置 3.2 抓包过滤器 4 wireshark的封包列表与封包详情 4.1 封包列表 4.2 封包详情 参考文献 1 wireshark介绍 wireshark是非常流行的网络…