基于OpenCV的YOLOv5图片检测

[复制链接]
发表于 2026-1-20 14:45:42 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
利用OpenCV的DNN模块加载onnx模子文件举行图片检测。
1、利用的yolov5工程代码,调用export.py导出onnx模子。
2、下载opencv版本,https://opencv.org/releases/
      利用opencv版本4.5.3或以上,本文利用的opencv4.6.0        

3、利用vc2015编写利用代码。
  1. // dnnUseOnnx.cpp : 定义控制台应用程序的入口点。
  2. #include <fstream>
  3. #include <iostream>
  4. #include <string>
  5. #include <map>
  6. #include <opencv2/opencv.hpp>
  7. struct DetectResult
  8. {
  9.         int classId;
  10.         float score;
  11.         cv::Rect box;
  12. };
  13. class YOLOv5Detector
  14. {
  15. public:
  16.         void initConfig(std::string onnxpath, int iw, int ih, float threshold);
  17.         void detect(cv::Mat& frame, std::vector<DetectResult>& result);
  18. private:
  19.         int input_w = 640;
  20.         int input_h = 640;
  21.         cv::dnn::Net net;
  22.         int threshold_score = 0.25;
  23. };
  24. void YOLOv5Detector::initConfig(std::string onnxpath, int iw, int ih, float threshold)
  25. {
  26.         this->input_w = iw;
  27.         this->input_h = ih;
  28.         this->threshold_score = threshold;
  29.         try
  30.         {
  31.                 this->net = cv::dnn::readNetFromONNX(onnxpath);
  32.         }
  33.         catch (cv::Exception & e) {
  34.                 printf("exception %s\n", e.err.c_str());
  35.         }
  36. }
  37. void YOLOv5Detector::detect(cv::Mat& frame, std::vector<DetectResult>& results)
  38. {
  39.         // 图象预处理 - 格式化操作
  40.         int w = frame.cols;
  41.         int h = frame.rows;
  42.         int _max = std::max(h, w);
  43.         cv::Mat image = cv::Mat::zeros(cv::Size(_max, _max), CV_8UC3);
  44.         if (frame.channels() == 1)
  45.         {
  46.                 cv::cvtColor(frame, frame, cv::COLOR_GRAY2BGR);
  47.         }
  48.         cv::Rect roi(0, 0, w, h);
  49.         frame.copyTo(image(roi));
  50.         float x_factor = image.cols / 640.0f;
  51.         float y_factor = image.rows / 640.0f;
  52.         cv::Mat blob = cv::dnn::blobFromImage(image, 1 / 255.0, cv::Size(this->input_w, this->input_h), cv::Scalar(0, 0, 0),
  53.                 true, false);
  54.         this->net.setInput(blob);
  55.         cv::Mat preds = this->net.forward("output0");//outputname,使用Netron看一下输出的名字,一般为output0或者output
  56.         //如果preds里有Mat的维数大于2,那么设断点调试的时候,可以看到rows和cols都等于-1,当Mat的dims>2时,想要访问Mat的高和宽,可以通过size属性获取。如下:
  57.         printf("output:%d,%d,%d\n", preds.size[0], preds.size[1], preds.size[2]);//打印输出:output:1,25200,85
  58.         //YOLOV5的输出1,25200,85如何理解和解析
  59.         //1、25200代表着检测框的数量,比如我们取出第一个检测框a,也就是[1,1,85],取出来之后我们解析85,前五个为box的中点坐标、长宽值以及置信,后面80我们取Max(80个类别)中最大值,类别的处于多少行对应于label class.txt别中的类是哪一类别。
  60.         cv::Mat det_output(preds.size[1], preds.size[2], CV_32F, preds.ptr<float>());
  61.         float confidence_threshold = 0.5;
  62.         std::vector<cv::Rect> boxes;
  63.         boxes.clear();
  64.         std::vector<int> classIds;
  65.         classIds.clear();
  66.         std::vector<float> confidences;
  67.         confidences.clear();
  68.         for (int i = 0; i < det_output.rows; i++)
  69.         {
  70.                 float confidence = det_output.at<float>(i, 4);
  71.                 if (confidence < 0.45)
  72.                 {
  73.                         continue;
  74.                 }
  75.                 cv::Mat classes_scores = det_output.row(i).colRange(5, preds.size[2]);//colRange(5, num_class);num_class:最大分类数
  76.                 cv::Point classIdPoint;
  77.                 double score;
  78.                 minMaxLoc(classes_scores, 0, &score, 0, &classIdPoint);
  79.                 // 置信度 0~1之间
  80.                 if (score > this->threshold_score)
  81.                 {
  82.                         float cx = det_output.at<float>(i, 0);
  83.                         float cy = det_output.at<float>(i, 1);
  84.                         float ow = det_output.at<float>(i, 2);
  85.                         float oh = det_output.at<float>(i, 3);
  86.                         int x = static_cast<int>((cx - 0.5 * ow) * x_factor);
  87.                         int y = static_cast<int>((cy - 0.5 * oh) * y_factor);
  88.                         int width = static_cast<int>(ow * x_factor);
  89.                         int height = static_cast<int>(oh * y_factor);
  90.                         cv::Rect box;
  91.                         box.x = x;
  92.                         box.y = y;
  93.                         box.width = width;
  94.                         box.height = height;
  95.                         boxes.push_back(box);
  96.                         classIds.push_back(classIdPoint.x);
  97.                         confidences.push_back(score * confidence);
  98.                 }
  99.         }
  100.         // NMS
  101.         std::vector<int> indexes;
  102.         cv::dnn::NMSBoxes(boxes, confidences, 0.25, 0.45, indexes);
  103.         for (size_t i = 0; i < indexes.size(); i++)
  104.         {
  105.                 DetectResult dr;
  106.                 int index = indexes[i];
  107.                 int idx = classIds[index];
  108.                 dr.box = boxes[index];
  109.                 dr.classId = idx;
  110.                 dr.score = confidences[index];
  111.                 cv::rectangle(frame, boxes[index], cv::Scalar(0, 0, 255), 2, 8);
  112.                 cv::rectangle(frame, cv::Point(boxes[index].tl().x, boxes[index].tl().y - 20),
  113.                         cv::Point(boxes[index].br().x, boxes[index].tl().y), cv::Scalar(0, 255, 255), -1);
  114.                 results.push_back(dr);
  115.         }
  116.         std::ostringstream ss;
  117.         std::vector<double> layersTimings;
  118.         double freq = cv::getTickFrequency() / 1000.0;
  119.         double time = net.getPerfProfile(layersTimings) / freq;
  120.         ss << "FPS: " << 1000 / time << " ; time : " << time << " ms";
  121.         putText(frame, ss.str(), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);
  122. }
  123. std::map<int, std::string> classNames = { { 0, "person" },{ 1, "bicycle" },{ 2, "car" },{ 3, "motorcycle" } ,{ 4, "airplane" } ,{ 5, "bus" },{ 6, "train" },{ 7, "truck" },{ 8, "boat" },{ 9, "traffic light" },
  124. { 10, "fire hydrant" },{ 11, "stop sign'" },{ 12, "parking meter" },{ 13, "bench" } ,{ 14, "bird" } ,{ 15, "cat" },{ 16, "dog" },{ 17, "horse" },{ 18, "sheep" },{ 19, "cow" },
  125. { 20, "elephant" },{ 21, "bear" },{ 22, "zebra" },{ 23, "giraffe" } ,{ 24, "backpack" } ,{ 25, "umbrella" },{ 26, "handbag" },{ 27, "tie" },{ 28, "suitcase" },{ 29, "frisbee" },
  126. { 30, "skis" },{ 31, "snowboard" },{ 32, "sports ball" },{ 33, "kite" } ,{ 34, "baseball bat" } ,{ 35, "baseball glove" },{ 36, "skateboard" },{ 37, "surfboard" },{ 38, "tennis racket" },{ 39, "bottle" },
  127. { 40, "wine glass" },{ 41, "cup" },{ 42, "fork" },{ 43, "knife" } ,{ 44, "spoon" } ,{ 45, "bowl" },{ 46, "banana" },{ 47, "apple" },{ 48, "sandwich" },{ 49, "orange" },
  128. { 50, "broccoli" },{ 51, "carrot" },{ 52, "hot dog" },{ 53, "pizza" } ,{ 54, "donut" } ,{ 55, "cake" },{ 56, "chair" },{ 57, "couch" },{ 58, "potted plant" },{ 59, "bed" },
  129. { 60, "dining table" },{ 61, "toilet" },{ 62, "tv" },{ 63, "laptop" } ,{ 64, "mouse" } ,{ 65, "remote" },{ 66, "keyboard" },{ 67, "cell phone" },{ 68, "microwave" },{ 69, "oven" },
  130. { 70, "toaster" },{ 71, "sink" },{ 72, "refrigerator" },{ 73, "book" } ,{ 74, "clock" } ,{ 75, "vase" },{ 76, "scissors" },{ 77, "teddy bear" },{ 78, "hair drier" },{ 79, "toothbrush" }
  131. };
  132. int main(int argc, char* argv[])
  133. {
  134.         std::shared_ptr<YOLOv5Detector> detector = std::make_shared<YOLOv5Detector>();
  135.         detector->initConfig(R"(D:\python-project\yolov5\yolov5s.onnx)", 640, 640, 0.25f);
  136.         cv::Mat frame = cv::imread(R"(D:\python-project\yolov5\data\images\bus.jpg)");
  137.         std::vector<DetectResult> results;
  138.         detector->detect(frame, results);
  139.         for (DetectResult& dr : results)
  140.         {
  141.                 cv::Rect box = dr.box;
  142.                 cv::putText(frame, classNames[dr.classId]+ " "+ std::to_string(dr.score), cv::Point(box.tl().x, box.tl().y - 10), cv::FONT_HERSHEY_SIMPLEX,
  143.                         .5, cv::Scalar(0, 0, 0));
  144.         }
  145.         cv::imshow("OpenCV-DNN-yolov5", frame);
  146.         cv::waitKey();
  147.         results.clear();
  148. }
复制代码
运行结果:

留意事项:
1)、readNetFromONNX加载onnx模子堕落。 interp_mode != "asymmetric",这个错误信息表明你在利用OpenCV的readNetFromONNX函数加载ONNX模子时,模子中的某些节点的插值模式(interp_mode)不是"asymmetric"。
办理方法:利用opencv版本4.5.3或以上,本文利用的opencv4.6.0
2)、监测到目标比yolov5工程detect.py推理出来的目标少。
办理方法:det_output.row(i).colRange(5, num_class);num_class:最大分类数
  1. cv::Mat classes_scores = det_output.row(i).colRange(5, preds.size[2]);
复制代码

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表