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

news2025/1/31 2:59:33

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

▌前言

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

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

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

.pt 转换为 .onnx

使用下列脚本 (将脚本放到 YOLOv5 lite 根目录中) 将 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 directoryifstr(ROOT) notin sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATHimport 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


classDetectV5(nn.Module):
    # YOLOv5 Detect head for detection models
    dynamic = False# force grid reconstruction
    export = True# export modedef__init__(self, old_detect):  # detection layersuper().__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.stride = old_detect.stride
        ifhasattr(old_detect, "inplace"):
            self.inplace = old_detect.inplace

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

    defforward(self, x):
        for i inrange(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 *= 2iflen(opt.img_size) == 1else1# 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 ifisinstance(labels, list) elselist(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():
        ifisinstance(m, Conv):  # assign export-friendly activations
            m._non_persistent_buffers_set = set()  # torch 1.6.0 compatibilityifisinstance(m.act, nn.SiLU):
                m.act = SiLU()
            ifisinstance(m.act, nn.Hardswish):
                m.act = Hardswish()
        elifisinstance(m, nn.Upsample):
            m.recompute_scale_factor = None# torch 1.11.0 compatibility

    model.model[-1] = DetectV5(model.model[-1])

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

    y = model(img)  # dry runs# ONNX exporttry:
        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 inrange(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 modeltry:
            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] ifhasattr(model, "module") else model.model[-1]
    for i inrange(num_branches):
        sides.append(int(opt.img_size[0] // m.stride[i]))
        for j inrange(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 inenumerate(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(
            {
                "nn_config": {
                    "output_format": "detection",
                    "NN_family": "YOLO",
                    "input_size": f"{opt.img_size[0]}x{opt.img_size[1]}",
                    "NN_specific_metadata": {
                        "classes": model.nc,
                        "coordinates": 4,
                        "anchors": anchors,
                        "anchor_masks": masks,
                        "iou_threshold": 0.5,
                        "confidence_threshold": 0.5,
                    },
                },
                "mappings": {"labels": labels},
            },
            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 v5lite.onnx  --scale 255 --reverse_input_channel

openvino -> blob

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

blobconvert cli

blobconverter --onnx v5lite.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]

是从 v5Lite-e.yaml 中 获取在这里插入图片描述

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("v5lite.blob")
dim = model.networkInputs.get("images").dims
W, H = dim[:2]
labelMap = [
    # "class_1","class_2","...""class_%s"%i for i inrange(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(
    [
        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 pipelinewith 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/heightdefframeNorm(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)

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

    whileTrue:
        inRgb = qRgb.tryGet()
        inDet = qDet.tryGet()

        if inRgb isnotNone:
            frame = inRgb.getCvFrame()

        if inDet isnotNone:
            detections = inDet.detections


        if frame isnotNone:
            displayFrame("rgb", frame)

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

▌参考资料

https://docs.oakchina.cn/en/latest/
https://www.oakchina.cn/selection-guide/


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

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

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

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

相关文章

Hadoop 使用Linux操作系统与Java熟悉常用的HDFS操作

注意看评论区获取完整代码资料 目录 一、实验目的 二、实验平台 三、实验步骤 一、实验目的 理解HDFS在Hadoop体系结构中的角色&#xff1b;熟练使用HDFS操作常用的Shell命令&#xff1b;熟悉HDFS操作常用的Java API。 二、实验平台 操作系统&#xff1a;Linux&#xff0…

运筹系列68:TSP问题Held-Karp下界的julia实现

1. 介绍 Held-Karp下界基于1tree下界&#xff0c;但是增加了点权重&#xff0c;如下图 通过梯度下降的方法找到最优的π\piπ。 这里用到的1tree有下面几种&#xff1a; 全部点用来生成最小生成树&#xff0c;再加上所有叶子结点第二短的边中数值最大的那个任意选一个点&…

Java集合进阶(一)

文章目录一、Collection1. 概述2. 常用方法3. 集合遍历4. 案例二、List1. 概述2. 特有方法3. 并发修改异常4. 列表迭代器5. 增强 for 循环6. 数据结构6.1 栈和队列6.2 数组和链表7. List 子类特点7.1 LinkedList一、Collection 集合类的特点&#xff1a;提供一种存储空间可变的…

raspberry Pi 连接蓝牙(小爱同学)

参数valueraspberry pi MOdel4B&#xff0c;4Gbbluetooth MOdel小爱同学writeTime2023年 2月11日 下午13&#xff1a;14分raspberry System ModelLinux raspberrypi 5.15.61-v8 #1579 SMP PREEMPT Fri Aug 26 11:16:44 BST 2022 aarch64 GNU/Linux 连接蓝牙 请在小爱同学app上…

Linux 网络编程(UDP模型,libevent库使用)

1.ctags的使用安装命令&#xff1a;sudo apt install exuberant-ctags要使用ctags需要在当前目录生成 tags 文件&#xff0c;可以组织目录内所有.c间函数调用关系生成方法&#xff1a;1.在项目目录下输入命令&#xff1a;ctags ./* -R2.在任意一个文件内使用 ctrlp一些快捷命令…

python入门项目06:批量处理文件

文章目录前言一、理论知识1.1 OS模块1.2 XML的解析二、使用步骤1.引入库2.创建新文件夹3文件操作4 修改文件总结前言 本文要完成的是对于较多XML文档的自动修改&#xff0c;这部分往往在大量的图像标注的修改中会使用到&#xff0c;同时也不要局限于本文中所提到的使用场景。 …

springbboot随笔

无效的源发行版问题 改springboot版本 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http:…

高校竞赛信息管理系统

摘要随着当今社会的发展&#xff0c;时代的进步&#xff0c;各行各业也在发生着变化&#xff0c;比如高校竞赛信息管理这一方面&#xff0c;利用网络已经逐步进入人们的生活。传统的高校竞赛信息管理&#xff0c;都是学生去学校查看竞赛信息然后再进行报名&#xff0c;这种传统…

linux面试高级篇

题目目录1.虚拟机常用有几种网络模式&#xff1f;请简述其工作原理或你个人的理解&#xff1f;2. Dockerfile中最常见的指令是什么&#xff1f;3.docker网络模式有哪些&#xff1f;4.Kubernetes有哪些核心组件这些组件负责什么工作&#xff1f;5. Pod是什么&#xff1f;6.描述一…

H264编码原理

1.什么是音视频&#xff1f; 比如我们下载的mp4格式&#xff08;还有rmvb、avi&#xff09;的电影 2.什么是h264&#xff1f; 对摄像头采集的每一帧视频需要进行编码&#xff0c;由于视频中存在空间和时间的冗余&#xff0c; 需要用算法来去除这些冗余。H264是专门去除这些冗…

SQL基础语句小结

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; 目录 一、SQL概述 1.简介 2.格式语法 3.SQL分类 二、DDL操作数据库 1.创建数据库 2.查询与使用 3.删除数据库 三、DDL:操作表 (1)数据类型 (2)创建表 (3)查询当前数据库的表 (4)删除表 (5)修改表 四、DML…

小鹏汽车「错失」智能化好牌

本周&#xff0c;作为小鹏品牌销售主力的P7正式迎来改款发布。3月6日&#xff0c;小鹏P7i迎来首发&#xff0c;作为P7的改款车型&#xff0c;在整车电子架构、三电系统、智能化硬件配置上都进行了全面升级。 基于和G9相同的双Orin-X计算平台两个盲区激光雷达&#xff0c;P7i实现…

RocketMQ基础篇(一)

目录一、发送消息类型1、同步消息2、异步消息3、单向消息4、顺序消费5、延迟消费二、消费模式1、集群模式2、广播模式3、消费模式扩展4、如何配置三、其他用法1、事务消息2、过滤消息1&#xff09;Tag过滤2&#xff09;SQL方式过滤源码放到了GitHub仓库上&#xff0c;地址 http…

HyperLPR3车牌识别-Android-SDK光速部署与使用

简介HyperLPR在2023年初已经更新到了v3的版本&#xff0c;该版本与先前的版本一样都是用于识别中文车牌的开源图像算法项目&#xff0c;最新的版本的源码可从github中提取&#xff1a;https://github.com/szad670401/HyperLPRHyperLPR-Android-SDK for JitPackHyperLPR3的官方源…

Prim算法和Kruskal算法到底哪个好?

Prim和Kruskal有啥区别&#xff1f;到底哪个好&#xff1f; 今天做了一道最小生成树的题&#xff0c;发现了一点猫腻&#xff01; 题目在这里 &#xff1a; 《修路问题1》 文章目录Prim和Kruskal有啥区别&#xff1f;到底哪个好&#xff1f;先说结论PrimKruskal修路问题1——…

不好!有敌情,遭到XSS攻击【网络安全篇】

XSS&#xff1a;当一个目标的站点&#xff0c;被我们用户去访问&#xff0c;在渲染HTMl的过程中&#xff0c;出现了没有预期到的脚本指令&#xff0c;然后就会执行攻击者用各种方法注入并执行的恶意脚本&#xff0c;这个时候就会产生XSS。 涉及方&#xff1a; 用户&#xff0…

Linux端安装MySQL并实现远程连接Navicat

文章目录Linux端安装MySQL&#xff08;centos版本&#xff09;Linux端安装MySQL&#xff08;centos版本&#xff09; 1、先将MySQL需要的四个rpm安装包上传上去&#xff0c;这里可以使用Xftp软件或者是通过window端使用ftp文件传输方式上传到Linux端&#xff0c;这里选择Xftp来…

基于JavaWeb学生选课系统开发与设计(附源码资料)

文章目录1. 适用人群2. 你将收获3.项目简介4.技术实现5.运行部分截图5.1.管理员模块5.2.教师模块5.3.学生模块1. 适用人群 本课程主要是针对计算机专业相关正在做毕业设计或者是需要实战项目的Java开发学习者。 2. 你将收获 提供&#xff1a;项目源码、项目文档、数据库脚本…

远程办公18年,把一个开源工具变成了价值 75亿美元的跨国企业

把自己的兴趣做成了一份事业&#xff0c;把一个开源工具发展成为一家价值75亿美元的跨国企业&#xff0c;而且还是那种员工做梦都想进入的公司&#xff0c;真正实现了功成名就&#xff0c;这或许是所有程序员的梦想吧。 先来看看这家公司的福利&#xff1a; 员工拥有没有限制的…

git快速入门(1)

1 git的下载与安装1&#xff09;下载git安装包下载路径&#xff1a;https://git-scm.com/我的操作系统是window&#xff0c;64位的&#xff0c;我下载的Git-2.33.0-64-bit.exe&#xff0c;从官网下载或者从网址下载链接&#xff1a;链接地址&#xff1a;https://pan.baidu.com/…