opencv3.0的GaussianBlur问题

问题


主函数中第一次执行GaussianBlur()非常耗时,执行过一次之后就恢复正常。


原因分析


分析底层源码


GaussianBlur函数定义(位于sources\modules\imgproc\src\smooth.cpp)

void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
                   double sigma1, double sigma2,
                   int borderType )
该函数的核心部分:

    createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2);
    sepFilter2D(_src, _dst, CV_MAT_DEPTH(type), kx, ky, Point(-1,-1), 0, borderType );
createGaussianKernels计算高斯核,sepFilter2D是可分离2维滤波函数


sepFilter2D函数定义(位于sources\modules\imgproc\src\filter.cpp)

void cv::sepFilter2D( InputArray _src, OutputArray _dst, int ddepth,
                      InputArray _kernelX, InputArray _kernelY, Point anchor,
                      double delta, int borderType )
该函数的核心部分:

    Ptr<FilterEngine> f = createSeparableLinearFilter(src.type(),
        dst.type(), kernelX, kernelY, anchor, delta, borderType & ~BORDER_ISOLATED );
    f->apply(src, dst, Rect(0,0,-1,-1), Point(), (borderType & BORDER_ISOLATED) != 0 );
包括FilterEngine的create和apply,这里就是opencv各种滤波函数的底层实现形式,FilterEngine在opencv2.x版本中开放给用户使用,到了3.0则封闭了。

这是官方论坛对3.0取消FilterEngine的提问,但并没有提供解决办法:

http://answers.opencv.org/question/56498/opencv-30-beta-no-filterengine-class/

http://answers.opencv.org/question/69111/opencv-30-filters-missing/

为了继续深入,这里将FilterEngine添加进我的代码,具体方法见本文最后的“附:opencv 3.0添加FilterEngine类


继续追查FilterEngine的来源


createSeparableLinearFilter函数的定义:

cv::Ptr<cv::FilterEngine> cv::createSeparableLinearFilter(
    int _srcType, int _dstType,
    InputArray __rowKernel, InputArray __columnKernel,
    Point _anchor, double _delta,
    int _rowBorderType, int _columnBorderType,
    const Scalar& _borderValue )
该函数的最后:

    Ptr<BaseRowFilter> _rowFilter = getLinearRowFilter(
        _srcType, _bufType, rowKernel, _anchor.x, rtype);
    Ptr<BaseColumnFilter> _columnFilter = getLinearColumnFilter(
        _bufType, _dstType, columnKernel, _anchor.y, ctype, _delta, bits );

    return Ptr<FilterEngine>( new FilterEngine(Ptr<BaseFilter>(), _rowFilter, _columnFilter,
        _srcType, _dstType, _bufType, _rowBorderType, _columnBorderType, _borderValue ));

可见,这里终于真正意义上创建了滤波引擎,通过new一个Ptr<FilterEngine>对象


FilterEngine类(声明位于sources\modules\imgproc\src\filterengine.hpp,实现位于opencv\sources\modules\imgproc\src\filter.cpp)

class FilterEngine
{
public:
    //! the default constructor
    FilterEngine();
    //! the full constructor. Either _filter2D or both _rowFilter and _columnFilter must be non-empty.
    FilterEngine(const Ptr<BaseFilter>& _filter2D,
                 const Ptr<BaseRowFilter>& _rowFilter,
                 const Ptr<BaseColumnFilter>& _columnFilter,
                 int srcType, int dstType, int bufType,
                 int _rowBorderType = BORDER_REPLICATE,
                 int _columnBorderType = -1,
                 const Scalar& _borderValue = Scalar());
    //! the destructor
    virtual ~FilterEngine();
    //! reinitializes the engine. The previously assigned filters are released.
    void init(const Ptr<BaseFilter>& _filter2D,
              const Ptr<BaseRowFilter>& _rowFilter,
              const Ptr<BaseColumnFilter>& _columnFilter,
              int srcType, int dstType, int bufType,
              int _rowBorderType = BORDER_REPLICATE,
              int _columnBorderType = -1,
              const Scalar& _borderValue = Scalar());
    //! starts filtering of the specified ROI of an image of size wholeSize.
    virtual int start(Size wholeSize, Rect roi, int maxBufRows = -1);
    //! starts filtering of the specified ROI of the specified image.
    virtual int start(const Mat& src, const Rect& srcRoi = Rect(0,0,-1,-1),
                      bool isolated = false, int maxBufRows = -1);
    //! processes the next srcCount rows of the image.
    virtual int proceed(const uchar* src, int srcStep, int srcCount,
                        uchar* dst, int dstStep);
    //! applies filter to the specified ROI of the image. if srcRoi=(0,0,-1,-1), the whole image is filtered.
    virtual void apply( const Mat& src, Mat& dst,
                        const Rect& srcRoi = Rect(0,0,-1,-1),
                        Point dstOfs = Point(0,0),
                        bool isolated = false);
    //! returns true if the filter is separable
    bool isSeparable() const { return !filter2D; }
    //! returns the number
    int remainingInputRows() const;
    int remainingOutputRows() const;

    int srcType;
    int dstType;
    int bufType;
    Size ksize;
    Point anchor;
    int maxWidth;
    Size wholeSize;
    Rect roi;
    int dx1;
    int dx2;
    int rowBorderType;
    int columnBorderType;
    std::vector<int> borderTab;
    int borderElemSize;
    std::vector<uchar> ringBuf;
    std::vector<uchar> srcRow;
    std::vector<uchar> constBorderValue;
    std::vector<uchar> constBorderRow;
    int bufStep;
    int startY;
    int startY0;
    int endY;
    int rowCount;
    int dstY;
    std::vector<uchar*> rows;

    Ptr<BaseFilter> filter2D;
    Ptr<BaseRowFilter> rowFilter;
    Ptr<BaseColumnFilter> columnFilter;
};

推测可能原因:创建FilterEngine耗费时间

实验:

方式一:

//变量定义
Mat &src = input();  //输入源数据
Mat &dst;    //输出数据  
Mat kx, ky;  //x和y方向的滤波核

	//方式一:sepFilter2D
	kx = cv::getGaussianKernel(5,1.0,6);ky = kx;
	sepFilter2D(src, dst, CV_MAT_DEPTH(CV_32F), kx, ky, cv::Point(-1,-1), 0,  cv::BORDER_REFLECT);

方式二:

        //方式二:GaussianBlur
	cv::GaussianBlur(src, dst, Size(5, 5), 1.0, 1.0, cv::BORDER_REFLECT); 
方式三:

        //方式三:调用FilterEngine类
	cv::Ptr<cv::FilterEngine> gauss = cv::createGaussianFilter(CV_32F,Size(5,5),1.0,1.0,cv::BORDER_REFLECT);
	gauss->apply(src,dst,cv::Rect(0,0,-1,-1),cv::Point(0,0),false);
注意,三种方式独立运行,不要依次运行

结果:

三种方式的耗时都相近,因为GaussianBlur的底层实现就是sepFilter2D,而sepFilter2D的底层实现则是FilterEngine中的apply方法(见源码分析中的代码)

对于CV_32F的数据处理速度最快,CV_64F次之,最慢是处理CV_16U的数据,所以不要直接对整型数据进行滤波。

每次执行sepFilter2D,都会重新定义一个Ptr<FilterEngine>  f,故不存在创建耗费时间的问题,否则就不仅仅是第一次运行GaussianBlur耗时了。

三种方式都存在一个问题, 就是在第一次调用时非常耗时(平时的几十倍)往后恢复正常。所以GaussianBlur第一次调用耗时的问题,其原因并非FilterEngine创建耗时


但如果不是这个原因,就显得无法解释了,到底问题的根源在哪里?


参考其它讨论GaussianBlur的博文:

关于高斯模糊与opencv中的GaussianBlur函数

http://blog.csdn.net/hachirou/article/details/6280200

二维高斯模糊和可分离核形式的快速实现

Opencv学习一:高斯滤波

点击打开链接


总结:解决第一次执行GaussianBlur()非常耗时的问题,只能通过在程序开始时执行一遍(经过测试,首次运行GaussianBlur可以是任意大小的输入数据和滤波核,也就是不必跟后面的一致),FilterEngine和sepFilter2D同理。而且,调用函数前先初始化输入变量Mat的值,将加快运行速度。如:

	Mat t0 = Mat::zeros(Size(1000,1000),CV_32F);
	Mat t1 = Mat::zeros(Size(1000,1000),CV_32F);
	Mat kx = cv::getGaussianKernel(5,1.0,6);Mat ky = kx;
	cv::sepFilter2D(t0,t1, CV_MAT_DEPTH(CV_32F), kx, ky, cv::Point(-1,-1), 0,  cv::BORDER_REFLECT);
就比以下用法省时

	Mat t0 = Mat(Size(1000,1000),CV_32F);
	Mat t1 = Mat(Size(1000,1000),CV_32F);
	Mat kx = cv::getGaussianKernel(5,1.0,6);Mat ky = kx;
	cv::sepFilter2D(t0,t1, CV_MAT_DEPTH(CV_32F), kx, ky, cv::Point(-1,-1), 0,  cv::BORDER_REFLECT);
当然,省时针对的是滤波函数本身,Mat::zeros也是有耗费时间的


进一步发现,首次调用涉及运算的opencv函数,其耗时都非常长,也就是说,不仅是滤波函数,连cv::multiply之类的函数也存在该问题。然而,Mat::convertTo、Mat::zeros等矩阵赋值操作则不存在该问题。

最后发现,只有opencv 3.0才存在该问题,2.4.9没有该问题!


推测:opencv3.0第一次执行这些函数(multiply、GaussianBlur等)时进行了一些初始化操作,导致第一次调用某些函数非常耗时,但具体进行了哪些操作无法得知,官方文档也没有就此问题进行解释


解决该问题的办法:程序开始时调用一次multiply函数。

经过测试,执行1000次multiply和GaussianBlur,处理Mat(Size(1000,1000),CV_32F)的数据,opencv3.0并没有什么优势,然而其官方声称IPP大幅提高了其性能,intel这……


附:opencv 3.0添加(重新开放)FilterEngine类


首先说明,为什么要往opencv 3.0中重新开放FilterEngine类?

就运行效率而言,其实上层封装好的各类滤波函数,如GaussianBlur,均是基于FilterEngine类的,所以无论是使用FilterEngine还是使用上层函数,其效率没有差别。因此官方才会封闭该类。

添加FilterEngine类,主要为了兼容旧代码,因为opencv 2.x有的代码中习惯使用FilterEngine的方式。


以下是具体方法:

1、编译opencv源码,必须自行编译一次,因为有一个用到的文件要编译后才能生成(假设编译opencv的project所在目录为D:\opencv\build_pure)

2、新建一个自己的应用程序的project,向其中添加以下文件并作相应修改(假设opencv源码解压目录为D:\opencv),往“文件所在目录”中找到相应的文件并复制到自己的project文件夹,然后再修改

(1)precomp.hpp

文件所在目录:D:\opencv\sources\modules\imgproc\src\precomp.hpp

共1项修改:

△ 删除语句,因为编译时会产生较多冲突

#include "opencv2/core/private.hpp"	
但去掉后会造成个别函数未定义,故还要手动添加这些函数,这些函数在(4)fiter.cpp中有说明

(2)opencl_kernels_imgproc.hpp和opencl_kernels_imgproc.cpp

文件所在目录:D:\opencv\build_pure\modules\imgproc\,注:这两个文件需要编译opencv源码之后才会生成

共1项修改:

△在D:\opencv\build_pure\install\include\opencv2\core下新建文件夹“opencl”,把位于D:\opencv\sources\modules\core\include\opencv2\core\opencl的ocl_defs.hpp拷贝至D:\opencv\build_pure\install\include\opencv2\core\opencl

(3)filterengine.hpp

文件所在目录:D:\opencv\sources\modules\imgproc\src\filterengine.hpp

共3项修改:

(说明:smooth.cpp位于D:\opencv\sources\modules\imgproc\src\smooth.cpp;templmatch.cpp位于D:\opencv\sources\modules\imgproc\src\templmatch.cpp)

△在smooth.cpp中找到createGaussianKernels函数,在filterengine.hpp中声明该函数,注意要在namespace cv中声明

void createGaussianKernels( Mat & kx, Mat & ky, int type, Size ksize,
									double sigma1, double sigma2 );

△在smooth.cpp中找到createGaussianFilter函数,在filterengine.hpp中声明该函数

Ptr<cv::FilterEngine> createGaussianFilter( int type, Size ksize,
                                        double sigma1, double sigma2,
                                        int borderType );

△在templmatch.cpp中找到crossCorr函数,在filterengine.hpp中声明该函数

void crossCorr( const Mat& src, const Mat& templ, Mat& dst,
               Size corrsize, int ctype,
               Point anchor=Point(0,0), double delta=0,
               int borderType=BORDER_REFLECT_101 );

注:以上仅仅包括了高斯滤波相关的函数,如果是其它滤波算法,则自行在opencv源码目录中查找该滤波算法所需的模块(可以通过debug单步跟踪了解某个函数涉及的底层代码);再次声明,这些函数均是filterengine中用到的底层代码,opencv3.0没有暴露给用户,所以需要手动添加,如果查找过程中发现某个函数是开放给用户的,即前面带有CV_EXPORTS关键字的则无需手动添加


(4)filter.cpp

文件所在目录:D:\opencv\sources\modules\imgproc\src\filter.cpp

共4项修改:

(说明:private.hpp位于D:\opencv\sources\modules\core\include\opencv2\core\private.hpp)

△承接(3),把在smooth.cpp中找到的createGaussianKernels函数,其实现拷贝至filter.cpp中

void cv::createGaussianKernels( Mat & kx, Mat & ky, int type, Size ksize,
									double sigma1, double sigma2 )
{
...
}

△承接(3),把在smooth.cpp中找到的createGaussianFilter函数,其实现拷贝至filter.cpp中

cv::Ptr<cv::FilterEngine> cv::createGaussianFilter( int type, Size ksize,
                                        double sigma1, double sigma2,
                                        int borderType )
{
...
}

△承接(3),把在templmatch.cpp中找到的crossCorr函数,其实现拷贝至filter.cpp中

void crossCorr( const Mat& img, const Mat& _templ, Mat& corr,
                Size corrsize, int ctype,
                Point anchor, double delta, int borderType )
{
...
}
△在private.hpp中找到scalarToRawData函数,将其实现拷贝至filter.cpp中

void scalarToRawData(const Scalar& s, void* _buf, int type, int unroll_to)
{
...
}

3、在程序中引用头文件即可使用FilterEngine类

#include "filterengine.hpp"

这是改好之后的5个文件,添加进去项目就可以使用FilterEngine类,基于VS2012:http://download.csdn.net/detail/kelvin_yan/9203965


----------------------END----------------------------










  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的OpenCV库提供了GaussianBlur函数,用于对图像进行高斯模糊处理。高斯模糊是一种常用的图像处理方法,可以有效地去除图像中的噪声和细节,使图像变得更加平滑。 GaussianBlur函数的原型如下: ```cpp void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT); ``` 参数说明: - src:输入图像,可以是单通道或多通道图像,类型为CV_8U、CV_16U、CV_32F或CV_64F。 - dst:输出图像,与输入图像具有相同的尺寸和类型。 - ksize:高斯核的大小,可以通过指定一个正的奇数值来表示核的宽度和高度。例如,Size(3, 3)表示一个3x3的核。 - sigmaX:X方向上的高斯核标准差。 - sigmaY:Y方向上的高斯核标准差。如果sigmaY为0,则默认与sigmaX相同。 - borderType:边界模式,默认为BORDER_DEFAULT。 下面是一个示例代码,演示如何使用GaussianBlur函数对图像进行高斯模糊处理: ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 读取图像 Mat image = imread("image.jpg"); // 检查图像是否成功读取 if (image.empty()) { std::cout << "Failed to read image!" << std::endl; return -1; } // 高斯模糊处理 Mat blurredImage; GaussianBlur(image, blurredImage, Size(5, 5), 0, 0); // 显示原始图像和模糊后的图像 imshow("Original Image", image); imshow("Blurred Image", blurredImage); waitKey(0); return 0; } ``` 这段代码首先使用imread函数读取一张图像,然后使用GaussianBlur函数对图像进行高斯模糊处理,并将结果显示出来。 希望对你有所帮助!如果你还有其他问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值