yolov5 预测框生成
配置文件
首先是配置文件, 我们在配置文件中可以看到这样的配置项
# YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
# 定义一些模型超参数
nc: 80 # 类别
depth_multiple: 0.33 # 模型深度 系数
width_multiple: 0.50 # 模型宽度 系数
anchors:
- [10,13, 16,30, 33,23] # P3/8 用于检测小目标的三个初始框(anchors)
- [30,61, 62,45, 59,119] # P4/16 用于检测中目标的三个初始框(anchors)
- [116,90, 156,198, 373,326] # P5/32 用于检测大目标的三个初始框(anchors)
# YOLOv5 v6.0 backbone 主干网络
backbone:
# [from, number, module, args] # from是指输入来自那里,number是指模块重复数量,module是模块组件的名称,args是创建模型需要的参数
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 第0层,相对原图做了2倍下采样
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4 第1层,相对原图做了4倍下采样
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8 第3层,相对原图做了8倍下采样
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16 第5层,相对原图做了16倍下采样
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 第7层,相对原图做了32倍下采样
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9 第9层
]
关于模型深度系数和模型宽度系数可以看这篇文档 链接
简单来说, 模型深度系数就是配置文件中 模型重复次数 ∗ 模型深度系数 模型重复次数 * 模型深度系数 模型重复次数∗模型深度系数
模型宽度系数就是 模型输出维度 ( a r g s 的第一个参数 ) ∗ 模型深度系数 模型输出维度(args的第一个参数) * 模型深度系数 模型输出维度(args的第一个参数)∗模型深度系数
我们主要关注的是锚点配置项
anchors:
- [10,13, 16,30, 33,23] # P3/8 用于检测小目标的三个初始框(anchors)
- [30,61, 62,45, 59,119] # P4/16 用于检测中目标的三个初始框(anchors)
- [116,90, 156,198, 373,326] # P5/32 用于检测大目标的三个初始框(anchors)
[10,13, 16,30, 33,23]
表示三组数据, 分别对应宽高, 是对应特征图缩小 8 倍时对应的参数, 其他类似, 后续我们将提到相关参数的使用方式
模型输出
yolov5 版本的模型流程如下图所示:
这个流程图模拟的输入图片维度为 [640,640,3] 会得到3个不同尺度的输出:80x80(640/8)、40x40(640/16)、20x20(640/32)分别对应着特征图缩小8倍, 缩小16倍, 缩小32倍 , 输出模块为CSP2_1
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
其中,80x80代表浅层的特征图(P3),包含较多的低层级信息,适合用于检测小目标,所以这一特征图所用的anchor尺度较小;同理,20x20代表深层的特征图(P5),包含更多高层级的信息,如轮廓、结构等信息,适合用于大目标的检测,所以这一特征图所用的anchor尺度较大。另外的40x40特征图(P4)上就用介于这两个尺度之间的anchor用来检测中等大小的目标。yolov5之所以能高效快速地检测跨尺度目标,这种对不同特征图使用不同尺度的anchor的思想功不可没。
在neck过程后, 最后还有一个 Prediction 的过程, 特征图的宽高不变, 但是维度发现改变, 均变成 (4 + 1 + 类别数) * 3
对应一个缩小系数为 8 的特征图, (160,92,256) ==> (1,3, 60,92,85)
到这一步后, 我们就可以得到我们所需要的内容了, 也就是维度 85 对应的前4个值, 坐标x,y和宽高w,h
下图给出了一些解释, 当前得到的x和y, w和h 是一个不确定大小的数, 后面我们需要对其进行处理
处理模型输出
xywh格式, 即边界框由中心坐标(x,y)和框的长宽(w,h)来表示,YOLO中采用的主要是这种形式。
现在, 我们的任务是根据模型的输出得到预测框的实际位置
由锚框的生成方法可知,它是以某个划分格子的中心坐标,以一定长宽比生成的,但在YOLO中预测框,则是以某个格子的左上角坐标为原点进行偏移的。
我们将特征图上一个格子称为grid , 比如说一个(640,640,3)的图片进行特征图提取后, 缩小32倍后的尺寸是 (20,20) , 那么这个特征图就有 20 ∗ 20 = 400 20 * 20 = 400 20∗20=400 个grid , 因为每个 grid 可以对应原图上的一片像素点, 我们可以得到每个 grid 的左上角真实坐标 , 我们将其缩小stride倍的值记作 ( c x , c y ) (c_x,c_y) (cx,cy)
已知 sigmoid 函数, 我们定义
σ
(
x
)
=
1
1
+
e
−
x
=
e
x
e
x
+
1
σ(x)=\frac1{1+e^{-x}}=\frac{e^x}{e^x+1}
σ(x)=1+e−x1=ex+1ex
我们设置每个网格获得目标预测值为:坐标( t x , t y ) t_x,t_y) tx,ty)和宽高 ( t w , t h ) (t_w,t_h) (tw,th), 分别对应输出维度85的前四个值
定义坐标( b x , b y ) b_x,b_y) bx,by)和宽高 ( b w , b h ) (b_w,b_h) (bw,bh)为真实的预测框
按照下式计算该网格的目标坐标和目标框大小
b
x
=
(
σ
(
t
x
)
∗
2
+
c
x
)
∗
s
t
r
i
d
e
b
y
=
(
σ
(
t
y
)
∗
2
+
c
y
)
∗
s
t
r
i
d
e
b
w
=
(
σ
(
t
w
)
∗
2
)
2
∗
a
n
c
h
o
r
[
0
]
b
h
=
(
σ
(
t
h
)
∗
2
)
2
∗
a
n
c
h
o
r
[
1
]
b_x=(\sigma(t_x)^*2+c_x) * stride \\ b_y=(\sigma(t_y)^*2+c_y) * stride\\ b_w=(\sigma(t_w)*2)^2* anchor[0]\\ b_h=(\sigma(t_h)*2)^2 * anchor[1]
bx=(σ(tx)∗2+cx)∗strideby=(σ(ty)∗2+cy)∗stridebw=(σ(tw)∗2)2∗anchor[0]bh=(σ(th)∗2)2∗anchor[1]
anchor 就是我们配置文件配置的数值
anchors: - [10,13, 16,30, 33,23] # P3/8 用于检测小目标的三个初始框(anchors) - [30,61, 62,45, 59,119] # P4/16 用于检测中目标的三个初始框(anchors) - [116,90, 156,198, 373,326] # P5/32 用于检测大目标的三个初始框(anchors)
当特征图为缩小8倍时, 有三组anchor分别为 (10,13) (16,39) (33,23) , 假设这里使用的是第一个anchor, 那么 anchor[0] = 10 , anchor[1] = 13
在yolo.py中代码是在Detect
类中(下面代码是用于检测时的处理方式, 如果是训练时的处理方式有一些改动)
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 self.stride = [8,16,32] 对应缩小的倍率
wh = (wh * 2) ** 2 * self.anchor_grid[i] # wh
y = torch.cat((xy, wh, conf), 4)
针对输入图像736x1280, 经目标框坐标变换后,得到所有网格中的目标框张量[1, 57960,85]。57960=(40x23+80x46+160x92)x3 是所有网格的目标框数量。
40x23 : 32倍缩小特征图
80x46 : 16倍缩小特征图
160x92 : 8倍缩小特征图
示例
对一张 736x1280 的图片(假设yolo没有进行图像缩放), 当进行8倍缩略图时,
s t r i d e = 8 stride = 8 stride=8
第二行第二个grid坐标为 ( c x , c y ) = ( 92 / 8 , 160 / 8 ) (c_x,c_y)=(92 / 8,160 / 8) (cx,cy)=(92/8,160/8)
a n c h o r = [ 10 , 13 ] anchor = [10,13] anchor=[10,13]
(
t
x
,
t
y
)
=
(
1.69
,
−
6.3
)
,
(t_x,t_y)=(1.69,-6.3),
(tx,ty)=(1.69,−6.3),
(
t
w
,
t
h
)
=
(
1.03
,
2.6
)
(t_w,t_h)=(1.03,2.6)
(tw,th)=(1.03,2.6)
b
x
=
(
σ
(
t
x
)
∗
2
+
c
x
)
∗
s
t
r
i
d
e
=
(
0.674
+
11.5
)
∗
8
=
97.394
b
y
=
(
σ
(
t
y
)
∗
2
+
c
y
)
∗
s
t
r
i
d
e
=
(
0.003
+
20
)
∗
8
=
160.024
b
w
=
(
σ
(
t
w
)
∗
2
)
2
∗
a
n
c
h
o
r
[
0
]
=
0.5534
∗
10
=
5.534
b
h
=
(
σ
(
t
h
)
∗
2
)
2
∗
a
n
c
h
o
r
[
1
]
=
3.198
∗
13
=
41.58
b_x=(\sigma(t_x)^*2+c_x) * stride = (0.674 + 11.5) * 8 = 97.394\\ b_y=(\sigma(t_y)^*2+c_y) * stride = (0.003 + 20) * 8 = 160.024\\ b_w=(\sigma(t_w)*2)^2* anchor[0] = 0.5534 * 10 = 5.534\\ b_h=(\sigma(t_h)*2)^2 * anchor[1] = 3.198 * 13 = 41.58
bx=(σ(tx)∗2+cx)∗stride=(0.674+11.5)∗8=97.394by=(σ(ty)∗2+cy)∗stride=(0.003+20)∗8=160.024bw=(σ(tw)∗2)2∗anchor[0]=0.5534∗10=5.534bh=(σ(th)∗2)2∗anchor[1]=3.198∗13=41.58
即中心点为 (97,394,160.024) 宽高为(5.534,41.58)像素
参考
https://blog.csdn.net/qq_41204464/article/details/130229847
https://blog.csdn.net/weixin_44238733/article/details/121180095
https://blog.csdn.net/lzzzzzzm/article/details/120621582
https://blog.csdn.net/qq_27278957/article/details/120036450
https://blog.csdn.net/weixin_43427721/article/details/123608508?spm=1001.2014.3001.5506
https://blog.csdn.net/lzzzzzzm/article/details/120621582