C++/opencv使用Pytorch+GPU训练的yolov8模型部署在CPU上

1、模型转换

1.在根目录下新建ptToOnnx.py文件,内容如下,运行后会在模型文件同目录下生成.onnx文件。

from ultralytics import YOLO
# Load a model
model = YOLO('runs/detect/train11/weights/best.pt')  # load an official model
# Export the model
model.export(format='onnx', imgsz=[480, 640], opset=12) # 导出一定不要修改这里参数

运行后如下所示

2、安装opencv4.8.1

1.到官网下载opencv4.8.1版本,在此之前使用opencv4.6.0报错读取不了onnx模型。

2.安装后修改系统环境变量,并在VS2022中配置opencv4.8.1的环境

3.以下是main.cpp内容

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include "inference.h"
using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    bool runOnGPU = false;

    /*1. 设置你的onnx模型
    Note that in this example the classes are hard-coded and 'classes.txt' is a place holder.*/
    Inference inf("./weight/best.onnx", cv::Size(640, 480), "classes.txt", runOnGPU); // classes.txt 可以缺失

    // 2. 设置你的输入图片
    std::vector<std::string> imageNames;
    imageNames.push_back("./image/testBatch.jpg");
    //imageNames.push_back("zidane.jpg");

    for (int i = 0; i < imageNames.size(); ++i)
    {
        cv::Mat frame = cv::imread(imageNames[i]);

        // Inference starts here...
        std::vector<Detection> output = inf.runInference(frame);

        int detections = output.size();
        std::cout << "Number of detections:" << detections << std::endl;

        // feiyull
        // 这里需要resize下,否则结果不对
        //cv::resize(frame, frame, cv::Size(480, 640));

        for (int i = 0; i < detections; ++i)
        {
            Detection detection = output[i];

            cv::Rect box = detection.box;
            cv::Scalar color = detection.color;

            // Detection box
            cv::rectangle(frame, box, color, 2);

            // Detection box text
            std::string classString = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4);
            cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);
            cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);

            cv::rectangle(frame, textBox, color, cv::FILLED);
            cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);
        }
        cv::imshow("Inference", frame);
        cv::waitKey(0);
        cv::destroyAllWindows();
    }
}

inference.h

classes中要根据自己的类别进行修改

#pragma once
#ifndef INFERENCE_H
#define INFERENCE_H

// Cpp native
#include <fstream>
#include <vector>
#include <string>
#include <random>

// OpenCV / DNN / Inference
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>

struct Detection
{
    int class_id{ 0 };
    std::string className{};
    float confidence{ 0.0 };
    cv::Scalar color{};
    cv::Rect box{};
};

class Inference
{
public:
    Inference(const std::string& onnxModelPath, const cv::Size& modelInputShape = { 640, 640 }, const std::string& classesTxtFile = "", const bool& runWithCuda = true);
    std::vector<Detection> runInference(const cv::Mat& input);

private:
    void loadClassesFromFile();
    void loadOnnxNetwork();
    cv::Mat formatToSquare(const cv::Mat& source);

    std::string modelPath{};
    std::string classesPath{};
    bool cudaEnabled{};

    std::vector<std::string> classes{ "batchnum" };
    cv::Size2f modelShape{};

    float modelConfidenceThreshold{ 0.25 };
    float modelScoreThreshold{ 0.45 };
    float modelNMSThreshold{ 0.50 };

    bool letterBoxForSquare = true;

    cv::dnn::Net net;
};

#endif // INFERENCE_H

inference.cpp

#include "inference.h"

Inference::Inference(const std::string& onnxModelPath, const cv::Size& modelInputShape, const std::string& classesTxtFile, const bool& runWithCuda)
{
    modelPath = onnxModelPath;
    modelShape = modelInputShape;
    classesPath = classesTxtFile;
    cudaEnabled = runWithCuda;

    loadOnnxNetwork();
    // loadClassesFromFile(); The classes are hard-coded for this example
}

std::vector<Detection> Inference::runInference(const cv::Mat& input)
{
    cv::Mat modelInput = input;
    if (letterBoxForSquare && modelShape.width == modelShape.height)
        modelInput = formatToSquare(modelInput);

    cv::Mat blob;
    cv::dnn::blobFromImage(modelInput, blob, 1.0 / 255.0, modelShape, cv::Scalar(), true, false);
    net.setInput(blob);

    std::vector<cv::Mat> outputs;
    net.forward(outputs, net.getUnconnectedOutLayersNames());

    int rows = outputs[0].size[1];
    int dimensions = outputs[0].size[2];

    bool yolov8 = false;
    // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c])
    // yolov8 has an output of shape (batchSize, 84,  8400) (Num classes + box[x,y,w,h])
    if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8)
    {
        yolov8 = true;
        rows = outputs[0].size[2];
        dimensions = outputs[0].size[1];

        outputs[0] = outputs[0].reshape(1, dimensions);
        cv::transpose(outputs[0], outputs[0]);
    }
    float* data = (float*)outputs[0].data;

    float x_factor = modelInput.cols / modelShape.width;
    float y_factor = modelInput.rows / modelShape.height;

    std::vector<int> class_ids;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;

    for (int i = 0; i < rows; ++i)
    {
        if (yolov8)
        {
            float* classes_scores = data + 4;

            cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);
            cv::Point class_id;
            double maxClassScore;

            minMaxLoc(scores, 0, &maxClassScore, 0, &class_id);

            if (maxClassScore > modelScoreThreshold)
            {
                confidences.push_back(maxClassScore);
                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));
            }
        }
        else // yolov5
        {
            float confidence = data[4];

            if (confidence >= modelConfidenceThreshold)
            {
                float* classes_scores = data + 5;

                cv::Mat scores(1, classes.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 > modelScoreThreshold)
                {
                    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));
                }
            }
        }

        data += dimensions;
    }

    std::vector<int> nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, modelScoreThreshold, modelNMSThreshold, nms_result);

    std::vector<Detection> detections{};
    for (unsigned long i = 0; i < nms_result.size(); ++i)
    {
        int idx = nms_result[i];

        Detection result;
        result.class_id = class_ids[idx];
        result.confidence = confidences[idx];

        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<int> dis(100, 255);
        result.color = cv::Scalar(dis(gen),
            dis(gen),
            dis(gen));

        result.className = classes[result.class_id];
        result.box = boxes[idx];

        detections.push_back(result);
    }

    return detections;
}

void Inference::loadClassesFromFile()
{
    std::ifstream inputFile(classesPath);
    if (inputFile.is_open())
    {
        std::string classLine;
        while (std::getline(inputFile, classLine))
            classes.push_back(classLine);
        inputFile.close();
    }
}

void Inference::loadOnnxNetwork()
{
    net = cv::dnn::readNetFromONNX(modelPath);
    if (cudaEnabled)
    {
        std::cout << "\nRunning on CUDA" << std::endl;
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
    }
    else
    {
        std::cout << "\nRunning on CPU" << std::endl;
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    }
}

cv::Mat Inference::formatToSquare(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;
}

4.运行该工程后如下所示

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 关于yolov5模型部署落地,可以考虑使用深度学习框架如PyTorch或TensorFlow等来完成模型训练部署。对于模型部署,可以考虑使用C++或Python等语言编写部署代码,并使用相关的库如OpenCV等来进行图像的读取和处理。在部署过程中,还需要考虑模型优化、硬件加速等因素,以提高模型的性能和运行效率。 ### 回答2: Yolov5是一种训练用于目标检测的深度学习模型。要将Yolov5模型部署到实际场景中,我们需要经过一下几个步骤: 首先,我们需要准备训练数据集。数据集应包含所要检测的目标类别的图像,以及相应的标签信息,包括目标的类别和位置。训练数据集的质量和多样性对于模型的准确性和鲁棒性至关重要。 其次,我们需要选择一个适当的硬件平台来运行Yolov5模型。可以选择使用GPU来加速模型的计算,以提高检测速度。然后,我们需要安装PyTorch框架和必要的软件依赖项。 接下来,我们需要进行模型训练训练过程涉及设置模型的超参数,如学习率、批量大小等,然后在训练数据集上进行迭代优化,以使模型能够学习目标的特征。 训练完成后,我们需要对模型进行评估和测试,以确定其在真实场景中的性能。可以使用测试数据集对模型进行评估,计算其检测精度、召回率等指标。 最后,我们可以将经过训练和评估的Yolov5模型部署到实际场景中。部署可以在不同的平台上完成,如PC、嵌入式设备或云服务器。部署过程中需要将训练好的模型以适当的方式集成到目标应用中,并进行必要的测试和调优,以确保模型在实际环境中的可用性和性能。 总之,Yolov5模型部署落地需要准备训练数据集、选择合适的硬件平台、进行模型训练和评估,并最终将模型部署到实际场景中,并完成必要的测试和优化。这样才能使得Yolov5模型能够在真实场景中实现准确、高效的目标检测。 ### 回答3: yolov5模型是一种基于深度学习的目标检测模型,在部署和落地方面有以下几个关键步骤。 首先,要将yolov5模型训练环境中导出,并进行转换,以便在部署环境中使用。可以使用工具如TorchScript或ONNX将yolov5模型转换成可供这些环境使用的格式。 其次,选择合适的部署方式。yolov5模型可以在各种硬件平台上运行,包括CPUGPU和边缘设备。根据具体的场景需求和实际资源情况,可以选择使用TensorRT、OpenVINO、NCS等优化工具或框架,或者将模型部署到边缘设备如Jetson Nano等。 然后,根据部署的需求进行模型的优化和加速。yolov5模型可以通过一些技术手段进行加速,例如剪枝、量化和模型压缩等。这些技术可以提升模型的推理速度和性能,使得在实际应用中能更加高效地运行。 接着,进行模型的集成和部署yolov5模型部署时需要与其他组件进行集成,例如数据预处理模块、后处理模块等。这些组件可以根据具体的场景需求进行设计和开发,以实现最终的目标检测功能。 最后,进行模型的测试和调优。在模型部署落地后,需要进行充分的测试和调优,以确保其在实际应用中的准确性和稳定性。可以利用真实数据集或者仿真数据进行测试,并通过不断地优化和迭代,改进模型的性能和效果。 综上所述,yolov5模型部署落地过程需要经历模型导出和转换、选择部署方式、模型优化和加速、模型集成和部署,以及模型测试和调优等环节。通过合理的选择和设计,可以将yolov5模型广泛应用于各个实际场景中,实现高效准确的目标检测功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为阿根廷助威

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值