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.0和opencv-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-Input – Additional 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.参考资料
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/Moonjou/p/17022193.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具