间实现)。从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。
图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。
高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。一维零均值高斯函数为:
其中,高斯分布参数Sigma决定了高斯函数的宽度。对于图像处理来说,常用二维零均值离散高斯函数作平滑滤波器。 二维高斯函数为:
2)在OpenCV中使用高斯滤波——GaussianBlur函数
GaussianBlur函数的作用是用高斯滤波器来模糊一张图片,对输入的图像src进行高斯滤波后用dst输出。它将源图像和指定的高斯核函数做卷积运算,并且支持就地过滤(In-placefiltering)。 [cpp] view plain copy print? 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,我们一般不去管它。
二、深入——OpenCV源码剖析
上一篇文章中我们已经和当前最新版的OpenCV的源码亲密接触过。 在这一部分中,浅墨将带领大家领略OpenCV的开源魅力,对OpenCV中本篇文章里讲解到线性滤波函数——
boxFilter,blur和GaussianBlur函数以及周边的涉及到的源码进行适当的剖析。 这样,我们就可以对 OpenCV有一个更加深刻的理解,成为一个高端大气的OpenCV使用者。
<1>OpenCV中boxFilter函数源码解析
我们可以在OpenCV的安装路径的\\sources\\modules\\imgproc\\src下的smooth.cpp源文件的第711行找到boxFilter函数的源代码。对应于浅墨将OpenCV 2.4.8安装在D:\\Program Files\\opencv的路径下,那么,smooth.cpp文件就在D:\\ProgramFiles\\opencv\\sources\\modules\\imgproc\\src路径下。
[cpp] view plain copy print? //-----------------------------------【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
其中的Ptr是用来动态分配的对象的智能指针模板类。可以发现,函数的内部代码思路是很清晰的,先拷贝源图的形参Mat数据到临时变量,定义一些临时变量,在处理ddepth小于零的情况,接着处理 borderType不为 BORDER_CONSTANT 且normalize为真的情况,最终调用FilterEngine滤波引擎创建一个BoxFilter,正式开始滤波操作。
这里的FilterEngine是OpenCV图像滤波功能的核心引擎,我们有必要详细剖析看其源代码。
<2>FilterEngine类解析——OpenCV图像滤波核心引擎
FilterEngine类是OpenCV关于图像滤波的主力军类,OpenCV图像滤波功能的核心引擎。各种滤波函数比如blur, GaussianBlur,到头来其实是就是在函数末尾处定义了一个Ptr
cv::createLinearFilter(),cv::createGaussianFilter(), cv::createDerivFilter(),
cv::createBoxFilter() 和cv::createMorphologyFilter().,这里给出其中一个函数的原型吧: [cpp] view plain copy print? Ptr
上面我们提到过了,其中的Ptr是用来动态分配的对象的智能指针模板类,而上面的尖括号里面的模板参数就是FilterEngine。
使用FilterEngine类可以分块处理大量的图像,构建复杂的管线,其中就包含一些进行滤波阶段。如果我们需要使用预先定义好的的滤波操作,cv::filter2D(), cv::erode(),以及cv::dilate(),可以选择,他们不依赖于FilterEngine,自立自强,在自己函数体内部就实现了FilterEngine提供的功能。不像其他的诸如我们今天讲的blur系列函数,依赖于FilterEngine引擎。
我们看下其类声明经过浅墨详细注释的源码: [cpp] view plain copy print? //-----------------------------------【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
constPtr
int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
const Scalar&_borderValue=Scalar()); //默认析构函数
virtual ~FilterEngine();
//重新初始化引擎。释放之前滤波器申请的内存。 void init(const Ptr
constPtr
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
vector
int bufStep, startY, startY0, endY, rowCount, dstY; vector
Ptr
Ptr
Ptr
<3>OpenCV中size类型剖析
size类型我们也讲一下,通过转到定义,我们可以在??\\opencv\\sources\\modules\\core\\include\\opencv2\\core\\core.hpp路径下,对应于浅墨的OpenCV安装路径,就是在
D:\\ProgramFiles\\opencv\\sources\\modules\\core\\include\\opencv2\\core\\core.hpp下,找到其原型声明:
[cpp] view plain copy print? typedef Size_
Size_ 是个模板类,在这里Size_
所以,连起来就是,Size_
// 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码 // OpenCV源代码版本:2.4.8
// 源码路径:…\\opencv\\sources\\modules\\core\\include\\opencv2\\core\\core.hpp // 源文件中如下代码的起始行数:816行 // 中文注释by浅墨
//-------------------------------------------------------------------------------------------------------- template
public: