手撕OpenCV源码之filter2D

Posted OpenCV学堂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手撕OpenCV源码之filter2D相关的知识,希望对你有一定的参考价值。

在上篇的GaussianBlur中提到,gaussianBlur使用的是filter2D的实现,因此上篇仅仅描述了高斯滤波器的生成细节,并没有针对滤波的计算细节及代码实现进行分析.本篇将详细介绍OpenCV中滤波的实现细节.

filter2D函数的整体结构分析

上篇文章中没有提到,本系列分析源码所使用的opencv版本是opencv-3.4.0.首先从OpenCV的主函数入手.

 
   
   
 
  1. void cv::filter2D( InputArray _src, OutputArray _dst, int ddepth,

  2.                   InputArray _kernel, Point anchor0,

  3.                   double delta, int borderType )        

  • 路径 opencv-3.4.0/modules/imgproc/filter.cpp

  • 函数原型

 
   
   
 
  1. void cv::filter2D( InputArray _src, OutputArray _dst, int ddepth,

  2.                   InputArray _kernel, Point anchor0,

  3.                   double delta, int borderType )

  4. {

  5.  //opencv的profiling,可以在内部追踪函数执行状况,默认情况下是关闭的,不会产生性能开销.

  6.    CV_INSTRUMENT_REGION()

  7. //若平台支持OpenCL则使用OpenCL执行

  8.    CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2,

  9.               ocl_filter2D(_src, _dst, ddepth, _kernel, anchor0, delta, borderType))

  10. //将传入的参数转换为Mat结构

  11.    Mat src = _src.getMat(), kernel = _kernel.getMat();

  12. //在api说明中知道调用cv::filter2D是允许将ddepth设置小于0的

  13.    if( ddepth < 0 )

  14.        ddepth = src.depth();

  15. //为函数输出创建Mat结构

  16.    _dst.create( src.size(), CV_MAKETYPE(ddepth, src.channels()) );

  17.    Mat dst = _dst.getMat();

  18.    Point anchor = normalizeAnchor(anchor0, kernel.size());

  19. //计算src在进行了边界填充后的数组中的位置

  20.    Point ofs;

  21.    Size wsz(src.cols, src.rows);

  22.    if( (borderType & BORDER_ISOLATED) == 0 )

  23.        src.locateROI( wsz, ofs );

  24. //调用hal命名空间下的filter2D函数

  25.    hal::filter2D(src.type(), dst.type(), kernel.type(),

  26.                  src.data, src.step, dst.data, dst.step,

  27.                  dst.cols, dst.rows, wsz.width, wsz.height, ofs.x, ofs.y,

  28.                  kernel.data, kernel.step,  kernel.cols, kernel.rows,

  29.                  anchor.x, anchor.y,

  30.                  delta, borderType, src.isSubmatrix());

  31. }  

接下来对代码注释中涉及的一些内容进行解释:

  • 关于OpenCL

在OpenCV的早起版本中其实就已经有OpenCL的优化了(比如2.4.8);当时版本中需要用户自己指定。在当前的opencv-3.4.0中则把OpenCL版本的实现做了改进;并且会优先选择使用OpenCL。 CV_OCL_RUN就是对OpenCL的检测,若OpenCL可用则执行OpenCL版本的filter2D。 可以看一下 CV_OCL_RUN这个宏 路径: opencv/core/include/opencv2/core/opencl/ocl_defs.hpp

 
   
   
 
  1. #define CV_OCL_RUN_(condition, func, ...)                                   \

  2.    {                                                                       \

  3.        if (cv::ocl::isOpenCLActivated() && (condition) && func)            \

  4.        {                                                                   \

  5.            printf("%s: OpenCL implementation is running\n", CV_Func);      \

  6.            fflush(stdout);                                                 \

  7.            CV_IMPL_ADD(CV_IMPL_OCL);                                       \

  8.            return __VA_ARGS__;                                             \

  9.        }                                                                   \

  10.        else                                                                \

  11.        {                                                                   \

  12.            printf("%s: Plain implementation is running\n", CV_Func);       \

  13.            fflush(stdout);                                                 \

  14.        }                                                                   \

  15.    }

可以看出,首先检测了opencl平台的可用性;之后添加OCL实现( CV_IMPL_ADD(CV_IMPL_OCL));接下来简单了解一下opencv目前的一些优化平台。 路径: opencv/modules/modules/core/include/opencv2/core/utility.hpp

 
   
   
 
  1.   #define CV_IMPL_PLAIN  0x01 // native CPU OpenCV implementation

  2.   #define CV_IMPL_OCL    0x02 // OpenCL implementation

  3.   #define CV_IMPL_IPP    0x04 // IPP implementation

  4.   #define CV_IMPL_MT     0x10 // multithreaded implementation

  5.   #define CV_IMPL_ADD(impl)                                                   \

  6.       if(cv::useCollection())                                                 \

  7.       {                                                                       \

  8.           cv::addImpl(impl, CV_Func);                                         \

  9.       }

  10.   #else

  11.   #define CV_IMPL_ADD(impl)

  12.   #endif

从这里可以看出,目前opencv主要的优化是,cpu,opencl,IPP库,以及多线程优化。另外opencv是支持cuda的,只是cuda是单独的模块。其他几种优化是作为通用模块实现的。 从cv::filter2D代码的组织形式看,opencv是优先使用opencl的,可以推测,opencv中opencl的实现性能是比较优秀的。

  • 接下来的处理再注释中写的比较明确了,不再赘述。

  • hal::filter2D 先上源码:

 
   
   
 
  1. void filter2D(int stype, int dtype, int kernel_type,

  2.              uchar * src_data, size_t src_step,

  3.              uchar * dst_data, size_t dst_step,

  4.              int width, int height,

  5.              int full_width, int full_height,

  6.              int offset_x, int offset_y,

  7.              uchar * kernel_data, size_t kernel_step,

  8.              int kernel_width, int kernel_height,

  9.              int anchor_x, int anchor_y,

  10.              double delta, int borderType,

  11.              bool isSubmatrix)

  12. {

  13.    bool res;

  14.    res = replacementFilter2D(stype, dtype, kernel_type,

  15.                              src_data, src_step,

  16.                              dst_data, dst_step,

  17.                              width, height,

  18.                              full_width, full_height,

  19.                              offset_x, offset_y,

  20.                              kernel_data, kernel_step,

  21.                              kernel_width, kernel_height,

  22.                              anchor_x, anchor_y,

  23.                              delta, borderType, isSubmatrix);

  24.    if (res)

  25.        return;

  26.        CV_IPP_RUN_FAST(ippFilter2D(stype, dtype, kernel_type,

  27.                                     src_data, src_step,

  28.                                     dst_data, dst_step,

  29.                                     width, height,

  30.                                     full_width, full_height,

  31.                                     offset_x, offset_y,

  32.                                     kernel_data, kernel_step,

  33.                                     kernel_width, kernel_height,

  34.                                     anchor_x, anchor_y,

  35.                                     delta, borderType, isSubmatrix))

  36.           res = dftFilter2D(stype, dtype, kernel_type,

  37.                             src_data, src_step,

  38.                             dst_data, dst_step,

  39.                             full_width, full_height,

  40.                             offset_x, offset_y,

  41.                             kernel_data, kernel_step,

  42.                             kernel_width, kernel_height,

  43.                             anchor_x, anchor_y,

  44.                             delta, borderType);

  45.           if (res)

  46.               return;

  47.               ocvFilter2D(stype, dtype, kernel_type,

  48.                              src_data, src_step,

  49.                              dst_data, dst_step,

  50.                              width, height,

  51.                              full_width, full_height,

  52.                              offset_x, offset_y,

  53.                              kernel_data, kernel_step,

  54.                              kernel_width, kernel_height,

  55.                              anchor_x, anchor_y,

  56.                              delta, borderType);

  57. }            

这段代码很简单,主要是各个平台计算法的执行顺序;首先是replacementFilter2D,这其实是可分离滤波器,从名字也可以看出来,是替代filter2D;上篇文章中提到的GaussianBlur就是可分离滤波器。满足这种性质的滤波器可以实现更好的计算性能,所以先检测,是否满足可分离特性。关于可分离滤波器会再后续的文章中详细说明,暂且不表。第二是调用IPP库,这是Inter的一个计算库;第三是使用dft进行滤波,再opencv中滤波器尺寸大于11的,会使用dft进行滤波,对于大尺寸滤波器,DFT(离散傅立叶变换-dft)会获得更好的性能。在dftFilter2D函数内部会判断滤波器尺寸;最后是ocvFilter2D,这是使用c++实现的二维滤波。 dftFilter2D函数内部判断:

 
   
   
 
  1. int dft_filter_size = 50;//不使用SSE的情况下      

  2. if (kernel_width * kernel_height < dft_filter_size)

  3.           return false;

到此为止,filter2D API中的函数结构介绍结束,目前本系列文章中着重介绍c++实现,也就是ocvFilter2D;在后续的文章中也会介绍dft的c++实现;对于各中优化版本的实现,不做深究。以后的别的系列可能会介绍opencl版本的优化。下一篇文章中将详细介绍ocvFilter2D的实现。

作者:2know

邮箱:dingjie_yao@163.com

逻辑可以让你从a到z,想象力则可以让你到达任何地方

          

更多相关阅读





善始者实繁

克终者盖寡


关注【OpenCV学堂】

长按或者扫码二维码即可关注

请戳广告支持,谢谢!

【关于学堂】 - 微信联系

技术交流合作!


以上是关于手撕OpenCV源码之filter2D的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV:filter2D函数的计算效率

OpenCv2.2 C++ filter2D

OpenCV filter2d 给出不正确的结果

卷积处理过程模拟:用Python实现OpenCV函数filter2D等效的卷积功能

OpenCV filter2D和sepFilter2D

opencv学习笔记-掩膜操作-调用filter2D功能