图像的滤波目的有两个:
一是抽出图像的特征作为图像识别的特征模式;
另一个是为适应图像处理的要求,消除图像数字化时所混入的噪声;
1、图像的平滑处理:
平滑滤波是指低频增强的空间滤波技术。主要目的是模糊和消除噪音;
2、常用的5种图像平滑处理操作方法:
1)方框滤波——boxFilter函数
2)均值滤波——Blur函数
3)高斯滤波——GaussianBlur函数
4)中值滤波——medianBlur函数
5)双边滤波——bilateralFilter函数
前三种都属于线性滤波,后两种属于非线性滤波;
滤波和模糊:滤波是将信号中特定波段频率滤除的操作,是抑制和防止干扰的一项重要措施。模糊是滤波的一种。高斯滤波是指用高斯函数作为滤波函数的滤波操作。高斯模糊就是高斯低通滤波。
1)方框滤波——boxFilter函数
函数原型:
void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
参数详解:
第一个参数:InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
第二个参数:OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数:int类型的ddepth,输出图像的深度,-1代表使用原图深度,即src.depth()。
第四个参数:Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
第五个参数:Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
第六个参数:bool类型的normalize,默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了。
第七个参数:int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
2)均值滤波——Blur函数
函数原型:
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
第一个参数:InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
第二个参数:OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
第三个参数:Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
第四个参数:Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
第五个参数:int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
3)高斯滤波——GaussianBlur函数
原型:
C++: 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,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
第三个参数:Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
第四个参数:double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。
第五个参数:double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。
第六个参数:int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
下面是线性滤波的综合事例:
#include
#include
#include
#include
using namespace std;
using namespace cv;
Mat g_srcImage,g_dstImage1,g_dstImage2,g_dstImage3;//存储图片的Mat类型
int g_nBoxFilterValue=3; //方框滤波参数值
int g_nMeanBlurValue=3; //均值滤波参数值
int g_nGaussianBlurValue=3; //高斯滤波参数值
//轨迹条的回调函数
static void on_BoxFilter(int, void *); //方框滤波
static void on_MeanBlur(int, void *); //均值滤波
static void on_GaussianBlur(int, void *); //高斯滤波
//-----------------------------------【main( )函数】---------------------------------------
int main( )
{
//改变console字体颜色
system("color5E");
//载入原图
g_srcImage= imread( "1.jpg", 1 );
if(!g_srcImage.data ) { printf("Oh,no,读取srcImage错误~!\n"); return false; }
//克隆原图到三个Mat类型中
g_dstImage1= g_srcImage.clone( );
g_dstImage2= g_srcImage.clone( );
g_dstImage3= g_srcImage.clone( );
//显示原图
namedWindow("【<0>原图窗口】", 1);
imshow("【<0>原图窗口】",g_srcImage);
//=================【<1>方框滤波】==================
//创建窗口
namedWindow("【<1>方框滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<1>方框滤波】",&g_nBoxFilterValue, 40,on_BoxFilter );
on_MeanBlur(g_nBoxFilterValue,0);
imshow("【<1>方框滤波】", g_dstImage1);
//=================【<2>均值滤波】==================
//创建窗口
namedWindow("【<2>均值滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<2>均值滤波】",&g_nMeanBlurValue, 40,on_MeanBlur );
on_MeanBlur(g_nMeanBlurValue,0);
//=================【<3>高斯滤波】=====================
//创建窗口
namedWindow("【<3>高斯滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<3>高斯滤波】",&g_nGaussianBlurValue, 40,on_GaussianBlur );
on_GaussianBlur(g_nGaussianBlurValue,0);
//输出一些帮助信息
cout<"\t嗯。好了,请调整滚动条观察图像效果~\n\n"
<<"\t按下“q”键时,程序退出~!\n"
<<"\n\n\t\t\t\tby浅墨";
//按下“q”键时,程序退出
while(char(waitKey(1))!= 'q') {}
return 0;
}
// 描述:方框滤波操作的回调函数
static void on_BoxFilter(int, void *)
{
//方框滤波操作
boxFilter(g_srcImage, g_dstImage1, -1,Size( g_nBoxFilterValue+1, g_nBoxFilterValue+1));
//显示窗口
imshow("【<1>方框滤波】", g_dstImage1);
}
// 描述:均值滤波操作的回调函数
static void on_MeanBlur(int, void *)
{
//均值滤波操作
blur(g_srcImage, g_dstImage2, Size( g_nMeanBlurValue+1, g_nMeanBlurValue+1),Point(-1,-1));
//显示窗口
imshow("【<2>均值滤波】", g_dstImage2);
}
static void on_GaussianBlur(int, void *)
{
//高斯滤波操作
GaussianBlur(g_srcImage, g_dstImage3, Size( g_nGaussianBlurValue*2+1,g_nGaussianBlurValue*2+1 ), 0, 0);
//显示窗口
imshow("【<3>高斯滤波】", g_dstImage3);
}
刚刚介绍的都是线性滤波,即两个信号之和的响应和他们各自响应之和相等。也就是每个像素的输出值是一些输入像素的加权和,线性滤波器易于构造,并且易于从频率响应角度来进行分析。其实在很多情况下,使用邻域像素的非线性滤波也许会得到更好的效果。
中值滤波(Median filter)是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节。
中值滤波在一定的条件下可以克服常见线性滤波器如最小均方滤波、方框滤波器、均值滤波等带来的图像细节模糊,而且对滤除脉冲干扰及图像扫描噪声非常有效,也常用于保护边缘信息, 保存边缘的特性使它在不希望出现边缘模糊的场合也很有用,是非常经典的平滑噪声处理方法。
缺点:中值滤波花费的时间是均值滤波的5倍以上。
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。
双边滤波器的好处是可以做边缘保存(edge preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
在双边滤波器中,输出像素的值依赖于邻域像素值的加权值组合:
4)中值滤波——medianBlur函数
medianBlur函数使用中值滤波器来平滑(模糊)处理一张图片,从src输入,而结果从dst输出。
且对于多通道图片,每一个通道都单独进行处理,并且支持就地操作(In-placeoperation)。
原型:
C++: void medianBlur(InputArray src,OutputArray dst, int ksize)
参数详解:
第一个参数:InputArray类型的src,函数的输入参数,填1、3或者4通道的Mat类型的图像;当ksize为3或者5的时候,图像深度需为CV_8U,CV_16U,或CV_32F其中之一,而对于较大孔径尺寸的图片,它只能是CV_8U。
第二个参数:OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。我们可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
第三个参数:int类型的ksize,孔径的线性尺寸(aperture linear size),注意这个参数必须是大于1的奇数,比如:3,5,7,9。
#include "opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//载入原图
Mat image=imread("1.jpg");
//创建窗口
namedWindow("中值滤波【原图】" );
namedWindow("中值滤波【效果图】");
//显示原图
imshow("中值滤波【原图】", image );
//进行中值滤波操作
Mat out;
medianBlur( image, out, 7);
//显示效果图
imshow("中值滤波【效果图】" ,out );
waitKey(0 );
}
5)双边滤波——bilateralFilter函数
原型:
C++: void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
第一个参数:InputArray类型的src,输入图像,即源图像,需要为8位或者浮点型单通道、三通道的图像。
第二个参数:OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数:int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
第四个参数:double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
第五个参数:double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
第六个参数:int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
#include "opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//载入原图
Mat image=imread("1.jpg");
//创建窗口
namedWindow("双边滤波【原图】" );
namedWindow("双边滤波【效果图】");
//显示原图
imshow("双边滤波【原图】", image );
//进行双边滤波操作
Mat out;
bilateralFilter( image, out, 25, 25*2, 25/2 );
//显示效果图
imshow("双边滤波【效果图】" ,out );
waitKey(0 );
}
5种滤波的综合实例:
#include
#include
#include
#include
using namespace std;
using namespace cv;
Mat g_srcImage,g_dstImage1,g_dstImage2,g_dstImage3,g_dstImage4,g_dstImage5;
int g_nBoxFilterValue=6; //方框滤波内核值
int g_nMeanBlurValue=10; //均值滤波内核值
int g_nGaussianBlurValue=6; //高斯滤波内核值
int g_nMedianBlurValue=10; //中值滤波参数值
int g_nBilateralFilterValue=10; //双边滤波参数值
//轨迹条回调函数
static void on_BoxFilter(int, void *); //方框滤波器
static void on_MeanBlur(int, void *); //均值滤波器
static void on_GaussianBlur(int, void *); //高斯滤波器
static void on_MedianBlur(int, void *); //中值滤波器
static void on_BilateralFilter(int, void *); //双边滤波器
int main( )
{
g_srcImage = imread( "1.png", 1 );
if( !g_srcImage.data ) { printf("读取srcImage错误~! \n"); return false; }
//克隆原图到四个Mat类型中
g_dstImage1 = g_srcImage.clone( );
g_dstImage2 = g_srcImage.clone( );
g_dstImage3 = g_srcImage.clone( );
g_dstImage4 = g_srcImage.clone( );
g_dstImage5 = g_srcImage.clone( );
//显示原图
namedWindow("【<0>原图窗口】", 1);
imshow("【<0>原图窗口】",g_srcImage);
//=================【<1>方框滤波】=========================
//创建窗口
namedWindow("【<1>方框滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<1>方框滤波】",&g_nBoxFilterValue, 50,on_BoxFilter );
on_MeanBlur(g_nBoxFilterValue,0);
imshow("【<1>方框滤波】", g_dstImage1);
//=================【<2>均值滤波】==========================
//创建窗口
namedWindow("【<2>均值滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<2>均值滤波】",&g_nMeanBlurValue, 50,on_MeanBlur );
on_MeanBlur(g_nMeanBlurValue,0);
//=================【<3>高斯滤波】===========================
//创建窗口
namedWindow("【<3>高斯滤波】", 1);
//创建轨迹条
createTrackbar("内核值:", "【<3>高斯滤波】",&g_nGaussianBlurValue, 50,on_GaussianBlur );
on_GaussianBlur(g_nGaussianBlurValue,0);
//=================【<4>中值滤波】===========================
//创建窗口
namedWindow("【<4>中值滤波】", 1);
//创建轨迹条
createTrackbar("参数值:", "【<4>中值滤波】",&g_nMedianBlurValue, 50,on_MedianBlur );
on_MedianBlur(g_nMedianBlurValue,0);
//=================【<5>双边滤波】===========================
//创建窗口
namedWindow("【<5>双边滤波】", 1);
//创建轨迹条
createTrackbar("参数值:", "【<5>双边滤波】",&g_nBilateralFilterValue, 50,on_BilateralFilter);
on_BilateralFilter(g_nBilateralFilterValue,0);
cout<"\t运行成功,请调整滚动条观察图像效果~\n\n"<<"\t按下“q”键时,程序退出。\n";
while(char(waitKey(1)) != 'q') {}
return 0;
}
static void on_BoxFilter(int, void *)
{
boxFilter( g_srcImage, g_dstImage1, -1,Size( g_nBoxFilterValue+1, g_nBoxFilterValue+1));
imshow("【<1>方框滤波】", g_dstImage1);
}
static void on_MeanBlur(int, void *)
{
blur( g_srcImage, g_dstImage2, Size( g_nMeanBlurValue+1, g_nMeanBlurValue+1), Point(-1,-1));
imshow("【<2>均值滤波】", g_dstImage2);
}
static void on_GaussianBlur(int, void *)
{
GaussianBlur( g_srcImage, g_dstImage3, Size( g_nGaussianBlurValue*2+1, g_nGaussianBlurValue*2+1 ), 0, 0);
imshow("【<3>高斯滤波】", g_dstImage3);
}
static void on_MedianBlur(int, void *)
{
medianBlur ( g_srcImage, g_dstImage4, g_nMedianBlurValue*2+1 );
imshow("【<4>中值滤波】", g_dstImage4);
}
static void on_BilateralFilter(int, void *)
{
bilateralFilter ( g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue*2, g_nBilateralFilterValue/2 );
imshow("【<5>双边滤波】", g_dstImage5);
}