yolov5-7转onnx并推理(包括缩放图推理与原始图片推理)

news2024/9/24 5:43:32

一、yolov5转onnx 

 先安装onnx, onnxruntime-gpu, ( pip install  就可以)

1. 静态模型:

python export.py --weights yolov5s.pt --include onnx

2.动态模型:

python export.py --weights yolov5s.pt --include onnx --dynamic

3.这里谈谈静态与动态的区别:

     3.1没有 --dynamic(静态):
          导出的 ONNX 模型的输入形状是静态的,通常是基于训练时的输入尺寸(默认 640x640, batch size = 1)。这意味着输入图片必须符合该尺寸。
优点:静态输入模型在推理时可能会有更好的优化,推理速度更快。

     3.2 有 --dynamic(动态):
           导出的 ONNX 模型支持动态输入尺寸。输入图片可以是任意尺寸,不局限于训练时的尺寸(即我w, h, batch 都是可变的)。
优点:灵活性更高,适用于不同分辨率的图片。
缺点:推理速度可能稍微降低,因为引擎需要处理不同的输入尺寸,无法进行特定尺寸的优化。

4. 这里以动态为例得到onnx模型:

     先用:python export.py --weights yolov5s.pt --include onnx --dynamic   得到yolov5s.onnx

5.onnx推理代码:

import onnxruntime
import onnx
import cv2
import numpy as np
import torch


"# 注意v5-7 的训练使用letterbox预处理缩放图片的时候是没用自动化pad的, 即函数内的auto=False,但是在推理的时候却用了自动化pad, 既这时候函数内的auto=True, 因此onnx 推理也是用的auto=True"



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']  # coco80类别

class YOLOV5():
    def __init__(self, onnxpath):
        # ==============  指定先用gpu, 没有gpu 则使用cpu ====================
        # 创建 session_options
        self.session_options = onnxruntime.SessionOptions()
        self.onnx_session = onnxruntime.InferenceSession(onnxpath, self.session_options, providers=["CUDAExecutionProvider", "CPUExecutionProvider"])

        self.input_name = self.get_input_name()
        self.output_name = self.get_output_name()
    # 获取模型输入的名称,并创建字典。 在ONNX Runtime 中进行模型推理时,不能直接将原始图片数据作为输入传递给模型。因为模型期望的输入数据通常具有特定的格式、维度和数据类型,这些要求是由模型的训练和转换过程决定的。get_input_feed方法的作用正是为了准备符合模型要求的输入数据。
    def get_input_name(self):
        input_name=[]
        for node in self.onnx_session.get_inputs():
            input_name.append(node.name)
        return input_name

    # 得到onnx 模型输出节点
    def get_output_name(self):
        output_name=[]
        for node in self.onnx_session.get_outputs():
            output_name.append(node.name)
        return output_name
    #-------------------------------------------------------
	#   输入图像
	#-------------------------------------------------------
    def get_input_feed(self,img_tensor):
        input_feed = {}
        for name in self.input_name:
            input_feed[name] = img_tensor

        return input_feed
    #-------------------------------------------------------
	#   1.cv2读取图像并resize
	#	2.图像转BGR2RGB和HWC2CHW
	#	3.图像归一化
	#	4.图像增加维度
	#	5.onnx_session 推理
	#-------------------------------------------------------
    def inference(self, img_path):
        org_img = cv2.imread(img_path)   # hwc
        # 图片等比缩放
        pad_img, r, (dw, dh) = letterbox(org_img, (892, 892))
        img = pad_img[:, :, ::-1].transpose(2, 0, 1)  # BGR 2 RGB 和 HWC 2 CHW
        img = img.astype(dtype=np.float32)
        img /= 255.0
        # 添加批次维度
        img = np.expand_dims(img, axis=0)

        input_feed = self.get_input_feed(img)
        pred = self.onnx_session.run(None, input_feed)[0]
        return pred, org_img, pad_img


### 将中心点坐标转换为左上角右下角坐标
def xywh2xyxy(x):
    # [x, y, w, h] to [x1, y1, x2, y2]
    y = np.copy(x)
    y[:, 0] = x[:, 0] - x[:, 2] / 2
    y[:, 1] = x[:, 1] - x[:, 3] / 2
    y[:, 2] = x[:, 0] + x[:, 2] / 2
    y[:, 3] = x[:, 1] + x[:, 3] / 2
    return y


#dets:  array [x,6] 6个值分别为x1,y1,x2,y2,score,class
#thresh: 阈值
def nms(dets, thresh):                           # 非极大值抑制
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    #-------------------------------------------------------
	#   计算框的面积
    #	置信度从大到小排序
	#-------------------------------------------------------
    areas = (y2 - y1 + 1) * (x2 - x1 + 1)
    scores = dets[:, 4]
    keep = []
    index = scores.argsort()[::-1]

    while index.size > 0:
        i = index[0]
        keep.append(i)
		#-------------------------------------------------------
        #   计算相交面积
        #	1.相交
        #	2.不相交
        #-------------------------------------------------------
        x11 = np.maximum(x1[i], x1[index[1:]])
        y11 = np.maximum(y1[i], y1[index[1:]])
        x22 = np.minimum(x2[i], x2[index[1:]])
        y22 = np.minimum(y2[i], y2[index[1:]])

        w = np.maximum(0, x22 - x11 + 1)
        h = np.maximum(0, y22 - y11 + 1)

        overlaps = w * h
        #-------------------------------------------------------
        #   计算该框与其它框的IOU,去除掉重复的框,即IOU值大的框
        #	IOU小于thresh的框保留下来
        #-------------------------------------------------------
        ious = overlaps / (areas[i] + areas[index[1:]] - overlaps)
        idx = np.where(ious <= thresh)[0]
        index = index[idx + 1]
    return keep



### 根据置信度过滤无用框
def filter_box(org_box,conf_thres,iou_thres): #过滤掉无用的框
    #-------------------------------------------------------
	#   删除为1的维度
    #	删除置信度小于conf_thres的BOX
	#-------------------------------------------------------
    org_box=np.squeeze(org_box)
    conf = org_box[..., 4] > conf_thres
    box = org_box[conf == True]
    #-------------------------------------------------------
    #	通过argmax获取置信度最大的类别
	#-------------------------------------------------------
    cls_cinf = box[..., 5:]
    cls = []
    for i in range(len(cls_cinf)):
        cls.append(int(np.argmax(cls_cinf[i])))
    all_cls = list(set(cls))

    # -------------------------------------------------------
    #   分别对每个类别进行过滤
    #	1.将第6列元素替换为类别下标
    #	2.xywh2xyxy 坐标转换
    #	3.经过非极大抑制后输出的BOX下标
    #	4.利用下标取出非极大抑制后的BOX
    # ------------------------------------------------------
    output = []
    for i in range(len(all_cls)):
        curr_cls = all_cls[i]
        curr_cls_box = []
        curr_out_box = []
        for j in range(len(cls)):
            if cls[j] == curr_cls:
                box[j][5] = curr_cls
                curr_cls_box.append(box[j][:6])
        curr_cls_box = np.array(curr_cls_box)
        # curr_cls_box_old = np.copy(curr_cls_box)
        curr_cls_box = xywh2xyxy(curr_cls_box)
        curr_out_box = nms(curr_cls_box, iou_thres)
        for k in curr_out_box:
            output.append(curr_cls_box[k])
    output = np.array(output)
    return output

## 画图
def draw(image: object, box_data: object) -> object:
    #-------------------------------------------------------
    #	取整,方便画框
	#-------------------------------------------------------
    boxes=box_data[...,:4].astype(np.int32)
    scores=box_data[...,4]
    classes=box_data[...,5].astype(np.int32)

    for box, score, cl in zip(boxes, scores, classes):
        top, left, right, bottom = box
        print('class: {}, score: {}'.format(CLASSES[cl], score))
        print('box coordinate left,top,right,down: [{}, {}, {}, {}]'.format(top, left, right, bottom))
        print()

        cv2.rectangle(image, (top, left), (right, bottom), (255, 0, 0), 2)
        cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
                    (top, left ),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.6, (0, 0, 255), 2)


""""#图片缩放成640 x 640  ======> img:原始图片。   # new_shape:目标尺寸,默认为 640x640。      # color:边框填充的颜色,默认为灰色 (114, 114, 114)。
# auto:是否自动调整,使边缘填充的宽高是 32 的倍数,(这里选择False , 这样处理完的图片就是640 x 640, 否则最长边是640 但是最短边会自动缩放成32的倍数,这样最短边就不会填充成640 了)。
 # scaleFill:如果为 True,图像将直接拉伸到目标大小,而不是保持长宽比。       # scaleup:如果为 False,则只缩小图片,不放大图片,以避免损失质量。"""
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True):
    # Resize image to a 32-pixel-multiple rectangle
    shape = img.shape[:2]  #[h, w]

    # 确保 new_shape 变量始终是一个包含两个元素的元组 (width, height),即图片的目标尺寸
    # 如果 new_shape 被传入为 640(一个整数),这行代码会将 new_shape 变为 (640, 640),表示目标尺寸是 640x640 的正方形图片。如果 new_shape 原本就是一个元组,如 (640, 480),则不会执行这行代码,因为它已经是一个元组。
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # Scale ratio (new / old) # == 这两行代码的目的是计算图片的缩放比例 r,并确保图片只会缩小而不会放大
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    if not scaleup:  # if not scaleup: (如果scaleu不是True 即 如果scaleu是False),也就是在 scaleup 为 False 的情况下执行代码。
        r = min(r, 1.0)

    # 等比缩放图像 。 round() 将计算出的浮点数尺寸四舍五入为最接近的整数,确保尺寸是整数像素值。
    new_unpad = (int(round(shape[1] * r)), int(round(shape[0] * r)))
    # 计算定义的模型输入尺寸与图片等比例缩放后的图片尺寸之间的差值,—(即计算图像在宽度和高度方向上需要的填充量(即多余的部分),dw 表示在宽度上的填充,dh 表示在高度上的填充)目的是为了后面把不是正方形的等比例缩放图片变成正方形图片
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # width, height padding
    if auto:  # minimum rectangle
        dw, dh = np.mod(dw, 32), np.mod(dh, 32)  # pad to the nearest 32-pixel multiples
    elif scaleFill:  # stretch
        dw, dh = 0, 0
        new_unpad = new_shape
        r = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    # 判断原始图片尺寸是否等于等比例缩放尺寸
    if shape[::-1] != new_unpad:  # resize
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)

    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return img, r, (dw, dh)

# 映射信息到原图
def scale_boxes(pad_img_shape, boxes, ori_img_shape, ratio_pad=None):
    # Rescale boxes (xyxy) from img1_shape to img0_shape
    if ratio_pad is None:  # calculate from img0_shape
        gain = min(pad_img_shape[0] / ori_img_shape[0], pad_img_shape[1] / ori_img_shape[1])  # gain  = old / new
        pad = (pad_img_shape[1] - ori_img_shape[1] * gain) / 2, (pad_img_shape[0] - ori_img_shape[0] * gain) / 2  # wh padding
    else:
        gain = ratio_pad[0][0]
        pad = ratio_pad[1]

    boxes[:, [0, 2]] -= pad[0]  # x padding
    boxes[:, [1, 3]] -= pad[1]  # y padding
    boxes[:, :4] /= gain
    clip_boxes(boxes, ori_img_shape)
    return boxes

def clip_boxes(boxes, shape):
    boxes[:, [0, 2]] = boxes[:, [0, 2]].clip(0, shape[1])  # x1, x2
    boxes[:, [1, 3]] = boxes[:, [1, 3]].clip(0, shape[0])  # y1, y2


if __name__ == '__main__':
    onnx_path = 'yolov5m.onnx'
    model = YOLOV5(onnx_path)
    # 模型输出, 缩放后的原始图片
    output, or_img, pad_img = model.inference('1.jpg')

    # 根据置信度过滤无用框   , 这里的outbox是预处理后的图片上的坐标 ====> outbox [[609.1487     195.49065    699.1415     365.24677      0.9417041     0. ]
    outbox = filter_box(output, conf_thres=0.5, iou_thres=0.5)

    ###########  ====== =========在预处理图上画框并显示 ====================
    draw(pad_img, outbox)
    cv2.imshow('88', pad_img)
    cv2.waitKey(0)


    #########  ================ 把预处理图片上检测的框映射到原图上 并画框显示。执行这段代码后的outbox的坐标已经返回到原图上了 =====================
    # outbox[:, :4] = scale_boxes(pad_img.shape, outbox[:, :4], or_img.shape).round()     # pad_img.shape == (508, 892, 3),        or_img.shape == (1440, 2560, 3)
    # print("outbox", outbox)
    # draw(or_img, outbox)
    # cv2.imshow('88', or_img)
    # cv2.waitKey(0)



     

6.推理效果:

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

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

相关文章

在虚幻引擎中实时显示帧率

引擎自带了显示帧率的功能 但是只能在编辑器中显示 , 在游戏发布后就没有了 , 所以我们要自己做一个 创建一个控件蓝图 创建画布和文本 , 修改文本 文本绑定函数 , 点击创建绑定 添加一个名为 FPS 的变量 格式化文本 用大括号把变量包起来 {FPS Int} FPS 然后转到事件图表…

【html】基础(一)

本专栏内容为&#xff1a;前端专栏 记录学习前端&#xff0c;分为若干个子专栏&#xff0c;html js css vue等 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;js专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &am…

怎么录制游戏视频?精选5款游戏录屏软件

对于热爱游戏的你来说&#xff0c;记录游戏中的精彩瞬间并分享给朋友或粉丝&#xff0c;无疑是一种享受。然而&#xff0c;在众多录屏软件中&#xff0c;如何选择最适合你的那一款&#xff1f;今天&#xff0c;我们就为大家精选了五款游戏录屏软件&#xff0c;需要的朋友快来选…

【LeetCode:1014. 最佳观光组合 + 思维题】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

EfficientFormer实战:使用EfficientFormerV2实现图像分类任务(一)

摘要 EfficientFormerV2是一种通过重新思考ViT设计选择和引入细粒度联合搜索策略而开发出的新型移动视觉骨干网络。它结合了卷积和变换器的优势&#xff0c;通过一系列高效的设计改进和搜索方法&#xff0c;实现了在移动设备上既轻又快且保持高性能的目标。这一成果为在资源受…

【Python报错已解决】NameError: name ‘variable‘ is not defined

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

把任务管理器里面的vmware usb arbitrition停了,虚拟机一直识别不到手机设备了

在设备管理器--服务 里面找到VMware usb arbitrition服务&#xff0c;点击“启用”就好了。 参考大佬的文章&#xff1a; 吐血经验&#xff01;&#xff01;&#xff01;解决虚拟机连不上USB&#xff01;最全&#xff01;_为什么vmware虚拟机不能连接上usb设备-CSDN博客

C语言 | Leetcode C语言题解之第432题全O(1)的数据结构

题目&#xff1a; 题解&#xff1a; //哈希队列 //哈希检查是否存在 //双编标维护次数顺序 #define MAXSIZE 769/* 选取一个质数即可 */ typedef struct hashNode {char* string; //字符串int count; //次数struct doubleListNode* dList;str…

箭头与数字识别系统源码分享

箭头与数字识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

JavaScript 安装库npm报错

今天在编写JavaScript代码时&#xff0c;缺少了包express。 const express require(express); const app express();app.get(/, (req, res) > {res.send(Hello, world!); });app.listen(3000, () > {console.log(Server is running on port 3000); });npm install exp…

【C++指南】C++中nullptr的深入解析

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 引言 一、nullptr的引入背景 二、nullptr的特点 1.类型安全 2.明确的空指针表示 3.函数重载支…

从决策树到GBDT、随机森林

何为决策树 决策树&#xff08;Decision Tree&#xff09;&#xff0c;它是一种以树形数据结构来展示决策规则和分类结果的模型&#xff0c;作为一种归纳学习算法&#xff0c;其重点是将看似无序、杂乱的已知数据&#xff0c;通过某种技术手段将它们转化成可以预测未知数据的树…

3D 模型GLTF、GLB格式文件介绍使用

一、介绍 GLTF&#xff08;GL Transmission Format&#xff09;和 GLB&#xff08;GL Binary&#xff09;是用于在 Web 和各种应用程序中传输和加载 3D 场景和模型的开放标准格式。它们由 Khronos Group 开发&#xff0c;旨在提供一种高效、可扩展且易于使用的 3D 内容格式。以…

Java项目中Linux跑起来

要让一个web项目跑起来首先需要tomat和jdk的包&#xff08;Linux版本&#xff09; 之后可以使用Xftp工具将包传到linux中 可以新建一个java包专门放这些文件 之后将其这些包解压出来 tar -xvf jdk-8u141-linux-x64.tar.gz //换自己的包名使用命令配置自己的环境变量 vim /et…

计算机专业选题推荐-基于python的协同过滤酒店推荐系统

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、协同过滤酒店…

【C++ 11多线程加速计算实操教程】

【C 11多线程加速计算实操教程】 1. 了解线程的基本概念2. 创建线程2.1 启动线程的基本示例&#xff1a;2.2 运行结果 3. 线程加速计算3.1 演示如何使用多个线程计算数组的和&#xff1a;3.2 运行结果3.3 结果分析3.4 拓展学习 4. 互斥量&#xff08;Mutex&#xff09;4.1 演示…

Qt中多语言的操作(以QtCreator为例)

1、首先&#xff0c;我们在代码中与文本相关的且需要支持多语言的地方&#xff0c;用tr来包含多语言key&#xff08;多语言key是我们自己定义的&#xff09;&#xff0c;如下 //举例 QPushButton* btnnew QPushButton(this); btn->move(20,20); btn->resize(100,50); //…

vue.js 展示一个树形结构的数据视图,并禁用其中默认选中的节点

功能描述 展示树形结构&#xff1a; 使用 Element UI 的 <el-tree> 组件展示树形结构数据。数据由 content 数组提供&#xff0c;树形结构包含了嵌套的节点及其子节点。 默认选中节点&#xff1a; 使用 defaultCheckedKeys 属性指定默认选中的节点。这些节点在树形结构渲…

自动换行且带下划线的居中长标题的论文封面一种绘图实现

自动换行且带下划线的居中长标题的论文封面一种绘图实现 引言 在一些学位论文的封面上要求标题带有下划线&#xff0c;但长标题的情况下标题自动换行后下划线就会面临一些问题。 因此&#xff0c;往往需要一些特殊的处理。 在《如何制作自动换行且有定长下划线的论文封面模板…

决策树+随机森林模型实现足球大小球让球预测软件

文章目录 前言一、决策树是什么&#xff1f;二、数据收集与整理1.数据收集2.数据清洗3.特征选择 三、决策树构建3.1绘制训练数据图像3.2 训练决策树模型3.3 依据模型绘制决策树的决策边界3.4 树模型可视化 四、模型预测五、随机森林模型总结 前言 之前搞足球数据分析的时候&…