【yolov8部署实战】VS2019环境下使用Onnxruntime环境部署yolo项目|含源码

news2024/11/14 21:30:34

一、前言

部署yolo项目,是我这几个月以来做的事情,最近打算把这几个月试过的方法,踩过的坑,以博客的形式,分享一下。关于下面动态中讲到的如何用opencv部署,我在上一篇博客中已经详细讲到了:【yolov8部署实战】VS2019环境下使用C++和OpenCV环境部署yolo项目|含详细注释源码。这篇博客主要讲讲使用onnxruntime部署
在这里插入图片描述
主要参考:https://github.com/Amyheart/yolov5v8-dnn-onnxruntime/tree/main

二、onnxruntime部署

2.0:环境依赖:

在这里插入图片描述
如果是只想要onnxruntime部署cpu版本的,即不调用gpu,则不需要配置后面两个(Cuda、cuDNN))Onnxruntime = onnxruntime-win-x64-gpu-1.16.0 or onnxruntime-win-x64-1.16.0都ok。若想调用gpu,图中所有环境必须配置好,且选用gpu版本的onnxruntime。

2.1:源代码

main.cpp

#include <iostream>
//#include <getopt.h>
#include "yolov8_ort.h"
int main(int argc, char *argv[])
{
	
 //**********************************onnxruntime推理*****************************************************************************************************
    std::string img_path = "D:/C++(2019)/data/video_good/init_photo.jpg";
    std::string video_path = "D:/C++(2019)/data/video_good/test_video.mp4";
    std::string model_path = "D:/C++(2019)/models/best.onnx";
    DCSP_CORE* yoloDetector = new DCSP_CORE;
    #ifdef USE_CUDA
        // GPU FP32 inference
        DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8, {640, 640},  0.25,  0.1, 0.5, true };
        // GPU FP16 inference
         //DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8_HALF, {640, 640},  0.25, 0.1, 0.5, true }; 
    #else
        // CPU inference
        DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V5, {640, 640},0.25, 0.45, 0.5, false };
    #endif
    yoloDetector->CreateSession(params);
        int freq = 15;//采样频率
        int idx = 0;
        cv::VideoCapture videoCapture(video_path);
        if (!videoCapture.isOpened()) {
            ui.status->setText("Failed to open video!");
            //std::cout << "Failed to open video!" << std::endl;
            return;
        }
        // 创建一个窗口并设置窗口名称
        cv::namedWindow("Inference", cv::WINDOW_NORMAL);
        // 设置窗口的大小
        cv::resizeWindow("Inference", 800, 600);
        // # 循环读取每一帧图像
        while (true)
        {
            idx++;
            cv::Mat frame;
            std::vector<DCSP_RESULT> res;
            //处理视频帧的时间戳。
            bool ret = videoCapture.grab();//从视频文件中抓取下一帧的图像。它返回一个布尔值,表示抓取是否成功。
            if (ret && idx % freq == 1)
            {
                ret = videoCapture.retrieve(frame);
                yoloDetector->RunSession(frame, res);
                if (res.size() != 0)
                    yoloDetector->DrawPred(frame, res);
                else
                    std::cout << "Detect Nothing!" << std::endl;
                // 在指定的窗口中展示图像
                cv::imshow("Inference", frame);
                /*cv::waitKey(0);
                cv::destroyAllWindows();*/
                if (cv::waitKey(1) == 27)  // Press Esc to exit
                {
                    cv::destroyAllWindows();
                    break;
                }
            }
        }
        videoCapture.release();
        cv::destroyAllWindows();
        delete yoloDetector;
        return 0;
}

Tips:这里没有设置getopt.h,也就是说没有一个专门定义宏的文件,你可以选择自己创建,或者像我下面图示中这样自己手动设置,你要不要调用GPU:
在这里插入图片描述
yolov8_ort.h:

#pragma once

#define    RET_OK nullptr
#define    USE_CUDA

#ifdef _WIN32
#include <Windows.h>
#include <direct.h>
#include <io.h>
#endif

#include <string>
#include <vector>
#include <cstdio>
#include <opencv2/opencv.hpp>
#include "onnxruntime_cxx_api.h"

#ifdef USE_CUDA
#include <cuda_fp16.h>
#endif


enum MODEL_TYPE {
    //FLOAT32 MODEL
    YOLO_ORIGIN_V5 = 0,//support v5 detector currently
    YOLO_ORIGIN_V8 = 1,//support v8 detector currently
    YOLO_POSE_V8 = 2,
    YOLO_CLS_V8 = 3,
    //FLOAT16 MODEL
    YOLO_ORIGIN_V8_HALF = 4,
    YOLO_POSE_V8_HALF = 5,
    YOLO_CLS_V8_HALF = 6
};


typedef struct _DCSP_INIT_PARAM {
    std::string ModelPath;
    MODEL_TYPE ModelType = YOLO_ORIGIN_V8;
    std::vector<int> imgSize = { 640, 640 };
    float modelConfidenceThreshold = 0.1;
    float RectConfidenceThreshold = 0.1;
    float iouThreshold = 0.1;
    bool CudaEnable = true;
    int LogSeverityLevel = 3;
    int IntraOpNumThreads = 1;
} DCSP_INIT_PARAM;


typedef struct _DCSP_RESULT {
    int classId;
    std::string className;
    float confidence;
    cv::Rect box;
    cv::Mat boxMask;       //矩形框内mask
    cv::Scalar color;
} DCSP_RESULT;


//DCSP_CORE是主类, 用于创建session, 预处理图像, 推理, 后处理等功能
class DCSP_CORE {
public:
    DCSP_CORE();
    ~DCSP_CORE();

public:

    void DrawPred(cv::Mat& img, std::vector<DCSP_RESULT>& result);//DrawPred绘制预测结果到图像上
    const char* CreateSession(DCSP_INIT_PARAM& iParams);//CreateSession函数用于创建OnnxRuntime session
    /*
    * RunSession函数主要做图像预处理、推理和后处理工作:
        预处理:图像resize、padding填充等操作
        推理:构建输入张量,运行session执行推理
        后处理:解析输出,NMS非极大抑制、绘制预测框等
    */
    char* RunSession(cv::Mat& iImg, std::vector<DCSP_RESULT>& oResult);
    char* WarmUpSession();//做预热测试,初始化CUDA设备
    template<typename N>
    /*
    * TensorProcess函数专门处理张量数据,包括解析推理输出、NMS、置信度筛选等操作
    */
    char* TensorProcess(clock_t& starttime_1, cv::Vec4d& params, cv::Mat& iImg, N& blob, std::vector<int64_t>& inputNodeDims,
        std::vector<DCSP_RESULT>& oResult);
    char* PreProcess(cv::Mat& iImg, std::vector<int> iImgSize, cv::Mat& oImg);
   /* std::vector<std::string> classes{ "person", "bicycle", "car", "motorcycle", "airplane", "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", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard",
    "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" };*/
    std::vector<std::string> classes{ "screw", "number", "pump" };
private:
    Ort::Env env;
    Ort::Session* session;
    bool cudaEnable;
    Ort::RunOptions options;
    bool RunSegmentation = false;
    std::vector<const char*> inputNodeNames;
    std::vector<const char*> outputNodeNames;

    MODEL_TYPE modelType;
    std::vector<int> imgSize;
    float modelConfidenceThreshold;
    float rectConfidenceThreshold;
    float iouThreshold;
    float resizeScales;//letterbox scale

};

//void LetterBox(const cv::Mat& image, cv::Mat& outImage,
//    cv::Vec4d& params, //[ratio_x,ratio_y,dw,dh]
//    const cv::Size& newShape = cv::Size(640, 640),
//    bool autoShape = false,
//    bool scaleFill = false,
//    bool scaleUp = true,
//    int stride = 32,
//    const cv::Scalar& color = cv::Scalar(114, 114, 114));

yolov8_ort.cpp:

#define _CRT_SECURE_NO_WARNINGS
#include "yolov8_ort.h"
#include <regex>
#include <random>
#define benchmark

#define min(a,b)            (((a) < (b)) ? (a) : (b))

DCSP_CORE::DCSP_CORE() {

}


DCSP_CORE::~DCSP_CORE() {
    delete session;
}

#ifdef USE_CUDA
namespace Ort
{
    template<>
    struct TypeToTensorType<half> { static constexpr ONNXTensorElementDataType type = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16; };
}
#endif


template<typename T>
char* BlobFromImage(cv::Mat& iImg, T& iBlob) {
    int channels = iImg.channels();
    int imgHeight = iImg.rows;
    int imgWidth = iImg.cols;
    for (int c = 0; c < channels; c++) {
        for (int h = 0; h < imgHeight; h++) {
            for (int w = 0; w < imgWidth; w++) {
                iBlob[c * imgWidth * imgHeight + h * imgWidth + w] = typename std::remove_pointer<T>::type(
                    (iImg.at<cv::Vec3b>(h, w)[c]) / 255.0f);
            }
        }
    }
    return RET_OK;
}


char* DCSP_CORE::PreProcess(cv::Mat& iImg, std::vector<int> iImgSize, cv::Mat& oImg) {
    if (iImg.channels() == 3)
    {
        oImg = iImg.clone();
        cv::cvtColor(oImg, oImg, cv::COLOR_BGR2RGB);
    }
    else
    {
        cv::cvtColor(iImg, oImg, cv::COLOR_GRAY2RGB);
    }

    switch (modelType)
    {
    case YOLO_ORIGIN_V8:
    case YOLO_POSE_V8:
    case YOLO_ORIGIN_V8_HALF:
    case YOLO_POSE_V8_HALF://LetterBox
    {
        if (iImg.cols >= iImg.rows)
        {
            resizeScales = iImg.cols / (float)iImgSize.at(0);
            cv::resize(oImg, oImg, cv::Size(iImgSize.at(0), int(iImg.rows / resizeScales)));
        }
        else
        {
            resizeScales = iImg.rows / (float)iImgSize.at(0);
            cv::resize(oImg, oImg, cv::Size(int(iImg.cols / resizeScales), iImgSize.at(1)));
        }
        cv::Mat tempImg = cv::Mat::zeros(iImgSize.at(0), iImgSize.at(1), CV_8UC3);
        oImg.copyTo(tempImg(cv::Rect(0, 0, oImg.cols, oImg.rows)));
        oImg = tempImg;
        break;
    }
    case YOLO_CLS_V8://CenterCrop
    {
        int h = iImg.rows;
        int w = iImg.cols;
        int m = min(h, w);
        int top = (h - m) / 2;
        int left = (w - m) / 2;
        cv::resize(oImg(cv::Rect(left, top, m, m)), oImg, cv::Size(iImgSize.at(0), iImgSize.at(1)));
        break;
    }
    }
    return RET_OK;
}

void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape = cv::Size(640, 640),
    bool autoShape = false, bool scaleFill = false, bool scaleUp = true, int stride = 32, const cv::Scalar& color = cv::Scalar(114, 114, 114))
{
    if (false) {
        int maxLen = MAX(image.rows, image.cols);
        outImage = cv::Mat::zeros(cv::Size(maxLen, maxLen), CV_8UC3);
        image.copyTo(outImage(cv::Rect(0, 0, image.cols, image.rows)));
        params[0] = 1;
        params[1] = 1;
        params[3] = 0;
        params[2] = 0;
    }

    cv::Size shape = image.size();
    float r = min((float)newShape.height / (float)shape.height,
        (float)newShape.width / (float)shape.width);
    if (!scaleUp)
        r = min(r, 1.0f);

    float ratio[2]{ r, r };
    int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) };

    auto dw = (float)(newShape.width - new_un_pad[0]);
    auto dh = (float)(newShape.height - new_un_pad[1]);

    if (autoShape)
    {
        dw = (float)((int)dw % stride);
        dh = (float)((int)dh % stride);
    }
    else if (scaleFill)
    {
        dw = 0.0f;
        dh = 0.0f;
        new_un_pad[0] = newShape.width;
        new_un_pad[1] = newShape.height;
        ratio[0] = (float)newShape.width / (float)shape.width;
        ratio[1] = (float)newShape.height / (float)shape.height;
    }

    dw /= 2.0f;
    dh /= 2.0f;

    if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
    {
        cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
    }
    else {
        outImage = image.clone();
    }

    int top = int(std::round(dh - 0.1f));
    int bottom = int(std::round(dh + 0.1f));
    int left = int(std::round(dw - 0.1f));
    int right = int(std::round(dw + 0.1f));
    params[0] = ratio[0];
    params[1] = ratio[1];
    params[2] = left;
    params[3] = top;
    cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
}

void GetMask(const int* const _seg_params, const float& rectConfidenceThreshold, const cv::Mat& maskProposals, const cv::Mat& mask_protos, const cv::Vec4d& params, const cv::Size& srcImgShape, std::vector<DCSP_RESULT>& output) {
    int _segChannels = *_seg_params;
    int _segHeight = *(_seg_params + 1);
    int _segWidth = *(_seg_params + 2);
    int _netHeight = *(_seg_params + 3);
    int _netWidth = *(_seg_params + 4);

    cv::Mat protos = mask_protos.reshape(0, { _segChannels,_segWidth * _segHeight });
    cv::Mat matmulRes = (maskProposals * protos).t();
    cv::Mat masks = matmulRes.reshape(output.size(), { _segHeight,_segWidth });
    std::vector<cv::Mat> maskChannels;
    split(masks, maskChannels);

    for (int i = 0; i < output.size(); ++i) {
        cv::Mat dest, mask;
        //sigmoid
        cv::exp(-maskChannels[i], dest);
        dest = 1.0 / (1.0 + dest);
        cv::Rect roi(int(params[2] / _netWidth * _segWidth), int(params[3] / _netHeight * _segHeight), int(_segWidth - params[2] / 2), int(_segHeight - params[3] / 2));
        dest = dest(roi);
        cv::resize(dest, mask, srcImgShape, cv::INTER_NEAREST);
        //crop
        cv::Rect temp_rect = output[i].box;
        mask = mask(temp_rect) > rectConfidenceThreshold;
        output[i].boxMask = mask;
    }
}


void DCSP_CORE::DrawPred(cv::Mat& img, std::vector<DCSP_RESULT>& result) {
    int detections = result.size();
    std::cout << "Number of detections:" << detections << std::endl;
    cv::Mat mask = img.clone();
    for (int i = 0; i < detections; ++i)
    {
        DCSP_RESULT detection = result[i];
        cv::Rect box = detection.box;
        //这里来改变颜色
        cv::Scalar color = detection.color ;
       /* switch (detection.classId)
        {
            case 0:
                color = cv::Scalar(0, 255, 0);
                break;
            case 1:
                color = cv::Scalar(255, 0, 0);
                break;
            case 2:
                color = cv::Scalar(0, 0, 255);
                break;

        }*/
        // Detection box
        cv::rectangle(img, box, color, 2);
        mask(detection.box).setTo(color, detection.boxMask);
        // 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(img, textBox, color, cv::FILLED);
        cv::putText(img, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);
    }
    // Detection mask
    if (RunSegmentation) cv::addWeighted(img, 0.5, mask, 0.5, 0, img); //将mask加在原图上面
    // // 创建一个窗口并设置窗口名称
    //cv::namedWindow("Inference", cv::WINDOW_NORMAL);

     设置窗口的大小
    //cv::resizeWindow("Inference", 800, 600);

    // 在指定的窗口中展示图像
    /*cv::imshow("Inference", img);*/
    /*cv::imshow("Inference", img);*/
    //cv::imwrite("out.bmp", img);
   /* cv::waitKey();
    cv::destroyWindow("Inference");*/
}

const char* DCSP_CORE::CreateSession(DCSP_INIT_PARAM& iParams) {
    const char* Ret = RET_OK;
    std::regex pattern("[\u4e00-\u9fa5]");
    bool result = std::regex_search(iParams.ModelPath, pattern);
    if (result) {
        Ret = "[DCSP_ONNX]:Model path error.Change your model path without chinese characters.";
        std::cout << Ret << std::endl;
        return Ret;
    }
    try {
        modelConfidenceThreshold = iParams.modelConfidenceThreshold;
        rectConfidenceThreshold = iParams.RectConfidenceThreshold;
        iouThreshold = iParams.iouThreshold;
        imgSize = iParams.imgSize;
        modelType = iParams.ModelType;
        env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "Yolo");
        Ort::SessionOptions sessionOption;
        if (iParams.CudaEnable) {
            cudaEnable = iParams.CudaEnable;
            OrtCUDAProviderOptions cudaOption;
            cudaOption.device_id = 0;
            sessionOption.AppendExecutionProvider_CUDA(cudaOption);
        }
        sessionOption.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
        sessionOption.SetIntraOpNumThreads(iParams.IntraOpNumThreads);
        sessionOption.SetLogSeverityLevel(iParams.LogSeverityLevel);

#ifdef _WIN32
        int ModelPathSize = MultiByteToWideChar(CP_UTF8, 0, iParams.ModelPath.c_str(), static_cast<int>(iParams.ModelPath.length()), nullptr, 0);
        wchar_t* wide_cstr = new wchar_t[ModelPathSize + 1];
        MultiByteToWideChar(CP_UTF8, 0, iParams.ModelPath.c_str(), static_cast<int>(iParams.ModelPath.length()), wide_cstr, ModelPathSize);
        wide_cstr[ModelPathSize] = L'\0';
        const wchar_t* modelPath = wide_cstr;
#else
        const char* modelPath = iParams.ModelPath.c_str();
#endif // _WIN32

        session = new Ort::Session(env, modelPath, sessionOption);
        Ort::AllocatorWithDefaultOptions allocator;
        size_t inputNodesNum = session->GetInputCount();
        for (size_t i = 0; i < inputNodesNum; i++) {
            Ort::AllocatedStringPtr input_node_name = session->GetInputNameAllocated(i, allocator);
            char* temp_buf = new char[50];
            strcpy(temp_buf, input_node_name.get());
            inputNodeNames.push_back(temp_buf);
        }
        size_t OutputNodesNum = session->GetOutputCount();
        for (size_t i = 0; i < OutputNodesNum; i++) {
            Ort::AllocatedStringPtr output_node_name = session->GetOutputNameAllocated(i, allocator);
            char* temp_buf = new char[10];
            strcpy(temp_buf, output_node_name.get());
            outputNodeNames.push_back(temp_buf);
        }
        if (outputNodeNames.size() == 2) RunSegmentation = true;
        options = Ort::RunOptions{ nullptr };
        WarmUpSession();
        return RET_OK;
    }
    catch (const std::exception& e) {
        const char* str1 = "[DCSP_ONNX]:";
        const char* str2 = e.what();
        std::string result = std::string(str1) + std::string(str2);
        char* merged = new char[result.length() + 1];
        std::strcpy(merged, result.c_str());
        std::cout << merged << std::endl;
        delete[] merged;
        return "[DCSP_ONNX]:Create session failed.";
    }
}


char* DCSP_CORE::RunSession(cv::Mat& iImg, std::vector<DCSP_RESULT>& oResult) {
#ifdef benchmark
    clock_t starttime_1 = clock();
#endif // benchmark
    char* Ret = RET_OK;
    cv::Mat processedImg;
    cv::Vec4d params;
    //resize图片尺寸,PreProcess是直接resize,LetterBox有padding操作
    //PreProcess(iImg, imgSize, processedImg);
    LetterBox(iImg, processedImg, params, cv::Size(imgSize.at(1), imgSize.at(0)));
   
    if (modelType < 4) {
        float* blob = new float[processedImg.total() * 3];
        BlobFromImage(processedImg, blob);
        std::vector<int64_t> inputNodeDims = { 1, 3, imgSize.at(0), imgSize.at(1) };
        TensorProcess(starttime_1, params, iImg, blob, inputNodeDims, oResult);
    }
    else {
#ifdef USE_CUDA
        half* blob = new half[processedImg.total() * 3];
        BlobFromImage(processedImg, blob);
        std::vector<int64_t> inputNodeDims = { 1,3,imgSize.at(0),imgSize.at(1) };
        TensorProcess(starttime_1, params, iImg, blob, inputNodeDims, oResult);
#endif
    }
    return Ret;
}


template<typename N>
char* DCSP_CORE::TensorProcess(clock_t& starttime_1, cv::Vec4d& params, cv::Mat& iImg, N& blob, std::vector<int64_t>& inputNodeDims, std::vector<DCSP_RESULT>& oResult)
{
    /*Step1
   * 创建一个输入张量inputTensor
   * 内存信息:使用Ort::MemoryInfo::CreateCpu函数创建一个CPU内存信息对象,并指定内存分配器和内存类型为CPU。
    数据指针:blob,即之前处理过的图像数据。
    数据元素数量:3 * 图像的宽度 * 图像的高度。
    数据维度:inputNodeDims.data(),即输入节点的维度信息。
    维度数量:inputNodeDims.size(),即输入节点的维度数量。
   */
    Ort::Value inputTensor = Ort::Value::CreateTensor<typename std::remove_pointer<N>::type>(
        Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1), inputNodeDims.data(), inputNodeDims.size());
#ifdef benchmark
    clock_t starttime_2 = clock();
#endif // benchmark
    /*Step2
    * 使用session->Run函数运行推理
    *   运行选项:options,可能是一些配置选项,不在提供的代码片段中展示。
        输入节点名称数组的数据指针:inputNodeNames.data(),即输入节点的名称。
        输入张量的数据指针:&inputTensor,即输入张量的地址。
        输入张量的数量:1,因为只有一个输入张量。
        输出节点名称数组的数据指针:outputNodeNames.data(),即输出节点的名称。
        输出节点数量:outputNodeNames.size(),即输出节点的数量。
    */
    auto outputTensor = session->Run(options, inputNodeNames.data(), &inputTensor, 1, outputNodeNames.data(), outputNodeNames.size());
#ifdef benchmark
    clock_t starttime_3 = clock();
#endif // benchmark
    /*Step3:解析输出张量:获取输出张量的形状信息和数据。
    * - 获取输出张量的形状信息:使用GetTensorTypeAndShapeInfo函数获取输出张量的类型和形状信息,并将形状信息存储在_outputTensorShape中。
    * - 获取输出张量的数据指针:使用GetTensorMutableData函数获取输出张量的可变数据指针,并将数据指针存储在output中。
    *
    */
    std::vector<int64_t> _outputTensorShape;
    _outputTensorShape = outputTensor[0].GetTensorTypeAndShapeInfo().GetShape();
    auto output = outputTensor[0].GetTensorMutableData<typename std::remove_pointer<N>::type>();
    delete blob;//删除blob:释放之前动态分配的blob内存


    /*Step4:处理输出张量数据:根据不同的modelType进行处理
    *
    */
    // 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])
    // yolov5
    int dimensions = _outputTensorShape[1];
    int rows = _outputTensorShape[2];
    cv::Mat rowData(dimensions, rows, CV_32F, output);
    // yolov8
    if (rows > dimensions) {
        dimensions = _outputTensorShape[2];
        rows = _outputTensorShape[1];
        rowData = rowData.t();
    }
    std::vector<int> class_ids;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;
    std::vector<std::vector<float>> picked_proposals;

    float* data = (float*)rowData.data;
    for (int i = 0; i < dimensions; ++i) {
        switch (modelType) {
        case 0://V5_ORIGIN_FP32
        {
            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 > rectConfidenceThreshold)
                {
                    if (RunSegmentation) {
                        int _segChannels = outputTensor[1].GetTensorTypeAndShapeInfo().GetShape()[1];
                        std::vector<float> temp_proto(data + classes.size() + 5, data + classes.size() + 5 + _segChannels);
                        picked_proposals.push_back(temp_proto);
                    }
                    confidences.push_back(confidence);
                    class_ids.push_back(class_id.x);
                    float x = (data[0] - params[2]) / params[0];
                    float y = (data[1] - params[3]) / params[1];
                    float w = data[2] / params[0];
                    float h = data[3] / params[1];
                    int left = MAX(round(x - 0.5 * w + 0.5), 0);
                    int top = MAX(round(y - 0.5 * h + 0.5), 0);
                    if ((left + w) > iImg.cols) { w = iImg.cols - left; }
                    if ((top + h) > iImg.rows) { h = iImg.rows - top; }
                    boxes.emplace_back(cv::Rect(left, top, int(w), int(h)));
                }
            }
            break;
        }
        case 1://V8_ORIGIN_FP32
        case 4://V8_ORIGIN_FP16
        {
            float* classesScores = data + 4;//获取各个类别的得分classesScores。
            cv::Mat scores(1, this->classes.size(), CV_32FC1, classesScores);
            cv::Point class_id;
            double maxClassScore;
            cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id);//找到最大得分的类别class_id和对应的最大得分maxClassScore。
            if (maxClassScore > rectConfidenceThreshold) {
                if (RunSegmentation) {
                    int _segChannels = outputTensor[1].GetTensorTypeAndShapeInfo().GetShape()[1];
                    std::vector<float> temp_proto(data + classes.size() + 4, data + classes.size() + 4 + _segChannels);
                    picked_proposals.push_back(temp_proto);
                }
                confidences.push_back(maxClassScore);
                class_ids.push_back(class_id.x);
                float x = (data[0] - params[2]) / params[0];
                float y = (data[1] - params[3]) / params[1];
                float w = data[2] / params[0];
                float h = data[3] / params[1];
                int left = MAX(round(x - 0.5 * w + 0.5), 0);
                int top = MAX(round(y - 0.5 * h + 0.5), 0);
                if ((left + w) > iImg.cols) { w = iImg.cols - left; }
                if ((top + h) > iImg.rows) { h = iImg.rows - top; }
                boxes.emplace_back(cv::Rect(left, top, int(w), int(h)));
            }
            break;
        }
        }
        data += rows;
    }
    std::vector<int> nmsResult;
    cv::dnn::NMSBoxes(boxes, confidences, rectConfidenceThreshold, iouThreshold, nmsResult);
    std::vector<std::vector<float>> temp_mask_proposals;
    for (int i = 0; i < nmsResult.size(); ++i) {
        int idx = nmsResult[i];
        DCSP_RESULT result;
        result.classId = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        result.className = classes[result.classId];
        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));
        if (result.box.width != 0 && result.box.height != 0) oResult.push_back(result);
        if (RunSegmentation) temp_mask_proposals.push_back(picked_proposals[idx]);
    }

    if (RunSegmentation) {
        cv::Mat mask_proposals;
        for (int i = 0; i < temp_mask_proposals.size(); ++i)
            mask_proposals.push_back(cv::Mat(temp_mask_proposals[i]).t());
        std::vector<int64_t> _outputMaskTensorShape;
        _outputMaskTensorShape = outputTensor[1].GetTensorTypeAndShapeInfo().GetShape();
        int _segChannels = _outputMaskTensorShape[1];
        int _segWidth = _outputMaskTensorShape[2];
        int _segHeight = _outputMaskTensorShape[3];
        float* pdata = outputTensor[1].GetTensorMutableData<float>();
        std::vector<float> mask(pdata, pdata + _segChannels * _segWidth * _segHeight);
        int _seg_params[5] = { _segChannels, _segWidth, _segHeight, inputNodeDims[2], inputNodeDims[3] };
        cv::Mat mask_protos = cv::Mat(mask);
        GetMask(_seg_params, rectConfidenceThreshold, mask_proposals, mask_protos, params, iImg.size(), oResult);
    }

#ifdef benchmark
    clock_t starttime_4 = clock();
    double pre_process_time = (double)(starttime_2 - starttime_1) / CLOCKS_PER_SEC * 1000;
    double process_time = (double)(starttime_3 - starttime_2) / CLOCKS_PER_SEC * 1000;
    double post_process_time = (double)(starttime_4 - starttime_3) / CLOCKS_PER_SEC * 1000;
    if (cudaEnable) {
        std::cout << "[DCSP_ONNX(CUDA)]: " << pre_process_time << "ms pre-process, " << process_time
            << "ms inference, " << post_process_time << "ms post-process." << std::endl;
    }
    else {
        std::cout << "[DCSP_ONNX(CPU)]: " << pre_process_time << "ms pre-process, " << process_time
            << "ms inference, " << post_process_time << "ms post-process." << std::endl;
    }
#endif // benchmark

    return RET_OK;

}


char* DCSP_CORE::WarmUpSession() {
    clock_t starttime_1 = clock();
    cv::Mat iImg = cv::Mat(cv::Size(imgSize.at(0), imgSize.at(1)), CV_8UC3);
    cv::Mat processedImg;
    cv::Vec4d params;
    //resize图片尺寸,PreProcess是直接resize,LetterBox有padding操作
    //PreProcess(iImg, imgSize, processedImg);
    LetterBox(iImg, processedImg, params, cv::Size(imgSize.at(1), imgSize.at(0)));
    if (modelType < 4) {
        float* blob = new float[iImg.total() * 3];
        BlobFromImage(processedImg, blob);
        std::vector<int64_t> YOLO_input_node_dims = { 1, 3, imgSize.at(0), imgSize.at(1) };
        Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
            Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1),
            YOLO_input_node_dims.data(), YOLO_input_node_dims.size());
        auto output_tensors = session->Run(options, inputNodeNames.data(), &input_tensor, 1, outputNodeNames.data(), outputNodeNames.size());
        delete[] blob;
        clock_t starttime_4 = clock();
        double post_process_time = (double)(starttime_4 - starttime_1) / CLOCKS_PER_SEC * 1000;
        if (cudaEnable) {
            std::cout << "[DCSP_ONNX(CUDA)]: " << "Cuda warm-up cost " << post_process_time << " ms. " << std::endl;
        }
    }
    else {
#ifdef USE_CUDA
        half* blob = new half[iImg.total() * 3];
        BlobFromImage(processedImg, blob);
        std::vector<int64_t> YOLO_input_node_dims = { 1,3,imgSize.at(0),imgSize.at(1) };
        Ort::Value input_tensor = Ort::Value::CreateTensor<half>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1), YOLO_input_node_dims.data(), YOLO_input_node_dims.size());
        auto output_tensors = session->Run(options, inputNodeNames.data(), &input_tensor, 1, outputNodeNames.data(), outputNodeNames.size());
        delete[] blob;
        clock_t starttime_4 = clock();
        double post_process_time = (double)(starttime_4 - starttime_1) / CLOCKS_PER_SEC * 1000;
        if (cudaEnable)
        {
            std::cout << "[DCSP_ONNX(CUDA)]: " << "Cuda warm-up cost " << post_process_time << " ms. " << std::endl;
        }
#endif
    }
    return RET_OK;
}

四、代码分析

  1. DCSP_CORE是主类,用于创建session,预处理图像,推理,后处理等功能。

  2. CreateSession函数用于创建OnnxRuntime session,主要做了以下工作:

    • 设置日志级别、设备(CPU/CUDA)、优化等配置
    • 获取模型输入输出节点名称
    • 根据模型类型初始化一些变量如分类类别数等
    • 创建session
  3. RunSession函数主要做图像预处理、推理和后处理工作:

    • 预处理:图像resize、padding填充等操作
    • 推理:构建输入张量,运行session执行推理
    • 后处理:解析输出,NMS非极大抑制、绘制预测框等
  4. TensorProcess函数专门处理张量数据,包括解析推理输出、NMS、置信度筛选等操作

  5. DrawPred绘制预测结果到图像上

  6. WarmUpSession做预热测试,初始化CUDA设备

  7. 一些工具函数如BlobFromImage、LetterBox用于图像与张量转换

  8. 定义模型类型、分类类别作为全局变量

  9. 使用OnnxRuntime C++ API实现了 end-to-end 的模型部署流程

主要特点是:

  • 使用OnnxRuntime高效执行模型推理
  • 完整实现预处理、推理、后处理流程
  • 设计了类和函数分模块编程
  • 支持CPU和CUDA加速

总体来看实现得很到位,利用OnnxRuntime成功部署和运行了yolov8模型。

五、后言

这里再次强调一下,如果想调用gpu,只下gpu版本的onnxruntime是不够的,必须把cuda和cudnn环境安排上。

另外,下篇博客计划分享一下yolov8分割模型的部署。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity(第二十三部)导航

你可以使用 unity官方提供的 unity导航组件或第三方 unity导航组件&#xff0c;以实现游戏中角色或其他物体的导航。 unity导航组件通常具有多种导航模式&#xff0c;如飞行模式、步行模式、车辆模式等&#xff0c;可以根据不同的需求选择合适的模式。同时&#xff0c;unity导…

【JSON2WEB】06 JSON2WEB前端框架搭建

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 前端技术路线太多了&#xff0c;知识点更多&…

【InternLM 实战营笔记】OpenCompass大模型评测

随着人工智能技术的快速发展&#xff0c; 大规模预训练自然语言模型成为了研究热点和关注焦点。OpenAI于2018年提出了第一代GPT模型&#xff0c;开辟了自然语言模型生成式预训练的路线。沿着这条路线&#xff0c;随后又陆续发布了GPT-2和GPT-3模型。与此同时&#xff0c;谷歌也…

leetcode 3.1

leetcode hot 100 双指针1.三数之和2.接雨水 多维动态规划1.最长公共子序列 双指针 1.三数之和 三数之和 排序 双指针的方法&#xff0c;固定一个数nums[i], 用两数和找target - nums[i] 的数需要注意两点: 1.需要去掉重复数字 while (l < r && nums[l] nums[…

2024年领取腾讯云优惠券的方法有哪些?程序员爆肝整理

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

如何自动化获取谷歌学术上学者的全部论文信息

如何自动化获取谷歌学术上学者的全部论文信息 在学术研究领域&#xff0c;追踪和分析学者的研究工作是非常重要的。本文介绍了如何使用Python自动化地收集指定学者的谷歌学术主页上的所有论文信息。 示例&#xff1a;施一公院士的谷歌学术主页 以施一公院士的谷歌学术主页为…

redis7.2.2|Dict

文章目录 StructredisDBdictdictTypedictEntry 宏定义散列函数散列冲突dictEntry pointer bit tricks[指针位技巧]API implementation_dictReset_dictInitdictCreatedictGetHashdictSetKeydictSetValdictSetNextdictGetNextdictGetValdictGetKey_dictCleardictEmptydictRelease…

Linux进程——信号详解(上)

文章目录 信号入门生活角度的信号技术应用角度的信号用kill -l命令可以察看系统定义的信号列表信号处理常见方式概述 产生信号通过键盘进行信号的产生&#xff0c;ctrlc向前台发送2号信号通过系统调用异常软件条件 信号入门 生活角度的信号 你在网上买了很多件商品&#xff0…

洗地机推荐购买要点全攻略:洗地机哪些品牌好用?热门洗地机详细体验点评

清洁家务可谓是家务清洁中最累人的存在&#xff0c;既浪费时间也浪费精力&#xff0c;还费腰。如果是家有萌宠的铲屎官们就更加辛苦了&#xff0c;不仅清洁时会被萌宠们打扰&#xff0c;还要处理漫天飞舞和沾在地面上的毛发。那么有没有一款智能家电可以帮助我们快速高效的完成…

百度SEO快排原理是什么?如何快速排名方法?

前言&#xff1a;我之前说过我不打算写这个快速排序。 首先&#xff0c;我从来没有在自己的网站上操作过所谓的快速排序。 其次&#xff0c;我不能像网上很多人写的那样透露百度快速排序的秘密&#xff08;说实话&#xff0c;你可以透露秘密&#xff09;。 方法是有了&#xff…

MWC 2024丨美格智能CEO杜国彬出席中国联通创新成果发布会并发表主题演讲

2月26日&#xff0c;中国联通在MWC2024 巴塞罗那期间举办了以“算网为基&#xff0c;智领未来”为主题的创新成果发布会&#xff0c;集中展示最新的创新成果与最佳实践。 中国通信标准化协会理事长闻库、GSMA首席财务官Louise Easterbrook、中国联通副总经理梁宝俊、华为ICT销…

xsslabs第五关

看一下源码 <!DOCTYPE html><!--STATUS OK--><html> <head> <meta http-equiv"content-type" content"text/html;charsetutf-8"> <script> window.alert function() { confirm("完成的不错&#xff01…

代码随想录第45天|● 70. 爬楼梯 (进阶) ● 322. 零钱兑换 ● 279.完全平方数

文章目录 ● 70. 爬楼梯 &#xff08;进阶&#xff09;思路&#xff1a;- 排列 先value后weight代码&#xff1a; ● 322. 零钱兑换思路&#xff1a;代码 ● 279.完全平方数思路&#xff1a;代码 ● 70. 爬楼梯 &#xff08;进阶&#xff09; 思路&#xff1a;- 排列 先value后…

Godot自定义控件样式语法解析

前言 本篇原始文章写于2023年8月7日&#xff0c;存储在我的语雀文档中。但是语雀分享有诸多不便&#xff0c;为了让更多Godoter更轻松的搜到和看到&#xff0c;就转过来了。 这个项目我上传了Github&#xff0c;后续会贴上链接。 概述 Godot控件体系存在的问题之一就是样式无…

C++ 快速排序快速选择

目录 1、75. 颜色分类 2、912. 排序数组 3、 215. 数组中的第K个最大元素 4、LCR 159. 库存管理 III 1、75. 颜色分类 思路&#xff1a;利用快速排序思路&#xff0c;使用三指针分块进行优化。 [0,left]——小于key[left1,right-1]——等于key[right,nums.size()]——大于k…

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis 问题发现问题解决方法一&#xff1a;检查Mapper文件的namespace路径是否正确方法二&#xff1a;使用其他方法是否正确 问题发现 在学习MyBatis框架的时候&#xff0c;不使用 XML 构建 SqlSessionFacto…

2023年NOC大赛软件创意编程(学而思)赛道图形化小高组初赛试题,包含答案

2023NOC初赛试题-小高-A卷(平行讲义) 一、判断 1、如果想要编程演奏乐曲需要添加下面的拓展模块 2、运行下面的程序,我们看不到角色位置在舞台上发生变化 3、运行下面的程序,我们会在舞台上看到一个正方形。 4、运行下面的程序,结果一定为true。 5、运行下面的程序,…

顶易海关数据怎么做获客?功能详解看这里!

顶易海关数据怎么做获客呢&#xff1f;详解看这里&#xff01; 海关数据系统登录&#xff1a;hg.smtso.com/?iEF6DCB 如果对开发国外优质客户感兴趣的话&#xff0c;关注Felicia外贸说&#xff0c;一键开发客户不是问题。 海关数据主要功能&#xff1a; 报关单详情查询&#…

YOLO v9训练自己数据集

原以为RT-DETR可以真的干翻YOLO家族&#xff0c;结果&#xff0c;&#xff01;&#xff01;&#xff01;&#xff01; 究竟能否让卷积神经网络重获新生&#xff1f; 1.数据准备 代码地址&#xff1a;https://github.com/WongKinYiu/yolov9 不能科学上网的评论区留言 数据集…

【JavaEE】_Spring MVC项目之建立连接

目录 1. Spring MVC程序编写流程 2. 建立连接 2.1 RequestMapping注解介绍 2.2 RequestMapping注解使用 2.2.1 仅修饰方法 2.2.2 修饰类与方法 2.3 关于POST请求与GET请求 2.3.1 GET请求 2.3.2 POST请求 2.3.3 限制请求方法 1. Spring MVC程序编写流程 1. 建立连接&…