【2023new】OAK相机如何将Yolov5转换成blob格式?

news2024/10/2 16:20:15

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

▌前言

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

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

其实,之前也发过将Yolov5转换成blob的教程,不过那篇教程和本篇教程有所差别,以本篇教程为准!

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

.pt 转换为 .onnx

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

示例用法:

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

export_onnx.py :

# coding=utf-8
import argparse
import json
import subprocess
import sys
import time
import warnings
from pathlib import Path

warnings.filterwarnings("ignore")

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH

import torch
import torch.nn as nn

from models.common import Conv
from models.experimental import attempt_load
from models.yolo import Detect
from utils.activations import Hardswish, SiLU
from utils.general import LOGGER, check_img_size
from utils.torch_utils import select_device


class DetectV5(nn.Module):
    # YOLOv5 Detect head for detection models
    dynamic = False  # force grid reconstruction
    export = True  # export mode

    def __init__(self, old_detect):  # detection layer
        super().__init__()
        self.nc = old_detect.nc  # number of classes
        self.no = old_detect.no  # number of outputs per anchor
        self.nl = old_detect.nl  # number of detection layers
        self.na = old_detect.na
        self.anchors = old_detect.anchors
        self.grid = old_detect.grid  # [torch.zeros(1)] * self.nl
        self.anchor_grid = old_detect.anchor_grid  # anchor grid

        self.inplace = old_detect.inplace
        self.stride = old_detect.stride

        self.f = old_detect.f
        self.i = old_detect.i
        self.m = old_detect.m

        self._make_grid = old_detect._make_grid

    def forward(self, x):
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # conv
            x[i] = x[i].sigmoid()
        return x


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-w", "--weights", type=str, default="./yolov5n.pt", help="weights path"
    )
    parser.add_argument(
        "-imgsz",
        "--img-size",
        nargs="+",
        type=int,
        default=[640, 640],
        help="image size",
    )  # height, width
    parser.add_argument("-op", "--opset", type=int, default=12, help="opset version")

    opt = parser.parse_args()
    opt.img_size *= 2 if len(opt.img_size) == 1 else 1  # expand
    LOGGER.info(opt)

    t = time.time()

    # Load PyTorch model
    device = select_device("cpu")
    model = attempt_load(
        opt.weights, device=device, inplace=True, fuse=True
    )  # load FP32 model
    labels = model.names
    labels = labels if isinstance(labels, list) else list(labels.values())

    # Checks
    gs = int(max(model.stride))  # grid size (max stride)
    opt.img_size = [
        check_img_size(x, gs) for x in opt.img_size
    ]  # verify img_size are gs-multiples

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

    # Update model
    model.eval()
    for k, m in model.named_modules():
        if isinstance(m, Conv):  # assign export-friendly activations
            if isinstance(m.act, nn.SiLU):
                m.act = SiLU()
            if isinstance(m.act, nn.Hardswish):
                m.act = Hardswish()
    if isinstance(model.model[-1], (Detect)):
        model.model[-1] = DetectV5(model.model[-1])

    m = model.module.model[-1] if hasattr(model, "module") else model.model[-1]
    num_branches = len(m.anchor_grid)

    y = model(img)  # dry runs

    # ONNX export
    try:
        import onnx

        LOGGER.info("\nStarting ONNX export with onnx %s..." % onnx.__version__)
        export_file = Path(opt.weights).with_suffix(".onnx")  # filename
        output_list = ["output%s_yolov5" % (i + 1) for i in range(num_branches)]
        torch.onnx.export(
            model,
            img,
            export_file,
            verbose=False,
            opset_version=opt.opset,
            input_names=["images"],
            output_names=output_list,
        )

        # 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("Simplifier failure: %s" % e)

        onnx.save(onnx_model, export_file)
        LOGGER.info("ONNX export success, saved as %s" % export_file)

    except Exception as e:
        LOGGER.warning("ONNX export failure: %s" % e)

    # generate anchors and sides
    anchors, sides = [], []
    m = model.module.model[-1] if hasattr(model, "module") else model.model[-1]
    for i in range(num_branches):
        sides.append(int(opt.img_size[0] // m.stride[i]))
        for j in range(m.anchor_grid[i].size()[1]):
            anchors.extend(m.anchor_grid[i][0, j, 0, 0].numpy())
    anchors = [float(x) for x in anchors]

    # generate masks
    masks = dict()
    # for i, num in enumerate(sides[::-1]):
    for i, num in enumerate(sides):
        masks[f"side{num}"] = list(range(i * 3, i * 3 + 3))

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

    # OpenVINO export
    LOGGER.info("\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("OpenVINO export success, saved as %s" % export_dir)
    except Exception as e:
        LOGGER.info("OpenVINO export failure: %s" % 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 yolov5n.onnx  --scale 255 --reverse_input_channel

openvino -> blob

<path>/compile_tool -m yolov5n.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(
            "yolov5n.onnx",	
            optimizer_params=[
                " --scale 255",
                "--reverse_input_channel",
            ],
            shaves=6,
        )

blobconvert cli

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

▌DepthAI 示例

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

使用 export_onnx.py 转换模型时会将相关参数写入 json 文件中,可根据 json 文件中数据添加下列参数

  • setNumClasses - YOLO 检测类别的数量

  • setIouThreshold - iou 阈值

  • setConfidenceThreshold - 置信度阈值,低于该阈值的对象将被过滤掉

  • setAnchors - yolo 锚点

  • setAnchorMasks - 锚掩码

Anchors:

训练模型时 cfg 中的 anchors,例如:

[10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326]

是从 yolov5n.yaml 中 获取

1

AnchorMasks :

如果使用不同的输入宽度,还应该重新设置sideX , sideY, sideZ, 其中 X = width/8 , Y = width/16Z = width/32 。如果您使用的是微型(tiny)模型,那么只要设置sideX , sideY,其中 X = width/16 , Y = width/32

import cv2
import depthai as dai
import numpy as np

model = dai.OpenVINO.Blob("yolov7.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)![image-20230223163536337](YOLOv7%20%E8%BD%AC%E6%8D%A2%E6%88%90%20blob%20%E6%A0%BC%E5%BC%8F.assets/image-20230223163536337.png)

# Network specific settings
detectionNetwork.setBlob(model)
detectionNetwork.setConfidenceThreshold(0.5)
detectionNetwork.setNumClasses(80)
detectionNetwork.setCoordinateSize(4)
detectionNetwork.setAnchors(
    [
        10,13, 16,30, 33,23,
        30,61, 62,45, 59,119, 
        116,90, 156,198, 373,326
    ]
)
detectionNetwork.setAnchorMasks(
    {
        "side%s"%(W/8): [0,1,2],
        "side%s"%(W/16): [3,4,5],
        "side%s"%(W/32): [6,7,8]
    }
)
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/2022/01/22/yolov5-blob/
https://docs.oakchina.cn/en/latest/
https://www.oakchina.cn/selection-guide/


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

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

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

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

相关文章

机械革命极光Pro电脑开启出现英文代码无法启动怎么办?

机械革命极光Pro电脑开启出现英文代码无法启动怎么办&#xff1f;有的小伙伴在使用机械革命极光Pro电脑的时候&#xff0c;正常开启电脑却无法进入到桌面中&#xff0c;而是显示一些英文错误提示。遇到这个问题是我们的系统故障了&#xff0c;可以通过U盘重装系统的方法来进行问…

logback 自定义日志输出到数据库

项目日志格式 Spring Boot 的默认日志输出类似于以下示例&#xff1a; 2021-12-14 22:40:14.159 INFO 20132 --- [ main] com.kuangstudy.SpringbootApplication : Started SpringbootApplication in 2.466 seconds (JVM running for 3.617)输出以下项目&…

SpringBoot 整合 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议

续上一篇 Linux 中使用 docker-compose 部署 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议 前提&#xff1a;此篇文章是对上一篇文章的实战和项目中相关配置的使用&#xff0c;我这边针对 MongoDB 原有基础上做了增强&#xff0c;简化了 MongoDB 配置 SSL / TLS 协议上的支…

Android Studio引入JNI第三方库

一、前言 JNI作为Java与native沟通的桥梁&#xff0c;项目开发中难免要使用到&#xff1b;而我们除了自己开发JNI之外&#xff0c;有时候还要在Android Studio引入别人开源的C第三方库&#xff0c;并在jni层实现第三方库的调用。 二、流程 1.导入头文件和实现文件 将第三方…

Linux内核进程地址空间与进程内存布局

一&#xff0c;进程空间分布概述 对于一个进程&#xff0c;其空间分布如下图所示&#xff1a; 程序段(Text):程序代码在内存中的映射&#xff0c;存放函数体的二进制代码。初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。未初始化过的数据(BSS):在程序运行初未…

九龙证券|券商春季策略扎堆来袭 风格切换成焦点

2月以来&#xff0c;国泰君安、中信建投、国金证券等10余家券商组织相继发布2023年春季战略。综合来看&#xff0c;组织对A股持达观预期&#xff0c;未来两三个月A股商场或迎来重要切换。风格上&#xff0c;“中心财物&#xff0c;生长接力”或许成为上半年装备主线&#xff0c…

java 系列之Mybatis

java 系列文章 文章目录java 系列文章前言一、Mybatis 入门1.1 认识 框架&#xff08;了解&#xff09;1.2 认识 ORM&#xff08;要知道&#xff09;1.3 认识 Mybatis&#xff08;要知道&#xff09;二、Mybatis 使用2.1 创建maven项目并导入依赖2.2 准备数据库&#xff0c;包和…

释放内存流程

你好&#xff0c;我是安然无虞。 thread cache回收内存 当从 thread cache 中申请的内存对象使用完毕需要还回来的时候, 只需要计算出该内存对象对应 thread cache 中的哪一个自由链表桶, 然后将该内存对象插入进去即可. 不过需要注意的是, 如果不断有内存对象释放回来, 那么…

Java实现根据拼音首字母的排序

1.项目 手机APP端要对企业列表按企业名称首字母(如果企业名是英文的就按)进行分类排序&#xff0c;效果如下&#xff1a; 2.实现过程 2.1 首先引入项目的pinyin4j-2.5.0.jar包。 这个jar的下载地址如下&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1hkP_gGAYcgzyK_D…

跨链桥:Web3黑客必争之地

跨链桥&#xff0c;区块链的基础设施之一&#xff0c;所实现的功能是允许用户将自己的资产从一条链转移至另外一条链上&#xff0c;是连接不同的区块链的关键桥梁&#xff0c;常使用中心化的方式进行实现。由于跨链桥自身往往存储有用户所质押的巨额资产&#xff0c;是Web3黑客…

KUKA KR C4机器人与S7-1200PLC进行PROFINET通信的具体方法和步骤

KUKA KR C4机器人与S7-1200PLC进行PROFINET通信的具体方法和步骤 首先,从KUKA机器人控制柜中将KOP备选软件包拷贝出来,然后在“WorkVisual Development Environment”安装KUKA备选软件包(版本非常重要,尽量从控制柜中拷贝), 也可以从以下链接中获取: KUKA机器人PROFINET…

php 任务调度

在日常开发中&#xff0c;我们总会遇到一些在某个指定的时刻去执行&#xff0c;或是每隔xx时间执行&#xff0c;或是需要一直在后台监听的任务执行。基于这个需求&#xff0c;对于php我找了一些办法来实现这些功能 1、依赖于laravel的任务调度。 每隔xx时间执行一次命令&#…

七、虚拟机栈

虚拟机栈出现的背景 1.由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的&#xff0c;不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 2.优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同…

XC7K70T-1FBG676I【FPGA】XC3S200-4FTG256C参数XC7K70T-2FBG676C

Kintex-7 FPGA为快速增长应用和无线通信提供最优性价比和低功耗。Kintex-7 FPGA允许设计人员构建卓越带宽和12位数字可编程模拟&#xff0c;同时满足成本和功耗要求。Kintex-7内置支持8通道PCI Express (Gen1/Gen2)&#xff0c;用于连接主机系统。7系列器件利用Xilinx统一架构保…

如何实现外网跨网远程控制内网计算机?快解析来解决

远程控制&#xff0c;是指管理人员在异地通过计算机网络异地拨号或双方都接入Internet等手段&#xff0c;连通需被控制的计算机&#xff0c;将被控计算机的桌面环境显示到自己的计算机上&#xff0c;通过本地计算机对远方计算机进行配置、软件安装程序、修改等工作。通俗来讲&a…

超图软件许可过期后的处理

超图软件许可过期&#xff0c;博主犯了一个很低级的错误&#xff0c;特此发帖记录一下。 按照官网的介绍&#xff0c;直接申请新的许可&#xff0c;配置本地许可很简单的。 1.打开桌面软件会弹出以下的框。 2.配置本地许可对话框 3.切换到激活更新页面 4.选择激活文件即可 以…

内容团队如何快速出稿

对于内容团队而言&#xff0c;每个内容选题就相当于一个小项目&#xff0c;它们并非简单的线性工作流&#xff0c;相反其复杂程度不亚于一个小型工厂。一个内容选题会涉及内容形式&#xff0c;选题类型等多个变量&#xff0c;这些变量因素组合起来就是十几种不同类型的工作流。…

我花6个月从0开始面上大厂自动化测试岗,拿个20K不过分吧?

我是着急忙慌的准备简历——3年软件测试经验&#xff0c;可独立测试大型产品项目&#xff0c;熟悉项目测试流程...薪资要求&#xff1f; 3年测试经验起码能要个20K吧&#xff1f;” 我一个朋友跟我说&#xff1a; “我加班肝了一页半简历&#xff0c;投出去一周&#xff0c;…

03--框架基础

1、目录结构1&#xff09;创建项目使用测试号或小程序申请成功之后的appid即可&#xff0c;注意这里选择“不使用云服务”&#xff0c;选择javascript模板工具结构如图目录结构小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成&…

python自学之《21天学通Python》(13)——第16章 数据库编程

数据库指的是以一定方式存储在一起、能为多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。而我们平时所说的数据库实际上是包含了数据库管理系统&#xff08;DBMS&#xff09;的&#xff0c;数据库管理系统是为管理数据库而设计的软件系统&#xff0c;它一…