图像滤波的主要目的是为了在保留图像细节的情况下尽量的对图像的噪声进行消除,从而是后来的图像处理变得更加的方便.
图像的滤波效果要满足两个条件:1.不能损坏图像的轮廓和边缘这些重要的特征信息.2.图像的视觉效果更好
opencv支持图像滤波,提供了五个基本算法,分别是方框滤波,均值滤波,高斯滤波,中值滤波以及双边滤波,前三种为线性滤波算法,后两种为非线性滤波算法,接下来分别对这五种滤波方式进行讲解和演示
一.方框滤波
首先,线性滤波算法必须知道的概念叫做邻域算子,是指利用一张图片中给点的像素点的周围的像素值决定该给定点的像素的一种算子,爱opencv中经常使用,用来做图像的模糊或者是锐化,例如,邻域算子实际上是一个X*Y的矩阵和该矩阵怎么和图像中的像素进行计算并得到最终的结果的一种集合性质的描述.
就像程序=算法+数据结构一样,邻域算子=矩阵+计算方法,计算方法可以是乘法,除法,求和,卷积等.
方框滤波算法的原理很简单,指定一个X*Y的矩阵大小,目标像素的周围X*Y矩阵内的像素全部相加作为目标像素的值,就这么简单.
API: void boxFilter(源图像,目的图像,int 输出图像的深度,Size 邻域算子的大小,Point 锚点,bool 归一化标志,int 边界模式).
说明:1.源图像可以是彩色图或者是灰度图,因为图像处理的时候,通道是分开来处理的
2.目的图像的大小和通道数必须和源图像相同,另外,目的图像的深度由后一个参 数决定
3.输出图像的深度,指定CV_8U....,当为-1的时候,目标图像的深度和源图像的深度 相同
4.锚点,指被平滑的那个点位于领域算子矩阵的哪一个位置,从而确定计算时候的 像素点的取值,如果是Point(-1,-1),那么自动取值中间的那个点.
5.归一化标志,默认归一化为真,会将目标图像归一化到源图像的取值范围
可以看到,我们可以改变的参数就是邻域算子的大小,通过这种算法,我们能够去除图像中突然出现的极大值点(噪点)的影响,但是同时,这种算法不能很好的保存图像的细节,在滤波的时候容易丢失细节.一下是方框滤波的程序演示.
//方¤?框¨°滤?波¡§ const int g_boxFilterMax = 100;//最大的邻域取值 int g_nboxFilterValue; Mat boxFilterImage; Mat srcImage; void onBoxFilterTrackBar(int pos,void* userData); int main(int argc,char* argv[]) { g_nboxFilterValue = 5; srcImage = imread("F:\\opencv\\OpenCVImage\\boxFilter.jpg"); namedWindow("box filter"); createTrackbar("size value ", "box filter", &g_nboxFilterValue, g_boxFilterMax,onBoxFilterTrackBar,0); onBoxFilterTrackBar(g_nboxFilterValue, 0); imshow("src image", srcImage); moveWindow("src image", 0, 0); moveWindow("box filter", srcImage.cols, 0); waitKey(0); return 0; } void onBoxFilterTrackBar(int pos,void* userData) { if(pos == 0) { imshow("box filter", srcImage); } else { boxFilter(srcImage, boxFilterImage, srcImage.depth(), Size(pos,pos)); imshow("box filter", boxFilterImage); } }
二.均值滤波
均值滤波其实就是方框滤波的默认归一化版本,这个算法接口取消了归一化参数,所以实际效果和方框滤波,差异不大
API void blur(源图,目标图,size,锚点,边界模式);
实际操作代码如下
// 均值滤波 Mat srcImage; const int g_blurMax = 100; int g_nblurValue; Mat blurImage; void onBlurTrackBar(int pos,void* userData); int main(int argc,char* argv[]) { srcImage = imread("F:\\opencv\\OpenCVImage\\boxFilter.jpg"); g_nblurValue = 5; namedWindow("blur"); createTrackbar("size value ", "blur", &g_nblurValue, g_blurMax,onBlurTrackBar,0); onBlurTrackBar(g_nblurValue, 0); imshow("src image", srcImage); moveWindow("src image", 0, 0); moveWindow("blur", srcImage.cols, 0); waitKey(0); return 0; } void onBlurTrackBar(int pos,void* userData) { if(pos == 0) { imshow("blur", srcImage); } else { boxFilter(srcImage, blurImage, srcImage.depth(), Size(pos,pos)); imshow("blur", blurImage); } }
三.高斯滤波
高斯滤波是专门用于消除满足高斯分布(正态分布)的误差而存在的滤波,此时邻域算子是专门的高斯核,图像中的像素与高斯核做卷积,生成的结果加权平均存放到目标像素中,对于抑制符合正态分布的噪声非常有效,并可以增强图像值不同比例下的图像效果,视觉效果类似于隔了一层半透明玻璃看图像.
API:void GaussianBlur(源图像,目标图像,Size 高斯内核,double 高斯核函数在X方向上的标准差,double 高斯核函数在Y方向的标准差,int 边界模式).
注:高斯核函数的大小必须为奇数,同时也必须为正数,原图片可以使单通道或者是多通道,如果X,Y方向标准差都为0,那么API将自动根据核函数的长宽,计算出一个合适的二维零均值高斯函数.
使用代码如下
Mat srcImage; const int g_GaussianBlurMax = 100; int g_nGaussianBlurValue; Mat GaussianBlurImage; void onGaussianBlurTrackBar(int pos,void* userData); int main(int argc,char* argv[]) { srcImage = imread("F:\\opencv\\OpenCVImage\\boxFilter.jpg"); g_nGaussianBlurValue = 5; namedWindow("GaussianBlur"); createTrackbar("size value ", "GaussianBlur", &g_nGaussianBlurValue, g_GaussianBlurMax,onGaussianBlurTrackBar,0); onGaussianBlurTrackBar(g_nGaussianBlurValue, 0); imshow("src image", srcImage); moveWindow("src image", 0, 0); moveWindow("GaussianBlur", srcImage.cols, 0); waitKey(0); return 0; } void onGaussianBlurTrackBar(int pos,void* userData) { if(pos == 0) { imshow("GaussianBlur", srcImage); } else { //高斯滤波核必须为奇数 if(pos%2 == 0) { pos++; } GaussianBlur(srcImage, GaussianBlurImage, Size(pos,pos), 0,0);//自动计算xy标准差 imshow("GaussianBlur",GaussianBlurImage); } }
通过以上的情况我们可以看到,对于那种散粒噪声,尖峰噪声,线性滤波往往不能很好的清除,或者清除之后必然会带来对邻域的影响,噪声只是变得柔和了而已.这时候就可以考虑非线性滤波
四.中值滤波
线性滤波的基本原理是对指定像素的指定邻域的灰度值进行一个排序,然后选择中间的灰度值来直接替代指定像素的灰度值,从而消除孤立的噪声点,对于斑点噪声和椒盐噪声尤其有用,同时还可以保存边缘.对脉冲型的干扰尤其有效,因为在实际情况中,噪声点的灰度和邻域的值的差别很大,所以,中值一般不容易是噪声点灰度.但是因为基于统计排序,所以运行时间一般是均值滤波的五倍以上.
API:void medianBlur(源图像,目标图像,int 邻域大小)
注:邻域必须是大于1的奇数
使用例子如下
const int g_MedianBlurMax = 100; int g_nMedianBlurValue; Mat MedianBlurImage; Mat srcImage; void onMedianBlurTrackBar(int pos,void* userData); int main(int argc,char* argv[]) { srcImage = imread("F:\\opencv\\OpenCVImage\\boxFilter.jpg"); g_nMedianBlurValue = 5; namedWindow("median filter"); createTrackbar("size value ", "median filter", &g_nMedianBlurValue, g_MedianBlurMax,onMedianBlurTrackBar,0); onMedianBlurTrackBar(g_nMedianBlurValue, 0); imshow("src image", srcImage); moveWindow("src image", 0, 0); moveWindow("median filter", srcImage.cols, 0); waitKey(0); return 0; } void onMedianBlurTrackBar(int pos,void* userData) { if(pos == 0) { imshow("median filter", srcImage); } else { //中值¦滤波孔径大小必须为奇数 if(pos%2 == 0) { pos++; } medianBlur(srcImage, MedianBlurImage, pos); imshow("median filter",MedianBlurImage); } }
五.双边滤波
前面的滤波,或多或少的都会使得目标点的像素受到邻域的影响,邻域越大,影响越大,而双边滤波同时考虑了图像的灰度相似性和空间域信息,基本理念是邻域的计算附带权重,距离目标点越远的邻域像素对目标像素的影响越小,也就是权重越低,这样,离得较远的像素就不会对边缘的影响过多,能很好的保存边缘,同时也能滤波部分噪声.
但是,双边滤波保存了过多的高频信息,对于彩色图像中的高频噪声,双边滤波不能很好的滤除掉.
API:void bilateralFilter(源图像,目标图像,int 像素邻域直径, ,double 颜色空间滤波器sigma doble 坐标空间滤波器sigma,int 边缘类型);
注:1.若像素直径为负数,会自动从坐标空间滤波器sigma来计算像素邻域直径.
2.颜色空间滤波器sigma值,这个值越大,代表像素领域内越宽广的颜色会被混合在一 起,造成更大的半相等的颜色区域
3.坐标空间滤波器sigma,这是坐标空间的标准方差,这个值越大,代表邻域空间中越远 的值会相互影响
例程如下
Mat srcImage; const int g_BialateralFilterMax = 100; int g_nBialateralFilterValue; Mat BialateralFilterImage; void onBialateralFilterTrackBar(int pos,void* userData); int main(int argc,char* argv[]) { srcImage = imread("F:\\opencv\\OpenCVImage\\boxFilter.jpg"); g_nBialateralFilterValue = 5; namedWindow("bilateral filter"); createTrackbar("size value ", "bilateral filter", &g_nBialateralFilterValue, g_BialateralFilterMax,onBialateralFilterTrackBar,0); onBialateralFilterTrackBar(g_nBialateralFilterValue, 0); imshow("src image", srcImage); moveWindow("src image", 0, 0); moveWindow("bilateral filter", srcImage.cols, 0); waitKey(0); return 0; } //双边滤波计算速度比较慢 void onBialateralFilterTrackBar(int pos,void* userData) { if(pos == 0) { imshow("bilateral filter", srcImage); } else { //必须为奇数 if(pos%2 == 0) { pos++; } bilateralFilter(srcImage, BialateralFilterImage, pos, pos/2, pos*2); imshow("bilateral filter",BialateralFilterImage); } }
小结:以上就是opencv的五种基本滤波操作,在实际应用中,要根据实际情况灵活的选择滤波方式,噪声不同,图像数据的分析重点不同,都会影响最终滤波算法的选择.