原教程(本系列文章由@浅墨_毛星云 出品,转载请注明出处。)地址:http://blog.csdn.net/poem_qianmo/article/details/22745559
一、一些概念
1、平滑处理(smoothing)或模糊处理(bluring):常用于去噪。
2、图像滤波:
(信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。)
图像滤波的目的:1)抽出对象的特征作为图像识别的特征模式;2)消除噪声。
对滤波处理的要求:1)不能损坏图像的轮廓及边缘等重要信息;2)使图像清晰视觉效果好。
3、平滑滤波:低频增强的空间域滤波技术。它的目的:1)模糊;2)消除噪音。
空间域的平滑滤波:一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。
新版本的OpenCV中的五种常用的图像平滑处理操作方法:
几种常见的线性滤波器:
滤波可分低通滤波和高通滤波两种。而高斯滤波是指用高斯函数作为滤波函数的滤波操作,至于是不是模糊,要看是高斯低通还是高斯高通,低通就是模糊,高通就是锐化。
邻域滤波(卷积):左边图像与中间图像的卷积产生右边图像。
线性滤波处理的输出像素值是输入像素值的加权和 :
其中的加权和为 ,我们称其为“核”,滤波器的加权系数,即滤波器的“滤波系数”。
上面的式子可以简单写作:
其中f表示输入像素值,h表示加权系数“核“,g表示输出像素值。
5、方波滤波box Filter
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 ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
- Point anchor,锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
- boolnormalize,默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了。注:若不进行归一化,则图像会偏亮甚至全白。
- int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
boxFilter()函数方框滤波所用的核为:
其中:
6、均值滤波
其中f表示原图,h表示核,g表示目标图。
归一化就是把要处理的量都缩放到一个范围内,比如(0,1),以便统一处理和直观量化。
而非归一化(Unnormalized)的方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(dense optical flow algorithms)中用到的图像倒数的协方差矩阵(covariance matrices of image derivatives)
如果我们要在可变的窗口中计算像素总和,可以使用integral()函数。
归一化后的方框滤波。
邻域平均法,即用一片图像区域的各个像素的均值来代替原图像中的各个像素值。
缺陷:不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。
blur函数:对输入的图像src进行均值滤波后用dst输出。
blur函数封装的就是均值滤波。
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
参数与boxFilter中的含义相同。
7、高斯滤波
适用于消除高斯噪声,广泛应用于图像处理的减噪过程。高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。也用于计算机视觉算法中的预先处理阶段,以增强图像在不同比例大小下的图像效果。
从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。
图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。
高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。一维零均值高斯函数为:
其中,高斯分布参数Sigma决定了高斯函数的宽度。对于图像处理来说,常用二维零均值离散高斯函数作平滑滤波器。
二维高斯函数为:
GaussianBlur函数的作用是用高斯滤波器来模糊一张图片,对输入的图像src进行高斯滤波后用dst输出。它将源图像和指定的高斯核函数做卷积运算,并且支持就地过滤(In-placefiltering)。
C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
部分参数:1、boxFilter:
//-----------------------------------【boxFilter()函数中文注释版源代码】----------------------------
// 代码作用:进行box Filter滤波操作的函数
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码
// OpenCV源代码版本:2.4.8
// 源码路径:…\opencv\sources\modules\imgproc\src\smooth.cpp
// 源文件中如下代码的起始行数:711行
// 中文注释by浅墨
//--------------------------------------------------------------------------------------------------------
void cv::boxFilter( InputArray _src,OutputArray _dst, int ddepth,
Size ksize, Point anchor,
bool normalize, int borderType)
{
Mat src = _src.getMat();//拷贝源图的形参Mat数据到临时变量,用于稍后的操作
int sdepth =src.depth(), cn = src.channels();//定义int型临时变量,代表源图深度的sdepth,源图通道的引用cn
//处理ddepth小于零的情况
if( ddepth < 0 )
ddepth = sdepth;
_dst.create( src.size(), CV_MAKETYPE(ddepth, cn) );//初始化目标图
Mat dst =_dst.getMat();//拷贝目标图的形参Mat数据到临时变量,用于稍后的操作
//处理 borderType不为 BORDER_CONSTANT 且normalize为真的情况
if( borderType != BORDER_CONSTANT && normalize ) {
if( src.rows == 1 )
ksize.height = 1;
if( src.cols == 1 )
ksize.width = 1;
}
//若之前有过HAVE_TEGRA_OPTIMIZATION优化选项的定义,则执行宏体中的tegra优化版函数并返回
#ifdef HAVE_TEGRA_OPTIMIZATION
if ( tegra::box(src, dst, ksize, anchor, normalize, borderType) )
return;
#endif
//调用FilterEngine滤波引擎,正式开始滤波操作
Ptr f = createBoxFilter( src.type(), dst.type(),
ksize, anchor,normalize, borderType );
f->apply( src, dst );
}
其中的Ptr是用来动态分配的对象的智能指针模板类。可以发现,函数的内部代码思路是很清晰的,先拷贝源图的形参Mat数据到临时变量,定义一些临时变量,在处理ddepth小于零的情况,接着处理 borderType不为 BORDER_CONSTANT 且normalize为真的情况,最终调用FilterEngine滤波引擎创建一个BoxFilter,正式开始滤波操作。
2、FilterEngine类:OpenCV图像滤波功能的核心引擎
各种滤波函数比如blur, GaussianBlur,到头来其实是就是在函数末尾处定义了一个Ptr
这个类可以把几乎是所有的滤波操作施加到图像上。它包含了所有必要的中间缓存器。有很多和滤波相关的create系函数的返回值直接就是Ptr
cv::createLinearFilter(),cv::createGaussianFilter(), cv::createDerivFilter(),
cv::createBoxFilter() 和cv::createMorphologyFilter().,这里给出其中一个函数的原型吧:
PtrcreateLinearFilter(int srcType, int dstType, InputArray kernel, Point_anchor=Point(-1,-1), double delta=0, int rowBorderType=BORDER_DEFAULT, intcolumnBorderType=-1, const Scalar& borderValue=Scalar() )
其中的Ptr是用来动态分配的对象的智能指针模板类,而上面的尖括号里面的模板参数就是FilterEngine。
使用FilterEngine类可以分块处理大量的图像,构建复杂的管线,其中就包含一些进行滤波阶段。如果我们需要使用预先定义好的的滤波操作,cv::filter2D(), cv::erode(),以及cv::dilate(),可以选择,他们不依赖于FilterEngine,自立自强,在自己函数体内部就实现了FilterEngine提供的功能。不像其他的诸如我们今天讲的blur系列函数,依赖于FilterEngine引擎。
//-----------------------------------【FilterEngine类中文注释版源代码】----------------------------
// 代码作用:FilterEngine类,OpenCV图像滤波功能的核心引擎
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码
// OpenCV源代码版本:2.4.8
// 源码路径:…\opencv\sources\modules\imgproc\include\opencv2\imgproc\imgproc.hpp
// 源文件中如下代码的起始行数:222行
// 中文注释by浅墨
//--------------------------------------------------------------------------------------------------------
class CV_EXPORTS FilterEngine
{
public:
//默认构造函数
FilterEngine();
//完整的构造函数。 _filter2D 、_rowFilter 和 _columnFilter之一,必须为非空
FilterEngine(const Ptr& _filter2D,
constPtr& _rowFilter,
constPtr& _columnFilter,
int srcType, int dstType, intbufType,
int_rowBorderType=BORDER_REPLICATE,
int _columnBorderType=-1,
const Scalar&_borderValue=Scalar());
//默认析构函数
virtual ~FilterEngine();
//重新初始化引擎。释放之前滤波器申请的内存。
void init(const Ptr& _filter2D,
constPtr& _rowFilter,
constPtr& _columnFilter,
int srcType, int dstType, intbufType,
int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
const Scalar&_borderValue=Scalar());
//开始对指定了ROI区域和尺寸的图片进行滤波操作
virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1);
//开始对指定了ROI区域的图片进行滤波操作
virtual int start(const Mat& src, const Rect&srcRoi=Rect(0,0,-1,-1),
bool isolated=false, intmaxBufRows=-1);
//处理图像的下一个srcCount行(函数的第三个参数)
virtual int proceed(const uchar* src, int srcStep, int srcCount,
uchar* dst, intdstStep);
//对图像指定的ROI区域进行滤波操作,若srcRoi=(0,0,-1,-1),则对整个图像进行滤波操作
virtual void apply( const Mat& src, Mat& dst,
const Rect&srcRoi=Rect(0,0,-1,-1),
Point dstOfs=Point(0,0),
bool isolated=false);
//如果滤波器可分离,则返回true
boolisSeparable() const { return (const BaseFilter*)filter2D == 0; }
//返回输入和输出行数
int remainingInputRows() const;
intremainingOutputRows() const;
//一些成员参数定义
int srcType, dstType, bufType;
Size ksize;
Point anchor;
int maxWidth;
Size wholeSize;
Rect roi;
int dx1, dx2;
int rowBorderType, columnBorderType;
vector borderTab;
int borderElemSize;
vector ringBuf;
vector srcRow;
vector constBorderValue;
vector constBorderRow;
int bufStep, startY, startY0, endY, rowCount, dstY;
vector rows;
Ptr filter2D;
Ptr rowFilter;
Ptr columnFilter;
};
typedef Size_ Size2i;
typedef Size2i Size;
Size_ 是个模板类,在这里Size_
那这两句的意思,就是首先给已知的数据类型Size_
然后又给已知的数据类型Size2i起个新名字,叫Size。
所以,连起来就是,Size_
然后我们追根溯源,找到Size_模板类的定义:
//-----------------------------------【Size_类中文注释版源代码】----------------------------
// 代码作用:作为尺寸相关数据结构的Size_ 模板类
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码
// OpenCV源代码版本:2.4.8
// 源码路径:…\opencv\sources\modules\core\include\opencv2\core\core.hpp
// 源文件中如下代码的起始行数:816行
// 中文注释by浅墨
//--------------------------------------------------------------------------------------------------------
template class Size_
{
public:
typedef _Tp value_type;
//不同的构造函数定义
Size_();
Size_(_Tp _width, _Tp _height); //用得最多的一种
Size_(const Size_& sz);
Size_(const CvSize& sz);
Size_(const CvSize2D32f& sz);
Size_(const Point_<_Tp>& pt);
Size_& operator = (const Size_& sz);
//区域(width*height)
_Tp area() const;
//转化另一种数据类型。
template operator Size_<_Tp2>() const;
//转换为旧式的OpenCV类型.
operator CvSize() const;
operator CvSize2D32f() const;
_Tp width, height; //宽度和高度,常用属性
};
Size(5, 5);//构造出的Size宽度和高度都为5,即XXX.width和XXX.height都为5
4、blur函数源码剖析
//-----------------------------------【blur()函数中文注释版源代码】----------------------------
// 代码作用:进行blur均值滤波操作的函数
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码
// OpenCV源代码版本:2.4.8
// 源码路径:…\opencv\sources\modules\imgproc\src\smooth.cpp
// 源文件中如下代码的起始行数:738行
// 中文注释by浅墨
//--------------------------------------------------------------------------------------------------------
void cv::blur(InputArray src, OutputArray dst,
Size ksize, Point anchor, int borderType )
{
//调用boxFilter函数进行处理
boxFilter( src, dst, -1, ksize, anchor, true, borderType );
}
//-----------------------------------【GaussianBlur()函数中文注释版源代码】-----------------------
// 代码作用:封装高斯滤波的GaussianBlur()函数
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码
// OpenCV源代码版本:2.4.8
// 源码路径:…\opencv\sources\modules\imgproc\src\smooth.cpp
// 源文件中如下代码的起始行数:832行
// 中文注释by浅墨
//--------------------------------------------------------------------------------------------------------
void cv::GaussianBlur( InputArray _src,OutputArray _dst, Size ksize,
double sigma1, doublesigma2,
int borderType )
{
//拷贝形参Mat数据到临时变量,用于稍后的操作
Mat src = _src.getMat();
_dst.create( src.size(), src.type() );
Mat dst =_dst.getMat();
//处理边界选项不为BORDER_CONSTANT时的情况
if( borderType != BORDER_CONSTANT )
{
if( src.rows == 1 )
ksize.height = 1;
if( src.cols == 1 )
ksize.width = 1;
}
//若ksize长宽都为1,将源图拷贝给目标图
if( ksize.width == 1 && ksize.height == 1 )
{
src.copyTo(dst);
return;
}
//若之前有过HAVE_TEGRA_OPTIMIZATION优化选项的定义,则执行宏体中的tegra优化版函数并返回
#ifdef HAVE_TEGRA_OPTIMIZATION
if(sigma1 == 0 && sigma2 == 0 && tegra::gaussian(src,dst, ksize, borderType))
return;
#endif
//如果HAVE_IPP&& (IPP_VERSION_MAJOR >= 7为真,则执行宏体中语句
#if defined HAVE_IPP &&(IPP_VERSION_MAJOR >= 7)
if(src.type() == CV_32FC1 && sigma1 == sigma2 &&ksize.width == ksize.height && sigma1 != 0.0 )
{
IppiSize roi = {src.cols, src.rows};
int bufSize = 0;
ippiFilterGaussGetBufferSize_32f_C1R(roi, ksize.width, &bufSize);
AutoBuffer buf(bufSize+128);
if( ippiFilterGaussBorder_32f_C1R((const Ipp32f *)src.data,(int)src.step,
(Ipp32f *)dst.data, (int)dst.step,
roi,ksize.width, (Ipp32f)sigma1,
(IppiBorderType)borderType, 0.0,
alignPtr(&buf[0],32)) >= 0 )
return;
}
#endif
//调动滤波引擎,正式进行高斯滤波操作
Ptr f = createGaussianFilter( src.type(), ksize,sigma1, sigma2, borderType );
f->apply( src, dst );
}
三、浅出
boxFilter函数--方波滤波
C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
//载入原图
Mat image=imread("2.jpg");
//进行均值滤波操作
Mat out;
boxFilter(image, out, -1,Size(5, 5));
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include "opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//载入原图
Mat image=imread("2.jpg");
//创建窗口
namedWindow("均值滤波【原图】" );
namedWindow("均值滤波【效果图】");
//显示原图
imshow("均值滤波【原图】", image );
//进行滤波操作
Mat out;
boxFilter(image, out, -1,Size(5, 5));
//显示效果图
imshow("均值滤波【效果图】" ,out );
waitKey(0 );
}
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
//载入原图
Mat image=imread("1.jpg");
//进行均值滤波操作
Mat out;
blur(image, out, Size(7, 7));
3、GaussianBlur函数--高斯函数
C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
注:参数Size ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
示例:
//载入原图
Mat image=imread("1.jpg");
//进行滤波操作
Mat out;
GaussianBlur( image, out, Size( 5, 5 ), 0, 0 );
//-----------------------------------【程序说明】----------------------------------------------
// 程序名称::【OpenCV入门教程之八】线性滤波专场:方框滤波、均值滤波与高斯滤波 配套源码
// 开发所用OpenCV版本:2.4.8
// 2014年3月31 日 Create by 浅墨
//------------------------------------------------------------------------------------------------
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#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<方框滤波】", g_dstImage1);
}
//-----------------------------【on_MeanBlur( )函数】------------------------------------
// 描述:均值滤波操作的回调函数
//-----------------------------------------------------------------------------------------------
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);
}
//-----------------------------【on_GaussianBlur( )函数】------------------------------------
// 描述:高斯滤波操作的回调函数
//-----------------------------------------------------------------------------------------------
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);
}
(有个问题没解决。综合示例里的,Size,一个是加1,一个是乘2加1,why?)
哦 明白了,上面有写到。高斯那个,size的数值只能是奇数和正数。