• win10系统下使用opencv-dnn部署yolov5模型



    前言

      最近工作中需要用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
    
    • 1

    在yolov5-master目录下,可以看到yolov5s.onnx模型已生成。

    三、新建Qt项目

    1、pro文件

    在pro文件中,添加opencv相关配置,内容如下:

    #-------------------------------------------------
    #
    # Project created by QtCreator 2022-10-09T13:28:19
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = untitled1
    TEMPLATE = app
    
    
    SOURCES += main.cpp\
            mainwindow.cpp \
        yolo.cpp
    
    HEADERS  += mainwindow.h \
        yolo.h
    
    FORMS    += mainwindow.ui
    
    INCLUDEPATH += C:/opencv4.6.0/build/include
                   C:/opencv4.6.0/build/include/opencv2
    
    LIBS += -LC:/opencv4.6.0/build/x64/vc14/lib/ -lopencv_world460
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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<
            //
    //        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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    四、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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    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<
        //std::cout<
    
        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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163

    3、classes.txt

    官方yolov5s.pt模型,能够识别80种物体,classes.txt文件记录的就是这80种物体类别名,如下:

    person
    bicycle
    car
    motorbike
    aeroplane
    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
    sofa
    pottedplant
    bed
    diningtable
    toilet
    tvmonitor
    laptop
    mouse
    remote
    keyboard
    cell phone
    microwave
    oven
    toaster
    sink
    refrigerator
    book
    clock
    vase
    scissors
    teddy bear
    hair drier
    toothbrush
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    五、效果

    以本地视频文件1.mp4为例,效果如下:
    在这里插入图片描述
    可以看到,在我的机器上,视频卡顿现象非常严重,FPS较小。

  • 相关阅读:
    iOS 16 Beta 2值不值得升级 iOS 16 Beta 2升级建议
    【Dubbo框架、Dubbo中角色以及作用、各组件启动流程执行步骤、基于Dubbo实现的远程通信的案例】
    PicGo配置阿里云oss
    【Excel】根据某单元格的值设置整行颜色(日文版excel)
    废液收集系统物联网远程监控解决方案
    《向量数据库指南》——LlamaIndex 和 Milvus Cloud对于 Chat Towards Data Science 的作用
    IDEA 22.2.3 创建web项目及Tomcat部署与服务器初始界面修改(保姆版)
    DINO学习
    音频如何转换成mp3格式?详细步骤解析
    零零信安-D&D数据泄露报警日报【第29期】
  • 原文地址:https://blog.csdn.net/u011832219/article/details/127749321