高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像。其作用原理和均值滤波器类似,都是取滤波器窗口内的像素的均值作为输出。其窗口模板的系数和均值滤波器不同,均值滤波器的模板系数都是相同的为1;而高斯滤波器的模板系数,则随着距离模板中心的增大而系数减小。所以,高斯滤波器相比于均值滤波器对图像个模糊程度较小。
什么是高斯滤波器
既然名称为高斯滤波器,那么其和高斯分布(正态分布)是有一定的关系的。一个二维的高斯函数如下:
![](https://i-blog.csdnimg.cn/blog_migrate/cce486a1d10141b953c92bf852dafdc7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4f32db407280758470c2004805a16471.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b3c164adadc5bae062be9dfc12b15631.png)
![](https://i-blog.csdnimg.cn/blog_migrate/36c0f36e095fb061f65d159796349b73.png)
![](https://i-blog.csdnimg.cn/blog_migrate/59846ef009a2a2d1574bfe75dc771e52.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d65fe68cf33f3fac346b00dc819b4d5c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7aad9f6a4db33ddccc71598aa1dfea45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6de38a1752e79f136b89aeeac89da9b6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8bab1761f277ad86ad167dc49937890c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5396f5cd51591aec9986bb1c22843b9d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/62ff7b71fedc9a0e04f653c849a3d101.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f530077b31c8e884eb50c75913a9842e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/89eeebcc4d2436e5bf09c9a2c5180374.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
其中
![](https://i-blog.csdnimg.cn/blog_migrate/4f32db407280758470c2004805a16471.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b3c164adadc5bae062be9dfc12b15631.png)
![](https://i-blog.csdnimg.cn/blog_migrate/36c0f36e095fb061f65d159796349b73.png)
![](https://i-blog.csdnimg.cn/blog_migrate/59846ef009a2a2d1574bfe75dc771e52.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d65fe68cf33f3fac346b00dc819b4d5c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e505592440aa912213ebe27151c57f86.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b5587efe15e6a5796ef428d704bdf7e3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb7a81e8e26c15f6220df17c73f4e3e7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b5587efe15e6a5796ef428d704bdf7e3.png)
![](http://images2015.cnblogs.com/blog/439761/201702/439761-20170216214155582-1833428662.png)
这样,将各个位置的坐标带入到高斯函数中,得到的值就是模板的系数。
对于窗口模板的大小为
,模板中各个元素值的计算公式如下:
![](https://i-blog.csdnimg.cn/blog_migrate/ecf02d35667d5eaba2d2ffbce3a9abaa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b046e2987762b99662e1d2c8f79de484.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4faf4be91aea777a6a6be2917063fc0b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f02dfd234e3af3ed89e362b733987f3b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7aad9f6a4db33ddccc71598aa1dfea45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ed0f63c39ab9321c1d4d873167820fdd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/314097c0ca93144dea3fb3fcd107ad7e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eb3887bce2b513a76cc2e63072a86467.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e505592440aa912213ebe27151c57f86.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8fe9bb007e5347dfd06b48bd4b2f8e39.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6de38a1752e79f136b89aeeac89da9b6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8bab1761f277ad86ad167dc49937890c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9161e803573665dd11a3c97f0eee1ead.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d0d930727914c9b34581c6f69a95303e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/db8cbc446e1aa2b87bcd4dc91edb5b3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/148a83747c0725cb731dfacf52d4b6bf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/db8cbc446e1aa2b87bcd4dc91edb5b3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c48ea072afa49356d8e2956fc761a42c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c9511a2121c696a145396d5c973366b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/62ff7b71fedc9a0e04f653c849a3d101.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9161e803573665dd11a3c97f0eee1ead.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d16fbbb8c8123af247c2de588b0d9f12.png)
![](https://i-blog.csdnimg.cn/blog_migrate/db8cbc446e1aa2b87bcd4dc91edb5b3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/148a83747c0725cb731dfacf52d4b6bf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/db8cbc446e1aa2b87bcd4dc91edb5b3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c48ea072afa49356d8e2956fc761a42c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c9511a2121c696a145396d5c973366b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/89eeebcc4d2436e5bf09c9a2c5180374.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2392ce8cc8840253f8049abd842f0a56.png)
这样计算出来的模板有两种形式:小数和整数。
- 小数形式的模板,就是直接计算得到的值,没有经过任何的处理;
- 整数形式的,则需要进行归一化处理,将模板左上角的值归一化为1,下面会具体介绍。使用整数的模板时,需要在模板的前面加一个系数,系数为
也就是模板系数和的倒数。
double **getTwoGuassionArray(int size, double sigma) {
double sum = 0.0;
int kerR = size / 2;
double **arr = new double*[size];
for (int i = 0; i < size; i++)
{
arr[i] = new double[size];
}
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
arr[i][j] = exp(-((i-kerR)*(i-kerR) + (j-kerR)*(j-kerR)) / (2 * sigma*sigma));
sum += arr[i][j];
}
}
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
arr[i][j] /= sum;
cout << arr[i][j] << endl;
}
}
return arr;
}
一维高斯模板的生成:
double *getOneGuassionArray(int size, double sigma)
{
double sum = 0.0;
int kerR = size / 2;
double *arr = new double[size];
for (int i = 0; i < size; i++)
{
arr[i] = exp(-((i - kerR)*(i - kerR)) / (2 * sigma*sigma));
sum += arr[i];
}
for (int i = 0; i < size; i++)
{
arr[i] /= sum;
cout << arr[i] << endl;
}
return arr;
}
值的意义及选取
通过上述的实现过程,不难发现,高斯滤波器模板的生成最重要的参数就是高斯分布的标准差
。标准差代表着数据的离散程度,如果
较小,那么生成的模板的中心系数较大,而周围的系数较小,这样对图像的平滑效果就不是很明显;反之,
较大,则生成的模板的各个系数相差就不是很大,比较类似均值模板,对图像的平滑效果比较明显。
来看下一维高斯分布的概率分布密度图:
横轴表示可能得取值x,竖轴表示概率分布密度F(x),那么不难理解这样一个曲线与x轴围成的图形面积为1。
(标准差)决定了这个图形的宽度,可以得出这样的结论:
越大,则图形越宽,尖峰越小,图形较为平缓;
越小,则图形越窄,越集中,中间部分也就越尖,图形变化比较剧烈。这其实很好理解,如果sigma也就是标准差越大,则表示该密度分布一定比较分散,由于面积为1,于是尖峰部分减小,宽度越宽(分布越分散);同理,当
越小时,说明密度分布较为集中,于是尖峰越尖,宽度越窄!
于是可以得到如下结论:
越大,分布越分散,各部分比重差别不大,于是生成的模板各元素值差别不大,类似于平均模板;
越小,分布越集中,中间部分所占比重远远高于其他部分,反映到高斯模板上就是中心元素值远远大于其他元素值,于是自然而然就相当于中间值得点运算。
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;
double **getTwoGuassionArray(int size, double sigma) {
double sum = 0.0;
int kerR = size / 2;
double **arr = new double*[size];
for (int i = 0; i < size; i++)
{
arr[i] = new double[size];
}
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
arr[i][j] = exp(-((i-kerR)*(i-kerR) + (j-kerR)*(j-kerR)) / (2 * sigma*sigma));
sum += arr[i][j];
}
}
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
arr[i][j] /= sum;
cout << arr[i][j] << endl;
}
}
return arr;
}
void MyGaussianBlur(Mat &srcImage, Mat &dst,int size)
{
CV_Assert(srcImage.channels() || srcImage.channels() == 3);
int kerR = size / 2;
dst = srcImage.clone();
int channels = dst.channels();
double** arr;
arr = getTwoGuassionArray(size, 1);
for (int i = kerR; i < dst.rows - kerR; i++)
{
for (int j = kerR; j < dst.cols - kerR; j++)
{
double GuassionSum[3] = { 0 };
for (int k = -kerR; k <= kerR; k++)
{
for (int n = -kerR; n <= kerR; n++)
{
if (channels == 1)
{
GuassionSum[0] += arr[kerR + k][kerR + n] * dst.at<uchar>(i + k, j + n);
}
else if(channels == 3)
{
Vec3b bgr = dst.at<Vec3b>(i + k, j + n);
auto a = arr[kerR + k][kerR + n];
GuassionSum[0] += a*bgr[0];
GuassionSum[1] += a*bgr[1];
GuassionSum[2] += a*bgr[2];
}
}
}
for (int k = 0; k < channels; k++)
{
if (GuassionSum[k] < 0)
GuassionSum[k] = 0;
else if (GuassionSum[k] > 255)
GuassionSum[k] = 255;
}
if (channels == 1)
dst.at<uchar>(i, j) = static_cast<uchar>(GuassionSum[0]);
else if (channels == 3)
{
Vec3b bgr = { static_cast<uchar>(GuassionSum[0]), static_cast<uchar>(GuassionSum[1]), static_cast<uchar>(GuassionSum[2]) };
dst.at<Vec3b>(i, j) = bgr;
}
}
}
for (int i = 0; i < size; i++)
delete[] arr[i];
delete[] arr;
}
int main()
{
Mat srcImage = imread("flower3.jpg");
if (!srcImage.data)
{
printf("could not load image...\n");
return -1;
}
//getGuassionArray(3, 1);
Mat resMat;
MyGaussianBlur(srcImage, resMat, 3);
imshow("src", srcImage);
imshow("resMat", resMat);
waitKey(0);
return 0;
}
只处理单通道或者三通道图像,模板生成后,其滤波(卷积过程)就比较简单了。不过,这样的高斯滤波过程,其循环运算次数为
,其中m,n为图像的尺寸;ksize为高斯滤波器的尺寸。这样其时间复杂度为
,随滤波器的模板的尺寸呈平方增长,当高斯滤波器的尺寸较大时,其运算效率是极低的。为了,提高滤波的运算速度,可以将二维的高斯滤波过程分解开来。
分离实现高斯滤波
由于高斯函数的可分离性,尺寸较大的高斯滤波器可以分成两步进行:首先将图像在水平(竖直)方向与一维高斯函数进行卷积;然后将卷积后的结果在竖直(水平)方向使用相同的一维高斯函数得到的模板进行卷积运算。具体实现代码如下:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;
double *getOneGuassionArray(int size, double sigma)
{
double sum = 0.0;
int kerR = size / 2;
double *arr = new double[size];
for (int i = 0; i < size; i++)
{
arr[i] = exp(-((i - kerR)*(i - kerR)) / (2 * sigma*sigma));
sum += arr[i];
}
for (int i = 0; i < size; i++)
{
arr[i] /= sum;
cout << arr[i] << endl;
}
return arr;
}
void MyGaussianBlur(Mat &srcImage, Mat &dst, int size)
{
CV_Assert(srcImage.channels() || srcImage.channels() == 3);
int kerR = size / 2;
dst = srcImage.clone();
int channels = dst.channels();
double* arr;
arr = getOneGuassionArray(size, 1);
for (int i = kerR; i < dst.rows - kerR; i++)
{
for (int j = kerR; j < dst.cols - kerR; j++)
{
double GuassionSum[3] = { 0 };
for (int k = -kerR; k <= kerR; k++)
{
if (channels == 1)
{
GuassionSum[0] += arr[kerR + k] * dst.at<uchar>(i , j + k);
}
else if (channels == 3)
{
Vec3b bgr = dst.at<Vec3b>(i, j + k);
auto a = arr[kerR + k];
GuassionSum[0] += a*bgr[0];
GuassionSum[1] += a*bgr[1];
GuassionSum[2] += a*bgr[2];
}
}
for (int k = 0; k < channels; k++)
{
if (GuassionSum[k] < 0)
GuassionSum[k] = 0;
else if (GuassionSum[k] > 255)
GuassionSum[k] = 255;
}
if (channels == 1)
dst.at<uchar>(i, j) = static_cast<uchar>(GuassionSum[0]);
else if (channels == 3)
{
Vec3b bgr = { static_cast<uchar>(GuassionSum[0]), static_cast<uchar>(GuassionSum[1]), static_cast<uchar>(GuassionSum[2]) };
dst.at<Vec3b>(i, j) = bgr;
}
}
}
for (int i = kerR; i < dst.rows - kerR; i++)
{
for (int j = kerR; j < dst.cols - kerR; j++)
{
double GuassionSum[3] = { 0 };
for (int k = -kerR; k <= kerR; k++)
{
if (channels == 1)
{
GuassionSum[0] += arr[kerR + k] * dst.at<uchar>(i + k, j);
}
else if (channels == 3)
{
Vec3b bgr = dst.at<Vec3b>(i + k, j);
auto a = arr[kerR + k];
GuassionSum[0] += a*bgr[0];
GuassionSum[1] += a*bgr[1];
GuassionSum[2] += a*bgr[2];
}
}
for (int k = 0; k < channels; k++)
{
if (GuassionSum[k] < 0)
GuassionSum[k] = 0;
else if (GuassionSum[k] > 255)
GuassionSum[k] = 255;
}
if (channels == 1)
dst.at<uchar>(i, j) = static_cast<uchar>(GuassionSum[0]);
else if (channels == 3)
{
Vec3b bgr = { static_cast<uchar>(GuassionSum[0]), static_cast<uchar>(GuassionSum[1]), static_cast<uchar>(GuassionSum[2]) };
dst.at<Vec3b>(i, j) = bgr;
}
}
}
delete[] arr;
}
int main()
{
Mat srcImage = imread("flower3.jpg");
if (!srcImage.data)
{
printf("could not load image...\n");
return -1;
}
//getGuassionArray(3, 1);
Mat resMat;
MyGaussianBlur(srcImage, resMat, 3);
imshow("src", srcImage);
imshow("resMat", resMat);
waitKey(0);
return 0;
}
代码没有重构较长,不过其实现原理是比较简单的。首先得到一维高斯函数的模板,在卷积(滤波)的过程中,保持
行不变,列变化,在水平方向上做卷积运算
;接着在上述得到的结果上,保持
列不边,行变化,在竖直方向上做卷积运算
。 这样分解开来,算法的时间复杂度为
![](https://i-blog.csdnimg.cn/blog_migrate/9b0e803c1c34468aef8b634b5abc89ba.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4f32db407280758470c2004805a16471.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ca3b8b37107473072f06381aff01998e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/267aef1bcd1d7399976d81923e5c4a2e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/315effcf4e9b5c02789c27fcfc0de849.png)
![](https://i-blog.csdnimg.cn/blog_migrate/235eaa7afbd91ce502a14d4c83222441.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6de38a1752e79f136b89aeeac89da9b6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d65fe68cf33f3fac346b00dc819b4d5c.png)
原图: