yolov5 opencv dnn部署自己的模型

yolov5 opencv dnn部署自己的模型

      • github开源代码地址
      • 使用github源码结合自己导出的onnx模型推理自己的视频
        • 推理条件
        • c++部署
        • c++ 推理结果

github开源代码地址

  1. yolov5官网还提供的dnn、tensorrt推理链接
  2. 本人使用的opencv c++ github代码,代码作者非本人,也是上面作者推荐的链接之一
  3. 如果想要尝试直接运行源码中的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的指标的85
    
    std::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的conf

        if (confidence >= CONFIDENCE_THRESHOLD) {
            // 修改代码 这样读取的值就是下一个的pre_box的class
            float * 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_conf
            if (max_class_score > SCORE_THRESHOLD) {

                confidences.push_back(max_class_score);
                class_ids.push_back(class_id.x);
                // 修改代码,这样读取的值就是下一个的pre_box的xywh
                float 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/341541.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Electron】Electron是什么

1. Electron是什么 Electron是使用JavaScript、HTML和CSS构建跨平台&#xff08;Windows、MacOs、Linux&#xff09;的桌面应用。Electron其实就是一个可以展示网页内容的壳子&#xff0c;相当于一个独立的浏览器&#xff0c;可以提供给你一些接口&#xff0c;去调用系统的资源…

验证码短信API:企业级安全验证的必备工具

引言 随着数字经济的蓬勃发展&#xff0c;企业对在线服务的安全性要求越来越高。在这种背景下&#xff0c;验证码短信API作为一种有效的安全验证工具&#xff0c;正逐渐成为企业级应用的标配。本文将探讨验证码短信API如何为企业级安全验证提供坚实保障。 验证码短信 API 的优…

MySQL-SQL-DQL

DQL-介绍 DQL-语法 基本查询 1、查询多个字段 2、设置别名 3、去除重复记录 条件查询 1、语法 2、条件 聚合函数 1、介绍 2、常见的聚合函数 3、语法 分组查询 1、语法 2、where与having区别 排序查询 1、语法 2、排序方式 分页查询 1、语法 DQL-执行顺序

关于在微信小程序中使用taro + react-hook后销毁函数无法执行的问题

问题&#xff1a; 在 taro中使用navigageTo() 跳转路由后hook中useEffect 的return函数没有执行 没有执行return函数 框架版本&#xff1a; tarojs: 3.6 react: 18.0 原因&#xff1a; 使用navigateTo() 跳转路由的话并不会销毁页面和组件&#xff0c;会加入一…

智能小程序环境配置流程

App 与智能小程序 在用户使用 App 扫描小程序的二维码或者点击设备&#xff0c;尝试进入小程序时&#xff0c;系统会对 App 当前环境与小程序所需运行环境进行比对&#xff0c;确定环境配置兼容后&#xff0c;App 才能启动并运行小程序。 比对规则中&#xff0c;主要涉及&…

「webpack面试系列」说说你对webpack的理解?解决了什么问题?(收藏好,用时好找)

文章目录 一、背景模块化 二、问题三、是什么webpack的能力&#xff1a; 一、背景 Webpack 最初的目标是实现前端项目的模块化&#xff0c;旨在更高效地管理和维护项目中的每一个资源 模块化 最早的时候&#xff0c;我们会通过文件划分的形式实现模块化&#xff0c;也就是将…

【计算机网络】应用层——HTTP 协议(一)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、什么是 HTTP 协…

Redis(六)

1、Redis的缓存淘汰策略 1.1、内存配置 首先查看Redis最大的占用内存&#xff0c;打开redis配置文件&#xff0c;设置maxmemory参数&#xff0c;maxmemory是bytes字节类型&#xff0c;注意转换。 打开配置文件发现没有配置&#xff0c;那么默认是多少的内存&#xff0c;是这样…

第2章-OSI参考模型与TCP/IP模型

目录 1. 引入 2. OSI参考模型 2.1. 物理层 2.2. 数据链路层 2.3. 网络层 2.4. 传输层 2.5. 会话层 2.6. 表示层 2.7. 应用层 3. 数据的封装与解封装 4. TCP/IP模型 4.1. 背景引入 4.2. TCP/IP模型&#xff08;4层&#xff09; 4.3. 拓展 1. 引入 1&#xff09;产…

Visual Studio2022实用使用技巧集

前言 对于.NET开发者而言Visual Studio是我们日常工作中比较常用的开发工具&#xff0c;掌握一些Visual Studio实用的搜索、查找、替换技巧可以帮助我们大大提高工作效率从而避免996。 Visual Studio更多实用技巧 https://github.com/YSGStudyHards/DotNetGuide 代码和功能搜…

记一次 stackoverflowerror 线上排查过程

一.线上 stackOverFlowError xxx日,突然收到线上日志关键字频繁告警 classCastException.从字面上的报警来看,仅仅是类型转换异常,查看细则发现其实是 stackOverFlowError.很多同学面试的时候总会被问到有没有遇到过线上stackOverFlowError?有么有遇到栈溢出?具体栈溢出怎么来…

植物神经功能紊乱是什么?

植物神经也叫自律神经&#xff0c;它是一种自发的&#xff0c;非主观意识控制的&#xff0c;低级的神经活动。包括呼吸的、心律的、汗腺的、胃肠道的调节等等&#xff0c;都叫植物神经功能调节。 植物神经它的一旦出现了障碍可以有两种倾向&#xff0c;一种倾向就是出汗、兴奋…

Spring Boot3整合knife4j(swagger3)

目录 1.前置条件 2.导依赖 3.配置 1.前置条件 已经初始化好一个spring boot项目且版本为3X&#xff0c;项目可正常启动。 作者版本为3.2.2最新版 2.导依赖 knife4j官网&#xff1a; Knife4j 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j (xiaominfo.com)http…

Docker初次体验:WSL+Docker+portanier

文章目录 前言Docker是什么&#xff1f;Docker的优点Docker的使用场景&#xff1a;一件安装 Docker安装开启虚拟化安装wsl下载慢的请看这个下载成功 安装Docker修改Docker安装位置 配置Docker安装portanier&#xff08;可视化的Docker操作页面&#xff09;登录网址 总结 前言 …

2008年苏州大学837复试机试C/C++

2008年苏州大学复试机试 题目 编写程序充成以下功能: 一、从键盘上输入随机变量x的 10个取样点。X0&#xff0c;X1—X9 的值; 1、计算样本平均值 2、判定x是否为等差数列 3、用以下公式计算z的值(t0.63) 注。请对程序中必要地方进行注释 补充&#xff1a;个人觉得这个题目回忆…

Hylicos - MINI2440 - 中断控制

中断 中断源管理 中断是一种异步异常&#xff0c;CPU需要处理很多来自设备的中断请求&#xff0c;而CPU引出的line只有IRQ线和FIQ线&#xff0c;所以就得引入中断控制器帮助CPU搞清楚是中断的来源。 MINI2440的中断控制器&#xff0c;可以接受来自60个中断源的请求。提供这些…

解决vue 2.6通过花生壳ddsn(内网穿透)实时开发报错Invalid Host header和websocket

请先核对自己的vue版本&#xff0c;我的是2.6.14&#xff0c;其他版本未测试 起因 这两天在维护一个基于高德显示多个目标&#xff08;门店&#xff09;位置的项目&#xff0c;由于高德要求定位必须使用https服务&#xff0c;遂在本地无法获取到定位坐标信息&#xff0c;于是…

020-信息打点-红蓝队自动化项目资产侦察企查产权武器库部署网络空间

020-信息打点-红蓝队自动化项目&资产侦察&企查产权&武器库部署&网络空间 #知识点&#xff1a; 1、工具项目-红蓝队&自动化部署 2、工具项目-自动化侦查收集提取 3、工具项目-综合&网络空间&信息 演示案例&#xff1a; ➢自动化-武器库部署-F8x ➢自…

MySQL数据库查询语句之组函数,子查询语句

组函数 以组为操作单位&#xff0c;一组数据得到一个结果。 在没有手动分组的前提下&#xff0c;整张表默认为一组数据 max(列名)&#xff1a;获取最大值 min(列名)&#xff1a;获取最小值 sum(列名)&#xff1a;获取总和 avg(列名)&#xff1a;获取平均值 count(列名)&a…

Operation

contents 服务器一、相关概念1.1 云服务器与实例1.2 关于域名解析延时与80端口1.3 关于备案1.4 关于SSL证书1.5 关于SSL证书的签发1.6 关于SSL证书的部署1.7 关于LNMP和LAMP1.8 关于bt面板 二、单服务器单一级域名多网站2.1 创建多个二级域名2.2 解析二级域名绑定到服务器上2.3…