图像处理基础(4):高斯滤波器详解
来源:互联网 发布:知乎同济大学环境工程 编辑:程序博客网 时间:2024/06/01 14:32
本文主要介绍了高斯滤波器的原理及其实现过程
高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像。其作用原理和均值滤波器类似,都是取滤波器窗口内的像素的均值作为输出。其窗口模板的系数和均值滤波器不同,均值滤波器的模板系数都是相同的为1;而高斯滤波器的模板系数,则随着距离模板中心的增大而系数减小。所以,高斯滤波器相比于均值滤波器对图像个模糊程度较小。
什么是高斯滤波器
既然名称为高斯滤波器,那么其和高斯分布(正态分布)是有一定的关系的。一个二维的高斯函数如下:
其中
这样,将各个位置的坐标带入到高斯函数中,得到的值就是模板的系数。
对于窗口模板的大小为
这样计算出来的模板有两种形式:小数和整数。
- 小数形式的模板,就是直接计算得到的值,没有经过任何的处理;
- 整数形式的,则需要进行归一化处理,将模板左上角的值归一化为1,下面会具体介绍。使用整数的模板时,需要在模板的前面加一个系数,系数为
1∑(i,j)∈wwi,j ,也就是模板系数和的倒数。
高斯模板的生成
知道模板生成的原理,实现起来也就不困难了
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),模板中的每个系数都乘以该值(左上角系数的倒数),然后将得到的值取整,就得到了整数型的高斯滤波器模板。
下面截图生成的是,大小为
对上述解结果取整后得到如下模板:
这个模板就比较熟悉了,其就是根据
至于小数形式的生成也比较简单,去掉归一化的过程,并且在求解过程后,模板的每个系数要除以所有系数的和。具体代码如下:
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; } }}
σ 值的意义及选取
通过上述的实现过程,不难发现,高斯滤波器模板的生成最重要的参数就是高斯分布的标准差
来看下一维高斯分布的概率分布密度图:
横轴表示可能得取值x,竖轴表示概率分布密度F(x),那么不难理解这样一个曲线与x轴围成的图形面积为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<uchar>(i + a, j + b); } else if (channels == 3) { Vec3b rgb = dst.at<Vec3b>(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<uchar>(i, j) = static_cast<uchar>(sum[0]); else if (channels == 3) { Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) }; dst.at<Vec3b>(i, j) = rgb; } } } // 释放模板数组 for (int i = 0; i < ksize; i++) delete[] templateMatrix[i]; delete[] templateMatrix;}
只处理单通道或者三通道图像,模板生成后,其滤波(卷积过程)就比较简单了。不过,这样的高斯滤波过程,其循环运算次数为
分离实现高斯滤波
由于高斯函数的可分离性,尺寸较大的高斯滤波器可以分成两步进行:首先将图像在水平(竖直)方向与一维高斯函数进行卷积;然后将卷积后的结果在竖直(水平)方向使用相同的一维高斯函数得到的模板进行卷积运算。具体实现代码如下:
// 分离的计算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<uchar>(i, j + k); // 行不变,列变化;先做水平方向的卷积 } else if (channels == 3) { Vec3b rgb = dst.at<Vec3b>(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<uchar>(i, j) = static_cast<uchar>(sum[0]); else if (channels == 3) { Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) }; dst.at<Vec3b>(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<uchar>(i + k, j); // 列不变,行变化;竖直方向的卷积 } else if (channels == 3) { Vec3b rgb = dst.at<Vec3b>(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<uchar>(i, j) = static_cast<uchar>(sum[0]); else if (channels == 3) { Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) }; dst.at<Vec3b>(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
的结果对比
上图是
总结
高斯滤波器是一种线性平滑滤波器,其滤波器的模板是对二维高斯函数离散得到。由于高斯模板的中心值最大,四周逐渐减小,其滤波后的结果相对于均值滤波器来说更好。
高斯滤波器最重要的参数就是高斯分布的标准差
- 图像处理基础(4):高斯滤波器详解
- 图像处理基础(4):高斯滤波器详解
- matlab 图像处理--高斯滤波器
- 【图像处理基础】滤波器篇
- 图像处理-线性滤波-3 高斯滤波器
- 图像处理-线性滤波-3 高斯滤波器
- 图像处理-线性滤波-3 高斯滤波器
- 图像处理-线性滤波-3 高斯滤波器
- 图像处理-线性滤波-3 高斯滤波器
- 图像处理-线性滤波-3 高斯滤波器
- Matlab 高斯_拉普拉斯滤波器处理医学图像
- 图像处理-线性滤波-3 高斯滤波器
- 高斯滤波器详解
- 图像处理滤波器(三)——高斯平滑滤波器(Gaussian Smoothing Filter)
- 图像处理基础(5):双边滤波器
- 图像处理基础(6):锐化空间滤波器
- 高斯滤波器理解与图像滤波
- Python图像处理(4):滤波器
- 地址无关码
- stl的vector和list的push_back效率比较
- 业余时间学习安排
- [mmc subsystem] host(第二章)——sdhci
- POJ3181-Dollar Dayz-完全背包+高精度
- 图像处理基础(4):高斯滤波器详解
- [缓慢前行]C# OLEDB方式访问Excel之bug
- eclipse 启动失败,报错org.eclipse.swt.SWTException: Failed to execute runnable
- java String.valueOf()的作用
- Myeclipse clean的作用
- BZOJ 3884: 上帝与集合的正确用法
- [mmc subsystem] host(第三章)——sdhci-pltfm说明
- java 线程 join
- Textview,EditText属性