yolov5 模型输出的格式解析

news2024/11/20 23:34:12

工作需要, 又需要对yolov5 输出的模型进行转onnx 再用c++进行后续处理。
两个问题。

  1. yolov5 的模型输出的是个啥啊?
  2. 转成onnx后输出的和yolov5输出的处理是否一样呢?

关于第一个问题,yolov5 的模型输出的是个啥啊?

以前只知道抄代码就行, 也不知道里面干了啥 , 输出的后处理也都是由现成的代码来实现。 我也懒得考虑内部的原理, 反正代码正常跑。系统正常运行就可以。
但是今天不行啦, 得自己解析输出。 被逼无奈之下, 只能仔细研究下yolov5的模型和其内部的神经网络结构。关于神经网络的结构, 主要是在文件 models\yolov5s.yaml 中定义的。具体的我在下面的文件中注释写进去了。

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 80  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args] 
  # 关键是这个说明, 这里得分成4个看,分别是
  # [from 数据从哪里来, number 有几个这样的层, module 层的名称, args参数] 
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   #下面这个配置的意思是,数据从-1层(上一层)来,创建3层类名叫C3的层, 参数是128
   [-1, 3, C3, [128]], 
   #下面这个配置的意思是,数据从-1层(上一层)来,创建1层类名叫Conv的层, 参数是256, 3, 2
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

这个配置文件的解析是在models\yolo.py 文件中 由 parse_model() 方法执行解析的。
下面贴一下这个函数的代码,如果没兴趣可以不看
从代码中可以看的出来,配置文件中的最后一列 arg参数, 并不是跟代码中class的参数一一对应的。
例如Conv参数在代码中需要好多个参数, 而配置文件中只配置了3个。(我非常反感这种配置来配置去,搞脑子的写法, 因为非常的不方便代码理解, 不如直接在代码中硬编码, 最好的代码是一看就懂。

def parse_model(d, ch):  # model_dict, input_channels(3)
    # Parse a YOLOv5 model.yaml dictionary
    LOGGER.info(f"\n{'':>3}{'from':>18}{'n':>3}{'params':>10}  {'module':<40}{'arguments':<30}")
    anchors, nc, gd, gw, act = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple'], d.get('activation')
    if act:
        Conv.default_act = eval(act)  # redefine default activation, i.e. Conv.default_act = nn.SiLU()
        LOGGER.info(f"{colorstr('activation:')} {act}")  # print
    na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors  # number of anchors
    no = na * (nc + 5)  # number of outputs = anchors * (classes + 5)

    layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
    for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):  # from, number, module, args
        m = eval(m) if isinstance(m, str) else m  # eval strings
        for j, a in enumerate(args):
            with contextlib.suppress(NameError):
                args[j] = eval(a) if isinstance(a, str) else a  # eval strings

        n = n_ = max(round(n * gd), 1) if n > 1 else n  # depth gain
        if m in {
                Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv,
                BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x}:
            c1, c2 = ch[f], args[0]
            if c2 != no:  # if not output
                c2 = make_divisible(c2 * gw, 8)

            args = [c1, c2, *args[1:]]
            if m in {BottleneckCSP, C3, C3TR, C3Ghost, C3x}:
                args.insert(2, n)  # number of repeats
                n = 1
        elif m is nn.BatchNorm2d:
            args = [ch[f]]
        elif m is Concat:
            c2 = sum(ch[x] for x in f)
        # TODO: channel, gw, gd
        elif m in {Detect, Segment}:
            args.append([ch[x] for x in f])
            if isinstance(args[1], int):  # number of anchors
                args[1] = [list(range(args[1] * 2))] * len(f)
            if m is Segment:
                args[3] = make_divisible(args[3] * gw, 8)
        elif m is Contract:
            c2 = ch[f] * args[0] ** 2
        elif m is Expand:
            c2 = ch[f] // args[0] ** 2
        else:
            c2 = ch[f]
        
        print("模型结构" + str(m) + str(args)+"\r\n")
        
        m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module
        t = str(m)[8:-2].replace('__main__.', '')  # module type
        np = sum(x.numel() for x in m_.parameters())  # number params
        m_.i, m_.f, m_.type, m_.np = i, f, t, np  # attach index, 'from' index, type, number params
        LOGGER.info(f'{i:>3}{str(f):>18}{n_:>3}{np:10.0f}  {t:<40}{str(args):<30}')  # print
        save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
        layers.append(m_)
        if i == 0:
            ch = []
        ch.append(c2)
    return nn.Sequential(*layers), sorted(save)

好了回归正题, 根据yolov5s.yaml配置文件来看, 模型的最后一层是Detect层
只要看懂了Detect的代码既能理解yolov5到底输出了啥。
为方波快速理解,我下面把Detect层的 class 源代码贴出来。
源代码在models\yolo.py 文件中 38 行


class Detect(nn.Module):
    # YOLOv5 Detect head for detection models
    stride = None  # strides computed during build
    dynamic = False  # force grid reconstruction
    export = False  # export mode

    def __init__(self, nc=80, anchors=(), ch=(), inplace=True):  # detection layer
        super().__init__()
        self.nc = nc  # number of classes
        self.no = nc + 5  # number of outputs per anchor
        self.nl = len(anchors)  # number of detection layers
        self.na = len(anchors[0]) // 2  # number of anchors
        self.grid = [torch.empty(0) for _ in range(self.nl)]  # init grid
        self.anchor_grid = [torch.empty(0) for _ in range(self.nl)]  # init anchor grid
        self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2))  # shape(nl,na,2)
        self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch)  # output conv
        self.inplace = inplace  # use inplace ops (e.g. slice assignment)

    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # conv
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

            if not self.training:  # inference
                if self.dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)

                if isinstance(self, Segment):  # (boxes + masks)
                    xy, wh, conf, mask = x[i].split((2, 2, self.nc + 1, self.no - self.nc - 5), 4)
                    xy = (xy.sigmoid() * 2 + self.grid[i]) * self.stride[i]  # xy
                    wh = (wh.sigmoid() * 2) ** 2 * self.anchor_grid[i]  # wh
                    y = torch.cat((xy, wh, conf.sigmoid(), mask), 4)
                else:  # Detect (boxes only)
                    xy, wh, conf = x[i].sigmoid().split((2, 2, self.nc + 1), 4)
                    xy = (xy * 2 + self.grid[i]) * self.stride[i]  # xy
                    wh = (wh * 2) ** 2 * self.anchor_grid[i]  # wh
                    # xy 是中心点
                    # wh 是宽高
                    # conf 是confidence 即可信度
                    y = torch.cat((xy, wh, conf), 4)  
                z.append(y.view(bs, self.na * nx * ny, self.no))

        return x if self.training else (torch.cat(z, 1),) if self.export else (torch.cat(z, 1), x)

    def _make_grid(self, nx=20, ny=20, i=0, torch_1_10=check_version(torch.__version__, '1.10.0')):
        d = self.anchors[i].device
        t = self.anchors[i].dtype
        shape = 1, self.na, ny, nx, 2  # grid shape
        y, x = torch.arange(ny, device=d, dtype=t), torch.arange(nx, device=d, dtype=t)
        yv, xv = torch.meshgrid(y, x, indexing='ij') if torch_1_10 else torch.meshgrid(y, x)  # torch>=0.7 compatibility
        grid = torch.stack((xv, yv), 2).expand(shape) - 0.5  # add grid offset, i.e. y = 2.0 * x - 0.5
        anchor_grid = (self.anchors[i] * self.stride[i]).view((1, self.na, 1, 1, 2)).expand(shape)
        return grid, anchor_grid

从代码

# xy 是中心点
# wh 是宽高
# conf 是confidence 即可信度
y = torch.cat((xy, wh, conf), 4)  

中可以看的出, 这里是输出的主要数据的格式, 分别是3个参数 xy 中心点, wh 宽高,conf 可信度
经过调试发现数据的格式如下。

在这里插入图片描述
关键要看shape的最后一位,在这一步的时候,不是最终输出的格式,这里只是把它们合并起来了。后面还有个y.view进行了重新改变维度, 让这个组合的矩阵变的更加的直观, 改变维度后, 它的shape变成了
在这里插入图片描述

实际内部存储仍然是

	 [
		xy1,wh1,conf1,
		xy2,wh2,conf2,
		xy3,wh3,conf3,
		....
	] 

这里再补充一下,实际内部格式是

	 [
		x1,y1,w1,h1,conf1,
		x2,y2,w2,h2,conf2,
		x3,y3,w3,h3,conf3,
		....
	] 

5位数,代表一个识别的框框box。
这里的xy,wh的单位,不是像素哦, 是归一化后的数值, 需要按比例转换成像素值。 然后画到图片上就行了
xy的坐标是框框中心点的坐标,而不是左上角的坐标哦。

知道了存储的数据格式,后续的处理,实际上就很简单了。
分别是去掉可信度比较低的框框, 叫做非极值抑制,也就是这个函数non_max_suppression(). 有的叫 nms
然后就是把可信度比较高的框框画到图片上。剩下的应该都能看的懂。

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

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

相关文章

【Rust】14. Rust 中的函数式语言功能:迭代器与闭包

14.1 闭包&#xff1a;捕获环境的匿名函数 14.1.1 闭包会捕获其环境 14.1.2 闭包类型推断和注解 闭包并不总是要求像 fn 函数那样在参数和返回值上注明类型闭包通常很短&#xff0c;并只关联于小范围的上下文而非任意情境如果尝试对同一闭包使用不同类型则就会得到类型错误&am…

selenium自动化测试框架

一、Selenium自动化测试&#xff08;基于python&#xff09; 1、Selenium简介&#xff1a; 1.1 Selenium是一款主要用于Web应用程序自动化测试的工具集合。Selenium测试直接运行在浏览器中&#xff0c;本质是通过驱动浏览器&#xff0c;模拟浏览器的操作&#xff0c;比如跳转…

测试碎碎念(基础篇_2)

一、软件测试的基础概念1.1 需求在企业中&#xff0c;需求 主要分为 用户需求 和 软件需求~用户需求&#xff1a;可以简单理解为甲方提出的需求&#xff0c;如果没有甲方&#xff0c;那么就是终端用户使用产品时必须要完成的任务&#xff1b;用户需求 一般是比较简略的&#xf…

Flink官方例子解析:带窗口的WordCount

1. 简介 本篇介绍的是带窗口的WordCount&#xff0c;使用窗口函数countWindow。 countWindow是一种计数窗口&#xff0c;有固定窗口和滑动窗口两种用法。 1.1 固定窗口 countWindow(windowSize) , windowSize指的是窗口大小。 例如countWindow(5)&#xff0c; 说明一个窗口可…

零基础机器学习做游戏辅助第七课--模型的保存与加载

一、保存模型 当我们训练好模型后将它保存下来,这样下次使用时就可以直接加载模型进行工作了。 常见的保存模型有三种: 只保存权重文件:model.save_weights(num_weights) 当我们使用save_weights保存权重文件时,没有指定后缀名,则会保存三个文件在指定目录下

linux 下ARC的中断机制

linux 下ARC的中断机制 一、Idu 中断控制器初始化 Idu 是arc 处理器内部中断控制模块&#xff0c; 类似于arm 内部的gic 中断控制模块 首先&#xff0c;Idu中断控制器在初始化时, 会解析DTS信息中定义了几个idu控制器&#xff0c;每个Idu控制器注册一个struct irq_domain数据…

嵌入式Linux系统开发笔记(十三)

U-Boot烧写验证测试 正点原子专门编写了一个软件来将编译出来的.bin 文件烧写到 SD 卡中&#xff0c;这个软件叫做“imxdownload” 【1】将 imxdownload 拷贝到工程根目录下 【2】给予 imxdownload 可执行权限 我们直接将软件 imxdownload 从 Windows 下复制到 Ubuntu 中以…

【干货】Windows下cmd中cd命令的使用方法

【干货】Windows下cmd中cd命令的使用方法什么是cd命令cd命令的使用打开cmdcd命令的常用方法进入某个盘进入某个目录返回上一级目录返回至当前工作目录下的根目录参考什么是cd命令 此处介绍两个概念&#xff1a; cmd&#xff1a;命令提示符cd&#xff1a;全称change directory…

model.train()与model.val()

一、问题描述 需要将mmpose框架下训练的模型单独保存出来&#xff0c;做后续处理。用torch.save()直接保存模型mmpose_model.pt&#xff0c;然后重新搭建模型&#xff0c;把保存的模型参数加载进去&#xff0c;得到scratch_model.pt使用scratch_model.pt进行推理&#xff0c;与…

基于Jmeter的百万级tps性能测试实践

【CSDN 编者按】如何对系统的承载能力和响应时间做出准确的评估&#xff0c;为资源的合理配置及优化提供依据&#xff0c;性能测试就成了必不可少的测试手段&#xff0c;本文会给读者推荐一款业界占有率最高的一款性能测试工具——Jmeter。 本文作者朱凯是环信测试主管&#x…

TCP实现回显服务器及客户端

目录 前言&#xff1a; Socket API SeverSocket API TCP中的长短连接 TCP实现回显服务器 代码实现&#xff08;有详细解释&#xff09; TCP实现回显客户端 代码实现&#xff08;有详细注释&#xff09; 小结&#xff1a; 前言&#xff1a; 上篇文章介绍了TCP的特点。由…

195136-58-4,2‘,7‘-Difluorofluorescein,2,7-二氟荧光素

产品描述&#xff1a;2&#xff0c;7-二氟荧光素中Fluorescein (Uranine) 生物应用中的荧光示踪剂&#xff0c;Fluorescein (Uranine) 是一种具有代表性的绿色荧光团&#xff0c;已被广泛用作实用绿色荧光探针的支架。结构式&#xff1a;理论分析&#xff1a;中文名&#xff1a…

1608_PC汇编语言_first例程分析

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 这一次的笔记主要是梳理一个例程以及部分辅助信息。 为什么要学习汇编语言呢&#xff1f;现在的高级语言开发效率高&#xff0c;而且有着不错的效率&#xff0c;是…

Compose之跨平台Activity页面

前言 Compose(Jetpackjb)是可以跨平台的 目前跨平台主流的页面导航方式一般有两种 一种是都在同一个页面内进行页面替换类型的导航,类似于单Activity,多Fragment 另一种是不同的页面在不同的页面载体上,类似多Activity 两种页面导航方式都可以使用,但我比较偏向于使用多Ac…

加密签名算法

序言如果我们要消费的一段字符串没有唯一标识,我们该如何防止重复消费.cuiyaonan2000163.com背景信息加密加密技术是最常用的安全保密手段&#xff0c;利用技术手段把重要的数据变为乱码&#xff08;加密&#xff09;传送&#xff0c;到达目的地后再用相同或不同的手段还原&…

【虚拟仿真】Unity3D中实现鼠标悬浮模型上显示文字

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在仿真项目开发中&#xff0c;会遇到让鼠标悬浮模型后显示模型…

《流浪地球2》MOSS出圈,现实中的量子计算机长啥样?

科技云报道原创。 春节档科幻电影《流浪地球2》上映后&#xff0c;影片中被誉为“全场最有价值道具”的智能量子计算机MOSS&#xff08;代号550W&#xff09;火了&#xff01; 如果你看完了片尾彩蛋&#xff0c;会发现MOSS或许是整个流浪地球系列电影中的最大反派&#xff0c…

支持向量机-数学模型

支持向量机&#xff08;support vector machines, SVM&#xff09;是一种二分类模型。基本模型是定义在特征空间上的间隔最大的线性分类器&#xff1b;学习策略&#xff1a;间隔最大化&#xff1b;学习算法&#xff1a;求解凸二次规划的最优化算法。 支持向量机分为线性可分支…

自动驾驶感知——激光雷达基本概念|激光雷达点云|激光雷达的标定

文章目录1. 激光雷达基本概念1.1 激光雷达特点1.2 激光雷达测距原理1.2.1 系统组成1.2.2 激光雷达测距原理1.3 常见的激光雷达1.3.1 机械旋转式激光雷达1.3.2 Velodyne HDL-64E1.3.3 固态激光雷达1.3.4 Flash型固态激光雷达1.3.5 相控阵固态激光雷达1.3.6 MEMS型固态激光雷达1.…

NX二开ufun函数UF_MODL_create_surf_from_cloud(使用现有点创建B曲面)

本节主要讲使用现有点创建B曲面的ufun函数UF_MODL_create_surf_from_cloud&#xff0c;这些点可以按任何顺序出现&#xff0c;但是必须有足够的点来定义平面。 先看实例效果图&#xff1a; 1、函数结构 int UF_MODL_create_surf_from_cloud &#xff08; int point_cnt&#…