yolov5 opencv dnn部署 github代码
源码地址
- yolov5官网还提供的dnn、tensorrt推理链接
- 本人使用的opencv c++ github代码,代码作者非本人,也是上面作者推荐的链接之一
实现推理源码中作者的yolov5s.onnx
推理条件
实现推理code中作者的yolov5s.onnx
windows 10
Visual Studio 2019
Nvidia GeForce GTX 1070
opencv 4.5.5、opencv4.7.0 (注意 4.7.0代码不适用,如果要使用opencv4.7.0来进行推理,可能会出现下面的问题图1 problem中的问题)(但是,如果添加了之后,4.7.0的推理速度会比4.5.5的速度慢了不少)
图
1
p
r
o
b
l
e
m
图1 problem
图1problem
解决方法
图
1
s
o
l
v
t
i
o
n
图1 solvtion
图1solvtion
python部署(因为python比较简单就直接介绍了)
一、直接用VScode打开代码
二、然后在终端输入命令
python python/yolo.py
三、结果如下图所示
c++部署
当然不管是使用opencv dnn的cpu还是gpu都得创建相应的环境,这里先不做介绍,以后有时间再介绍。
一、使用VS2019创建一个新的项目,这里不做过多赘述
二、该项目搭建公共的opencv属性,或者使用已搭建好的公共的opencv455属性(以opencv455为例,添加其它的也是这样的,例如opencv455_cuda等)
这里如果使用公共的opencv455属性,只需要在新建的c++空项目中使用以下步骤即可添加
1、 “属性管理器”——>“鼠标右键点击Release|x64”——>“添加现有属性表(E)”
2、 选在对应的已创建好的属性表,然后点击“打开”
3、添加后的结果
三、将code中的下列文件复制到新建的项目中的
repos/Project4/Project4
中,如下图所示
四、将code中的
cpp/yolo.cpp
添加到新建项目的
源文件
中,添加过程和结果如下图所示
结果
五、使用x64进行Release,结果如下图所示
1、opencv4.5.5推理结果
2、opencv4.7.0推理结果
六、yolo.cpp的代码在这(本人应该没做改动吧,忘记了)
#include<fstream>#include<opencv2/opencv.hpp>/*下文所有注释全是自我理解*//*加载classes.txt*/
std::vector<std::string>load_class_list(){
std::vector<std::string> class_list;// 该行代码可以理解为申明一个可变容量的字符串数组class_list
std::ifstream ifs("config_files/classes.txt");
std::string line;while(getline(ifs, line)){
class_list.push_back(line);}return class_list;}voidload_net(cv::dnn::Net& net,bool is_cuda){auto result = cv::dnn::readNet("config_files/yolov5s.onnx");if(is_cuda){
std::cout <<"Attempty to use CUDA\n";
result.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);// result.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
result.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);// 经过验证,这里必须去掉FP16,才能使用cuda加速,就是yolov5导出的是half onnx也不行,opencv只能读取16,但是还是按照32运行的}else{
std::cout <<"Running on CPU\n";
result.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
result.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);}
net = result;}const std::vector<cv::Scalar> colors ={ cv::Scalar(255,255,0), cv::Scalar(0,255,0), cv::Scalar(0,255,255), cv::Scalar(255,0,0)};constfloat INPUT_WIDTH =640.0;constfloat INPUT_HEIGHT =640.0;constfloat SCORE_THRESHOLD =0.2;// NMS中的score阈值(一般来说,这个应该是conf * class的阈值)constfloat NMS_THRESHOLD =0.4;// NMS中的IoU阈值constfloat CONFIDENCE_THRESHOLD =0.4;// conf阈值 (class, conf, x, y, w, h)structDetection{int class_id;float confidence;
cv::Rect box;};/*这个作用是将需要预测的图片都变为正方形图片,以左上角对齐,将短的边都填充0*/
cv::Mat format_yolov5(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;}voiddetect(cv::Mat& image, cv::dnn::Net& net, std::vector<Detection>& output,const std::vector<std::string>& className){
cv::Mat blob;auto input_image =format_yolov5(image);
cv::dnn::blobFromImage(input_image, blob,1./255., cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(),true,false);// 将预测图片resize到640,并将所有的像素都归一化
net.setInput(blob);
std::vector<cv::Mat> outputs;// 预测框作为一个矩阵保存在Mat,这个可变数组永远只有一个Mat,因为一个Mat即可保存所有的预测框,类似于图片[1, H, W],所以下文中outputs[0].data是所有预测框的地址
net.forward(outputs, net.getUnconnectedOutLayersNames());float x_factor = input_image.cols / INPUT_WIDTH;// 缩放因子float y_factor = input_image.rows / INPUT_HEIGHT;float* data =(float*)outputs[0].data;constint dimensions =85;// 其实就是COCO的class + conf + xywhconstint rows =25200;// pre_box的数量小于25200
std::vector<int> class_ids;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;// 原始的NMS使用的非极大值抑制,并不是yolov5中的多分类非极大值抑制for(int i =0; i < rows;++i){float confidence = data[4];if(confidence >= CONFIDENCE_THRESHOLD){float* classes_scores = data +5;
cv::Mat scores(1, className.size(), CV_32FC1, classes_scores);// 将classes_scores转化为一个Mat格式的数据
cv::Point class_id;double max_class_score;minMaxLoc(scores,0,&max_class_score,0,&class_id);// 将scores中的最大的值以及其id分别赋给max_class_score, class_idif(max_class_score > SCORE_THRESHOLD){
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);
boxes.push_back(cv::Rect(left, top, width, height));}}
data +=85;// 这个是cv::Mat中的每一个[x, y, w, h, conf, class]的首地址,一个预测框的首地址都加85[xywh+conf+class]即[5+80]后就是下一个预测框的首地址}
std::vector<int> nms_result;
cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, nms_result);// 将NMS筛选之后的索引返回给nms_resultfor(int i =0; i < nms_result.size(); i++){int idx = nms_result[i];
Detection result;
result.class_id = class_ids[idx];
result.confidence = confidences[idx];
result.box = boxes[idx];
output.push_back(result);}}intmain(int argc,char** argv){
std::vector<std::string> class_list =load_class_list();
cv::Mat frame;
cv::VideoCapture capture("sample.mp4");if(!capture.isOpened()){
std::cerr <<"Error opening video file\n";return-1;}//bool is_cuda = argc > 1 && strcmp(argv[1], "cuda") == 0;bool is_cuda =true;
cv::dnn::Net net;load_net(net, is_cuda);auto start = std::chrono::high_resolution_clock::now();int frame_count =0;float fps =-1;int total_frames =0;while(true){
capture.read(frame);if(frame.empty()){
std::cout <<"End of stream\n";break;}
std::vector<Detection> output;detect(frame, net, output, class_list);
frame_count++;
total_frames++;int detections = output.size();for(int i =0; i < detections;++i){auto detection = output[i];auto box = detection.box;auto classId = detection.class_id;constauto color = colors[classId % colors.size()];
cv::rectangle(frame, box, color,3);
cv::rectangle(frame, cv::Point(box.x, box.y -20), cv::Point(box.x + box.width, box.y), color, cv::FILLED);
cv::putText(frame, class_list[classId].c_str(), cv::Point(box.x, box.y -5), cv::FONT_HERSHEY_SIMPLEX,0.5, cv::Scalar(0,0,0));}if(frame_count >=30){auto end = std::chrono::high_resolution_clock::now();
fps = frame_count *1000.0/ std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
frame_count =0;
start = std::chrono::high_resolution_clock::now();}if(fps >0){
std::ostringstream fps_label;
fps_label << std::fixed << std::setprecision(2);
fps_label <<"FPS: "<< fps;
std::string fps_label_str = fps_label.str();
cv::putText(frame, fps_label_str.c_str(), cv::Point(10,25), cv::FONT_HERSHEY_SIMPLEX,1, cv::Scalar(0,0,255),2);}
cv::imshow("output", frame);if(cv::waitKey(1)!=-1){
capture.release();
std::cout <<"finished by user\n";break;}}
std::cout <<"Total frames: "<< total_frames <<"\n";return0;}
参考链接
- 代码参考链接
- https://github.com/doleron/yolov5-opencv-cpp-python
- https://github.com/Hexmagic/ONNX-yolov5/tree/master
- https://github.com/yzy12-max/yolov5_deploy(这个是理论参考链接2中对应的仓库)
- 理论参考链接
版权归原作者 yuanjun0416 所有, 如有侵权,请联系我们删除。