yolov5 opencv dnn部署自己的模型
- github开源代码地址
- 使用github源码结合自己导出的onnx模型推理自己的视频
- 推理条件
- c++部署
- c++ 推理结果
github开源代码地址
- yolov5官网还提供的dnn、tensorrt推理链接
- 本人使用的opencv c++ github代码,代码作者非本人,也是上面作者推荐的链接之一
- 如果想要尝试直接运行源码中的yolo.cpp文件和yolov5s.pt推理sample.mp4,请参考这个链接的介绍
使用github源码结合自己导出的onnx模型推理自己的视频
推理条件
windows 10
Visual Studio 2019
Nvidia GeForce GTX 1070
opencv 4.5.5、opencv4.7.0 (注意 4.7.0中也会出现跟yolov5 opencv dnn部署 github代码一样的问题)
yolov5 v6.1版本
c++部署
环境和代码的大致步骤跟yolov5 opencv dnn部署 github代码一样
在将所有前置布置好了之后,运行yolo.cpp的时候可能会出现图1problem的问题。
这个是由于yolov5 v6.1版本的问题,可以参考github源码中的issue的解决方案。当然,也可以按照下面的进行代码进行修改。
#include <fstream>#include <opencv2/opencv.hpp>std::vector<std::string> load_class_list()
{std::vector<std::string> class_list;std::ifstream ifs("./config_files/classes_fire.txt");std::string line;while (getline(ifs, line)){class_list.push_back(line);}return class_list;
}void load_net(cv::dnn::Net &net, bool is_cuda)
{auto result = cv::dnn::readNet("./config_files/yolov5n.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);}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)};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;struct Detection
{int class_id;float confidence;cv::Rect box;
};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;
}// 所有的代码修改都在这个函数中
void detect(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);net.setInput(blob);std::vector<cv::Mat> outputs;// 添加代码,使用opencv4.5.5的时候注释掉,使用opencv4.7.0可以使用net.enableWinograd(false);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;const int dimensions = 85;const int rows = 25200;const int max_wh = 768; // 这个值是偏移量,这个酌情选择,不然太大会导致dnn:nms不工作// 添加代码int out_dim2 = outputs[0].size[2]; // 这里的是class+conf+xywh,相当于COCO的指标的85std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;std::vector<cv::Rect> boxes_muti;for (int i = 0; i < rows; ++i) {// 添加代码int index = i * out_dim2; // 每一次循环索引都是下一个pre_box的初始位置float confidence = data[4 + index]; // 修改代码 这样读取的值就是下一个的pre_box的confif (confidence >= CONFIDENCE_THRESHOLD) {// 修改代码 这样读取的值就是下一个的pre_box的classfloat * classes_scores = data + 5 + index;cv::Mat scores(1, className.size(), CV_32FC1, classes_scores);cv::Point class_id;double max_class_score;minMaxLoc(scores, 0, &max_class_score, 0, &class_id);max_class_score *= confidence; // conf = obj_conf * cls_confif (max_class_score > SCORE_THRESHOLD) {confidences.push_back(max_class_score);class_ids.push_back(class_id.x);// 修改代码,这样读取的值就是下一个的pre_box的xywhfloat x = data[0 + index];float y = data[1 + index];float w = data[2 + index];float h = data[3 + index];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));// 实现多分类NMS,如果不需要实现,就直接删掉该部分// 在这里添加的是类似yolov5nms的class_id位置偏移int left_muti = int((x - 0.5 * w) * x_factor + class_id.x * max_wh);int top_muti = int((y - 0.5 * h) * y_factor + class_id.x * max_wh);int width_muti = int(w * x_factor + class_id.x * max_wh);int height_muti = int(h * y_factor + class_id.x * max_wh);boxes_muti.push_back(cv::Rect(left_muti, top_muti, width_muti, height_muti));}}}std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes_muti, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, nms_result);for (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);}
}int main(int argc, char **argv)
{std::vector<std::string> class_list = load_class_list();cv::Mat frame;cv::VideoCapture capture("sample_fire2.mp4");// 如果想要将结果保存为视频/*cv::VideoWriter writer;int coder = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');double fps_w = 25.0;//设置视频帧率std::string filename = "fire.avi";//保存的视频文件名称writer.open(filename, coder, fps_w, cv::Size(640, 360));//创建保存视频文件的视频流 Size(640, 360)是smaple_fire2.mp4的分辨率*/if (!capture.isOpened()){std::cerr << "Error opening video file\n";return -1;}// 因为是window系统,且直接使用VStudio运行代码的,如果想使用cuda,直接将is_cuda = true即可bool is_cuda = argc > 1 && strcmp(argv[1], "cuda") == 0;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;const auto 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);// writer.write(frame); // 如果想要将结果保存为视频if (cv::waitKey(1) != -1){capture.release();// writer.release(); // 如果想要将结果保存为视频std::cout << "finished by user\n";break;}}std::cout << "Total frames: " << total_frames << "\n";return 0;
}
c++ 推理结果
opencv 4.5.5
yolov5 v6.1 导出的是yolov5n.onnx
yolov5_deploy_fire
opencv 4.7.0
yolov5 v6.1 导出的是yolov5n.onnx
yolov5_deploy_fire2