【模型部署】使用opencv C++ 加速YOLO V5

这篇具有很好参考价值的文章主要介绍了【模型部署】使用opencv C++ 加速YOLO V5。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在ultralytics/YOLO V5中官方给出了利用opencv c++ cuda 进行YOLO V5加速的实例代码,但是代码中并没有给出相关注释,今天花了些时间,把示例源码仔细看了看,并把每一部分都进行了详细注释。内容在下方,欢迎大家交流学习。

官网示例源码参考链接:doleron/yolov5-opencv-cpp-python: Example of using ultralytics YOLO V5 with OpenCV 4.5.4, C++ and Python (github.com)

#include <fstream>
#include <iostream>
#include <opencv2/opencv.hpp>


//将class加载到vector中
std::vector<std::string> load_class_list()
{
    std::vector<std::string> class_list;
    std::ifstream ifs("config_files/classes.txt");
    std::string line;
    while (getline(ifs, line))
    {
        //std::cout << "Class:---------------:::::::" << line << std::endl;

        class_list.push_back(line);
    }
    return class_list;
}

// 加载网络
void load_net(cv::dnn::Net &net, bool is_cuda)
{
    //使用readNet()函数加载YOLOV5S.ONNX文件
    auto result = cv::dnn::readNet("config_files/yolov5s.onnx");

    //依据情况选定是否使用CUDA
    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);
    }
    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;
}


//YOLOV5网络的数据预处理以及前向推理(包括NMS处理)
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);  // blob-[3,640,640]
    std::vector<cv::Mat> outputs;

    //网络计算到指定层(第二个参数指定的层),并返回该层的所有输出
    net.forward(outputs, net.getUnconnectedOutLayersNames()); // getUnconnectedOutLayersNames()返回具有未连接输出的层的名称,返回最终输出层

    //计算x_factor和y_factor,用于后面还原bounding box的位置和大小
    float x_factor = input_image.cols / INPUT_WIDTH;
    float y_factor = input_image.rows / INPUT_HEIGHT;
    
    /*
    通过outputs[0]可以获得该输出层的结果,其中包含了该层所有的预测框的信息,包括预测框的位置、大小、置信度和类别概率。
    这些信息被保存在一个指向连续内存的地址中,可以通过.data来访问。

    outputs[0].data返回一个指向float类型的连续内存的指针,即该指针指向的是一个float类型的数组,其中包含了该层所有预测框的位置、大小、置信度和类别概率。
    因此,将该指针赋值给float* data后,就可以通过data来访问该数组中的每一个元素。
    同时,由于该数组是连续内存,因此可以通过指针的算术运算来访问该数组中的每一个元素,即使用data[i]来访问数组中第i个元素
    */
    float *data = (float *)outputs[0].data;
    //std::cout << "---------------------------------------:" << sizeof(&data) << std::endl;
    /*
    Yolov5s模型的输出大小为(1, 25200, 85),其中:

    第一维是batch size,为1;
    第二维为每张输入图片生成的预测框数,即anchors数量 x (S1 x S1 + S2 x S2 + S3 x S3),这里的S1, S2, S3分别为输出层的三个特征图的大小,取值为{80, 40, 20},anchors数量为3,因此总的预测框数为25200;
    第三维为每个预测框的信息,包括4个坐标信息、1个置信度信息和80个类别得分信息,共85个信息。
    */
    const int dimensions = 85;
    const int rows = 25200;
    
    std::vector<int> class_ids;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;

    for (int i = 0; i < rows; ++i) {
        /*
        在C++中,指针使用[]操作符时,其作用与数组类似。
        当指针指向的是连续的内存区域时,可以使用[]操作符来访问该区域中的数据。
        例如,如果有一个指向float类型数据的指针p,我们可以通过p[i]来访问它所指向的内存中的第i个float类型的数据。
        这里的i是一个整数索引,指定了要访问的数据在内存中的偏移量。

        data[4]和data + 5可以分别访问指针所指向的内存中的第5个float类型的数据和从第6个float类型的数据开始的一段连续数据。
        */
        float confidence = data[4];
        if (confidence >= CONFIDENCE_THRESHOLD) {

            float * classes_scores = data + 5;
            /*
            cv::Mat的构造函数参数如下:
            第一个参数:矩阵的行数,这里设置为1,表示只有一行;
            第二个参数:矩阵的列数,这里设置为className.size(),表示有className.size()列;
            第三个参数:矩阵的数据类型,这里设置为CV_32FC1,表示元素类型为单通道32位浮点数;
            第四个参数:矩阵的数据指针,这里设置为classes_scores,表示矩阵的数据存储在classes_scores所指向的内存地址处,指向的是内存的首地址。
            */
            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);

            //通过阈值进行筛选,将符合要求的类别、置信度以及框体进行保存
            if (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));
            }

        }
        //一个边界框包含85个值————4个坐标信息、1个置信度信息和80个类别得分信息
        //data所指内存地址包含输出层所有预测框的位置、大小、置信度和类别概率,在yolov5s中共有25200个边界框,即data所指内存地址包含25200*85个值
        //在遍历一个边界框后,data指向需要向后移动85个位置,即 +85
        data += 85;

    }

    std::vector<int> nms_result;
    /*
    在目标检测任务中,一个目标可能会被多个边界框检测到,这些边界框可能会有不同的位置和大小,但表示同一个目标。
    非极大值抑制(Non-Maximum Suppression,NMS)是一种常用的方法,用于抑制这些重叠的边界框,只保留置信度最高的那个边界框,从而得到最终的目标检测结果。

    NMS的原理如下:

    首先,对所有的边界框按照其置信度进行排序,置信度最高的边界框排在最前面。

    从置信度最高的边界框开始,依次遍历其余边界框。

    对于当前遍历到的边界框,如果它与前面已经保留的边界框的重叠程度(通过计算IOU值)大于一定阈值(比如0.5),那么就将其抑制掉,不保留。

    继续遍历下一个边界框,重复上述过程,直到所有的边界框都被处理完毕。

    通过这样的处理,NMS可以抑制掉大量重叠的边界框,只保留最好的那个边界框,从而得到最终的目标检测结果。这种方法虽然简单,但是在实践中非常有效,已经被广泛应用于各种目标检测任务中。
    
    关于非极大值抑制,我在新的一篇进行了详细讲解,可在我们博客内容中搜索参考。
    */
    cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, nms_result);
    
    //将经过NMS处理后的结果加载到const vector<Detection> output中
    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)
{
    // 加载class列表
    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;
    }

    //cv::Mat frame = cv::imread("misc/araras.jpg");
    //判断是否使用CUDA,在选定使用CUDA前,请确保电脑支持GPU以及安装了CUDA、cudnn。
    bool is_cuda = argc > 1 && strcmp(argv[1], "cuda") == 0;

    //加载YOLOV5网络
    cv::dnn::Net net;
    load_net(net, is_cuda);

    //创建高精度计时器
    auto start = std::chrono::high_resolution_clock::now();
    //纪录视频帧数
    int frame_count = 0;
    //计算FPS(每秒传输帧数)
    float fps = -1;
    int total_frames = 0;

    while (true)
    {
        //读取视频帧, 关于关于视频处理应用也可参考我其他的opencv讲解内容
        capture.read(frame);
        if (frame.empty())
        {
            std::cout << "End of stream\n";
            break;
        }

        std::vector<Detection> output;
        //YOLOV5S前向推理
        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));
        }

        //根据帧数以及计时器结果计算FPS
        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();
        }

        //如果FPS大于0,就在视频左上角写出来
        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);

        //如果用户按下了键,那么capture.release()函数会释放视频捕获对象,跳出循环,关闭视频流。同时,程序输出一条消息提示用户程序已经结束。
        if (cv::waitKey(1) != -1)
        {
            capture.release();
            std::cout << "finished by user\n";
            break;
        }
    }

    //输出视频检测的总帧数
    std::cout << "Total frames: " << total_frames << "\n";

    return 0;
}

yolov5 opencv c++,深度学习,OpenCV,# 模型部署,opencv,c++,YOLO,计算机视觉,人工智能文章来源地址https://www.toymoban.com/news/detail-785230.html

到了这里,关于【模型部署】使用opencv C++ 加速YOLO V5的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [C++]使用纯opencv去部署yolov9的onnx模型

    【介绍】 部署 YOLOv9 ONNX 模型在 OpenCV 的 C++ 环境中涉及一系列步骤。以下是一个简化的部署方案概述,以及相关的文案。 部署方案概述: 模型准备 :首先,你需要确保你有 YOLOv9 的 ONNX 模型文件。这个文件包含了模型的结构和权重。 环境配置 :安装 OpenCV 库,并确保它支持

    2024年03月13日
    浏览(38)
  • Ubuntu环境下C++使用onnxruntime和Opencv进行YOLOv8模型部署

    目录 环境配置 系统环境 项目文件路径  文件环境  config.txt  CMakeLists.txt type.names  读取config.txt配置文件 修改图片尺寸格式 读取缺陷标志文件 生成缺陷随机颜色标识 模型推理 推理结果获取 缺陷信息还原并显示 总代码 Ubuntu18.04 onnxruntime-linux-x64 1.12.1:https://github.com/microsof

    2024年01月17日
    浏览(26)
  • 使用OpenCV和CUDA实现更好的模型加速

    作者:禅与计算机程序设计艺术 随着计算机视觉技术的发展和应用领域的广泛拓展,人们越来越多地将注意力集中在如何提升机器视觉系统的性能上。近年来,深度学习(Deep Learning)和高性能计算(High Performance Computing,HPC)等新兴技术正朝着成为主流的方向发展。基于深度

    2024年02月09日
    浏览(27)
  • 36、RK3399Pro 环境搭建和Yolov5 c++调用opencv进行RKNN模型部署和使用

    基本思想:记录rk3399 pro配置环境和c++ npu开发记录,主要想搞一份c++代码和其它图像算法结合一下,好进行部署,淘宝链接见附录  需要的python3.7对应的aarch64的whl包:包含opencv-whl 、h5py-whl包: 链接: https://pan.baidu.com/s/1cvCAmHBa_4KgEjrcFIYnig 提取码: 5ui4 链接: https://pan.baidu.com/s/1hrc

    2024年02月07日
    浏览(22)
  • 使用C++调用Yolo模型的方法与步骤

    目录 ## 1. 引言 ## 2. Yolo算法简介 ## 3. 准备工作 ## 4. 安装依赖库 ## 5. 下载Yolo模型权重文件 ## 6. 加载Yolo模型 ## 7. 图像预处理 ## 8. 目标检测与后处理 ## 9. 结果可视化 ## 10. 总结 随着计算机视觉技术的不断发展,目标检测在许多应用领域都起到了重要作用。Yolo(You Only Look Onc

    2024年02月16日
    浏览(18)
  • yolov8 opencv模型部署(C++版)

    TensorRT系列之 Windows10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速部署 TensorRT系列之 Linux下 yolox tensorrt模型加速部

    2024年02月08日
    浏览(19)
  • 使用c++onnxruntime部署yolov5模型并使用CUDA加速(超详细)

    前言 1.Yolo简介 2.onnxruntime简介 3.Yolov5模型训练及转换 4.利用cmake向C++部署该onnx模型 总结 接到一个项目,需要用c++和单片机通信,还要使用yolo模型来做到目标检测的任务,但目前网上的各种博客并没有完整的流程教程,让我在部署过程费了不少劲,也踩了不少坑(甚至一度把

    2024年02月02日
    浏览(26)
  • C++模型部署:qt+yolov5/6+onnxruntime+opencv

    作者平时主要是写 c++ 库的,界面方面了解不多,也没有发现“美”的眼镜,界面有点丑,大家多包涵。 本次介绍的项目主要是通过 cmake 构建一个 基于 c++ 语言的,以 qt 为框架的,包含 opencv 第三方库在内的,跨平台的,使用 ONNX RUNTIME 进行前向推理的 yolov5/6 演示平台。文章

    2024年02月05日
    浏览(28)
  • Opencv C++实现yolov5部署onnx模型完成目标检测

    头文件 命名空间 结构体 Net_config 里面存了三个阈值和模型地址,其中 置信度 ,顾名思义,看检测出来的物体的精准度。以测量值为中心,在一定范围内,真值出现在该范围内的几率。 endsWith()函数 判断sub是不是s的子串 anchors_640图像接收数组 根据图像大小,选择相应长度的

    2024年02月13日
    浏览(20)
  • 【模型部署 01】C++实现分类模型(以GoogLeNet为例)在OpenCV DNN、ONNXRuntime、TensorRT、OpenVINO上的推理部署

    深度学习领域常用的基于CPU/GPU的推理方式有OpenCV DNN、ONNXRuntime、TensorRT以及OpenVINO。这几种方式的推理过程可以统一用下图来概述。整体可分为模型初始化部分和推理部分,后者包括步骤2-5。 以GoogLeNet模型为例,测得几种推理方式在推理部分的耗时如下: 结论: GPU加速首选

    2024年02月06日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包