c/c++语言开发共享OpenCV中的HAL方法调用流程分析

OpenCV中的HAL方法调用流程分析 在OpenCV中有一些所谓HAL(Hardware Acceleration Layer)实现,看名字好像和硬件相关,其实也不尽然,可以理解为比常规的OCV实现更快的版本就好了。此文要做的就是要找到其实现或者切入流程,打通整个函数调用逻辑。本文将以 和`Gau …


opencv中的hal方法调用流程分析

在opencv中有一些所谓hal(hardware acceleration layer)实现,看免费精选名字大全好像和硬件相关,其实也不尽然,可以理解为比常规的ocv实现更快的版本就好了。此文要做的就是要找到其实现或者切入流程,打通整个函数调用逻辑。c/c++开发分享OpenCV中的HAL方法调用流程分析将以resizegaussianblur两个函数来分析。

resize

首先定位到imgproc模块的imgproc.hpp文件,找到其中的cv_exports_w void resize( inputarray src, outputarray dst, size dsize, double fx = 0, double fy = 0, int interpolation = inter_linear );方法。因为我们在外部使用的时候都是引用头文件来使用,也就是头文件的函数是我们使用的入口函数。而ocv实现会有许多的分支,一下难以确定,所以我们从入口来找是比较方便的。然后跳转到该函数的实现,如果ide不支持,可以在对应的resize.cpp搜索有相同的函数声明函数就是对应函数的实现,如下:

void cv::resize( inputarray _src, outputarray _dst, size dsize,                  double inv_scale_x, double inv_scale_y, int interpolation ) {     cv_instrument_region();      size ssize = _src.size();      cv_assert( !ssize.empty() );     if( dsize.empty() )     {         cv_assert(inv_scale_x > 0); cv_assert(inv_scale_y > 0);         dsize = size(saturate_cast<int>(ssize.width*inv_scale_x),                      saturate_cast<int>(ssize.height*inv_scale_y));         cv_assert( !dsize.empty() );     }     else     {         inv_scale_x = (double)dsize.width/ssize.width;         inv_scale_y = (double)dsize.height/ssize.height;         cv_assert(inv_scale_x > 0); cv_assert(inv_scale_y > 0);     }      if (interpolation == inter_linear_exact && (_src.depth() == cv_32f || _src.depth() == cv_64f))         interpolation = inter_linear; // if depth isn't supported fallback to generic resize      cv_ocl_run(_src.dims() <= 2 && _dst.isumat() && _src.cols() > 10 && _src.rows() > 10,                ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation))      mat src = _src.getmat();     _dst.create(dsize, src.type());     mat dst = _dst.getmat();      if (dsize == ssize)     {         // source and destination are of same size. use simple copy.         src.copyto(dst);         return;     }      hal::resize(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, inv_scale_x, inv_scale_y, interpolation); }

我们看到该函数实现做了三件事:

  1. 参数检查
  2. 检测是否有opencl支持或启用
  3. 使用hal空间的resize函数来实现

跳转到hal的实现,同样位于resize.cpp,部分代码:

namespace hal {  void resize(int src_type,             const uchar * src_data, size_t src_step, int src_width, int src_height,             uchar * dst_data, size_t dst_step, int dst_width, int dst_height,             double inv_scale_x, double inv_scale_y, int interpolation) {     cv_instrument_region();      cv_assert((dst_width > 0 && dst_height > 0) || (inv_scale_x > 0 && inv_scale_y > 0));     if (inv_scale_x < dbl_epsilon || inv_scale_y < dbl_epsilon)     {         inv_scale_x = static_cast<double>(dst_width) / src_width;         inv_scale_y = static_cast<double>(dst_height) / src_height;     }      call_hal(resize, cv_hal_resize, src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation);     //剩下部分代码是常规实现

然后我们就看到这里有call_hal这样一个宏,跳转到其实现,位于hal_replacement.hpp,

#define call_hal(name, fun, ...)      int res = __cv_expand(fun(__va_args__));      if (res == cv_hal_error_ok)          return;      else if (res != cv_hal_error_not_implemented)          cv_error_(cv::error::stsinternal,              ("hal implementation " cvaux_str(name) " ==> " cvaux_str(fun) " returned %d (0x%08x)", res, res));

我们可以看到,它实际上调用了fun函数,如果该函数返回cv_hal_error_ok,那么就会return,显然hal::resize也会返回;否则,会调用cv_error_,这个并不会让函数结束或者和程序异常一样直接终止整个函数,以后再细讲。反正其结果就是会让hal::resize继续往下执行,下面就是常规的实现,并不会在此宏里就return
然后我们在hal_replacement.hpp找到cv_hal_resize的定义为

#define cv_hal_resize hal_ni_resize

然后继续找到hal_ni_resize的实现为

inline int hal_ni_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, double inv_scale_x, double inv_scale_y, int interpolation) { return cv_hal_error_not_implemented; }

到这里,我们发现该函数直接返回cv_hal_error_not_implemented,按照上面的分析,hal::resize继续往下执行。那么,hal的实现是怎么切入进来的呢?
我们发现,hal_replacement.hpp中的call_hal宏上有一句#include "custom_hal.hpp",好奇怪,include不一般都放在开头嘛?然后我们看下这个custom_hal.cpp,发现它只有一句#include "carotene/tegra_hal.hpp",我们继续跟踪下去。因为前面分析的函数为hal_ni_resize,直接findhal_ni_resize,没有结果。然后我们findcv_hal_resize,发现有:

#undef cv_hal_resize #define cv_hal_resize tegra_resize

顿时就感觉快打通了,这里竟然把cv_hal_resize给undef掉了,我们知道在hal_replacement.hpp中是#define cv_hal_resize hal_ni_resize的,并且从文件的位置来看,这个def就会被undef掉,然后重新定义为tegra_resize,find它,发现其定义:

#define tegra_resize(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation)  (      interpolation == cv_hal_inter_linear ?          cv_mat_depth(src_type) == cv_8u && carotene_ns::isresizelinearopencvsupported(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height), ((src_type >> cv_cn_shift) + 1)) &&          inv_scale_x > 0 && inv_scale_y > 0 &&          (dst_width - 0.5)/inv_scale_x - 0.5 < src_width && (dst_height - 0.5)/inv_scale_y - 0.5 < src_height &&          (dst_width + 0.5)/inv_scale_x + 0.5 >= src_width && (dst_height + 0.5)/inv_scale_y + 0.5 >= src_height &&          std::abs(dst_width / inv_scale_x - src_width) < 0.1 && std::abs(dst_height / inv_scale_y - src_height) < 0.1 ?              carotene_ns::resizelinearopencv(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height),                                              src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> cv_cn_shift) + 1)),              cv_hal_error_ok : cv_hal_error_not_implemented :      interpolation == cv_hal_inter_area ?          cv_mat_depth(src_type) == cv_8u && carotene_ns::isresizeareasupported(1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> cv_cn_shift) + 1)) &&          std::abs(dst_width / inv_scale_x - src_width) < 0.1 && std::abs(dst_height / inv_scale_y - src_height) < 0.1 ?              carotene_ns::resizeareaopencv(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height),                                            src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> cv_cn_shift) + 1)),              cv_hal_error_ok : cv_hal_error_not_implemented :      /*nearest neighbour interpolation disabled due to rounding accuracy issues*/      /*interpolation == cv_hal_inter_nearest ?          (src_type == cv_8uc1 || src_type == cv_8sc1) && carotene_ns::isresizenearestneighborsupported(carotene_ns::size2d(src_width, src_height), 1) ?              carotene_ns::resizenearestneighbor(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height),                                                 src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 1),              cv_hal_error_ok :          (src_type == cv_8uc3 || src_type == cv_8sc3) && carotene_ns::isresizenearestneighborsupported(carotene_ns::size2d(src_width, src_height), 3) ?              carotene_ns::resizenearestneighbor(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height),                                                 src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 3),              cv_hal_error_ok :          (src_type == cv_8uc4 || src_type == cv_8sc4 || src_type == cv_16uc2 || src_type == cv_16sc2 || src_type == cv_32sc1) &&          carotene_ns::isresizenearestneighborsupported(carotene_ns::size2d(src_width, src_height), 4) ?              carotene_ns::resizenearestneighbor(carotene_ns::size2d(src_width, src_height), carotene_ns::size2d(dst_width, dst_height),                                                 src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 4),              cv_hal_error_ok : cv_hal_error_not_implemented :*/      cv_hal_error_not_implemented  )

这个宏的定义大概做了这些事情:

  1. 如果是双线性插值,为cv_8u数据类型,且尺寸以及通道满足一定的要求,那么就resizelinearopencv去真正实现resize,且返回cv_hal_error_ok,不满足这些条件的双线性插值就不支持,返回cv_hal_error_not_implemented,这样,就会走hal::resize的普通实现
  2. 如果是area插值,情况也是和双线性插值类似
  3. 其他插值方式则目前不支持。但是从注释的这些代码来看,应该是计划支持的,只是现在还没做好而已。
    这个宏的定义用到了不常注意的逗号运算符来实现cv_hal_error_ok值的返回,逗号运算符返回的是其最右边的值。
    然后我们就可以跳转到resizelinearopencv以及resizeareaopencv来追踪真正的快速实现方法了。

可以发现,切入的关键就在于那个undef和define操作了。

gaussianblur

同样,我们在smooth.cpp中找到cv_hal_gaussianblur方法的实现,发现其hal宏为cv_hal_gaussianblur,然后到tegra_hal.hpp中findcv_hal_gaussianblur,发现没有结果。这说明高斯模糊没有对应的hal快速版本。然后发现carotene库中有高斯模糊相关的代码,看起来应该有实现?我们通过写demo以及在源码中打log的方式,发现这些实现函数确实没有被调用,都是在call_hal宏那里就返回cv_hal_error_not_implemented了。应该是这些实现还不够好,所以没有切入进去,慢慢等待吧。

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/600180.html

(0)
上一篇 2021年5月9日
下一篇 2021年5月9日

精彩推荐