YangYouji's WebSite

opencv 中使用 cudnn 预测CNN网络

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

退出移动版