使用onnxruntime进行yolov5s模型推理实现

该博客介绍了如何将YOLOv5s模型转换为ONNX格式,并在ONNXRuntime上进行推理。首先,通过pip安装onnxruntime,然后使用YOLOv5源码的export.py将模型导出为onnx。接着,利用ONNXRuntime进行推理,得到包含多个检测框及其信息的结果矩阵。在非极大值抑制阶段,分别进行了置信度抑制和IOU抑制,以减少冗余检测框,提高目标检测的准确性。最后,提供了nms函数的实现,用于完成这一过程。
部署运行你感兴趣的模型镜像

        首先,使用onnxruntime模型推理比使用pytorch快很多,所以模型训练完后,将模型导出为onnx格式并使用onnxruntime进行推理部署是一个不错的选择。接下来就逐步实现yolov5s在onnxruntime上的推理流程。

1、安装onnxruntime

pip install onnxruntime

2、导出yolov5s.pt为onnx,在YOLOv5源码中运行export.py即可将pt文件导出为onnx。

3、使用onnx进行推理,这一步只能获取模型推理产生的候选框,拿到推理结果后还需要进行下一步处理,通过非极大值抑制对候选框进行筛选。

使用onnx加载yolov5s.onnx模型,进行推理计算,打印推理结果:

height, width = 640, 640
img0 = cv2.imread('img.jpg') 
img = cv2.resize(img0, (height, width))  # 尺寸变换
img = img / 255.
img = img[:, :, ::-1].transpose((2, 0, 1))  # HWC转CHW
data = np.expand_dims(img, axis=0)  # 扩展维度至[1,3,640,640]
sess = rt.InferenceSession('yolov5s.onnx')
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name
pred_onx = sess.run([label_name], {input_name: data.astype(np.float32)})[0] 
pred = np.squeeze(pred_onx) 
print(pred)
print(pred.shape)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQUnooYzkuJrlupTnlKg=,size_20,color_FFFFFF,t_70,g_se,x_16

 

此时打印的推理结果矩阵为[25200, 85],意味着有25200个检测框,每个检测框包含85个结果,85个结果中[0~3]为xywh,即检测框中心坐标和检测框宽高,[4]为每个检测框置信度,[5~85]为coco数据集80个分类对应预测结果。

   显然拿到直接的推理结果还无法在图像上进行准确的目标绘制,接下来还要进行非极大值抑制才能获取准确推理结果。

4、非极大值抑制:非极大值抑制可以采用OpenCV等接口实现,也可以手动实现。这里根据非极大值原理进行手动实现。

   非极大值抑制分为置信度和iou抑制。首先进行置信度抑制,此处置信度为检测框置信度而非分类结果的置信度,设置检测框置信度阈值0.25。

   置信度抑制非常简单,即根据置信度阈值,移除阈值以下的候选框,保留阈值以上的候选框进入下一步处理。使用一张图像进行测试,输出结果为(27,85),即剩下27个检测框。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQUnooYzkuJrlupTnlKg=,size_20,color_FFFFFF,t_70,g_se,x_16

在iou抑制之前,先对所有候选框列表进行置信度排序,然后从候选框列表中找出不同类别候选框形成子列表候选框。

   iou抑制整体思路:对每个类别候选框取出最大置信度候选框优先输出,再通过最大置信度候选框与其他候选框计算iou,通过iou阈值判断,大于iou阈值则删除,循环计算直至候选框列表为空。

   最后将保留的候选框输出,在图像上绘制检测结果。

非极大值抑制实现:

def nms(pred, conf_thres, iou_thres):
    # 置信度抑制,小于置信度阈值则删除
    conf = pred[..., 4] > conf_thres
    box = pred[conf == True]
    # 类别获取
    cls_conf = box[..., 5:]
    cls = []
    for i in range(len(cls_conf)):
        cls.append(int(np.argmax(cls_conf[i])))
    # 获取类别
    total_cls = list(set(cls))  #删除重复项,获取出现的类别标签列表,example=[0, 17]
    output_box = []   #最终输出的预测框
    # 不同分类候选框置信度
    for i in range(len(total_cls)):
        clss = total_cls[i]   #当前类别标签
        # 从所有候选框中取出当前类别对应的所有候选框
        cls_box = []
        for j in range(len(cls)):
            if cls[j] == clss:
                box[j][5] = clss
                cls_box.append(box[j][:6])
        cls_box = np.array(cls_box)
        box_conf = cls_box[..., 4]   #取出候选框置信度
        box_conf_sort = np.argsort(box_conf)   #获取排序后索引
        max_conf_box = cls_box[box_conf_sort[len(box_conf) - 1]]
        output_box.append(max_conf_box)   #将置信度最高的候选框输出为第一个预测框
        cls_box = np.delete(cls_box, 0, 0)  #删除置信度最高的候选框
        while len(cls_box) > 0:
            max_conf_box = output_box[len(output_box) - 1]     #将输出预测框列表最后一个作为当前最大置信度候选框
            del_index = []
            for j in range(len(cls_box)):
                current_box = cls_box[j]      #当前预测框
                interArea = getInter(max_conf_box, current_box)    #当前预测框与最大预测框交集
                iou = getIou(max_conf_box, current_box, interArea)  # 计算交并比
                if iou > iou_thres:
                    del_index.append(j)   #根据交并比确定需要移出的索引
            cls_box = np.delete(cls_box, del_index, 0)   #删除此轮需要移出的候选框
            if len(cls_box) > 0:
                output_box.append(cls_box[0])
                cls_box = np.delete(cls_box, 0, 0)
    return output_box

最终推理结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQUnooYzkuJrlupTnlKg=,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

您可能感兴趣的与本文相关的镜像

Yolo-v5

Yolo-v5

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>好的,我现在需要帮助用户了解如何使用Qt结合ONNXRuntime实现YOLOv5s模型的完整推理流程。首先,我需要回忆相关的知识,并查看用户提供的引用内容,看看是否有可用的信息。 用户提供的引用中,引用[3]提到了YOLOv5的C++和ONNXRuntime在Ubuntu上的部署,包括命令行参数的使用和示例代码的位置。这可能对构建Qt应用程序有帮助,因为Qt通常使用C++,所以可以借鉴这些C++的代码结构。 接下来,我需要整理整个推理流程的步骤。通常,YOLOv5推理包括模型加载、预处理、推理执行、后处理(包括NMS)和结果绘制。Qt部分需要处理图像输入和界面显示。 预处理步骤可能需要将图像转换为模型输入的尺寸,比如640x640,并进行归一化。用户提到的引用[3]中的代码可能有相关的处理,比如读取图像、调整大小和转换颜色空间。需要注意OpenCV的图像格式是BGR,而模型可能需要RGB格式,或者根据训练时的预处理步骤进行调整。 后处理部分,需要解析模型的输出。YOLOv5的输出通常是多个尺度的检测结果,每个检测结果包含边界框坐标、置信度和类别概率。需要将这些结果转换为实际的框坐标,应用置信度阈值和NMS来过滤冗余的检测框。引用[3]中的C++代码可能有实现这些步骤的部分,可以借鉴。 关于Qt的部分,需要将OpenCV的Mat图像转换为Qt的QImage或QPixmap以在界面上显示。此外,可能需要在界面上添加文件选择按钮、推理按钮和结果显示区域。使用Qt的信号和槽机制来处理用户交互和异步操作,比如加载模型和运行推理。 在集成ONNXRuntime时,需要确保项目配置正确,包含ONNXRuntime的头文件和链接库。引用[3]提到使用CMake进行构建,所以在Qt项目中可能需要调整.pro文件或CMakeLists.txt来包含必要的依赖。 另外,用户提到多线程和性能的问题,引用[2]指出Python的GIL可能导致性能问题,而C++可能有更好的多线程支持。在Qt中,可以使用QThread或QtConcurrent来并行处理推理任务,避免阻塞主界面线程。不过,用户的问题主要是关于实现流程,性能优化可能属于后续步骤。 现在需要确保示例代码的结构清晰,涵盖模型加载、预处理、推理、后处理和显示。可能还需要处理不同的输入源,如图像文件或摄像头输入。引用[3]中的命令行参数处理可以参考,但在Qt中可能需要通过界面元素获取这些参数,比如模型路径和输入文件。 最后,要检查代码的正确性,比如内存管理是否正确,避免内存泄漏。例如,ONNXRuntime的Session需要正确释放,OpenCV和Qt对象的生命周期管理也需注意。 可能遇到的问题包括模型输入输出的正确解析、图像预处理与模型要求的匹配、Qt界面与后端逻辑的交互等。需要确保每一步都正确衔接,并进行测试验证。</think>以下为使用Qt结合ONNXRuntime实现YOLOv5s模型的完整推理流程指南: ### 一、环境准备 1. 安装Qt 5.15+(需包含C++17支持) 2. 配置ONNXRuntime 1.12+ C++库 3. 准备YOLOv5s.onnx模型文件(使用`export.py`导出) 4. 安装OpenCV 4.5+用于图像处理 ### 二、Qt项目配置 在.pro文件中添加: ```qmake INCLUDEPATH += /path/to/onnxruntime/include LIBS += -L/path/to/onnxruntime/lib -lonnxruntime ``` ### 三、核心代码实现 #### 1. 模型加载模块 ```cpp #include <onnxruntime_cxx_api.h> Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "YOLOv5"); Ort::SessionOptions session_options; Ort::Session session{env, "yolov5s.onnx", session_options}; // 获取输入输出信息 auto input_info = session.GetInputTypeInfo(0); auto input_shape = input_info.GetTensorTypeAndShapeInfo().GetShape(); // [1,3,640,640] ``` #### 2. 图像预处理 ```cpp cv::Mat preprocess(cv::Mat img) { cv::cvtColor(img, img, cv::COLOR_BGR2RGB); cv::resize(img, img, cv::Size(640, 640)); img.convertTo(img, CV_32F, 1.0/255.0); return img; // 输出形状HWC [640,640,3] } ``` #### 3. 推理执行 ```cpp std::vector<Ort::Value> runInference(cv::Mat& img) { // 转换为CHW格式 cv::Mat channels[3]; cv::split(img, channels); std::vector<float> input_tensor; for (auto& c : channels) { input_tensor.insert(input_tensor.end(), c.ptr<float>(), c.ptr<float>()+c.total()); } Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); std::vector<const char*> input_names = {"images"}; std::vector<const char*> output_names = {"output"}; std::vector<Ort::Value> inputs; inputs.emplace_back(Ort::Value::CreateTensor<float>( memory_info, input_tensor.data(), input_tensor.size(), input_shape.data(), input_shape.size() )); return session.Run(Ort::RunOptions{nullptr}, input_names.data(), inputs.data(), inputs.size(), output_names.data(), output_names.size()); } ``` #### 4. 后处理(含NMS) ```cpp struct Detection { cv::Rect box; float conf; int class_id; }; std::vector<Detection> postprocess(std::vector<Ort::Value>& outputs) { float* data = outputs[0].GetTensorMutableData<float>(); auto shape = outputs[0].GetTensorTypeAndShapeInfo().GetShape(); // [1,25200,85] std::vector<Detection> results; for (int i = 0; i < shape[1]; ++i) { float conf = data[4]; if (conf < 0.5) continue; // 置信度阈值 // 解析边界框 float* cls_scores = data + 5; int class_id = std::max_element(cls_scores, cls_scores+80) - cls_scores; float score = cls_scores[class_id] * conf; if (score > 0.5) { Detection det; det.conf = score; det.class_id = class_id; // 转换坐标到原图尺寸 float xc = data[0], yc = data[1], w = data[2], h = data[3]; det.box = cv::Rect(xc-w/2, yc-h/2, w, h); results.push_back(det); } data += shape[2]; // 移动到下一个预测结果 } // 执行NMS std::sort(results.begin(), results.end(), [](auto& a, auto& b){return a.conf > b.conf;}); std::vector<Detection> final_results; // ... NMS实现(IOU计算略) return final_results; } ``` #### 5. Qt界面集成 ```cpp // 在QMainWindow中显示结果 void MainWindow::showDetection(cv::Mat img, std::vector<Detection> dets) { QImage qimg(img.data, img.cols, img.rows, img.step, QImage::Format_RGB888); QPixmap pix = QPixmap::fromImage(qimg); QPainter painter(&pix); painter.setPen(Qt::green); for (auto& det : dets) { painter.drawRect(det.box.x, det.box.y, det.box.width, det.box.height); } ui->label_result->setPixmap(pix.scaled(ui->label_result->size(), Qt::KeepAspectRatio)); } ``` ### 四、完整流程调用 ```cpp void MainWindow::on_btnInfer_clicked() { cv::Mat img = cv::imread("input.jpg"); cv::Mat processed = preprocess(img); auto outputs = runInference(processed); auto detections = postprocess(outputs); showDetection(img, detections); // 注意显示原图尺寸 } ``` ### 五、性能优化建议 1. 使用异步推理:通过`QtConcurrent::run`分离界面线程与推理线程[^2] 2. GPU加速:初始化会话时添加 ```cpp Ort::SessionOptions session_options; OrtCUDAProviderOptions cuda_options; session_options.AppendExecutionProvider_CUDA(cuda_options); ``` 3. 多Batch推理:合并多个请求提升NPU利用率[^2]
评论 17
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TheMatrixs

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

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

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

打赏作者

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

抵扣说明:

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

余额充值