OpenCV-DNN模块安装及Tensor结构测试

0.前言

本文介绍OpenCV-DNN模块的安装和以OpenCV-Mat为基础结构的Tensor的解析。
OpenCV-DNN模块为3.x开始加入OpenCV的推理引擎,前后端分离,且后端支持CPU,GPU和部分NPU。不仅作为一个C++图像处理库,也能作为深度学习的推理引擎。

1.安装CUDA & CUDNN & VS2019

  • 此处下载CUDA ;
  • 此处下载CuDNN,注意Opencv4.6.0对应8.2.0+ cuDNN版本;
  • 安装CUDA及CuDNN;
  • 此处下载CUDA samples ,用VS运行其中的DeviceQuery.sln,记录当前GPU的compute capacity. 在后续OpenCV中需要使用到

2.编译CUDA-OpenCV代码

  • 首先安装Microsoft Visual Studio,建议Microsoft Visual Studio2019,通用Microsoft Visual Studio2019+即可,建议别用最新版本防止部分特性不支持。
  • github拉opencv-4.6.0opencv-contrib(opencv-4.4.0+即可支持cuDNN)
  • 默认拉取最新版本,如需使用指定版本,请在release界面下载源代码;
  • opencv-4.6.0中分别新建build目录作为编译结果目录,.cache目录作为缓存目录。
  • .cache是Opencv编译过程中自动下载的目录,国内网络环境差,故提供.cache.7z来避免下载缓存浪费过多时间。
  • Cmake-gui
    • Configure 配置选择VS版本及x64;
    • 第一次Configure:生成基础属性表,并修改属性表:
      • Entable extra modules;此处输入opencv-contrib/modules位置;
      • With CUDA 设为True;
      • OpenCV-dnn-CUDA 设为True;
    • 第二次Configure;生成CUDA属性表:
      • 如果正确安装Cudnn,则默认CuDnn和CuFFT都为True;
      • CUDA_ARCH_BIN,GPU计算能力,不修改默认生成所有版本,会非常慢;此处修改成你使用的GPU计算能力版本(1030-6.1、3070-8.6);
    • 第三次Configure(可选:删除不需要的OpenCV包以提高编译速度);
    • Generate:
  • VS2019打开Opencv.sln,先确认的版本是x64-Release,或者x64-Debug,默认为x64-Debug,建议二者都编译。在CmakeTargets选项下选择ALL_BUILD即可开始编译;
  • 编译完成后,在CmakeTargets选项下选择INSTALL来将编译结果打包成可供调用的文件结构;
  • 以上完成后,在opencv-4.6.0的build目录下,会生成编译结果install目录。使用Cmake时候,使用特有OpenCV-DIR赋值该路径即可
  • (可选)删除不需要的OpenCV包,Cmake-gui界面取消一些不需要的模块的勾选,可以显著提高Build速度,建议百度OpenCV各模块的作用。对于使用OpenCV-DNN,建议勾选所有CUDA相关模块,即名字带cuda的模块;

3.使用OpenCV-DNN模块

1.在项目中使用OpenCV-DNN

  • CMake项目

    Cmake项目使用Dependencies.cmake 及CmakeLists.txt

    // Dependencies.cmake
    set(OpenCV_DIR E:{opencv-4.6.0-dir}/build/install)
    
    find_package(OpenCV CONFIG REQUIRED)
    ...
    // CMakeLists.txt
    include("Dependencies.cmake")
    
    add_executable(project_name src.cpp)
    target_include_directories(project_name PRIVATE ${OpenCV_INCLUDE_DIRS})
    target_link_directories(project_name PRIVATE ${OpenCV_LIBS})
    target_link_libraries(project_name PRIVATE opencv_core opencv_imgproc opencv_dnn  opencv_highgui)
    
  • VS2019项目

    VS项目即sln项目,需要分别添加包含目录(include), 库目录(lib),以及在链接器(Linker)中,将所有lib文件都链接到输出结果中。项目名称邮件->属性(Properties):

    • VC++ Directories中 包含目录( Include Directories ):添加 E:{opencv-4.6.0-dir}/build/install/include
    • VC++ Directories中库目录( Library Directories):添加 E:{opencv-4.6.0-dir}/build/install/x64/vc16/lib
    • Linker-InputAdditional Dependencie中:添加库目录下所有的lib名称或针对debug代码添加*d.lib,对于release添加所有**.lib 库
  • 添加bin目录到环境变量

    如果需要执行某个程序,需要将E:{opencv-4.6.0-dir}/build/install/x64/vc16/bin目录添加到环境变量下,或者复制需要的dll程序到可执行文件夹中,或者IDE支持局部环境变量,可以将dll程序添加到局部环境变量下。

2.DNN模块运行流程

  • readNetFromONNX

    读取ONNX模型。注意ONNX模型应当包括input Shape。DNN模组只支持拥有输入Size的模型。

    启动DNN模块的GPU支持:

      Gpu_net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
      Gpu_net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
    
  • blobFromImage & blobFromImages

    将OpenCV读取的图片Mat转换成Tensor,blobFromImage 将单个Mat转换成1CHW的tensor,blobFromImages 将N个Mat转换成NCHW的Tensor。

        CV_EXPORTS void blobFromImage(InputArray image, OutputArray blob, double scalefactor=1.0,
                                      const Size& size = Size(), const Scalar& mean = Scalar(),
                                      bool swapRB=false, bool crop=false, int ddepth=CV_32F);
                                      
    // 1.image:   源图像
    // 2.blob:    转换后Tensor
    // 3.scalefactor: 缩放因子 用于正则化
    // 4.size:   转换后Tensor的大小
    // 5.mean:   image每个通道减去的值
    // 6.swapRB: 交换R B 通道,为true 相当于cvtColor的 RGB2BGR
    // 7.crop:   是否在缩放后做裁剪,一般为false
    // 8.ddepth: Tensor的符号,CV_32F 或 CV_8U
    // 归一化公式为:(image channel - scalar mean) * sacalefactor
    举例:
    double scale = 1.0;
    cv::dnn::blobFromImage(src, blob, scale, cv::Size(2,2),cv::Scalar(127.5,127.5,127.5), true, false,CV_32F);
    
  • setInput & forward

    可以先读取onnx模型,确定输出以后再forward指定输出。

        Gpu_net.setInput(blob);
    
        auto out_name =  Gpu_net.getUnconnectedOutLayersNames();
    
        for(auto it : out_name)
        {
            std::cout << "Net Outputs Name:";
            std::cout << it << ", ";
        }
    
        auto locations = Gpu_net.forward("output1");
        auto probs = Gpu_net.forward("output2")
    

3.Tensor的解析

​ OpenCV中基础结构是Mat,Tensor是一种特殊的4D-Mat。

cv::Mat src = cv::imread("xxx.jpg");
cv::resize(src, src, cv::Size(100, 80));

// 以src创建tensor,做RGB2BGR,做正则化,做resize 
cv::Mat blob;
double scale = 0.0078125;
cv::dnn::blobFromImage(src, blob, scale, cv::Size(100,80),cv::Scalar(127.5,127.5,127.5), true, false,CV_32F);

// tensor 默认 rows 和cols 都为 -1
// nchw = size[0][1][2][3]
auto h = blob.size[2];
auto w = blob.size[3];

// tensor也不支持类似 blob.at<float>(i,j) 只支持blob.ptr属性
// blob.ptr只支持到H层, blob.ptr(N,C,H) 一般只用blob.ptr(N,C) 来获取对应channel的指针
cv::Mat c0 = cv::Mat(cv::Size(locations.size[3],locations.size[2]), CV_32F, locations.ptr(0,0));
cv::Mat c1 = cv::Mat(cv::Size(locations.size[3],locations.size[2]), CV_32F, locations.ptr(0,1));
cv::Mat c2 = cv::Mat(cv::Size(locations.size[3],locations.size[2]), CV_32F, locations.ptr(0,2));

// Tensor的Step(步长) 可用,且很有用
auto step0 = blob.step[0];		// N * C * H * W * sizeof(float)
auto step1 = blob.step[1];		// H * W * sizeof(float)
auto step2 = blob.step[2];		// H * sizeof(float)
auto step3 = blob.step[3];		// sizeof(float)

// 内存拷贝仍然可用,记得先限制vector的size
std::vector<float> blob_vec(blob.size[2] * blob.size[3]);

// 这样编写的函数也具有通用性。
std::memcpy(blob_vec.data(), blob.data + blob.step[1], blob.step[1]);

4.参考资料

posted @   MoonJou  阅读(1083)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示