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

运行环境:

  • 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()

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【面试题】JavaScript高级循环方法

给大家推荐一个实用面试题库 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;web前端面试题库 除了for循环♻️&#xff0c;for-of,for-each循环♻️也是一个不错的选择 先说for-of循环♻️ 认识for-of循环♻️…

【Flutter】graphic图表实现自定义tooltip

renderer graphic中tooltip的TooltipGuide类提供了renderer方法,接收三个参数Size类型,Offset类型,Map<int, Tuple>类型。可查到的文档是真的少,所以只能在源码中扒拉例子,做符合需求的修改。 官方github示例 官方示例 这个例子感觉像是tooltip和提供的那些属性的…

翻页电子书怎么制作?用简单的方法做出炫酷的效果!

现在很多公司都喜欢把一些内容做成电子书的形式&#xff0c;与传统的纸质文献相比呢&#xff0c;电子书具有存储量大、体积小、成本低、信息更新快、方便阅读等不可替代的优势&#xff0c;受到了越来越多人的喜爱。 如何制作翻页电子书呢&#xff1f;今天小编就专门给大家安利…

转录组学习第5弹-比对参考基因组

比对参考基因组 在构建文库的过程中需要将DNA片段化&#xff0c;因此测序得到的序列只是基因组的部分序列。为了确定测序reads在基因组上的位置&#xff0c;需要将reads比对回参考基因组上&#xff0c;这个步骤叫做比对&#xff0c;即文献中所提到的alignment或mapping。包括基…

自动化接口测试:Pytest让你轻松搞定!了解一般流程及方法

首先我们要明确&#xff0c;通常所接口测试其实就属于功能测试&#xff0c;主要校验接口是否实现预定的功能&#xff0c;虽然有些情况下可能还需要对接口进行性能测试、安全性测试。 在学习接口自动化测试之前&#xff0c;我们先来了解手工接口测试怎样进行。 URL组成 为了更…

智能AIGC写作系统ChatGPT系统源码+Midjourney绘画+支持GPT-4-Turbo模型+支持GPT-4图片对话

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

Python爬虫404错误:解决方案总结

在进行网络爬虫开发的过程中&#xff0c;经常会遇到HTTP 404错误&#xff0c;即“Not Found”错误。这种错误通常表示所请求的资源不存在。对于爬虫开发者来说&#xff0c;处理这类错误是至关重要的&#xff0c;因为它们可能会导致爬虫无法正常工作。本文将探讨Python爬虫遇到4…

事务的自动提交机制和隐式提交机制

自动提交机制就是一个sql语句完成默认提交一次&#xff0c;也就是说一个sql语句是原子性的。想关闭这种功能&#xff0c;两种方式一种写START TRANSACTION&#xff0c;另一种SET autocommit OFF 隐式提交机制&#xff0c;在START TRANSACTION后&#xff0c;会有一些情况导致语…

云表|低代码助力职场人,一招制敌解决办公难题

身在职场&#xff0c;我们时常会面临一系列令人头疼的难题&#xff1a; ● 突然被领导要求30分钟内汇总所有人的填报信息&#xff0c;看着面前格式五花八门的Excel表格&#xff0c;我们无所适从&#xff0c;不知从何下手。 ● 在这个数字化的时代&#xff0c;公司仍然沿用古老的…

鸿蒙应用开发-初见:入门知识、应用模型

基础知识 Stage模型应用程序包结构 开发并打包完成后的App的程序包结构如图 开发者通过DevEco Studio把应用程序编译为一个或者多个.hap后缀的文件&#xff0c;即HAP一个应用中的.hap文件合在一起称为一个Bundle&#xff0c;bundleName是应用的唯一标识 需要特别说明的是&…

【数据中台】开源项目(2)-Davinci可视应用平台

1 平台介绍 Davinci 是一个 DVaaS&#xff08;Data Visualization as a Service&#xff09;平台解决方案&#xff0c;面向业务人员/数据工程师/数据分析师/数据科学家&#xff0c;致力于提供一站式数据可视化解决方案。既可作为公有云/私有云独立部署使用&#xff0c;也可作为…

Docker配置Halo搭建个人博客-快速入门

Docker配置Halo搭建个人博客-快速入门 1 官方文档2 安装Halo2.1 创建Halo主目录2.2 远程下载配置文件2.3 编辑配置文件2.4 拉取最新镜像2.6 查看容器2.7 开放服务器的防火墙 3 运行3.1 运行项目3.2 停止项目 4 常见问题4.1 没有权限4.2 ommand netstart not found, did you mea…

5种方法,教你如何清理接口测试后的测试数据!

在接口测试之后&#xff0c;清理测试数据是一个很重要的步骤&#xff0c;以确保下一次测试的准确性和一致性。以下是一些常见的测试数据清理方法&#xff1a; 1. 手动清理&#xff1a; 这是最基本的方法&#xff0c;即手动删除或重置测试数据。您可以通过访问数据库、控制台或…

打破障碍:克服数字化应用挑战的策略

通过正确的方法&#xff0c;企业可以成功地克服复杂性&#xff0c;并从数字化中获益。 数字技术的出现彻底改变了我们的生活和工作方式。从智能手机到社交媒体&#xff0c;数字工具在我们的日常生活中无处不在。对于许多个人和组织而言&#xff0c;采用数字技术可能是一个重大…

Java HashMap

HashMap 是 Map 接口中基于哈希表的非同步实现, 自身也可以自动扩容。使用时可以通过 key 快速定位到对应的 value。key 和 value 同时可以都为 null。 1 HashMap 的结构定义 JDK1.8 对 HashMap 进行了比较大的优化, 底层实现由之前的 “数组 链表” 改为 “数组 链表 红黑…

怎么解决 申请获取你的手机号,但该功能使用次数已达当前小程序上限,暂时无法使用。

微信出新规了&#xff0c; 获取手机号数据需要收费&#xff0c;1分钱一条。 在以前的开发中&#xff0c;获取手机号是默认不需要收费的&#xff0c;现在收费等于微信现在作为运营商一样&#xff0c;验证一个手机短信&#xff0c;需要收费 几分钱。 如果你的程序遇到了问题&am…

面试题:说一下你对 OAuth2 协议原理的理解?

文章目录 OAuth2简介角色流程客服端注册Client Type四种授权模式授权码模式隐藏式密码式凭证式RefreshToken OAuth2简介 OAuth 是一个开放授权协议标准&#xff0c;允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息&#xff0c;而不需要将用户名和密码提供给第三…

Python爬虫之代理IP与访问控制

目录 前言 一、代理IP 1.1.使用代理IP的步骤 1.2.寻找可用的代理IP 1.3.设置代理IP 1.4.验证代理IP的可用性 二、访问控制 2.1.遵守Robots协议 2.2.设置访问时间间隔 2.3.多线程爬取 总结 前言 在进行Python爬虫过程中&#xff0c;代理IP与访问控制是我们经常需要处…

11.盛最多的水的容器

一、题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 题目难度&#xff1a;中等 示例&a…

为什么API管理工具对开发人员有益?

应用程序编程接口 &#xff08;API&#xff09; 用于在应用程序之间创建连接&#xff0c;以允许它们相互通信。这种连接是当今数字世界运作方式不可或缺的一部分。实际上&#xff0c;API 使企业能够集成系统&#xff0c;通过创新提供更好的服务和产品。 这就是为什么在 IT 内部…