YOLOv8-Openvino和ONNXRuntime推理【CPU】

news2024/12/25 1:30:41

1 环境:

CPU:i5-12500

2 安装Openvino和ONNXRuntime

2.1 Openvino简介

Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包,主要用于对深度推理做优化。

Openvino内部集成了Opencv、TensorFlow模块,除此之外它还具有强大的Plugin开发框架,允许开发者在Openvino之上对推理过程做优化。

Openvino整体框架为:Openvino前端→ Plugin中间层→ Backend后端
Openvino的优点在于它屏蔽了后端接口,提供了统一操作的前端API,开发者可以无需关心后端的实现,例如后端可以是TensorFlow、Keras、ARM-NN,通过Plugin提供给前端接口调用,也就意味着一套代码在Openvino之上可以运行在多个推理引擎之上,Openvino像是类似聚合一样的开发包。

2.2 ONNXRuntime简介

ONNXRuntime是微软推出的一款推理框架,用户可以非常便利的用其运行一个onnx模型。ONNXRuntime支持多种运行后端包括CPU,GPU,TensorRT,DML等。可以说ONNXRuntime是对ONNX模型最原生的支持。

虽然大家用ONNX时更多的是作为一个中间表示,从pytorch转到onnx后直接喂到TensorRT或MNN等各种后端框架,但这并不能否认ONNXRuntime是一款非常优秀的推理框架。而且由于其自身只包含推理功能(最新的ONNXRuntime甚至已经可以训练),通过阅读其源码可以解深度学习框架的一些核心功能原理(op注册,内存管理,运行逻辑等)
总体来看,整个ONNXRuntime的运行可以分为三个阶段,Session构造,模型加载与初始化和运行。和其他所有主流框架相同,ONNXRuntime最常用的语言是python,而实际负责执行框架运行的则是C++。

2.3 安装

pip install openvino -i  https://pypi.tuna.tsinghua.edu.cn/simple
pip install onnxruntime -i  https://pypi.tuna.tsinghua.edu.cn/simple

3 准备YOLOv8s.onnx文件

YOLOv8官网
.pt文件转.onnx文件示例代码【注意自己转需要安装YOLOv8的环境】:

from ultralytics import YOLO
model = YOLO("yolov8s.pt")  # load a pretrained model
path = model.export(format="onnx", dynamic=True)  # export the mode l to ONNX format

4 Openvino和ONNXRuntime推理脚本

4.1 预处理

注:其中pad部分去除能减少预处理时间,且推理精度几乎一致。

def preprocess(image, img_h, img_w):
    '''
    Yolo系列算法通用预处理
    '''
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    scale = max(image.shape[0] / img_h, image.shape[1] / img_w)
    image = cv2.resize(image, (int(image.shape[1] / scale), int(image.shape[0] / scale)))
   
    wpad = img_w - image.shape[1]
    hpad = img_h - image.shape[0]
    image_pad = np.ones((image.shape[0]+hpad, image.shape[1]+wpad, 3)) * 114.0
    image_pad[:image.shape[0], :image.shape[1], :] = image
    image_array = image_pad
    
    image_array = image_array / 255.0
    image_array = image_array.transpose((2, 0, 1))
    image_array = image_array.astype(np.float32)
    input_array = np.ascontiguousarray(np.expand_dims(image_array, 0))
    return input_array, scale, image.shape[0], image.shape[1]

4.2 后处理

注:尝试多种后处理写法,该种写法速度最快。

def postprocess(pred, conf_thres, iou_thres, img_w, img_h):
    """
    Args:
        pred: np.array([(x, y, w, h, cls1_conf, cls2_conf, cls3_conf, ...), ...]), shape=(-1, 4 + num_cls)
        conf_thres: 置信度阈值
        iou_thres: IOU阀值,若两个box的交并比大于该值,则置信度较小的box将会被抑制
        img_w: 原图w大小
        img_h: 原图h大小
        Returns:
        out: 经过NMS后的值,np.array([(x, y, w, h, conf, cls), ...]), shape=(-1, 4 + 1 + 1)
    """
    pred = np.squeeze(pred).transpose((1, 0))  # (1, 80+4, -1) -> (80+4, -1) -> (-1, 80+4)
    # 按置信度过滤
    conf = np.max(pred[..., 4:], axis=-1)
    mask = conf >= conf_thres

    # Where the score larger than score_threshold
    box = pred[mask][..., :4]
    confidences = conf[mask]
  	clsid = np.argmax(pred[mask][..., 4:], axis=1)  
    
    # 下面进行非极大抑制NMS处理
    # 对box进行转换,以及对不同类别分不同区间处理
    bounding_boxes = np.zeros_like(box)
    bounding_boxes[:, 0] = (box[:, 0] - box[:, 2] / 2) + clsid * img_w  # xmin + 每个类别分不同区间
    bounding_boxes[:, 1] = (box[:, 1] - box[:, 3] / 2) + clsid * img_h  # ymin + 每个类别分不同区间
    bounding_boxes[:, 2] = box[:, 2]  # w
    bounding_boxes[:, 3] = box[:, 3]  # h
    # xywh2xyxy
    bounding_boxes[:, 2] += bounding_boxes[:, 0]
    bounding_boxes[:, 3] += bounding_boxes[:, 1]
    if bounding_boxes.shape[0] != confidences.shape[0]:
        raise ValueError("Bounding box 与 Confidence 的数量不一致")
    if bounding_boxes.shape[0] == 0:
        return []
    bounding_boxes, confidences = bounding_boxes.astype(np.float32), np.array(confidences)
    x1, y1, x2, y2 = bounding_boxes[:, 0], bounding_boxes[:, 1], bounding_boxes[:, 2], bounding_boxes[:, 3]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    idxs = np.argsort(confidences)

    pick = []
   	while len(idxs) > 0:
        # 因为idxs是从小到大排列的,last_idx相当于idxs最后一个位置的索引
        last_idx = len(idxs) - 1
        # 取出最大值在数组上的索引
        max_value_idx = idxs[last_idx]
        # 将这个添加到相应索引上
      	pick.append(max_value_idx)

        xx1 = np.maximum(x1[max_value_idx], x1[idxs[: last_idx]])
        yy1 = np.maximum(y1[max_value_idx], y1[idxs[: last_idx]])
        xx2 = np.minimum(x2[max_value_idx], x2[idxs[: last_idx]])
        yy2 = np.minimum(y2[max_value_idx], y2[idxs[: last_idx]])
        w, h = np.maximum(0, xx2 - xx1 + 1), np.maximum(0, yy2 - yy1 + 1)
        iou = w * h / areas[idxs[: last_idx]]

        # 删除最大的value,并且删除iou > threshold的bounding boxes
        idxs = np.delete(idxs, np.concatenate(([last_idx], np.where(iou > iou_thres)[0])))
    out = np.concatenate([box[pick], confidences[pick].reshape(-1, 1), clsid[pick].reshape(-1, 1)], axis=1)
    return out

4.3 全部代码

import os
import time

# openvino速度比onnxruntime快一倍
from openvino.runtime import Core  # pip install openvino -i  https://pypi.tuna.tsinghua.edu.cn/simple
import onnxruntime as rt  # 使用onnxruntime推理用上,pip install onnxruntime
import numpy as np
import cv2

def preprocess(image, img_h, img_w):
    '''
    Yolo系列算法通用预处理
    '''
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    scale = max(image.shape[0] / img_h, image.shape[1] / img_w)
    image = cv2.resize(image, (int(image.shape[1] / scale), int(image.shape[0] / scale)))
   
    wpad = img_w - image.shape[1]
    hpad = img_h - image.shape[0]
    image_pad = np.ones((image.shape[0]+hpad, image.shape[1]+wpad, 3)) * 114.0
    image_pad[:image.shape[0], :image.shape[1], :] = image
    image_array = image_pad
    
    image_array = image_array / 255.0
    image_array = image_array.transpose((2, 0, 1))
    image_array = image_array.astype(np.float32)
    input_array = np.ascontiguousarray(np.expand_dims(image_array, 0))
    return input_array, scale, image.shape[0], image.shape[1]

def postprocess(pred, conf_thres, iou_thres, img_w, img_h):
    """
    Args:
        pred: np.array([(x, y, w, h, cls1_conf, cls2_conf, cls3_conf, ...), ...]), shape=(-1, 4 + num_cls)
        conf_thres: 置信度阈值
        iou_thres: IOU阀值,若两个box的交并比大于该值,则置信度较小的box将会被抑制
        img_w: 原图w大小
        img_h: 原图h大小
        Returns:
        out: 经过NMS后的值,np.array([(x, y, w, h, conf, cls), ...]), shape=(-1, 4 + 1 + 1)
    """
    pred = np.squeeze(pred).transpose((1, 0))  # (1, 80+4, -1) -> (80+4, -1) -> (-1, 80+4)
    # 按置信度过滤
    conf = np.max(pred[..., 4:], axis=-1)
    mask = conf >= conf_thres

    # Where the score larger than score_threshold
    box = pred[mask][..., :4]
    confidences = conf[mask]
  	clsid = np.argmax(pred[mask][..., 4:], axis=1)  
    
    # 下面进行非极大抑制NMS处理
    # 对box进行转换,以及对不同类别分不同区间处理
    bounding_boxes = np.zeros_like(box)
    bounding_boxes[:, 0] = (box[:, 0] - box[:, 2] / 2) + clsid * img_w  # xmin + 每个类别分不同区间
    bounding_boxes[:, 1] = (box[:, 1] - box[:, 3] / 2) + clsid * img_h  # ymin + 每个类别分不同区间
    bounding_boxes[:, 2] = box[:, 2]  # w
    bounding_boxes[:, 3] = box[:, 3]  # h
    # xywh2xyxy
    bounding_boxes[:, 2] += bounding_boxes[:, 0]
    bounding_boxes[:, 3] += bounding_boxes[:, 1]
    if bounding_boxes.shape[0] != confidences.shape[0]:
        raise ValueError("Bounding box 与 Confidence 的数量不一致")
    if bounding_boxes.shape[0] == 0:
        return []
    bounding_boxes, confidences = bounding_boxes.astype(np.float32), np.array(confidences)
    x1, y1, x2, y2 = bounding_boxes[:, 0], bounding_boxes[:, 1], bounding_boxes[:, 2], bounding_boxes[:, 3]
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    idxs = np.argsort(confidences)

    pick = []
   	while len(idxs) > 0:
        # 因为idxs是从小到大排列的,last_idx相当于idxs最后一个位置的索引
        last_idx = len(idxs) - 1
        # 取出最大值在数组上的索引
        max_value_idx = idxs[last_idx]
        # 将这个添加到相应索引上
      	pick.append(max_value_idx)

        xx1 = np.maximum(x1[max_value_idx], x1[idxs[: last_idx]])
        yy1 = np.maximum(y1[max_value_idx], y1[idxs[: last_idx]])
        xx2 = np.minimum(x2[max_value_idx], x2[idxs[: last_idx]])
        yy2 = np.minimum(y2[max_value_idx], y2[idxs[: last_idx]])
        w, h = np.maximum(0, xx2 - xx1 + 1), np.maximum(0, yy2 - yy1 + 1)
        iou = w * h / areas[idxs[: last_idx]]

        # 删除最大的value,并且删除iou > threshold的bounding boxes
        idxs = np.delete(idxs, np.concatenate(([last_idx], np.where(iou > iou_thres)[0])))
    out = np.concatenate([box[pick], confidences[pick].reshape(-1, 1), clsid[pick].reshape(-1, 1)], axis=1)
    return out

def draw(img, xscale, yscale, pred, color=(255, 0, 0), tmp=True):
    img_ = img.copy()
    if len(pred):
        for detect in pred:
            caption = str('{:.2f}_{}'.format(detect[4], int(detect[5])))
            detect = [int((detect[0] - detect[2] / 2) * xscale), int((detect[1] - detect[3] / 2) * yscale),
                      int((detect[0] + detect[2] / 2) * xscale), int((detect[1] + detect[3] / 2) * yscale)]
            img_ = cv2.rectangle(img, (detect[0], detect[1]), (detect[2], detect[3]), color, 2)

            # 是否显示置信度类别
            if tmp:
                cv2.putText(img, caption, (detect[0], detect[1] - 5), 0, 1, color, 2, 16)
            
    return img_

class OpenvinoInference(object):
    def __init__(self, onnx_path):
        self.onnx_path = onnx_path
        ie = Core()
        self.model_onnx = ie.read_model(model=self.onnx_path)
        self.compiled_model_onnx = ie.compile_model(model=self.model_onnx, device_name="CPU")
        self.output_layer_onnx = self.compiled_model_onnx.output(0)

    def predirts(self, datas):
        predict_data = self.compiled_model_onnx([datas])[self.output_layer_onnx]
        return predict_data

if __name__ == '__main__':
    
    height, width = 640, 640  # 修改1:图像resize大小
    conf, nms_iou = 0.15, 0.6  # 修改2:置信度阈值与nms的iou阈值
    openvino_tmp = True  # 修改3:是否进行openvino推理,False为onnxruntime推理

    onnx_path = 'D:\\C++\\yolov8s.onnx'  # 修改4:onnx文件路径
    input_path = 'D:\\C++\\bus.jpg'  # 修改5:原图路径
    output_path = 'D:\\C++\\out.jpg'  # 修改6:图像保存路径

    img = cv2.imread(input_path)
    
    if openvino_tmp:
        model = OpenvinoInference(onnx_path)
    else:
    	sess = rt.InferenceSession(onnx_path)

    t1 = time.time()
    data, scale, img_w, img_h = preprocess(img, height, width)  # resize_img
    print('预处理时间:{:.3f}s'.format(time.time() - t1))

	t2 = time.time()
    if openvino_tmp:
        pred = model.predirts(data)
    else:
        input_name = sess.get_inputs()[0].name
        label_name = sess.get_outputs()[0].name
        pred = sess.run([label_name], {input_name: data.astype(np.float32)})[0]
    print('推理时间:{:.3f}s'.format(time.time() - t2))

	t3 = time.time()
    result = postprocess(pred, conf, nms_iou, img_w, img_h)
    print('后处理时间:{:.3f}s'.format(time.time() - t3))

    ret_img = draw(img, scale, scale, result, color=(0, 255, 0), tmp=True)
    cv2.imwrite(output_path, ret_img)      

5 结果

在这里插入图片描述

具体时间消耗:

预处理时间:0.014s(预处理无Pad为0.007s)
推理时间:0.08s
后处理时间:0.001s
注:640×640下,Openvino和ONNXRuntime推理速度相差不大,1280×1280下,Openvino速度更快。

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

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

相关文章

ClickHouse 基础(一)

官网 以毫秒为单位查询数十亿行 ClickHouse是用于实时应用和分析的最快、资源效率最高的开源数据库。 安装ClickHouse 使用ClickHouse,你有三个选择: ClickHouse云:官方ClickHouse作为一项服务,-由ClickHouse的创建者构建,维护和支持快速安…

C语言中关于#include的一些小知识

写代码的过程中,因为手误,重复包含了头文件 可以看到没有报错 如果是你自己编写的头文件,那么如果没加唯一包含标识的话,那么编译器会编译报错的。如果是系统自带的头文件,由于其每个头文件都加了特殊标识&#xff0c…

【软考】系统集成项目管理工程师(十六)变更管理【1分】

一、 变更的概念 1、定义、原因、分类 2、变更流程 二、 变更的原则 1、变更管理原则、配置管理工具 2、变更管理流程 三、 变更的流程及角色职责 1、提出变更申请、变更影响分析 2、变更测试 1、有些变更很小,客户着急要,可以不用走变更程序直接修改…

Android widget基础指南

widget的概念最早是由一名叫Rose的苹果工程师提出,后来经过多方面机缘巧合的发展下,便有了今天Android平台上的小组件widget,一般APP开发可能应用场景较少,最常见的莫过于天气APP的widget。但对于从事IOT或车载方向的同学&#xf…

FL Studio2024年最新21.2破解中文版本下载地址

FL Studio 21的中文版本是一款非常受欢迎的音乐制作软件,它为用户提供了丰富的功能和工具,使他们能够轻松创作和编辑音乐。以下是一些关于FL Studio 21中文版本的主要特点和功能: FL Studio 21 Win-安装包下载如下: https://wm.makeding.co…

- 工程实践 - 《QPS百万级的有状态服务实践》03 - 消息队列

本文属于专栏《构建工业级QPS百万级服务》 继续上篇《QPS百万级的有状态服务实践》02 - 冷启动和热更新。我们的架构如图1。上一章在热更新部分,我们引入了消息队列。本章我们介绍下各个消息队列的优缺点,并选择其中一个说下核心概念和原理。 图1 目前市…

【Go语言】Go语言的数据类型

GO 语言的数据类型 Go 语言内置对以下这些基本数据类型的支持: 布尔类型:bool 整型:int8、byte、int16、int、uint、uintptr 等 浮点类型:float32、float64 复数类型:complex64、complex128 字符串:st…

嵌入式学习 Day21

一. 文件IO: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …

基于STM32F407的coreJSON使用教程

目录 概述 工程建立 代码集成 函数介绍 使用示例 概述 coreJSON是FreeRTOS中的一个组件库,支持key查找的解析器,他只是一个解析器,不能生成json数据。同时严格执行 ECMA-404 JSON 标准。该库用 C 语言编写,设计符合 ISO C90…

杨氏矩阵和杨辉三角

杨氏矩阵 有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。 要求:时间复杂度小于O(N); 分析 若要满足要求时间复杂度小于O(N),就不能每一行一个个…

机器学习基础(一)理解机器学习的本质

导读:在本文中,将深入探索机器学习的根本原理,包括基本概念、分类及如何通过构建预测模型来应用这些理论。 目录 机器学习 机器学习概念 相关概念 机器学习根本:模型 数据的语言:特征与标签 训练与测试&#xf…

elementui 中 el-date-picker 控制选择当前年之前或者之后的年份

文章目录 需求分析 需求 对 el-date-picker控件做出判断控制 分析 给 el-date-picker 组件添加 picker-options 属性&#xff0c;并绑定对应数据 pickerOptions html <el-form-item label"雨量年份&#xff1a;" prop"date"><el-date-picker …

正整数A+B(PTA团体天题练习题)细节题刨析

哎呀&#xff0c;又是看似简单的AB模型&#xff0c;这题确实也是AB&#xff0c;不过这个题让我debug1个多小时才找出来问题所在&#xff0c;服了&#xff0c;真是所谓细节决定成败&#xff0c;这题也挺值得记录下来的&#xff0c;话不多嗦&#xff0c;看题 题的目标很简单&…

[ansible] playbook运用

一、复习playbook剧本 --- - name: first play for install nginx #设置play的名称gather_facts: false #设置不收集facts信息hosts: webservers:dbservers #指定执行此play的远程主机组remote_user: root #指定执行此play的用…

css3的var()函数

css3的var()函数 变量要以两个连字符--(横杆)(减号)为开头 变量可以在:root{}中定义, :root可以在css中创建全局样式变量。通过 :root本身写的样式&#xff0c;相当于 html&#xff0c;但优先级比后者高。 在CSS3中&#xff0c;var()函数是一个用于插入CSS自定义属性&#xff…

如何创建WordPress付款表单(简单方法)

您是否正在寻找一种简单的方法来创建付款功能WordPress表单&#xff1f; 小企业主通常需要创建一种简单的方法来在其网站上接受付款&#xff0c;而无需设置复杂的购物车。简单的付款表格使您可以轻松接受自定义付款金额、设置定期付款并收集自定义详细信息。 在本文中&#x…

QT编写工具基本流程(自用)

以后有人让你写工具的时候&#xff0c;可以方便用这个模版及时提高工作效率&#xff0c;可以争取早点下班。包含库目录&#xff0c;头文件目录&#xff0c;输出目录以及翻译和部署&#xff0c;基本上都全了&#xff0c;也可以做收藏用用。 文章目录 1、创建项目Dialog Widget都…

Vue | (三)使用Vue脚手架(上) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;初始化脚手架&#x1f407;创建初体验&#x1f407;分析脚手架结构&#x1f407;关于render&#x1f407;查看默认配置 &#x1f4da;ref与props&#x1f407;ref属性&#x1f407;props配置项 &#x1f4da;混入&#x1f4da;插件&#x1f4da;scoped样…

element-plus日期选择器2次封装

预期效果 官网默认样式&#xff1a; 修改后的样式&#xff1a; 代码实现 DatePicker.vue <template><div class"date-picker-container"><el-date-picker v-model"date" change"handleChange" type"date" value-for…

TypeScript中的Omit、Pick、Partial、Required类型

首先有这么个人员接口 interface IInfo {name: string; // 姓名age: number; // 年龄height: string; // 身高phone: string; // 联系电话email: string; // 邮箱 } 1. Omit Omit类型可以从一个对象类型中 忽略某些属性 假设我们使用IInfo接口&#xff0c;但是不想要其中p…