YOLOv8 onnx 文件推理多线程加速视频流

news2024/11/9 9:32:21

运行环境:

  • MacOS:14.0
  • Python 3.9
  • Pytorch2.1
  • onnx 运行时

模型文件:

https://wwxd.lanzouu.com/iBqiA1g49pbc
密码:f40v

  • 下载 best.apk后将后缀名修改为 onnx 即可
  • 模型在英伟达 T4GPU 使用 coco128 训练了 200 轮
  • 如遇下载不了可私信获取

代码:

import copy
import time

import onnxruntime as rt
import numpy as np
import cv2
import concurrent.futures


# 前处理
def resize_image(image, size, letterbox_image):
    """
        对输入图像进行resize
    Args:
        size:目标尺寸
        letterbox_image: bool 是否进行letterbox变换
    Returns:指定尺寸的图像
    """
    ih, iw, _ = image.shape
    h, w = size
    # letterbox_image = False
    if letterbox_image:
        scale = min(w / iw, h / ih)
        nw = int(iw * scale)
        nh = int(ih * scale)
        image = cv2.resize(image, (nw, nh), interpolation=cv2.INTER_LINEAR)
        image_back = np.ones((h, w, 3), dtype=np.uint8) * 128
        image_back[(h - nh) // 2: (h - nh) // 2 + nh, (w - nw) // 2:(w - nw) // 2 + nw, :] = image
    else:
        image_back = image
    return image_back


def img2input(img):
    img = np.transpose(img, (2, 0, 1))
    img = img / 255
    return np.expand_dims(img, axis=0).astype(np.float32)


def std_output(pred):
    """
    将(1,84,8400)处理成(8400, 85)  85= box:4  conf:1 cls:80
    """
    pred = np.squeeze(pred)
    pred = np.transpose(pred, (1, 0))
    pred_class = pred[..., 4:]
    pred_conf = np.max(pred_class, axis=-1)
    pred = np.insert(pred, 4, pred_conf, axis=-1)
    return pred


def xywh2xyxy(*box):
    """
    将xywh转换为左上角点和左下角点
    Args:
        box:
    Returns: x1y1x2y2
    """
    ret = [box[0] - box[2] // 2, box[1] - box[3] // 2, \
           box[0] + box[2] // 2, box[1] + box[3] // 2]
    return ret


def get_inter(box1, box2):
    """
    计算相交部分面积
    Args:
        box1: 第一个框
        box2: 第二个狂
    Returns: 相交部分的面积
    """
    x1, y1, x2, y2 = xywh2xyxy(*box1)
    x3, y3, x4, y4 = xywh2xyxy(*box2)
    # 验证是否存在交集
    if x1 >= x4 or x2 <= x3:
        return 0
    if y1 >= y4 or y2 <= y3:
        return 0
    # 将x1,x2,x3,x4排序,因为已经验证了两个框相交,所以x3-x2就是交集的宽
    x_list = sorted([x1, x2, x3, x4])
    x_inter = x_list[2] - x_list[1]
    # 将y1,y2,y3,y4排序,因为已经验证了两个框相交,所以y3-y2就是交集的宽
    y_list = sorted([y1, y2, y3, y4])
    y_inter = y_list[2] - y_list[1]
    # 计算交集的面积
    inter = x_inter * y_inter
    return inter


def get_iou(box1, box2):
    """
    计算交并比: (A n B)/(A + B - A n B)
    Args:
        box1: 第一个框
        box2: 第二个框
    Returns:  # 返回交并比的值
    """
    box1_area = box1[2] * box1[3]  # 计算第一个框的面积
    box2_area = box2[2] * box2[3]  # 计算第二个框的面积
    inter_area = get_inter(box1, box2)
    union = box1_area + box2_area - inter_area  # (A n B)/(A + B - A n B)
    iou = inter_area / union
    return iou


def nms(pred, conf_thres, iou_thres):
    """
    非极大值抑制nms
    Args:
        pred: 模型输出特征图
        conf_thres: 置信度阈值
        iou_thres: iou阈值
    Returns: 输出后的结果
    """
    box = pred[pred[..., 4] > conf_thres]  # 置信度筛选
    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))  # 记录图像内共出现几种物体
    output_box = []
    # 每个预测类别分开考虑
    for i in range(len(total_cls)):
        clss = total_cls[i]
        cls_box = []
        temp = box[:, :6]
        for j in range(len(cls)):
            # 记录[x,y,w,h,conf(最大类别概率),class]值
            if cls[j] == clss:
                temp[j][5] = clss
                cls_box.append(temp[j][:6])
        #  cls_box 里面是[x,y,w,h,conf(最大类别概率),class]
        cls_box = np.array(cls_box)
        sort_cls_box = sorted(cls_box, key=lambda x: -x[4])  # 将cls_box按置信度从大到小排序
        # box_conf_sort = np.argsort(-box_conf)
        # 得到置信度最大的预测框
        max_conf_box = sort_cls_box[0]
        output_box.append(max_conf_box)
        sort_cls_box = np.delete(sort_cls_box, 0, 0)
        # 对除max_conf_box外其他的框进行非极大值抑制
        while len(sort_cls_box) > 0:
            # 得到当前最大的框
            max_conf_box = output_box[-1]
            del_index = []
            for j in range(len(sort_cls_box)):
                current_box = sort_cls_box[j]
                iou = get_iou(max_conf_box, current_box)
                if iou > iou_thres:
                    # 筛选出与当前最大框Iou大于阈值的框的索引
                    del_index.append(j)
            # 删除这些索引
            sort_cls_box = np.delete(sort_cls_box, del_index, 0)
            if len(sort_cls_box) > 0:
                # 我认为这里需要将clas_box先按置信度排序, 才能每次取第一个
                output_box.append(sort_cls_box[0])
                sort_cls_box = np.delete(sort_cls_box, 0, 0)
    return output_box


def cod_trf(result, pre, after):
    """
    因为预测框是在经过letterbox后的图像上做预测所以需要将预测框的坐标映射回原图像上
    Args:
        result:  [x,y,w,h,conf(最大类别概率),class]
        pre:    原尺寸图像
        after:  经过letterbox处理后的图像
    Returns: 坐标变换后的结果,
    """
    res = np.array(result)
    x, y, w, h, conf, cls = res.transpose((1, 0))
    x1, y1, x2, y2 = xywh2xyxy(x, y, w, h)  # 左上角点和右下角的点
    h_pre, w_pre, _ = pre.shape
    h_after, w_after, _ = after.shape
    scale = max(w_pre / w_after, h_pre / h_after)  # 缩放比例
    h_pre, w_pre = h_pre / scale, w_pre / scale  # 计算原图在等比例缩放后的尺寸
    x_move, y_move = abs(w_pre - w_after) // 2, abs(h_pre - h_after) // 2  # 计算平移的量
    ret_x1, ret_x2 = (x1 - x_move) * scale, (x2 - x_move) * scale
    ret_y1, ret_y2 = (y1 - y_move) * scale, (y2 - y_move) * scale
    ret = np.array([ret_x1, ret_y1, ret_x2, ret_y2, conf, cls]).transpose((1, 0))
    return ret


def draw(res, image, cls):
    """
    将预测框绘制在image上
    Args:
        res: 预测框数据
        image: 原图
        cls: 类别列表,类似["apple", "banana", "people"]  可以自己设计或者通过数据集的yaml文件获取
    Returns:
    """
    for r in res:
        # 画框
        image = cv2.rectangle(image, (int(r[0]), int(r[1])), (int(r[2]), int(r[3])), (255, 0, 0), 1)
        # 表明类别
        text = "{}:{}".format(cls[int(r[5])], round(float(r[4]), 2))
        h, w = int(r[3]) - int(r[1]), int(r[2]) - int(r[0])  # 计算预测框的长宽
        font_size = min(h / 640, w / 640) * 3  # 计算字体大小(随框大小调整)
        image = cv2.putText(image, text, (max(10, int(r[0])), max(20, int(r[1]))), cv2.FONT_HERSHEY_COMPLEX,
                            max(font_size, 0.3), (0, 0, 255), 1)  # max()为了确保字体不过界
    return image

def display_fps(frame, start_time):
    global global_fps
    end_time = time.time()
    elapsed_time = end_time - start_time
    global_fps = 1 / elapsed_time

    # 在图像上显示帧率
    cv2.putText(frame, f"FPS: {global_fps:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)

global_fps = 0.0

if __name__ == '__main__':
    cap = cv2.VideoCapture(0)
    sess = rt.InferenceSession('./best.onnx')
    cv2.namedWindow('Video Stream', cv2.WINDOW_NORMAL)
    names = ['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']
    def inference_task(frame):
        class_list = list(names)
        std_h, std_w = 640, 640
        img_after = resize_image(frame, (std_w, std_h), True)
        data = img2input(img_after)
        input_name = sess.get_inputs()[0].name
        label_name = sess.get_outputs()[0].name
        pred = sess.run([label_name], {input_name: data})[0]
        pred = std_output(pred)
        result = nms(pred, 0.6, 0.4)
        result = cod_trf(result, frame, img_after)
        image = draw(result, frame, class_list)
        return image


    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

        while True:
            start_time = time.time()
            # 读取一帧
            ret, frame = cap.read()

            if not ret:
                print("无法读取帧")
                break

            # 提交任务并获取 Future 对象
            future = executor.submit(inference_task, frame)
            display_fps(frame, start_time)
            # 获取结果
            try:
                image = future.result()
                # 显示窗口
                cv2.imshow('Video Stream', image)
                cv2.waitKey(1)
            except Exception as e:
                cv2.imshow('Video Stream', frame)
                cv2.waitKey(1)

    # 释放资源
    cap.release()
    cv2.destroyAllWindows()

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

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

相关文章

Ubuntu18.04安装Ipopt-3.12.8流程

本文主要介绍在Ubuntu18.04中安装Ipopt库的流程&#xff0c;及过程报错的解决方法&#xff0c;已经有很多关于Ipopt安装的博客&#xff0c;但经过我的测试&#xff0c;很多都失效了&#xff0c;经过探索&#xff0c;找到可流畅的安装Ipopt的方法&#xff0c;总结成本篇博客。 …

《尚品甄选》:后台系统——权限管理之菜单管理,递归实现树形结构查询(debug一遍)

文章目录 一、表结构设计二、菜单管理接口2.1 查询菜单2.2 添加菜单2.3 修改菜单2.4 删除菜单 三、分配菜单3.1 查询菜单3.2 保存菜单(批量插入) 四、动态菜单五、解决bug 一、表结构设计 菜单管理就是对系统的首页中的左侧菜单进行维护。 一个用户可以担任多个角色&#xff…

解密人工智能:线性回归

导言 人工智能&#xff08;AI&#xff09;已经成为当今科技领域的热门话题&#xff0c;其应用领域涵盖了各个行业。线性回归作为人工智能中的一种关键统计学方法&#xff0c;被广泛应用于预测和决策支持系统中。本文将为您详细介绍线性回归在人工智能中的应用原理与方法&#x…

将用户的session改为分布式共享session

将用户的session改为分布式session 分布式session理解 使用分布式session的原因&#xff1a; 后台服务器是分布式的&#xff08;比如要负载均衡&#xff09;&#xff0c;在A服务器请求的的信息&#xff08;如用户登录信息&#xff09;存在A的session中&#xff0c;B服务器并不…

代码随想录算法训练营 ---第四十八天

第一题&#xff1a; 简介&#xff1a; 注&#xff1a;本题简介是我的思路&#xff0c;题解思路看下方。 动态规划五部曲&#xff1a; 1.确定dp数组的含义 //dp[i]表示 偷到第i家能偷到的最大金额 for(int i2;i<nums.size();i){if(i-3>0)dp[i] max(dp[i-2],dp[i-3])nu…

vue中的keep-alive详解与应用场景

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-keep-alive 目录 一、Keep-alive 是什么 二、使用场景 三、原理分析 四、案例实现 activa…

NB-IoT BC260Y Open CPU SDK④开发环境搭建

NB-IoT BC260Y Open CPU SDK④开发环境搭建 1、SDK包的介绍2、编程工具3、程序框架1、SDK包的介绍 (1)、SDK包的下载: 链接: (2)、文件目录介绍 文件名描述device启动文件、底层配置文档等doc存放 QuecOpen 项目相关的说明文档osFreeRTOS 相关代码out输出编译 App 和调…

06-学成在线添加课程,包含课程基本信息,营销信息,课程计划信息,师资信息

添加课程 界面原型 第一步: 用户进入课程查询列表,点击添加课程按钮,选择课程类型是直播还是录播,课程类型不同那么授课方式也不同 添加的课程和教学机构是一对一的关系 第二步: 用户选完课程形式后,点击下一步填写课程的基本信息和营销信息(两张表) 用户只要填完课程信息就…

SpringCloud--分布式事务实现

一、分布式事务 首先要明白事务是指数据库中的一组操作&#xff0c;这些操作要么全部成功执行&#xff0c;要么全部不执行&#xff0c;以保持数据的一致性和完整性。在本地事务中&#xff0c;也就是传统的单机事务&#xff0c;必须要满足原子性(Atomicity)、一致性(Consistenc…

错误:FinalShell连接CentOs连接失败

需要说明的是:这个错误不是首次连接发生的,而是多次使用后可能发生的错误 正文: 可能的原因是虚拟机的ip地址发生了变更,原因有以下几点: 最最可能的原因:1.DHCP分配变更&#xff1a; 如果虚拟机使用DHCP来获取IP地址&#xff0c;那么DHCP服务器可能会分配给虚拟机一个新的I…

java设计模式学习之【单例模式】

文章目录 引言单例模式简介定义与用途实现方式&#xff1a;饿汉式懒汉式 UML 使用场景优势与劣势单例模式在spring中的应用饿汉式实现懒汉式实现数据库连接示例代码地址 引言 单例模式是一种常用的设计模式&#xff0c;用于确保在一个程序中一个类只有一个实例&#xff0c;并且…

不小心删除了短信,如何在 Android 上恢复已删除的短信

不小心删除了文字消息在 Android 手机上使用可能会是一种令人痛苦的体验。这些消息可能包含有价值的信息、珍贵的回忆或重要的细节。幸运的是&#xff0c;您可以探索多种方法来恢复这些丢失的消息。在本文中&#xff0c;我们将深入研究可用于检索已删除短信的选项&#xff0c;并…

vue3中readonly和shallowReadonly

readonly: 深度只读数据 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。 只读代理是深层的&#xff1a;访问的任何嵌套 property 也是只读的。 shallowReadonly 浅只读数据 创建一个代理&#xff0c;使其自身的 property 为只读&#xff0c;但不执行…

WhatsApp API号解封教程(内含图片指引和申诉模板)

WhatsApp API 是专门为中大型企业设置的WhatsApp APP页面&#xff0c;API号并不像WhatsApp个人号和企业号一样可以直接从App Store 或Google Play 下载&#xff0c;而是需要对接官方来连接API。 虽然申请WhatsApp API号的程序和手续比较复杂&#xff0c;但是这个操作对于企业来…

算法通关村第二关—手写链表反转(青铜)

链表反转的三种方式 一、建立虚拟头结点辅助反转 为了方便反转&#xff0c;可以创建一个ans结点&#xff0c;让ans.next head,然后后面的结点一次插入到ans.next 在下图中&#xff0c;对&#xff08;1->2->3->4->5&#xff09;进行反转&#xff0c;可以创建ans&…

easyexcel指定sheet页动态给行列加背景色

easyexcel&#xff0c;有多个sheet页&#xff0c;某些sheet页的行、列动态需要加背景色 import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.m…

Spring Cloud,注册中心,配置中心,原理详解

文章目录 Spring Cloud&#xff0c;注册中心&#xff0c;配置中心&#xff0c;原理详解谈谈我个人对 spring Cloud 的理解 注册中心Eureka&#xff1a;服务搭建小结 Ribbo - 负载均衡1. 负载均衡流程2. 负载均衡策略 nacos注册中心1. 配置集群1. 创建 namespace2. 配置命名空间…

MATLAB实战 | 不同形式的三维曲面图

通常&#xff0c;MATLAB中绘制三维曲面图&#xff0c;先要生成网格数据&#xff0c;再调用mesh函数和surf函数绘制三维曲面。若曲面用含两个自变量的参数方程定义&#xff0c;则还可以调用fmesh函数和fsurf函数绘图。若曲面用隐函数定义&#xff0c;则可以调用fimplicit3函数绘…

RuoYi若依前后端分离框架二次开发基础项目

一键改标题&#xff1a;点击ruoyi-ui(ctrlshiftr)改完重启项目 连接本地数据库 全智能生成开发 新建maven项目 菜单创建---栏目创建 无权限删除时&#xff1a;去掉要删除的菜单权限 子菜单&#xff1a;新增 代码生成器cv进去 后端&#xff1a; 前端&#xff1a; 完成&#x…

人工智能与供应链行业融合:预测算法的通用化与实战化

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 让我们一起深入探索人工智能与供应链的融合&#xff0c;以及预测算法在实际应用中的价值&#xff01;&#x1f50d;&#x1f680; 文章目录 前言供应链预测算法的基本流程统计学习模型与机…