赞
踩
利用全连接层直接对边界框进行预测
YOLOv2通过缩减网络,使用416x416的输入,模型下采样的总步长为32,最后得到13x13的特征图,然后对13x13的特征图的每个cell预测5个anchor boxes,对每个anchor box预测边界框的位置信息、置信度和一套分类概率值。使用anchor boxes之后,YOLOv2可以预测13x13x5=845个边界框
完成cfg文件的解析,模型的创建与权重文件的加载之后,现在要做的就是执行检测操作,主要调用了utils/utils.py中的do_detect() 函数,在demo.py中:
boxes = do_detect(m, sized, 0.5, 0.4, use_cuda)
模型forward后输出结果存在list_boxes中,因为有3个yolo输出层,所以这个列表list_boxes中又分为3个子列表;
其中list_boxes[0]
中存放的是第一个yolo层输出,其特征图大小对于原图缩放尺寸为8,即strides[0], 对于608x608图像来说,该层的featuremap尺寸为608/8=76;则该层的yolo输出数据维度为[batch, (classnum+4+1)*num_anchors, feature_h, feature_w]
, 对于80类的coco来说,测试图像为1,每个yolo层每个特征图像点有3个锚点,该yolo层输出是[1,255,76,76];对应锚点大小为[1.5,2.0,2.375,4.5,5.0,3.5]; (这6个数分别是3个锚点的w和h,按照
w
1
,
h
1
,
w
2
,
h
2
,
w
3
,
h
3
w_1,h_1,w_2,h_2,w_3,h_3
w1,h1,w2,h2,w3,h3排列);
do_detect()
函数中主要是调用了
get_region_boxes1(output, conf_thresh, num_classes, anchors, num_anchors, only_objectness=1, validation=False)
这个函数对forward后的output做解析并做nms操作;
每个yolo层输出数据分析,对于第一个yolo层,输出维度为[1,85*3,76,76 ]; 会将其reshape为[85, 1*3*76*76],即有1*3*76*76个锚点在预测,每个锚点预测信息有80个类别的概率和4个位置信息和1个是否包含目标的置信度;
yolov4的输出需要包含的信息有:物体的位置信息、有物体的概率、物体的分类,可以写为:( t x , t y , t w , t h , o b j , c l s t_x, t_y, t_w, t_h, obj, cls tx,ty,tw,th,obj,cls),其中前四个是物体的位置信息,最后一个cls,根据分类的类别数,维度不同,如果只有1个类别,那就只占1个位置,如果是2个类别,就是2个位置,使用one-hot编码。
而在实际的yolov4的最终输出之前是(19,19,1024),通过蓝色的18个(当只有1个分类类别的时候,如果有2个,那就是19个卷积核)111024的卷积核,得到(19,19,18),最后reshape成(3,19,19,tx, ty, tw, th, obj, cls)。后面的是检测的物体信息,而前面的(3,19,19)的理解如下:
19*19个特征点,等同于把原图分成了19 *19个网格,原图的输入是608 *608的,那么每一个网格的大小就是608/19=32,每一个特征点只关注对应的网格,判断对应的网格是否有物体。
模型的输出有三个,分别是(B, 255, 19, 19),(B, 255, 38, 38),(B, 255, 76, 76),因此需要对这三个输出分别解码。
首先需要将输出view成(B, A, n_ch, H, W)的形式,其中H和W就是输出的尺寸,A是锚框数量,n_ch是包含了bx, by, bw, bh, obj, cls的信息,维度为4+1+80=85。之后再进行一个维度变换,最终得到(B, 3, 19, 19, 85)的维度(以第一个为例)。此时,最后一个维度85,包含了我们解码所需的所有信息,也就是说,我们需要对前面B319*19这么多的数据,都进行同样方式的解码。
接下来我们取出来bx, by, bw, bh, obj, cls。注意,此时除了cls之外,其他所有的维度,都减少了一维变成了(B, 3, 19, 19),因为cls是以切片形式取的,所以维度数量不变,是(B, 3, 19, 19, 80)。
# 取出来 bx, by, bw, bh
bx, by = output[..., 0], output[..., 1]
bw, bh = output[..., 2], output[..., 3]
# 取出来obj和cls
obj = output[..., 4]
cls = output[..., 5:]
我们需要先把bx, by取一个sigmoid,把bw和bh取一个exp。这里加上了缩放因子,据说当图片中的目标既有大又有小的时候,会起作用,暂时没见到实际起作用的情况,不过先加上了,当缩放因子为1的时候,相当于不起作用。这里我们同时也对物体以及分类的置信度取sigmoid。
# 进行初步转换
bx = torch.sigmoid(bx)
by = torch.sigmoid(by)
bw = torch.exp(bw) * scale_x_y - 0.5 * (scale_x_y - 1)
bh = torch.exp(bh) * scale_x_y - 0.5 * (scale_x_y - 1)
# 对物体置信度,分类置信度也取sigmoid
det_confs = torch.sigmoid(obj)
cls_confs = torch.sigmoid(cls)
目标是找到点相对于整张图的偏移的比例,但由于我们分了网格,因此先找相对于网格的偏移比例。
这里,图像被分成了19*19个网格,假如中心点在第2行第3列的网格里面。
相对网格的偏移量肯定是小数,比如在x轴方向上偏移是0.5,在y轴上偏移是0.2,也就是在网格中间偏上的位置。
那么,以网格为单位,相对于所有网格来说,中心点的实际偏移量:
在x轴方向的0.5,加上偏移的网格数,也就是2(从0开始计数),那么得到了2.5就是以网格为单位,相对于整张网格图的x轴偏移量。同理,y轴方向上,相对于y轴的,就是1.5。
因此我们需要把x和y方向上的网格数量构建一下,并找到以网格为单位的偏移量。
# 构建网格grid_x和grid_y
grid_x = torch.arange(W, dtype=torch.float).repeat(1, 3, W, 1).to(device)
grid_y = torch.arange(W, dtype=torch.float).repeat(1, 3, H, 1).permute(0, 1, 3, 2).to(device)
# 求bx和by
bx = bx + grid_x
by = by + grid_y
既然找到了相对于网格的偏移量,那么偏移的比例就是偏移量除以网格长度,这个比例,就是相对于网格的偏移比例,同时也是相对于整张图的偏移比例。另外根据解码图,我们对于w和h,还需要乘上先验框的宽、高,得到最终的bw和bh。
# 取每个anchor的长和宽,求bw和bh
for i in range(num_anchors):
bw[:, i, ...] *= anchors[i * 2]
bh[:, i, ...] *= anchors[i * 2 + 1]
# 对数据转换,除以网格数量,得到相对整张图的偏移的比例,并增加一个维度
bx = (bx / W).unsqueeze(-1)
by = (by / H).unsqueeze(-1)
bw = (bw / W).unsqueeze(-1)
bh = (bh / H).unsqueeze(-1)
现在对于中心点相对于原图的偏移量,以及宽高都得到了,我们把这四个数据结合起来,再把obj和cls的置信度都合在一起,就得到了我们最终解码后的输出,用于后续的画图等计算。
# 四个数据拼接起来,并reshape成[B, -1, 4]的形状
boxes = torch.cat([bx, by, bw, bh], dim=-1).reshape(B,A*W*H, 4)
det_confs = det_confs.unsqueeze(-1).reshape(B,A*W*H, 1)
cls_confs = cls_confs.reshape(B,A*W*H, num_classes)
outputs = torch.cat([boxes, det_confs, cls_confs], dim=-1)
注:(网格生成)
grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat(
batch_size * len(self.anchors_mask[i]), 1, 1).view(x.shape).type(FloatTensor)
#先生成一行(width),在重复一列(height),再扩充维度,最后view成[batchsize, 3, h, w]
grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_width, 1).t().repeat(
batch_size * len(self.anchors_mask[i]), 1, 1).view(y.shape).type(FloatTensor)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。