OAK相机如何将yoloV8模型转换成blob格式?

news2024/11/26 8:26:09

编辑:OAK中国
首发:oakchina.cn
喜欢的话,请多多👍⭐️✍
内容可能会不定期更新,官网内容都是最新的,请查看首发地址链接。

▌前言

Hello,大家好,这里是OAK中国,我是助手君。

最近咱社群里有几个朋友在将yolox转换成blob的过程有点不清楚,所以我就写了这篇博客。(请夸我贴心!咱的原则:合理要求,有求必应!)

1.其他Yolo转换及使用教程请参考
2.检测类的yolo模型建议使用在线转换(地址),如果在线转换不成功,你再根据本教程来做本地转换。

.pt 转换为 .onnx

使用下列脚本(将脚本放到 YOLOv8 根目录中)将 pytorch 模型转换为 onnx 模型,若已安装 openvino_dev,则可进一步转换为 OpenVINO 模型:

示例用法:

python export_onnx.py -w <path_to_model>.pt -imgsz 640 

export_onnx.py :

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import argparse
import json
import math
import subprocess
import sys
import time
import warnings
from pathlib import Path

import onnx
import torch
import torch.nn as nn

warnings.filterwarnings("ignore")

ROOT = Path.cwd()
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))

from ultralytics.nn.modules import Detect
from ultralytics.nn.tasks import attempt_load_weights
from ultralytics.yolo.utils import LOGGER


class DetectV8(nn.Module):
    """YOLOv8 Detect head for detection models"""

    dynamic = False  # force grid reconstruction
    export = False  # export mode
    shape = None
    anchors = torch.empty(0)  # init
    strides = torch.empty(0)  # init

    def __init__(self, old_detect):
        super().__init__()
        self.nc = old_detect.nc  # number of classes
        self.nl = old_detect.nl  # number of detection layers
        self.reg_max = (
            old_detect.reg_max
        )  # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)
        self.no = old_detect.no  # number of outputs per anchor
        self.stride = old_detect.stride  # strides computed during build

        self.cv2 = old_detect.cv2
        self.cv3 = old_detect.cv3
        self.dfl = old_detect.dfl
        self.f = old_detect.f
        self.i = old_detect.i

    def forward(self, x):
        shape = x[0].shape  # BCHW

        for i in range(self.nl):
            x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)

        box, cls = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2).split(
            (self.reg_max * 4, self.nc), 1
        )
        box = self.dfl(box)
        cls_output = cls.sigmoid()
        # Get the max
        conf, _ = cls_output.max(1, keepdim=True)
        # Concat
        y = torch.cat([box, conf, cls_output], axis=1)
        # Split to 3 channels
        outputs = []
        start, end = 0, 0
        for i, xi in enumerate(x):
            end += xi.shape[-2] * xi.shape[-1]
            outputs.append(
                y[:, :, start:end].view(xi.shape[0], -1, xi.shape[-2], xi.shape[-1])
            )
            start += xi.shape[-2] * xi.shape[-1]

        return outputs

    def bias_init(self):
        # Initialize Detect() biases, WARNING: requires stride availability
        m = self  # self.model[-1]  # Detect() module

        for a, b, s in zip(m.cv2, m.cv3, m.stride):  # from
            a[-1].bias.data[:] = 1.0  # box
            b[-1].bias.data[: m.nc] = math.log(
                5 / m.nc / (640 / s) ** 2
            )  # cls (.01 objects, 80 classes, 640 img)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser.add_argument(
        "-w", "--weights", type=Path, default="./yolov8s.pt", help="weights path"
    )
    parser.add_argument(
        "-imgsz",
        "--img-size",
        nargs="+",
        type=int,
        default=[640, 640],
        help="image size",
    )  # height, width
    parser.add_argument("--opset", type=int, default=12, help="opset version")
    args = parser.parse_args()
    args.img_size *= 2 if len(args.img_size) == 1 else 1  # expand
    LOGGER.info(args)
    t = time.time()

    # Check device
    device = torch.device("cpu")
    # Load PyTorch model
    model = attempt_load_weights(
        str(args.weights), device=device, inplace=True, fuse=True
    )  # load FP32 model

    labels = (
        model.module.names if hasattr(model, "module") else model.names
    )  # get class names

    labels = labels if isinstance(labels, list) else list(labels.values())

    # check num classes and labels
    assert model.nc == len(
        labels
    ), f"Model class count {model.nc} != len(names) {len(labels)}"

    # Replace with the custom Detection Head
    if isinstance(model.model[-1], (Detect)):
        model.model[-1] = DetectV8(model.model[-1])

    num_branches = model.model[-1].nl

    # Input
    img = torch.zeros(1, 3, *args.img_size).to(
        device
    )  # image size(1,3,320,192) iDetection

    # Update model
    model.eval()

    # ONNX export
    try:
        LOGGER.info("\nStarting to export ONNX...")
        output_list = [f"output{i+1}_yolov6r2" for i in range(num_branches)]
        export_file = args.weights.with_suffix(".onnx")  # filename
        torch.onnx.export(
            model,
            img,
            export_file,
            verbose=False,
            opset_version=args.opset,
            training=torch.onnx.TrainingMode.EVAL,
            do_constant_folding=True,
            input_names=["images"],
            output_names=output_list,
            dynamic_axes=None,
        )

        # Checks
        onnx_model = onnx.load(export_file)  # load onnx model
        onnx.checker.check_model(onnx_model)  # check onnx model
        try:
            import onnxsim

            LOGGER.info("\nStarting to simplify ONNX...")
            onnx_model, check = onnxsim.simplify(onnx_model)
            assert check, "assert check failed"
        except Exception as e:
            LOGGER.warning(f"Simplifier failure: {e}")
        LOGGER.info(f"ONNX export success, saved as {export_file}")
    except Exception as e:
        LOGGER.error(f"ONNX export failure: {e}")

    export_json = export_file.with_suffix(".json")
    export_json.with_suffix(".json").write_text(
        json.dumps(
            {
                "anchors": [],
                "anchor_masks": {},
                "coordinates": 4,
                "labels": labels,
                "num_classes": model.nc,
            },
            indent=4,
        )
    )
    LOGGER.info("Labels data export success, saved as %s" % export_json)

    # OpenVINO export
    print("\nStarting to export OpenVINO...")
    export_dir = Path(str(export_file).replace(".onnx", "_openvino"))
    OpenVINO_cmd = (
        "mo --input_model %s --output_dir %s --data_type FP16 --scale 255 --reverse_input_channel --output '%s' "
        % (export_file, export_dir, ",".join(output_list))
    )
    try:
        subprocess.check_output(OpenVINO_cmd, shell=True)
        LOGGER.info(f"OpenVINO export success, saved as {export_dir}")
    except Exception as e:
        LOGGER.warning(f"OpenVINO export failure: {e}")
        LOGGER.info("\nBy the way, you can try to export OpenVINO use:")
        LOGGER.info("\n%s" % OpenVINO_cmd)

    # OAK Blob export
    LOGGER.info("\nThen you can try to export blob use:")
    export_xml = export_dir / export_file.with_suffix(".xml")
    export_blob = export_dir / export_file.with_suffix(".blob")
    blob_cmd = (
        "compile_tool -m %s -ip U8 -d MYRIAD -VPU_NUMBER_OF_SHAVES 6 -VPU_NUMBER_OF_CMX_SLICES 6 -o %s"
        % (export_xml, export_blob)
    )
    LOGGER.info("\n%s" % blob_cmd)

    # Finish
    LOGGER.info("\nExport complete (%.2fs)" % (time.time() - t))

可以使用 Netron 查看模型结构:

在这里插入图片描述

▌转换

openvino 本地转换

onnx -> openvino

mo 是 openvino_dev 2022.1 中脚本,

安装命令为 pip install openvino-dev

mo --input_model yolov8n.onnx --scale 255 --reverse_input_channel

openvino -> blob

<path>/compile_tool -m yolov8n.xml \
-ip U8 -d MYRIAD \
-VPU_NUMBER_OF_SHAVES 6 \
-VPU_NUMBER_OF_CMX_SLICES 6

在线转换

blobconvert 网页 http://blobconverter.luxonis.com/

  • 进入网页,按下图指示操作:

在这里插入图片描述

  • 修改参数,转换模型:

在这里插入图片描述

  1. 选择 onnx 模型
  2. 修改 optimizer_params--data_type=FP16 --scale 255 --reverse_input_channel
  3. 修改 shaves6
  4. 转换

blobconverter python 代码

blobconverter.from_onnx(
            "yolov8n.onnx",	
            optimizer_params=[
                "--scale 255",
                "--reverse_input_channel",
            ],
            shaves=6,
        )

blobconvert cli

blobconverter --onnx yolov8n.onnx -sh 6 -o . --optimizer-params "scale=255 --reverse_input_channel"

▌DepthAI 示例

正确解码需要可配置的网络相关参数:

  • setNumClasses - YOLO 检测类别的数量
  • setIouThreshold - iou 阈值
  • setConfidenceThreshold - 置信度阈值,低于该阈值的对象将被过滤掉
import cv2
import depthai as dai
import numpy as np

model = dai.OpenVINO.Blob("yolov8n.blob")
dim = model.networkInputs.get("images").dims
W, H = dim[:2]
labelMap = [
    # "class_1","class_2","..."
    "class_%s"%i for i in range(80)
]

# Create pipeline
pipeline = dai.Pipeline()

# Define sources and outputs
camRgb = pipeline.create(dai.node.ColorCamera)
detectionNetwork = pipeline.create(dai.node.YoloDetectionNetwork)
xoutRgb = pipeline.create(dai.node.XLinkOut)
nnOut = pipeline.create(dai.node.XLinkOut)

xoutRgb.setStreamName("rgb")
nnOut.setStreamName("nn")

# Properties
camRgb.setPreviewSize(W, H)
camRgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
camRgb.setInterleaved(False)
camRgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
camRgb.setFps(40)

# Network specific settings
detectionNetwork.setBlob(model)
detectionNetwork.setConfidenceThreshold(0.5)
detectionNetwork.setNumClasses(80)
detectionNetwork.setCoordinateSize(4)
detectionNetwork.setAnchors([])
detectionNetwork.setAnchorMasks({})
detectionNetwork.setIouThreshold(0.5)

# Linking
camRgb.preview.link(detectionNetwork.input)
camRgb.preview.link(xoutRgb.input)
detectionNetwork.out.link(nnOut.input)

# Connect to device and start pipeline
with dai.Device(pipeline) as device:
    # Output queues will be used to get the rgb frames and nn data from the outputs defined above
    qRgb = device.getOutputQueue(name="rgb", maxSize=4, blocking=False)
    qDet = device.getOutputQueue(name="nn", maxSize=4, blocking=False)

    frame = None
    detections = []
    color2 = (255, 255, 255)

    # nn data, being the bounding box locations, are in <0..1> range - they need to be normalized with frame width/height
    def frameNorm(frame, bbox):
        normVals = np.full(len(bbox), frame.shape[0])
        normVals[::2] = frame.shape[1]
        return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int)

    def displayFrame(name, frame):
        color = (255, 0, 0)
        for detection in detections:
            bbox = frameNorm(frame, (detection.xmin, detection.ymin, detection.xmax, detection.ymax))
            cv2.putText(frame, labelMap[detection.label], (bbox[0] + 10, bbox[1] + 20), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)
            cv2.putText(frame, f"{int(detection.confidence * 100)}%", (bbox[0] + 10, bbox[1] + 40), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)
            cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)
        # Show the frame
        cv2.imshow(name, frame)

    while True:
        inRgb = qRgb.tryGet()
        inDet = qDet.tryGet()

        if inRgb is not None:
            frame = inRgb.getCvFrame()

        if inDet is not None:
            detections = inDet.detections


        if frame is not None:
            displayFrame("rgb", frame)

        if cv2.waitKey(1) == ord('q'):
            break

▌参考资料

https://www.oakchina.cn/2023/02/24/yolov8-blob/
https://docs.oakchina.cn/en/latest/
https://www.oakchina.cn/selection-guide/


OAK中国
| OpenCV AI Kit在中国区的官方代理商和技术服务商
| 追踪AI技术和产品新动态

戳「+关注」获取最新资讯↗↗

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

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

相关文章

Paddle配置

目录&#xff1a; 1.激活环境 2.版本选择 突发情况&#xff1a;ModuleNotFoundError: No module named paddle 检验是否安装成功 1.激活环境 Anaconda&#xff1a; conda remove -n paddle --all conda activate paddle 2.版本选择 打开链接&#xff1a;https://www.pa…

银行数字化转型导师坚鹏:BLM银行数字化转型战略课程大纲

BLM银行数字化转型战略——以BLM模型为核心&#xff0c;实现知行果合一 课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不知道如何系统地制定银行数字化转型战略&#xff1f; 不清楚其它银行数字化转型战略是如何制定的&#xff1f; 不知道其它银行数字化转型战略…

【离线数仓-8-数据仓库开发DWD层设计要点-工具域互动域流量域用户域相关事实表】

离线数仓-8-数据仓库开发DWD层-工具域&互动域&流量域&用户域相关事实表离线数仓-8-数据仓库开发DWD层设计要点-工具域&互动域&流量域&用户域相关事实表一、工具域相关事实表1.工具域优惠券领取事务事实表&使用&#xff08;下单&#xff09;事务事实…

基于nodejs+vue的果蔬商城在线销售系统vscode

水果蔬菜在线销售借助于当今盛行互联网技术&#xff0c;为消费者和供应商提供了一个更加方便的交易平台&#xff0c;使消费者足不出户就可以选购所需商品&#xff0c;省下许多时间和精力。商家通过该销售系统可以快速了解市场行情&#xff0c;更好地适应市场需求&#xff0c;扩…

【华为OD机试模拟题】用 C++ 实现 - 找到它(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明找到它题目输入输出示例一输入输出示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD …

工具篇(四)基于WPS的数据处理和分析

作者的话 大家好&#xff0c;我是一名练习时长两年半的数据分析师&#xff0c;今天想和大家分享一下我在使用WPS进行数据操作时的经验。 在日常工作中&#xff0c;数据处理是我们最常用到的功能之一。而在处理数据时&#xff0c;一个强大的工具是至关重要的。我个人非常喜欢使…

Clickhouse学习:MergeTree

MergeTree一、MergeTree逻辑存储结构二、MergeTree物理存储结构三、总结一、MergeTree逻辑存储结构 如上图所示,在排序键(CountrID、Date)上做索引,数据会按照这两个字段先后排序ClickHouse是稀疏索引,每隔8192行做一个索引,如(a,1),(a,2),比如想查a,要读取[0,3)之间的内容,稀疏…

2023年三月份图形化四级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

网络原理之初识

目录 一. 网络互连 1. 局域网 2. 广域网 二. 网络通信基础 1. IP 地址 2. 端口号 3. 网络协议 4. 协议分层 5. TCP/IP 五层网络模型 &#xff08;简述&#xff09; 6. 网络数据传输的基本流程 一. 网络互连 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&am…

【华为OD机试模拟题】用 C++ 实现 - IPv4 地址转换成整数(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明IPv4 地址转换成整数题目输入输出示例一输入输出说明示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,

使用Phpstorm进行项目管理

对于项目管理我们还是使用传统的终端命令行&#xff08;命令行很重要是基础中的基础&#xff09;么 &#xff1f; 不现在我们要通过工具提高我们的效率&#xff0c;作为一名合格的程序猿下班提交代码是我们的基操&#xff01;&#xff01;&#xff01;&#xff01;但是经过一天…

ctf pwn基础-4

今天是学pwn的第四天&#xff0c;去接触了pwn的整数溢出。 目录 基础 实例讲解 实例讲解2 基础 关于整数溢出&#xff0c;这里以int为例&#xff0c;因为我php之前搞的比较多&#xff0c;以为这个int也是想php一样是64&#xff0c;最大值是9开头的那个&#xff0c;闹了不少笑…

关于程序员中年危机的一个真实案例

​ 关于中年危机&#xff0c;网上已经有了各种各样的解读。但是&#xff0c;这两天一个学员跟我简单几句聊天&#xff0c;却触发了对于中年危机的另一种思考。如果你曾经也有点迷茫&#xff0c;或许你可以稍微花几分钟看下这个故事。 一、无奈的故事 ​ 39岁还出来面试&#x…

论文阅读:NeRF Representing Scenes as Neural Radiance Fields for View Synthesis

论文阅读–NeRF Representing Scenes as Neural Radiance Fields for View Synthesis 这是 2020 ECCV 的一篇文章&#xff0c;记得好像还获得了最佳论文奖的提名&#xff0c;这篇文章相当于将自由视点生成这个方向开辟出了一个新的解决思路。 文章的作者们提出了一种可以对复…

数据结构与算法——1.数据结构概述

从这篇文章开始&#xff0c;我们来讲一下数据结构与算法的相关内容 1.数据结构概述 什么是数据结构&#xff1f; 官方解释&#xff1a; 数据结构是一门研究非数值计算的程序设计问题中的操作对象&#xff0c;以及他们之间的关系和操作等相关问题的学科。 大白话&#xff1…

nodejs+vue+elementui,毕业生导师双选系统 vscode双向选择

为了直观显示系统的功能&#xff0c;运用用例图这样的工具显示分析的结果。分析的导师功能如下。导师管理导师选择信息&#xff0c;管理项目&#xff0c;管理项目提交并对学员提交的项目进行指导。 为了直观显示系统的功能&#xff0c;运用用例图这样的工具显示分析的结果。分析…

XpdfViewer ActiveX 4.0.3 Retail

XpdfViewer 库/ActiveX 控件提供了一个用于 Windows 应用程序的 PDF 文件查看器组件。XpdfViewer 使任何 Windows 开发人员都可以将 PDF 查看功能添加到他们的应用程序中。它为开发人员提供了对 PDF 查看器的完全控制——XpdfViewer 适合您的GUI。 XpdfViewer 功能包括&#xf…

Elasticsearch:索引数据是如何完成的

在我在之前的文章 “Elasticsearch&#xff1a;彻底理解 Elasticsearch 数据操作” 文章中&#xff0c;我详细地描述了如何索引数据到 Elasticsearch 中。在今天的文章中&#xff0c;我想更进一步来描述这个流程。 Elasticsearch 是一个非常强大和灵活的分布式数据系统&#x…

layui框架学习(10:时间线)

时间线&#xff0c;英文timeline&#xff0c;也叫时光轴、时间轴&#xff0c;是指以时间为记录方式的一种网络布局形式&#xff0c;其形式之一为下图所示&#xff08;示例图来自参考文献5&#xff09;。   Layui官网教程中的更新日志页面也用了时间线样式&#xff0c;如下图…

【经典蓝牙】 蓝牙HFP层协议分析

HFP 概述 HFP概念介绍 HFP(Hands-Free Profile)&#xff0c; 是蓝牙免提协议&#xff0c; 可以让蓝牙设备对对端蓝牙设备的通话进行控制&#xff0c;例如蓝牙耳机控制手机通话的接听、 挂断、 拒接、 语音拨号等。HFP中蓝牙两端的数据交互是通过定义好的AT指令来通讯的。 &am…