OpenCv源码解析:对HAL硬件加速层的支持
OpenCV的硬件加速层全称是OpenCV Hardware Acceleration Layer (HAL),一般来说,硬件厂商或开发人员可能会根据自己的实际情况,开发出独立于OpenCV的运算函数,以支持硬件优化,加快运算速度。这些功能性模块有可能是闭源的。为了实现顺利对接,OpenCV提供了一个简单的接口模块,当各厂家需要开发自己的运算模块时,只要实现这些接口即可。
根据【1】,OpenCV能用构建三种不同的模式
(1)无HAL支持模式。在这种情况下,默认采用OpenCv的函数实现;
(2)用静态链接的HAL。OpenCV会调用这些HAL提供的加速运算的函数。
(3)动态加载的HAL支持。OpenCV 会在运行时加载这些HAL模块,如果失败,就会调用默认的OpenCV中的函数实现。
OpenCV为CMake选项提供了 HAL_MODE, STATIC_HAL_INCLUDE_DIR 和STATIC_HAL_LIB选项,以方便用户选择使用模式和相应的HAL库。
可能因为版本的原因,目前的OpenCV3.4.1中给出的方案是采用内联函数的形式,也就是,如果厂家要实现这些运算,直接用自己的函数替换掉hal_replacement.hpp 即可,顾名思义,所以文件名叫replacement。我们可以在arithm.cpp文件中看到大量这样的语句,
void sub8u( const uchar* src1, size_t step1,
const uchar* src2, size_t step2,
uchar* dst, size_t step, int width, int height, void* )
{
CALL_HAL(sub8u, cv_hal_sub8u, src1, step1, src2, step2, dst, step, width, height)
CALL_IPP_BIN_E_21(ippiSub_8u_C1RSfs)
(vBinOp<uchar, cv::OpSub<uchar>, IF_SIMD(VSub<uchar>)>(src1, step1, src2, step2, dst,
step, width, height));
}
其中CALL_HAL是一个宏定义,如下
#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)); \
}
在运行sub8u运行时,这个宏先得到运行,此时CALL_HALL中的fun函数,就是cv_hal_sub8u,它在hal_replacement中是这样定义的,
#define cv_hal_sub8u hal_ni_sub8u
inline int hal_ni_sub8u(const uchar *src1_data, size_t src1_step, const uchar *src2_data,
size_t src2_step, uchar *dst_data, size_t dst_step, int width, int height)
{ return CV_HAL_ERROR_NOT_IMPLEMENTED; }
注意当没有提供HAL的实现函数时,默认的cv_hal_sub8u的返回值是CV_HAL_ERROR_NOT_IMPLEMENTED。当然,如果有提供HAL的实现函数,根据CALL_HAL这个宏,返回值必然需要是CV_HAL_ERROR_OK。
所以,这句
if (res == CV_HAL_ERROR_OK)
return;
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED)
CV_Error…
的意思是,如果有提供HAL的实现函数并返回CV_HAL_ERROR_OK, 就直接退出(指退出void sub8u(…)这个函数);如果没有提供HAL的实现函数,此时cv_hal_sub8u必然返回CV_HAL_ERROR_NOT_IMPLEMENTED(否则报错)。
我们看到hal_ni_sub8u返回的就是CV_HAL_ERROR_NOT_IMPLEMENTED,所以接下来,OpenCV会采用默认的
(vBinOp<uchar, cv::OpSub<uchar>, IF_SIMD(VSub<uchar>)>(src1, step1, src2, step2, dst, step, width, height));
来执行sub8u的计算。
OpenCV就是通过这种方式,实现对HAL硬件层的支持的。