文章目录
- 前言
- 一、环境
- 1、硬件
- 2、软件
- 二、YOLO模型
- 三、新建Qt项目
- 1、pro文件
- 2、main函数
- 四、YOLO 类封装
- 1、yolo.h
- 2、yolo.cpp
- 3、classes.txt
- 五、效果
前言
最近工作中需要用yolov5训练模型,然后在win10系统下,完成推理部署。本篇主要介绍使用opencv-dnn模块实现模型推理部署。
一、环境
1、硬件
Intel® Core i5-7400 CPU @ 3.00GHZ
Intel® HD Graphics 630 内存4G 核显
内存 8G
win10 64位系统
2、软件
opencv4.6.0
yolov5 6.2版本
二、YOLO模型
我使用的是onnx模型,如果没有训练过自己的模型,可以使用官方的yolov5s.pt模型,将其转化为yolov5s.onnx模型,转化方法如下:
python export.py
在yolov5-master目录下,可以看到yolov5s.onnx模型已生成。
三、新建Qt项目
1、pro文件
在pro文件中,添加opencv相关配置,内容如下:
#-------------------------------------------------## Project created by QtCreator 2022-10-09T13:28:19##-------------------------------------------------QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = untitled1TEMPLATE = appSOURCES += main.cpp\ mainwindow.cpp \ yolo.cppHEADERS += mainwindow.h \ yolo.hFORMS += mainwindow.uiINCLUDEPATH += C:/opencv4.6.0/build/include C:/opencv4.6.0/build/include/opencv2LIBS += -LC:/opencv4.6.0/build/x64/vc14/lib/ -lopencv_world460
2、main函数
#include "mainwindow.h"#include #include #include "yolo.h"#include #include #include //const vector colors = { Scalar(255, 255, 0), Scalar(0, 255, 0), Scalar(0, 255, 255), Scalar(255, 0, 0) };int main(int argc, char *argv[]){ Mat frame; VideoCapture capture("G:/1.mp4");//sample if (!capture.isOpened()) { std::cerr << "Error opening video file\n"; return -1; } cv::VideoWriter video("out.avi",cv::VideoWriter::fourcc('M','J','P','G'),10, cv::Size(1920,1080)); // YOLO *yolov5 = new YOLO; yolov5->init("F:/SVN-ZJKY/YiFeiShouJiRobot/yolov5-6.1/yolov5s.onnx"); // std::vector<detect_result> output; int total_frames = 0; // clock_t start_name = 0; clock_t end_time = 0; // while ( true ) { // start_name = clock(); capture.read(frame); if (frame.empty()) { std::cout << "End of stream\n"; break; } // total_frames++;// auto start = std::chrono::system_clock::now();// t = (double)cv::getTickCount(); yolov5->detect(frame,output);// auto end = std::chrono::system_clock::now();// auto detect_time =std::chrono::duration_cast(end - start).count();//ms// std::cout<<detect_time<<":"<<total_frames<<":"<<output.size()<<std::endl; //// t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();// fps = 1.0 / t; end_time=clock();//ms auto text = "FPS: " + std::to_string(1 / ((double)(end_time - start_name) / CLOCKS_PER_SEC)); qDebug() << "Frame time(ms): " << (double)(end_time - start_name) /*/ CLOCKS_PER_SEC*/; cv::putText(frame, text, cv::Point(3, 25), cv::FONT_ITALIC, 0.8, cv::Scalar(0, 0, 255), 2); // yolov5->draw_frame(frame,output); imshow("output", frame); video.write(frame); if (waitKey(1) != -1) { std::cout << "finished by user\n"; break; } output.clear(); } // capture.release(); video.release(); cv::destroyAllWindows(); waitKey(0); //std::cout << "Total frames: " << total_frames << "\n"; return 0;}
四、YOLO 类封装
1、yolo.h
#include #include #include #include #include #include using namespace cv;using namespace dnn;using namespace std;class detect_result{public: int classId; float confidence; cv::Rect_<float> box;};class YOLO{public: YOLO(); ~YOLO(); void init(std::string onnxpath); //Net loadNet(bool is_cuda); //Mat format_yolov5(const Mat &source); std::vector<std::string> classes_; void detect(cv::Mat & frame, std::vector<detect_result> &result); //void drawRect(Mat &image,vector &output); //std::vector load_class_list(); void draw_frame(cv::Mat & frame, std::vector<detect_result> &results);private: cv::dnn::Net net; //cv::dnn::Net m_net;// const float INPUT_WIDTH = 640.0;// const float INPUT_HEIGHT = 640.0;// const float SCORE_THRESHOLD = 0.2;// const float NMS_THRESHOLD = 0.4;// const float CONFIDENCE_THRESHOLD = 0.4; const float confidence_threshold_ =0.25f; const float nms_threshold_ = 0.4f; const int model_input_width_ = 640; const int model_input_height_ = 640;};
2、yolo.cpp
#include "yolo.h"YOLO::YOLO(){ //loadNet(false);}YOLO::~YOLO(){}//std::vector YOLO::load_class_list()//{// std::vector class_list;// std::ifstream ifs("G:/classes.txt");// std::string line;// while (getline(ifs, line))// {// class_list.push_back(line);// }// return class_list;//}//Net YOLO::loadNet(bool is_cuda)//{// //F:\SVN-ZJKY\YiFeiShouJiRobot\yolov5-master// //m_net = readNet("F:\\SVN-ZJKY\\YiFeiShouJiRobot\\yolov5-master\\yolov5s.onnx");// m_net = readNet("F:\\SVN-ZJKY\\YiFeiShouJiRobot\\demo\\yolov5-opencv-dnn-cpp-main\\QtDemo\\build-yolov5-opencv-dnn-cpp-main-Qt5_6_2_MSVC2015_64bit-Release\\release\\yolov5s.onnx");// if (is_cuda)// {// cout << "Attempty to use CUDA\n";// m_net.setPreferableBackend(DNN_BACKEND_CUDA);// m_net.setPreferableTarget(DNN_TARGET_CUDA_FP16);// }// else// {// cout << "Running on CPU12\n";// m_net.setPreferableBackend(DNN_BACKEND_OPENCV);// m_net.setPreferableTarget(DNN_TARGET_CPU);// }// return m_net;//}//Mat YOLO::format_yolov5(const Mat &source)//{// int col = source.cols;// int row = source.rows;// int _max = MAX(col, row);// Mat result = Mat::zeros(_max, _max, CV_8UC3);// source.copyTo(result(Rect(0, 0, col, row)));// return result;//}void YOLO::init(std::string onnxpath){ this->net = cv::dnn::readNetFromONNX(onnxpath); std::string file="G:/classes.txt"; std::ifstream ifs(file); if (!ifs.is_open()) CV_Error(cv::Error::StsError, "File " + file + " not found"); std::string line; while (std::getline(ifs, line)) { classes_.push_back(line); }}void YOLO::detect(cv::Mat & frame, std::vector<detect_result> &results){ int w = frame.cols; int h = frame.rows; int _max = std::max(h, w); cv::Mat image = cv::Mat::zeros(cv::Size(_max, _max), CV_8UC3); cv::Rect roi(0, 0, w, h); frame.copyTo(image(roi)); float x_factor = static_cast<float>(image.cols) / model_input_width_; float y_factor = static_cast<float>(image.rows) / model_input_height_; //std::cout<<x_factor<<std::endl; //std::cout<<y_factor<<std::endl; cv::Mat blob = cv::dnn::blobFromImage(image, 1 / 255.0, cv::Size(model_input_width_, model_input_height_), cv::Scalar(0, 0, 0), true, false); this->net.setInput(blob); cv::Mat preds = this->net.forward("output");//outputname cv::Mat det_output(preds.size[1], preds.size[2], CV_32F, preds.ptr<float>()); std::vector<cv::Rect> boxes; std::vector<int> classIds; std::vector<float> confidences; for (int i = 0; i < det_output.rows; i++) { float box_conf = det_output.at<float>(i, 4); if (box_conf < nms_threshold_) { continue; } cv::Mat classes_confidences = det_output.row(i).colRange(5, 85); cv::Point classIdPoint; double cls_conf; cv::minMaxLoc(classes_confidences, 0, &cls_conf, 0, &classIdPoint); if (cls_conf > confidence_threshold_) { float cx = det_output.at<float>(i, 0); float cy = det_output.at<float>(i, 1); float ow = det_output.at<float>(i, 2); float oh = det_output.at<float>(i, 3); int x = static_cast<int>((cx - 0.5 * ow) * x_factor); int y = static_cast<int>((cy - 0.5 * oh) * y_factor); int width = static_cast<int>(ow * x_factor); int height = static_cast<int>(oh * y_factor); cv::Rect box; box.x = x; box.y = y; box.width = width; box.height = height; boxes.push_back(box); classIds.push_back(classIdPoint.x); confidences.push_back(cls_conf * box_conf); } } std::vector<int> indexes; cv::dnn::NMSBoxes(boxes, confidences, confidence_threshold_, nms_threshold_, indexes); for (size_t i = 0; i < indexes.size(); i++) { detect_result dr; int index = indexes[i]; int idx = classIds[index]; dr.box = boxes[index]; dr.classId = idx; dr.confidence = confidences[index]; results.push_back(dr); }}void YOLO::draw_frame(cv::Mat & frame, std::vector<detect_result> &results){ for(auto dr : results) { cv::rectangle(frame, dr.box , cv::Scalar(0, 0, 255), 2, 8); cv::rectangle(frame, cv::Point(dr.box .tl().x, dr.box .tl().y - 20), cv::Point(dr.box .br().x, dr.box .tl().y), cv::Scalar(255, 0, 0), -1); std::string label = cv::format("%.2f", dr.confidence); label = classes_[dr.classId ] + ":" + label; cv::putText(frame, label, cv::Point(dr.box.x, dr.box.y + 6), 1, 2, cv::Scalar(0, 255, 0),2); }}
3、classes.txt
官方yolov5s.pt模型,能够识别80种物体,classes.txt文件记录的就是这80种物体类别名,如下:
personbicyclecarmotorbikeaeroplanebustraintruckboattraffic lightfire hydrantstop signparking meterbenchbirdcatdoghorsesheepcowelephantbearzebragiraffebackpackumbrellahandbagtiesuitcasefrisbeeskissnowboardsports ballkitebaseball batbaseball gloveskateboardsurfboardtennis racketbottlewine glasscupforkknifespoonbowlbananaapplesandwichorangebroccolicarrothot dogpizzadonutcakechairsofapottedplantbeddiningtabletoilettvmonitorlaptopmouseremotekeyboardcell phonemicrowaveoventoastersinkrefrigeratorbookclockvasescissorsteddy bearhair driertoothbrush
五、效果
以本地视频文件1.mp4为例,效果如下:
可以看到,在我的机器上,视频卡顿现象非常严重,FPS较小。