OpenCV学习笔记(二)常用滤波函数之上篇

引言

OpenCV中提供了五种滤波方法,分别是方框滤波、均值滤波、中值滤波、高斯滤波、双边滤波。除此之外还有一种也能达到滤波效果的方法,那就是卷积滤波。前两种滤波实质上属于一种滤波方法,只不过“均值滤波”是“方框滤波”归一化后的结果,最后一种卷积滤波比较特殊,根据卷积核的不同选择可以实现其他几种平滑滤波,也可以实现锐化滤波,是非常好用的一种方法。下面分别以上、中、下篇的形式来介绍OpenCV中使用的滤波函数。

1、均值滤波

均值滤波在OpenCV中是
void blur( InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT );
函数中第一个参数表示输入图像,第二个参数表示输出图像,第三个参数表示滤波核大小(如Size(3,3)表示滤波核为3*3的矩阵),第四个参数为Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。第五个参数为int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT。均值滤波适用于图像去噪及有意识地图像模糊处理,属于一种低通线性滤波,均值滤波的原理是:
在这里插入图片描述
以3 × 3邻域为例,假设领域内像素点为
OpenCV学习笔记(二)常用滤波函数之上篇_第1张图片
其中,邻域的均值四舍五入后为24,根据选取的锚点位置,用24替换锚点处的像素点的值,依次对整张图片进行滤波替换处理即可。
滤波效果如下图:
OpenCV学习笔记(二)常用滤波函数之上篇_第2张图片
加入噪点后的滤波图:
OpenCV学习笔记(二)常用滤波函数之上篇_第3张图片

2、中值滤波

中值滤波也是我们最常用的滤波方法,它在OpenCV中的函数为:void medianBlur( InputArray src, OutputArray dst, int ksize );
第一个参数为输入图像,第二个参数为输出图像,第三个参数为滤波核。中值滤波的原理很简单,如下:还是以均值滤波中的3 × 3邻域为例,假设邻域中像素点的值如下,
OpenCV学习笔记(二)常用滤波函数之上篇_第4张图片
则排序后邻域中的中值为21,以21去代替邻域中原有的中值25,依次遍历整张图片,即得到中值滤波后图像。
滤波效果如下图:
OpenCV学习笔记(二)常用滤波函数之上篇_第5张图片
可以看出,中值滤波相对于原图像有一定模糊,但比均值滤波要好很多。
下面是加入椒盐噪声后的滤波图像:
OpenCV学习笔记(二)常用滤波函数之上篇_第6张图片
可以很明显的看出,中值滤波对噪点的抑制作用相当明显,滤波效果较之均值滤波有相当大的提高。
虽然中值滤波对噪点抑制作用很好,但是也导致了图像的模糊,图像边缘信息表现不明显,锐利度有一定程度的下降,所以后来对中值滤波有了多种改进方案,其中比较出名的就有自适应中值滤波。该滤波方法不仅能滤除噪点,还能极大程度的保留图像边缘信息。后面会专门写一篇介绍自适应中值滤波的博客。

3、高斯滤波

高斯滤波也是一种线性平滑滤波器,它比较适合用于滤除呈正态分布的噪点(高斯噪声),它与均值滤波有点相似之处,均值滤波是简单平均,而高斯滤波是加权平均,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。其原理为高斯函数,高斯函数又满足正态分布(高斯分布),从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。
图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。
高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。一维高斯函数为:
一维高斯函数
一维高斯函数图像为:
OpenCV学习笔记(二)常用滤波函数之上篇_第7张图片
二维高斯函数为:
二维高斯函数
二维高斯函数图像为:
OpenCV学习笔记(二)常用滤波函数之上篇_第8张图片
OpenCV中的高斯滤波函数为:
void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数,Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
第四个参数,double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。
第五个参数,double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为0,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。
第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
加入椒盐噪声后的高斯滤波:
OpenCV学习笔记(二)常用滤波函数之上篇_第9张图片
加入高斯噪声后的高斯滤波:
OpenCV学习笔记(二)常用滤波函数之上篇_第10张图片
可以看出,对于加入高斯噪声的图像,即使是高斯滤波也不能很好的去除噪声,只能说尽可能的抑制噪声,除高斯滤波外,还有维纳滤波与小波变换滤波也可以对高斯噪声进行较好的滤除。值得说明的一点是:上文中所说的高斯滤波严格意义上应该叫高斯低通滤波(也即高斯模糊),很多含有噪声的图像,有的噪声如椒盐噪声,其一般都集中在图像的高频部分,所以我们用于图像平滑的滤波器基本都是低通滤波器。而像图像的边缘信息等也属于高频成分,因此也会被低通滤波器滤除,所以上图中lena的帽子和肩膀部分的边缘细节有一定程度的模糊。而高斯高通滤波器,顾名思义其就是用来滤除低频分量的,可用于图像的边缘检测等跟高频相关的信息。

4、双边滤波

双边滤波与中值滤波一样属于非线性滤波,实际上是结合图像的空间领近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。优点是可以做边缘保存(edge preserving),缺点是保存了过多的高频信息,对彩色图像里的高频噪声,不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
双边滤波器中,输出像素的值依赖于邻域像素的值的加权组合,
在这里插入图片描述
权重系数w(i,j,k,l)取决于定义域核
在这里插入图片描述
和值域核
在这里插入图片描述
的乘积
在这里插入图片描述
同时考虑了空间域与值域的差别。
其在OpenCV中的API函数为:
void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
函数中第一个参数表示输入图像。第二个参数表示输出图像。第三个参数表示过滤过程中每个像素领域的直径。第四个参数表示颜色空间滤波器的sigma值,这个参数越大,表明该像素领域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。第五个参数表示坐标空间中滤波器的sigma值,坐标空间的标准方差,这个数值越大,表明越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。第五个参数表示图像的边界填充模式,一般保持默认即可。
滤波效果如下图:
OpenCV学习笔记(二)常用滤波函数之上篇_第11张图片
下面是加入椒盐噪声后的双边滤波效果:
OpenCV学习笔记(二)常用滤波函数之上篇_第12张图片
从以上两图可以看出,双边滤波对lena的头发及帽子区域有一定的模糊,但是帽子边缘及手臂边缘部分的辨识度还是很高的,而其滤波效果对椒盐噪声这种高频噪声的滤除作用就不是那么明显了,因为它在保存图像边缘信息的时候不可避免的就会保留部分高频噪声,因此,双边滤波的适用场合是噪声信号在低频部分,同时需要保留图像边缘信息的时候。

OpenCV源码部分

#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

#define Mean_Blur 1
#define Gaussian_Blur 2
#define Median_Blur 3
#define Bilateral_Blur 4

CvMat temp,*dst1=NULL;
IplImage* dstout=NULL;
IplImage* Filter(int FilterType,IplImage *FilterImage)
{
	Mat dst;
	Mat srcm(FilterImage,true);
	switch(FilterType)
	{
	case 1:
		//均值滤波
		blur(srcm, dst, Size(8,8),Point(0,0));
		break;
	case 2:
		//高斯滤波
		GaussianBlur(srcm, dst, Size(3, 3), 0, 0);
		break;
	case 3:
		//中值滤波
		medianBlur(srcm, dst, 3);
		break;
	case  4:
		//双边滤波
		bilateralFilter(srcm, dst, 60, 120, 30);
		break;
	default:
		break;
	}
	temp=dst;
	dst1=cvCreateMat(dst.rows,dst.cols,CV_8UC3);  //CV_8UC3  8U表示8无符号数,3表示RGB三通道图像
	cvCopy(&temp,dst1);
	dstout=cvCreateImage(cvSize(dst1->cols,dst1->rows),IPL_DEPTH_8U,3);
	cvGetImage(dst1,dstout);
	return dstout;
}

Mat addSaltNoise(const Mat srcImage, int n)  
{  
	Mat dstImage = srcImage.clone();  
	for (int k = 0; k < n; k++)  
	{  
		//随机取值行列  
		int i = rand() % dstImage.rows;  
		int j = rand() % dstImage.cols;  
		//图像通道判定  
		if (dstImage.channels() == 1)  
		{  
			dstImage.at<uchar>(i, j) = 255;       //盐噪声  
		}  
		else  
		{  
			dstImage.at<Vec3b>(i, j)[0] = 255;  
			dstImage.at<Vec3b>(i, j)[1] = 255;  
			dstImage.at<Vec3b>(i, j)[2] = 255;  
		}  
	}  
	for (int k = 0; k < n; k++)  
	{  
		//随机取值行列  
		int i = rand() % dstImage.rows;  
		int j = rand() % dstImage.cols;  
		//图像通道判定  
		if (dstImage.channels() == 1)  
		{  
			dstImage.at<uchar>(i, j) = 0;     //椒噪声  
		}  
		else  
		{  
			dstImage.at<Vec3b>(i, j)[0] = 0;  
			dstImage.at<Vec3b>(i, j)[1] = 0;  
			dstImage.at<Vec3b>(i, j)[2] = 0;  
		}  
	}  
	return dstImage;  
}  

int _tmain(int argc, _TCHAR* argv[])
{
	IplImage* image=NULL,*OutImage=NULL;
	char* src,*dst;
	Mat outmatimage;
	src="src";
	dst="Blurdst";
	cvNamedWindow(src,CV_WINDOW_AUTOSIZE);
	cvNamedWindow(dst,CV_WINDOW_AUTOSIZE);
	image=cvLoadImage("D:\\opencv_study\\Lena.jpg",1);

	Mat matImage(image,true);
	outmatimage=addSaltNoise(matImage,3000);
	temp=outmatimage;
	dst1=cvCreateMat(outmatimage.rows,outmatimage.cols,CV_8UC3);  //CV_8UC3  8U表示8无符号数,3表示RGB三通道图像
	cvCopy(&temp,dst1);
	dstout=cvCreateImage(cvSize(dst1->cols,dst1->rows),IPL_DEPTH_8U,3);
	cvGetImage(dst1,dstout);
	image=dstout;

	OutImage=Filter(Bilateral_Blur,image);
	cvShowImage(src,image);
	cvShowImage(dst,OutImage);
	cvWaitKey(0);
	cvReleaseImage(&dstout);
	cvReleaseMat(&dst1);
	cvReleaseImage(&image);

	cvDestroyAllWindows();
	return 0;
}

这篇介绍了OpenCV中常用的封装好的滤波函数,下一篇介绍自适应中值滤波,它就需要在使用API函数的同时再加点自己写的代码了。

你可能感兴趣的:(OpenCV学习笔记)