本文整理自以下博客:
https://blog.csdn.net/u013355826/article/details/69257526
https://blog.csdn.net/sunmc1204953974/article/details/50634652
滤波器的作用
1、数字图像信号的频率分布?
回答:信号或者图像的能量大部分在中低频,少部分有用信号在高频段被噪声淹没(噪声都是高频信号)。因此设计滤波器能降低高频成分幅度就能减弱噪声影响。
2、为什么进行图像滤波?
回答:适应图像处理要求,消除图像数字化时所混入的噪声;提取对象特征作为图像识别的特征模式。
3、如何理解滤波器?
回答:把滤波器想象成一个包含加权系数的窗口,当时用滤波器平滑图像时,相当于把窗口放大图像上,透过窗口看图像。
4、滤波器实现的结果是怎样的?
回答:对图像做平滑或者滤波后图像变得更模糊。
在介绍双边滤波器之前先介绍高斯滤波器的原理:
图像滤波之高斯滤波(Gauss filter)
高斯滤波:
高斯滤波在图像处理概念下,将图像频域处理和时域处理相联系,作为低通滤波器使用,可以将低频能量(比如噪声)滤去,起到图像平滑作用。
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值用。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。
高斯模糊:
我们常说的高斯模糊就是使用高斯滤波器完成的,高斯模糊是低通滤波的一种,也就是滤波函数是低通高斯函数,但是高斯滤波是指用高斯函数作为滤波函数,至于是不是模糊,要看是高斯低通还是高斯高通,低通就是模糊,高通就是锐化。
算法步骤:
在图像处理中,高斯滤波一般有两种实现方式,一是用离散化窗口滑窗卷积,另一种通过傅里叶变换。最常见的就是第一种滑窗实现,只有当离散化的窗口非常大,用滑窗计算量非常大(即使用可分离滤波器的实现)的情况下,可能会考虑基于傅里叶变化的实现方法。
由于高斯函数可以写成可分离的形式,因此可以采用可分离滤波器实现来加速。所谓的可分离滤波器,就是可以把多维的卷积化成多个一维卷积。具体到二维的高斯滤波,就是指先对行做一维卷积,再对列做一维卷积。这样就可以将计算复杂度从O(M*M*N*N)降到O(2*M*M*N),M,N分别是图像和滤波器的窗口大小。
卷积是一个单纯的定义,本身没有什么意义可言,但是其在各个领域的应用是十分广泛的,在滤波中可以理解为一个加权平均过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到,而如何加权则是依据核函数高斯函数。
二维高斯函数(中心为原点):
计算平均值的时候,我们只需要将”中心点”作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值,而这就是上述的与二维高斯核进行卷积的过程。
假定中心点的坐标是(0,0),那么距离它最近的8个点的坐标如下:
假定σ=1.5,则模糊半径为1的权重矩阵如下:
这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵:
有了权重矩阵,就可以计算高斯模糊的值了。
中心点以及周边n个点,每个点乘以自己的权重值并将这些值相加,就是中心点的高斯模糊的值。对所有点重复这个过程,就得到了高斯模糊后的图像。
如果原图是彩色图片,可以对RGB三个通道分别做高斯模糊。
代码:
#include "opencv2/opencv.hpp"
#include"opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#define n 5
#define sigma 1
#define PI 3.14125
void GaussianFilter(Mat &src, Mat &dst,double* ker)
{
dst = src.clone();
int row = dst.rows;
int col = dst.cols;
int channal = dst.channels();
int ncol = col * channal;
int k = n/2;
double sum ;
int index;
for (int i = k; i < (row-k) ; i++) //n/2保证边缘的像素点不被处理
{
uchar* ptdst = dst.ptr<uchar>(i);
for (int j = channal*k;j < (ncol-channal*k); j++) //n/2保证边缘的像素点不被处理
{
sum = index = 0;
for (int kx = (i-k);kx <= i+k; kx++) //内层 kx、ky循环求和
{
uchar* ptrsrc = src.ptr<uchar>(kx);
for (int ky = (j-channal*k);ky <=(j+channal*k); ky = (ky+channal))
sum += (ptrsrc[ky]*ker[index++]);
}
ptdst[j] =saturate_cast<uchar>(sum);
}
}
}
void GetGaussianKernal(double* nkernal)
{
double temp = 0;
double h_sum = 0;
double distance = 0;
double kernal[n][n];
int center_x = n/2,center_y = n/2;
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
distance = (i-center_x)*(i-center_x)+(j-center_x)*(j-center_x);
temp = exp(-(distance)/(2*sigma*sigma))/(2*PI*sigma*sigma);
kernal[i][j] = temp;
h_sum += temp;
}
}
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
kernal[i][j] /= h_sum;
nkernal[n*i+j] = kernal[i][j];
}
}
}
int main()
{
Mat src, dst,dst1;
int index = 0;
double nkernal[n*n];
src = imread("image//depth_right.jpg");
namedWindow("Win1");
imshow("Win1", src);
GetGaussianKernal(nkernal);
for (int i=0;i<n*n;i++)
printf("%0.10f ",nkernal[index++]);
GaussianFilter(src,dst1,nkernal);
namedWindow("Win2");
imshow("Win2", dst1);
GaussianBlur(src,dst,Size(5,5),0,0);
namedWindow("Win3");
imshow("Win3", dst);
waitKey(0);
return 0;
}
也可调用opencv中已经写好的高斯滤波器的函数。
使用实例:
#include<opencv2/opencv.hpp>
void mian()
{
cv::Mat srcimage = cv::imread("腐蚀_膨胀.png");
cv::namedWindow("srcimage");
cv::namedWindow("dstimage");
cv::imshow("srcimage", srcimage);
cv::Mat dstimage;
cv::GaussianBlur(srcimage, dstimage, cv::Size(5, 5), 0, 0);
cv::imshow("dstimage", dstimage);
cv::waitKey(0);
}