一、前言
之前一阵子一直在做的就是怎么把yolo项目部署成c++项目,因为项目需要嵌套进yolo模型跑算法。因为自己也是本科生小白一枚,基本上对这方面没有涉猎过,自己一个人从网上到处搜寻资料,写代码,调试,期间遇到的bug不能说多,只能说很多!!!
最开始的思路一直都是,有没有什么办法能够直接用C++代码直接调用整个yolo项目,也就是如何用C++调用python项目。
这期间真的,碰壁不少,先是安装opencv环境,能显示图像了,然后就是调用python。网上的教程很少或者说基本没有关于如何直接用c++调用整个python项目的。一般也是用c++调用一个python脚本文件的。可即使就是用c++调用一个python脚本文件,也遇到了数不尽的bug。无法找到python36.dll呀、python环境变量冲突呀…怎么说呢,反正是网上关于c++调用python脚本的bug,不管是查得到的还是查不到的,我全遇到了…
下面是自己记录的一些问题以及解决办法:
-
途中报错:由于找不到python36.dll,无法继续执行代码。重新安装程序可能会解决此问题
只要重新下载python37.dll解压复制到C:\Windows\System32\这里就行了
-
QT调用python脚本时遇到的坑(十一大坑全有)
-
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding, when trying to start uwsgi这个问题应该还是跟环境变量什么的有关系,最后还是没解决(可能是我电脑上python环境太多太杂乱了?)反正试了很多办法也没解决。最后的最后呢,换了台电脑,重新按照教程,注意一些坑点,成功调用了一个python脚本文件。
主要参考教程如下:
-
VS+QT调用python脚本
-
C++ QT调用python脚本并将软件打包发布
在这里特别想感谢第一篇教程的博主十里春风_jzh,因为在调用python脚本的时缺少遇到了很多bug和困难,这个博主一直耐心回答帮助我解决问题,真的十分感谢!这也是为什么我一直坚持写博客,分享知识的原因,因为一个人的力量总是渺小的,而更多人的智慧是无量的!
最后由于精力耗费太大,网上相关资料又太少,虽然实现了C++调用一个python脚本文件,但是还是没有实现C++直接调用一整个python项目的。(当时不知道为什么QT项目又只能在Debug模式下跑、也想到又要集成python整个大的项目,最后的软件体积是否会非常大?))况且网上的方法一般还是把yolo模型用其他C++框架(opencv-dnn、onnxruntime、TensorRT)部署的比较多,于是转战直接用c++和相关框架来部署了。
当时记录的新路历程:
⭐yolo转为onnx,用c++进行推理
发现直接用c++去调用整个yolov8的ultralytics项目网上的方法少之又少,而且通过了解知道yolo的底层框架什么的其实也是c++,看到很多用c++部署yolo的都是转换为onnx模型,现在那就按照这种方法试试吧,毕竟参考资料很多。虽然之前一开始也想到了这种部署方法,但是出于对c++的恐惧以及对Yolov8项目的没有很深入的了解,还有pytorch这些框架的不了解,感觉很害怕,怕自己弄错,于是想着偷懒,如果能找到c++直接调用python整个项目的该多好。但是现在才反应过来,技术的懒你是一点也偷不了,这块你不克服、你不去弄懂、你不去尝试,你就跨不过去这个坎。反而弄懂之后不但扫清了你的障碍,还对这块技术有了更深入的了解,还可以反观之前那种偷懒方法隐含的弊端。
一开始是发现YOLO官方直接有相关的onnxruntime-cpp
的代码实现:ultralytics/examples
/YOLOv8-ONNXRuntime-CPP/。
-
注意点一、改变语言标准为c++17
VS2019修改C++标准(支持C++17)
-
注意点2:配置好onnx环境()
-
cuda和onnxruntime的环境配置(40系列的显卡至cuda至少要11.8->这个点暂时不确定,因为后来我在项目中使用11.2版本的cuda没有问题)
VS2019配置onnxruntime推理环境
使用gpu版本onnxruntime的推理需要使用cuda
cuda的安装过程看这个CUDA安装及环境配置——最新详细版
结合CUDA11.0+VS2019+WIN10环境配置
⭐⭐⭐❗❗❗❗主要还是参考官方yolo教程,但是yolo的教程运行起来还是报错,然后还是一开始使用的这个博客使用opencv的方法进行Yolov8的推理:(注意环境必须是opencv4.8.0/4.8.1
yolov8 opencv模型部署(C++版)
但是出现问题:opencv4.8版本ok,enableCuda也设置了true
,但是推理一张图片居然要5s,看任务管理器也发现没有用GPU,看这个博客评论得到以下点\
- 使用CUDA需要将cv::Mat类型转换为GpuMat(好吧,后来试了这个发现显示cv没有GpuMat,不知道是不是英文opencv需要进行编译的原因(劝退了,opencv编译太难了
- ❗博主也给出opencv+cuda源码编译有(看来需要将opencv进行特定的编译?)同时也给出可以直接使用tensorrt,速度会比opencv+cuda快很多,说折腾这个时间成本高,且折腾完了所以对于也相对较慢(博主也给出了tensorrt进行部署的教程:win10下 yolov8 tensorrt模型部署✨
(🙇🏻♀️说实话当时tensorrt有点劝退了,好像有点复杂,再试试如何启用opencv进行cuda加速把)
下面先给出基于opencv版本的yolo部署:
二、opencv部署:
2.1:前言
yolov8 opencv模型部署(C++ 版)
参考学习的博客:win10下 yolov8 tensorrt模型部署✨
使用opencv推理yolov8模型,仅依赖opencv,无需其他库,以yolov8s为例子,注意:
使用opencv4.8.1 !
使用opencv4.8.1 !
使用opencv4.8.1 !
如果你使用别的版本,例如opencv4.5,可能会出现错误
至于怎么安装yolov8、训练模型、导出onnx博客中都有,这里不做详细解释。关于vs2019配置opencv环境的博客网上也是一大堆,这里也不再重复造轮子了
2.2:代码(含详细注释版)
inference.h
#ifndef INFERENCE_H
#define INFERENCE_H// Cpp native
#include <fstream>
#include <vector>
#include <string>
#include <random>// OpenCV / DNN / Inference
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>//Detection结构体用来保存目标检测的结果实例
struct Detection
{int class_id{ 0 };//整形变量用来存储检测到的目标的类别,默认值为0std::string className{};//字符串变量用来存储检测到的目标的名称,默认值为空字符串float confidence{ 0.0 };//目标检测的置信度(即对目标存在的确定程度)。默认值为0.0。cv::Scalar color{};//OpenCV库中的Scalar类型变量,用于存储颜色信息。它可以表示RGB、BGR或灰度颜色空间中的颜色cv::Rect box{}; //cv::Rect 类型包含四个成员变量:x、y、width 和 height
};//Infrence类用来执行目标检测
class Inference
{
public://构造函数(modelInputShape是值模型的大小,默认为640,640;classesTxtFile是类别名称的文本文件路径(可选参数,默认为空字符串);runWithCuda是一个布尔值,指示是否使用CUDA加速运行(可选参数,默认为true)。Inference(const std::string& onnxModelPath, const cv::Size& modelInputShape = { 640, 640 }, const std::string& classesTxtFile = "", const bool& runWithCuda = true);//公有成员函数,用于执行目标检测推断。它接受一个cv::Mat类型的输入图像,并返回一个std::vector<Detection>类型的检测结果。该函数将执行目标检测算法,将检测到的目标信息封装到Detection结构体中,并将所有检测结果存储在一个向量中。std::vector<Detection> runInference(const cv::Mat& input);//私有成员函数,用于内部操作
private://loadClassesFromFile函数从文本文件中加载类别名称void loadClassesFromFile();//loadOnnxNetwork函数加载ONNX模型void loadOnnxNetwork();//formatToSquare函数将输入图像调整为正方形形状。cv::Mat formatToSquare(const cv::Mat& source);//这些是私有成员变量std::string modelPath{};//存储模型文件路径std::string classesPath{};//类别文件路径bool cudaEnabled{};//CUDA加速的状态//字符串向量,用于存储目标检测的类别名称。默认情况下,它包含了一些通用的目标类别名称std::vector<std::string> classes{ "screw", "number", "pump" };//std::vector<std::string> classes{ "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" };//这是一个OpenCV库中的Size2f类型变量,用于存储模型的输入形状(宽度和高度)。cv::Size2f modelShape{};//设置目标检测的阈值float modelConfidenceThreshold{ 0.25 };//目标置信度的阈值float modelScoreThreshold{ 0.45 };//目标得分的阈值float modelNMSThreshold{ 0.50 };//非最大抑制的阈值//布尔变量,指示是否使用letterbox技术将输入图像调整为正方形形状bool letterBoxForSquare = true;//该类封装了目标检测推断的相关操作和参数,通过调用构造函数和成员函数,你可以加载模型、执行推断,并获取目标检测的结果cv::dnn::Net net;//penCV库中的Net类型变量,用于存储加载的目标检测网络模型
};#endif // INFERENCE_H
inference.cpp
#include "inference.h"Inference::Inference(const std::string& onnxModelPath, const cv::Size& modelInputShape, const std::string& classesTxtFile, const bool& runWithCuda)
{modelPath = onnxModelPath;modelShape = modelInputShape;classesPath = classesTxtFile;cudaEnabled = runWithCuda;loadOnnxNetwork();// loadClassesFromFile(); The classes are hard-coded for this example
}std::vector<Detection> Inference::runInference(const cv::Mat& input)
{cv::Mat modelInput = input;if (letterBoxForSquare && modelShape.width == modelShape.height)modelInput = formatToSquare(modelInput);cv::Mat blob;cv::dnn::blobFromImage(modelInput, blob, 1.0 / 255.0, modelShape, cv::Scalar(), true, false);net.setInput(blob);std::vector<cv::Mat> outputs;net.forward(outputs, net.getUnconnectedOutLayersNames());//cv::Mat cpuOutput;//outputs[0].copyTo(cpuOutput); // 将数据从 GPU 复制到 CPU 的 cv::Mat 对象中//float* data = reinterpret_cast<float*>(outputs.data); // 将数据赋值给 float* 指针int rows = outputs[0].size[1];int dimensions = outputs[0].size[2];bool yolov8 = true;// yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c])// yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h])if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8){yolov8 = true;rows = outputs[0].size[2];dimensions = outputs[0].size[1];outputs[0] = outputs[0].reshape(1, dimensions);cv::transpose(outputs[0], outputs[0]);}//if (cv::cuda::getCudaEnabledDeviceCount() > 0) { // 检查是否启用了GPU计算// // cv::cuda::GpuMat gpuData(outputs[0]); // 将 GPU 数据包装到 cv::cuda::GpuMat 中// cv::Mat cpuData;// gpuData.download(cpuData); // 将 GPU 数据下载到 CPU 的 cv::Mat 中// float* data = (float*)cpuData.data; // 获取 CPU 上的数据指针//}//else { // 在没有启用GPU计算时,直接使用CPU内存中的数据指针// data = (float*)outputs[0].data;//}//float* data = (float*)outputs[0].data;//************************GPU和CPU的数据交换//cv::UMat umatData = outputs[0].getUMat(cv::ACCESS_READ);//cv::Mat cpuData;//umatData.copyTo(cpuData);//********************************//float* data = (float*)cpuData.data;float* data = (float*)outputs[0].data;float x_factor = modelInput.cols / modelShape.width;float y_factor = modelInput.rows / modelShape.height;std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;for (int i = 0; i < rows; ++i){if (yolov8){float* classes_scores = data + 4;cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);cv::Point class_id;double maxClassScore;minMaxLoc(scores, 0, &maxClassScore, 0, &class_id);if (maxClassScore > modelScoreThreshold){confidences.push_back(maxClassScore);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);boxes.push_back(cv::Rect(left, top, width, height));}}else // yolov5{float confidence = data[4];if (confidence >= modelConfidenceThreshold){float* classes_scores = data + 5;cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);cv::Point class_id;double max_class_score;minMaxLoc(scores, 0, &max_class_score, 0, &class_id);if (max_class_score > modelScoreThreshold){confidences.push_back(confidence);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);}}}data += dimensions;}std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes, confidences, modelScoreThreshold, modelNMSThreshold, nms_result);std::vector<Detection> detections{};for (unsigned long i = 0; i < nms_result.size(); ++i){int idx = nms_result[i];Detection result;result.class_id = class_ids[idx];result.confidence = confidences[idx];std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<int> dis(100, 255);result.color = cv::Scalar(dis(gen),dis(gen),dis(gen));result.className = classes[result.class_id];result.box = boxes[idx];detections.push_back(result);}return detections;
}void Inference::loadClassesFromFile()
{std::ifstream inputFile(classesPath);if (inputFile.is_open()){std::string classLine;while (std::getline(inputFile, classLine))classes.push_back(classLine);inputFile.close();}
}void Inference::loadOnnxNetwork()
{net = cv::dnn::readNetFromONNX(modelPath);if (cudaEnabled){std::cout << "\nRunning on CUDA" << std::endl;net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);}else{std::cout << "\nRunning on CPU" << std::endl;net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);}
}cv::Mat Inference::formatToSquare(const cv::Mat& source)
{int col = source.cols;int row = source.rows;int _max = MAX(col, row);cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);source.copyTo(result(cv::Rect(0, 0, col, row)));return result;
}
核心调用功能代码
//执行视频检测算法和处理bool runOnGPU = false;int deviceId = 0; // 指定要使用的GPU设备的索引cv::cuda::setDevice(deviceId);// 1. 设置你的onnx模型// 注意,在这个例子中类别是硬编码的,'classes.txt'只是一个占位符。Inference inf("D:/C++(2019)/models/static_best.onnx", cv::Size(640, 640), "classes.txt", runOnGPU); // classes.txt 可以缺失// 2. 设置你的输入视频路径std::vector<std::string> videoPaths;videoPaths.push_back("D:/C++(2019)/data/video_good/test_video.mp4");//ui.status->setText("video path:D:/C++(2019)/data/video_good/test_video.mp4");//这里还是硬编码,可以自主选择所有视频列表中的哪些视频进行检测(这里测试第一个视频for (int i = 0; i < 1; ++i){const std::string& videoPath = videoPaths[i];cv::VideoCapture videoCapture(videoPath);if (!videoCapture.isOpened()){ui.status->setText("Failed to open video: ");std::cerr << "Failed to open video: " << videoPath << std::endl;continue;}cv::Mat frame;while (videoCapture.read(frame)){// Inference starts here...std::vector<Detection> output = inf.runInference(frame);int detections = output.size();std::cout << "Number of detections: " << detections << std::endl;for (int i = 0; i < detections; ++i){Detection detection = output[i];cv::Rect box = detection.box;cv::Scalar color = detection.color;//根据类别不同,显示不同的颜色if (detection.class_id == 0) {color = cv::Scalar(0, 255, 0); // 红色 (B, G, R)}else if (detection.class_id == 1) {color = cv::Scalar(0, 0, 255); // 红色 (B, G, R)}else {color = cv::Scalar(255, 0, 0); // 红色 (B, G, R)}// Detection boxcv::rectangle(frame, box, color, 2);// Detection box textstd::string classString = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);cv::rectangle(frame, textBox, color, cv::FILLED);cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}cv::namedWindow("Inference", cv::WINDOW_NORMAL); // 创建具有可调整大小功能的窗口cv::imshow("Inference", frame); // 在窗口中显示图像if (cv::waitKey(1) == 27) // Press Esc to exitbreak;}cv::destroyAllWindows();}
三、调用GPU-OpenCV的编译问题
你会发现,从官网上下下来的opencv4.8.1
,配置到vs环境中,就算你把上面核心调用功能中的代码runOnGPU
设置为了true
,打开任务管理器,还是发现没有调用GPU,这是为什么呢?
我们看看GPT的回答:
当使用OpenCV部署模型时,无法调用GPU可能有以下几个原因:
- OpenCV版本: 确保你使用的OpenCV版本是支持GPU的。OpenCV 4.2及以上版本在DNN模块中提供了对GPU的支持。
- GPU支持的构建: OpenCV需要使用具有GPU支持的构建版本。如果你是从预编译的二进制文件安装的OpenCV,可能没有包含GPU支持。你可能需要从源代码编译OpenCV,并确保在编译时启用了CUDA支持。
- CUDA和cuDNN: 为了让OpenCV利用GPU加速,你的系统上应该安装了NVIDIA的CUDA Toolkit和cuDNN库,并且它们应该与你的GPU兼容。
模型兼容性:并非所有的深度学习模型都能在GPU上运行。确保你的模型支持GPU加速。 - 代码配置: 在代码中,你需要明确指定使用GPU。例如,在OpenCV的DNN模块中,你可以使用cv::dnn::Net::setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA)和cv::dnn::Net::setPreferableTarget(cv::dnn::DNN_TARGET_CUDA)来指定后端和目标。
- GPU资源: 如果你的系统中有多个GPU或者GPU资源被其他应用占用,OpenCV可能无法访问它们。你可能需要配置你的代码或环境,以确保正确的GPU被选中和使用。
- 操作系统和驱动程序: 确保你的操作系统和NVIDIA驱动程序是最新的,并且与你的CUDA Toolkit版本兼容。
- 硬件限制: 你的GPU硬件必须支持CUDA,这意味着它应该是NVIDIA的GPU。并非所有的NVIDIA GPU都支持CUDA,而且较老的型号可能不支持最新版本的CUDA Toolkit。
如果你确定了以上各项都已满足,但仍然无法使用GPU,你可能需要检查你的具体代码配置或查阅OpenCV的官方文档以获取更多信息。在某些情况下,可能还需要查看错误日志或使用调试工具来确定问题所在。
说到底一句话核心就是:官网下载下来的都是预编译的Opencv
,预编译的版本不支持GPU!需要你自己手动进行编译!!!!
你看到这个时候可能跟我当时有一样的疑问:为什么官方非要弄一个预编译版本呢?直接加上支持GPU的不可以吗?
我们再来看GPT的回答:
预编译的OpenCV二进制文件通常不包括GPU支持,原因包括:
- 兼容性问题: 预编译的二进制文件是为了在尽可能多的系统上运行,而不是针对特定的硬件或软件配置。因为CUDA和GPU加速是NVIDIA专有的技术,只适用于NVIDIA的GPU,预编译的OpenCV版本需要在没有NVIDIA硬件的系统上也能工作。
- 版本控制: CUDA和cuDNN有多个版本,与特定版本的NVIDIA GPU驱动和硬件兼容。预编译的OpenCV需要用户自己根据他们的具体环境编译CUDA支持。
- 文件大小: 包括GPU支持的预编译二进制文件会显著增大,因为它们需要包含额外的库和资源。为了减少下载和安装时间,通常不包含这些额外的组件。
许可和法律问题:CUDA是NVIDIA的专有技术,与之相关的分发可能涉及特定的许可和法律限制。 - 用户需求: 并非所有OpenCV用户都需要GPU支持。许多用户在不需要进行大规模图像处理或机器学习任务的情况下使用OpenCV,这时CPU运行可能就足够了。
因此,预编译的OpenCV版本通常不包括对GPU的支持,以保持最大的通用性和便捷性。需要GPU加速的用户可以根据自己的具体硬件配置从源代码编译OpenCV,并启用CUDA支持。
说到底,核心问题还是前面两点。因为各个用户的硬件各种各样,要想调用GPU,就必须和具体场景兼容。像我们后面使用Tensorrt
和onnxruntime
进行调用GPU时,也要根据自己的硬件选择合适的CUDA
和CUDNN
,这个原理是一样的!
所以,那我们就编译一下呗,编译一下一个支持当前硬件水平的OpenCV
呗。
没错,当时心高气傲的我也是这么说的,知道我第四天还以为各种报错而编译不出来gpu版本的opencv时,我终于像它低头了!
这里,关于网上编译gpu版本的opencv的教程很多,我也试了很多(虽然最后我也没编译成功)。下面是我当时根据各个教程以及实验总结出来的步骤:
OpenCV+CUDA进行编译(cmake+vs2019)
- Step1:下载好OpenCV4.8.1的源码、OpenCV4.8.1 contrib
- Step2:放入cmake:
- 第一次configure
- 第2次configure后:
- 搜索
CUDA
,勾选三个; - 搜索world,不用勾选BUILD_opencv_world(后续用Vs编译时会导致不正确:无法引入opencv_worldxxx.lib文件)
- 搜索MOULDELS,把value改成
OpenCV4.8.1 contrib
的moulds路径 - 输入SET,查找OPENCV_GENERATE_SETUPVARS,不勾选
- 输入test查找OPENCV_PERF_TESTS、BUILD_TESTS\BUILD_opencv_tests,不勾选
- 输入java和JAVA,取消相应勾选
- 输入python,取消相应勾选
- 搜索Nonfree,这个控制是否编译扩展库,如果使用需要勾选
- 第3次configure后:
- 再次搜索cuda,将CUDA_arch_bin中的显卡算例改成自己的显卡算力,并且勾选enable_fast_math,取消勾选rgbd了
- 然后configure,然后再generate,再open project
- 在vs2019中先双击CMakeTargets目录先的ALL_BUILD右键点击·生成·,然后漫长等待之后再点击同样目录下的INSTALL右键生成
Tips:这处坑点挺多的,比如有文件下不下来要手动下载,最好对参考几个博客再开始用
cmake
编译!!!
最后,是我的同学在第四天终于编译成功!但是呢,我用的时候却出现问题!
现象就是一个检测结果也没有!
后来我们一步一步debug,在源码发现了问题:我们现在在c++环境下用opencv可以运行yolov8n. onnx模型,而且推理结果是准确的。但是启用cuda之后模型输出结果有问题,net.forward(outputs, net.getUnconnectedOutLayersNames());
的结果outputs[0]的data总是0,导致推理结果不准确,好像是cpu到gpu之间数据传输的问题,网上也没搜到。
这个问题也不了了之了,不知道是同学编译的opencv有问题,还是其他的问题!因为在网上也没有找到答案…
另外,在这期间我们还用onnxruntime
部署了,可喜可贺的是终于能成功调用gpu了!但是!精度特别差,很多在python版本的yolo里面跑,几乎92+%的置信度,在onnxruntime
部署的根本没检测出来!!!由于知识能力有限,我也不知道是神经网络的问题还是其他的问题,这个问题也搁置了。。。
关于onnxruntime部署的我会再写一篇博客详细介绍一下,以及代码!