C++部署yolov5s.onnx并使用GPU进行加速

#include <fstream>
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <onnxruntime_cxx_api.h>
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
#include<opencv2/dnn/dnn.hpp>
using namespace cv;

Mat resize_image(Mat srcimg, int* newh, int* neww, int* top, int* left)
{
    int srch = srcimg.rows, srcw = srcimg.cols;
    int inpHeight = 640;
    int inpWidth = 640;
    *newh = inpHeight;
    *neww = inpWidth;
    bool keep_ratio = true;
    Mat dstimg;
    if (keep_ratio && srch != srcw) {
        float hw_scale = (float)srch / srcw;
        if (hw_scale > 1) {
            *newh = inpHeight;
            *neww = int(inpWidth / hw_scale);
            resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
            *left = int((inpWidth - *neww) * 0.5);
            copyMakeBorder(dstimg, dstimg, 0, 0, *left, inpWidth - *neww - *left, BORDER_CONSTANT, 0);
        }
        else {
            *newh = (int)inpHeight * hw_scale;
            *neww = inpWidth;
            resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
            *top = (int)(inpHeight - *newh) * 0.5;
            copyMakeBorder(dstimg, dstimg, *top, inpHeight - *newh - *top, 0, 0, BORDER_CONSTANT, 0);
        }
    }
    else {
        resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
    }
    return dstimg;
}

int main(int argc, char* argv[])
{
    std::string imgpath = "zidane.jpg";
    utils::logging::setLogLevel(utils::logging::LOG_LEVEL_ERROR);//设置OpenCV只输出错误日志
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "yolov5s");
    Ort::SessionOptions session_options;
    session_options.SetIntraOpNumThreads(1);//设置线程数
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);//启用模型优化策略
   
    //CUDA option set
    OrtCUDAProviderOptions cuda_option;
    cuda_option.device_id = 0;
    cuda_option.arena_extend_strategy = 0;
    cuda_option.cudnn_conv_algo_search = OrtCudnnConvAlgoSearchExhaustive;
    cuda_option.gpu_mem_limit = SIZE_MAX;
    cuda_option.do_copy_in_default_stream = 1;
	//CUDA 加速
    //session_options.SetGraphOptimizationLevel() 函数用于设置在使用 ORT 库执行模型时应用的图优化级别。
    //ORT_ENABLE_ALL 选项启用所有可用的优化,这可以导致更快速和更高效的执行。
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
    //OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
    session_options.AppendExecutionProvider_CUDA(cuda_option);

#ifdef _WIN32//预处理器指令,用于在编译源代码时判断当前编译环境是否是WIndows环境
    /*
    wchar_t和普通的char之间的区别比较大。主要有以下几个方面:

    1.存储大小:wchar_t类型通常占用两个字节,而char类型通常占用一个字节。这是因为wchar_t类型用于表示Unicode字符集中的字符,
    每个字符需要占用更多的存储空间。

    2.字符集:wchar_t类型用于表示Unicode字符集中的字符,而char类型则用于表示ASCII字符集中的字符。
    Unicode字符集可以表示更广泛的字符集,包括中文、日文、韩文等,而ASCII字符集只能表示英文字母、数字和少量的符号。

    3.字符串表示方式:由于wchar_t类型占用更多的存储空间,
    因此在表示字符串时通常需要使用特殊的字符串类型,如const wchar_t*表示一个指向宽字符字符串的常量指针,
    而普通的char*则表示一个指向普通字符串的指针。

    4.字符串处理函数:由于wchar_t类型和普通的char类型在存储大小和字符集上有所不同,
    因此在处理字符串时通常需要使用不同的字符串处理函数。例如,wcslen()函数用于计算宽字符字符串的长度,
    而strlen()函数用于计算普通字符串的长度。
    */
    const wchar_t* model_path = L"yolov5s.onnx"; //表示一个指向常量宽字符字符串L"yolov5s.onnx"的指针
#else
    const char* model_path = "yolov5s.onnx";
#endif

    std::vector<std::string> class_names;
    //std::string classesFile = "class.names";
    std::string classesFile = "coco.txt";//加载label
    std::ifstream ifs(classesFile.c_str());//用了classesFile.c_str()函数将字符串classesFile转换为C风格的字符串(即以null结尾的字符数组),并将其作为参数传递给std::ifstream类的构造函数。
    std::string line;
    while (getline(ifs, line)) class_names.push_back(line);
    //第一个参数env是一个ORT环境对象,用于管理ORT会话的资源和配置。
    //第二个参数model_path是一个指向ONNX模型文件的路径的常量指针,用于指定ORT会话要加载的模型。
   // 第三个参数session_options是一个ORT会话选项对象,用于配置ORT会话的选项和优化策略。
    Ort::Session session(env, model_path, session_options);
    // print model input layer (node names, types, shape etc.)
    Ort::AllocatorWithDefaultOptions allocator;//通过使用Ort::AllocatorWithDefaultOptions类,可以方便地进行内存分配和管理,而无需手动实现内存分配和释放的细节。

    // print number of model input nodes
    size_t num_input_nodes = session.GetInputCount();
     //const char* input_name = session.GetInputName(0, allocator);
     // const char* output_name = session.GetOutputName(0, allocator);
    std::vector<const char*> input_node_names = { "images" };
    std::vector<const char*> output_node_names = { "output" };

    const size_t input_tensor_size = 3 * 640 * 640;

    std::vector<float> input_tensor_values(input_tensor_size);
    //读取图像
    cv::Mat srcimg = cv::imread(imgpath);
    int newh = 0, neww = 0, padh = 0, padw = 0;
    //将图像的大小调整为指定的尺寸并在调整过程中保持图像的完整性和比例
    Mat dstimg = resize_image(srcimg, &newh, &neww, &padh, &padw);//Padded resize
      //  cv::Mat dstimg;
     Create a blob from the input image
     //第一个问题
    //cv::resize(srcimg, dstimg, cv::Size(640, 640));
    //std::cout << dstimg.type() << std::endl;//图像中的数据类型是CV_8UC3
    //Mat dstimg = dnn::blobFromImage(srcimg, 1.0 / 255, Size(640, 640), Scalar(0, 0, 0), true, false);

    //dstimg.convertTo(dstimg, CV_32FC3, 1 / 2
    for (int c = 0; c < 3; c++)
    {
        for (int i = 0; i < 640; i++)
        {
            for (int j = 0; j < 640; j++)
            {
                float pix = dstimg.ptr<uchar>(i)[j * 3 + 2 - c];//转换通道,输入onnx模型的图片通道顺序是RGB,但是opencv存储默认是BGR
                input_tensor_values[c * 640 * 640 + i * 640 + size_t(j)] = pix / 255.0;//归一化
            }
        }
    }
    // create input tensor object from data values
    std::vector<int64_t> input_node_dims = { 1, 3, 640, 640 };
    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    //auto memory_info = Ort::MemoryInfo::CreateGpu(OrtDeviceAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), input_node_dims.size());

    std::vector<Ort::Value> ort_inputs;
    ort_inputs.push_back(std::move(input_tensor));//右值引用,避免不必要的拷贝和内存分配操作
    // score model & input tensor, get back output tensor
    //开始时间
    std::vector<Ort::Value> output_tensors;
    for (int i = 0; i < 10; i++)
    {
        clock_t start_time = clock();
        output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), ort_inputs.data(), input_node_names.size(), output_node_names.data(), output_node_names.size());
        // auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);
        //结束时间
        clock_t end_time = clock();
        // 计算执行时间
        double exec_time = static_cast<double>(end_time - start_time) / CLOCKS_PER_SEC;
        // 输出执行时间
        std::cout << "Execution time: " << exec_time << " seconds" << std::endl;
    }
    // std::cout << output_tensors.size() << std::endl;
    // Get pointer to output tensor float values
    const float* rawOutput = output_tensors[0].GetTensorData<float>();
    //generate proposals
    std::vector<int64_t> outputShape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();
   // std::cout << outputShape[0] << outputShape[1] << std::endl;
    size_t count = output_tensors[0].GetTensorTypeAndShapeInfo().GetElementCount();
    std::vector<float> output(rawOutput, rawOutput + count);

    std::vector<cv::Rect> boxes;
    std::vector<float> confs;
    std::vector<int> classIds;
    int numClasses = (int)outputShape[2] - 5;
    int elementsInBatch = (int)(outputShape[1] * outputShape[2]);

    float confThreshold = 0.5;//设定阈值
    for (auto it = output.begin(); it != output.begin() + elementsInBatch; it += outputShape[2])
    {
        float clsConf = *(it + 4);//object scores
        if (clsConf > confThreshold)
        {
            int centerX = (int)(*it);
            int centerY = (int)(*(it + 1));
            int width = (int)(*(it + 2));
            int height = (int)(*(it + 3));
            int x1 = centerX - width / 2;
            int y1 = centerY - height / 2;
            boxes.emplace_back(cv::Rect(x1, y1, width, height));

            // first 5 element are x y w h and obj confidence
            int bestClassId = -1;
            float bestConf = 0.0;

            for (int i = 5; i < numClasses + 5; i++)
            {
                if ((*(it + i)) > bestConf)
                {
                    bestConf = it[i];
                    bestClassId = i - 5;
                }
            }

            //confs.emplace_back(bestConf * clsConf);
            confs.emplace_back(clsConf);
            classIds.emplace_back(bestClassId);
        }
    }

    float iouThreshold = 0.5;
    std::vector<int> indices;
    // Perform non maximum suppression to eliminate redundant overlapping boxes with
    // lower confidences极大值抑制
    cv::dnn::NMSBoxes(boxes, confs, confThreshold, iouThreshold, indices);

    //随机数种子
    RNG rng((unsigned)time(NULL));
    for (size_t i = 0; i < indices.size(); ++i)
    {
        int index = indices[i];
        int colorR = rng.uniform(0, 255);
        int colorG = rng.uniform(0, 255);
        int colorB = rng.uniform(0, 255);

        //保留两位小数
        float scores = round(confs[index] * 100) / 100;
        std::ostringstream oss;
        oss << scores;
        rectangle(dstimg, Point(boxes[index].tl().x, boxes[index].tl().y), Point(boxes[index].br().x, boxes[index].br().y), Scalar(colorR, colorG, colorB), 1.5);
        putText(dstimg, class_names[classIds[index]] + " " + oss.str(), Point(boxes[index].tl().x, boxes[index].tl().y - 5), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(colorR, colorG, colorB), 2);
    }
    imshow("检测结果", dstimg);
    cv::waitKey();
}

onnxruntime下载链接:Releases · microsoft/onnxruntime (github.com)

我下载的是1.13.1GPU版本,VS2022,cuda11.6,cudnn8.6.0,仅供参考

下面是一些环境配置问题:

 

 

 其他关于opencv的配置属性可以查阅其他资料

除此之外关于cuda也要注意添加一些路径保证cuda能正常启用

有可能提示缺少

Could not locate zlibwapi.dll. Please make sure it is in your library path!

参考解决方案:(83条消息) 解决“Could not locate zlibwapi.dll. Please make sure it is in your library path!”问题_xp_fangfei的博客-CSDN博客

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
要将yolov7.pt转化为yolov7.onnx,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了PyTorch和OpenCV库。这两个库是进行转换所必需的。 2. 下载并安装yolov7-opencv-dnn-cpp的代码库。你可以通过提供的链接进行下载。 3. 打开终端或命令提示符,并导航到yolov7-opencv-dnn-cpp库的目录。 4. 运行以下命令来将yolov7.pt转化为yolov7.onnx: ``` python export.py --weights ./yolov7.pt --grid --end2end --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 ``` 在这个命令中,`--weights`参数指定了原始的yolov7.pt文件的路径,`--grid`、`--end2end`和`--simplify`参数用于设置转换过程中的一些选项,`--topk-all`、`--iou-thres`和`--conf-thres`参数用于设置筛选目标框时的阈值,`--img-size`参数用于指定图像的大小。 5. 完成上述步骤后,你应该能够在yolov7-opencv-dnn-cpp库的输出目录中找到生成的yolov7.onnx文件。 请注意,这只是将yolov7.pt转化为yolov7.onnx的一种方法,你也可以根据自己的需求选择其他方法来完成转换。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [opencv调用yolov7 yolov7 c++ yolov7转onnx opencv调用yolov7 onnx](https://blog.csdn.net/zhulong1984/article/details/126314684)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值