opencv从3版本开始就已经支持CNN网络模型的预测,到4版本,主流工具tensorflow,pytorch 生成的模型文件大部分都可以支持。但其一直没有使用到CUDNN。但最新发布的4.2版本的opencv已经支持CUDNN了。以下是功能测试
一。安装编译环境
CUDNN的安装方法可以参考tensorflow中关于cudnn的安装方法https://www.tensorflow.org/install/gpu 接下来也会使用tensorflow作为例子。
二。编译opencv
编译方法参考链接https://docs.opencv.org/4.2.0/d3/d52/tutorial_windows_install.html 其中为了加入CUDNN的支持,有以下需要设置一下
1.勾选cuda和cudnn
2.设置CUDA_ARCH_BIN
cudnn不支持3.5下的显卡。需要根据自己使用的显卡来设置,列表参考https://developer.nvidia.com/cuda-gpus
3.代码bug修正
由于我使用的是vs2015,版本低了点。导致编译时会出错。github上已经修复了这个bug,估计会在下一个版本发布。所以低版本vs编译时 需要手动修改一下
文件 modules/dnn/src/cuda/grid_stride_range.hpp
namespace detail {
template <int> __device__ auto getGridDim()>decltype(dim3::x);
template <> inline __device__ auto getGridDim<0>()>decltype(dim3::x) { return gridDim.x; }
template <> inline __device__ auto getGridDim<1>()>decltype(dim3::x) { return gridDim.y; }
template <> inline __device__ auto getGridDim<2>()>decltype(dim3::x) { return gridDim.z; }
template <int> __device__ auto getBlockDim()>decltype(dim3::x);
template <> inline __device__ auto getBlockDim<0>()>decltype(dim3::x) { return blockDim.x; }
template <> inline __device__ auto getBlockDim<1>()>decltype(dim3::x) { return blockDim.y; }
template <> inline __device__ auto getBlockDim<2>()>decltype(dim3::x) { return blockDim.z; }
template <int> __device__ auto getBlockIdx()>decltype(uint3::x);
template <> inline __device__ auto getBlockIdx<0>()>decltype(uint3::x) { return blockIdx.x; }
template <> inline __device__ auto getBlockIdx<1>()>decltype(uint3::x) { return blockIdx.y; }
template <> inline __device__ auto getBlockIdx<2>()>decltype(uint3::x) { return blockIdx.z; }
template <int> __device__ auto getThreadIdx()>decltype(uint3::x);
template <> inline __device__ auto getThreadIdx<0>()>decltype(uint3::x) { return threadIdx.x; }
template <> inline __device__ auto getThreadIdx<1>()>decltype(uint3::x) { return threadIdx.y; }
template <> inline __device__ auto getThreadIdx<2>()>decltype(uint3::x) { return threadIdx.z; }
}
替换为
namespace detail {
using dim3_member_type = decltype(dim3::x);
template <int> __device__ dim3_member_type getGridDim();
template <> inline __device__ dim3_member_type getGridDim<0>() { return gridDim.x; }
template <> inline __device__ dim3_member_type getGridDim<1>() { return gridDim.y; }
template <> inline __device__ dim3_member_type getGridDim<2>() { return gridDim.z; }
template <int> __device__ dim3_member_type getBlockDim();
template <> inline __device__ dim3_member_type getBlockDim<0>() { return blockDim.x; }
template <> inline __device__ dim3_member_type getBlockDim<1>() { return blockDim.y; }
template <> inline __device__ dim3_member_type getBlockDim<2>() { return blockDim.z; }
using uint3_member_type = decltype(uint3::x);
template <int> __device__ uint3_member_type getBlockIdx();
template <> inline __device__ uint3_member_type getBlockIdx<0>() { return blockIdx.x; }
template <> inline __device__ uint3_member_type getBlockIdx<1>() { return blockIdx.y; }
template <> inline __device__ uint3_member_type getBlockIdx<2>() { return blockIdx.z; }
template <int> __device__ uint3_member_type getThreadIdx();
template <> inline __device__ uint3_member_type getThreadIdx<0>() { return threadIdx.x; }
template <> inline __device__ uint3_member_type getThreadIdx<1>() { return threadIdx.y; }
template <> inline __device__ uint3_member_type getThreadIdx<2>() { return threadIdx.z; }
}
文件 modules/dnn/src/cuda4dnn/csl/cudnn/cudnn.hpp
template <class> auto get_data_type()->decltype(CUDNN_DATA_FLOAT);
template <> inline auto get_data_type<half>()->decltype(CUDNN_DATA_HALF) { return CUDNN_DATA_HALF; }
template <> inline auto get_data_type<float>()->decltype(CUDNN_DATA_FLOAT) { return CUDNN_DATA_FLOAT; }
替换为
using cudnn_data_enum_type = decltype(CUDNN_DATA_FLOAT);
template <class> cudnn_data_enum_type get_data_type();
template <> inline cudnn_data_enum_type get_data_type<half>() { return CUDNN_DATA_HALF; }
template <> inline cudnn_data_enum_type get_data_type<float>() { return CUDNN_DATA_FLOAT; }
修改完成后正常编译就可以了
三。使用
设置为使用cudnn非常简单,只需要在原来的基础上添加两行代码。
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
具体opencv怎么使用模型文件可以参考博客之前的文章https://www.yangyouji.info/archives/349