【深度学习】目标检测之YOLOv2算法&6D姿态估计之YOLO-6D算法
深度解析YOLOv2算法原理
网络结构
YOLOv2的网络backbone是Darknet-19(19个卷积层的全卷积网络),Darknet-19网络结构如下(input image size 224 * 224),进行了32倍下采样:
YOLOv2在Darknet-19的基础上,做了如下改动:
-
YOLOv2的网络结构
上图中有一处错误:
用于网络结构输出的最后一个卷积层的kernel size不是图中所示的 3 ∗ 3 3 * 3 3∗3,而是 1 ∗ 1 1 * 1 1∗1
YOLOv2选择416 * 416作为网络的输入size,这样经过32倍下采样后,输出的featuer map的size是13 * 13,是奇数,这是因为考虑到图片一般待检测目标会居中。 -
reorg层的操作
引入reorg层就是为了将靠前的feature map的信息和靠后的feature map结合起来 -
两条branch的结合方法
将两个width和height相同的tensor,按channel方向拼接在一起 -
最后一个卷积层输出channel的含义
每个cell生成五个anchor,每个anchor对应自己的输出:4(center_x, center_y, width, height) + 1(置信度) + num_classes(和SSD不同,这个num_classes不包括background,SSD就是通过增加一个background起到YOLO中置信度的作用)
anchor的编解码
首先,YOLOv2给出的anchor的width和height的值,是在最后一个13 * 13的feature map上的值。这和SSD中anchor的width和height是不一样的,SSD的anchor的witdh和height都在(0, 1)之间,而YOLOv2的anchor的width和heigth都在(0, 13)之间。
如上图,最后一个13 * 13的feature map,width-height平面上每一个点对应的,5(anchor数量) * [4(center_x, center_y, width, height) + 1(置信度) + num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)],个输出值,以一个位置的一个anchor为例:
先验:
- 这个anchor的width和height分别是 p w p_w pw和 p h p_h ph,这是先验
- 这个位置的坐标(cell的左上点坐标)是 c x c_x cx和 c y c_y cy,这是先验
输出:
- 网络的输出4(center_x, center_y, width, height)部分: t x t_x tx、 t y t_y ty、 t w t_w tw、 t y t_y ty
- 网络的输出 1(置信度)部分: t o t_o to
- 网络的输出num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)部分:分类结果,num_classes个值
label:
- 网络的输出4(center_x, center_y, width, height)部分:归一化后的ground truth box中心点坐标 g x g_x gx、 g y g_y gy和边长 g w g_w gw、 g h g_h gh
- 网络的输出 1(置信度)部分: P r ( o b j e c t ) ∗ I O U ( b , o b j e c t ) Pr(object) * IOU(b, object) Pr(object)∗IOU(b,object),如果这个cell中落入了某一个object,则 P r ( o b j e c t ) = 1 Pr(object)=1 Pr(object)=1,否则 P r ( o b j e c t ) = 0 Pr(object)=0 Pr(object)=0, I O U ( b , o b j e c t ) IOU(b, object) IOU(b,object)是这个cell的这个anchor的 t x t_x tx、 t y t_y ty、 t w t_w tw、 t y t_y ty输出部分编码后所表示的框,和这个object的ground truth box的IOU。如果一个cell落入多个object,那么这个cell的每一个anchor的置信度label取值最大的。那么如何判断一个目标落在哪个cell中呢?看 ( i n t ( g x ∗ 13 ) , i n t ( g y ) ∗ 13 ) (int(g_x * 13), int(g_y) * 13) (int(gx∗13),int(gy)∗13)等于上图中的哪个坐标,就落入哪个cell。( i n t int int这里表示向下取整)
- 网络的输出num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)部分:类别 ( 0 , n u m _ c l a s s e s − 1 ) (0, num\_classes-1) (0,num_classes−1),经过one-hot
box的编解码方式:
- b x = S i g m o i d ( t x ) + c x b_x =Sigmoid(t_x) + c_x bx=Sigmoid(tx)+cx
- b y = S i g m o i d ( t y ) + c y b_y = Sigmoid(t_y) + c_y by=Sigmoid(ty)+cy
- b w = p w e t w b_w=p_we^{t_w} bw=pwetw
- b h = p h e t h b_h =p_he^{t_h} bh=pheth
- b o = S i g m o i d ( t o ) b_o=Sigmoid(t_o) bo=Sigmoid(to)
b x 、 b y 、 b w 、 b h 、 b o b_x、b_y、b_w、b_h、b_o bx、by、bw、bh、bo分别是 t x 、 t y 、 t w 、 t h 、 t o t_x、t_y 、t_w、t_h、t_o tx、ty、tw、th、to编码后的结果。为什么使用 S i g m o i d Sigmoid Sigmoid函数,是因为 S i g m o i d Sigmoid Sigmoid函数的输出值范围是 ( 0 , 1 ) (0, 1) (0,1),这样 b x 、 b y b_x、b_y bx、by就保持在cell中。
损失函数
当一个object落入某个cell后,要选择哪个anchor负责这个object,选择方法如下:
将anchor的中心和ground truth box(width * 13、height * 13)的中心重合,计算两者的IOU,IOU最大的那个anchor负责这个cell对这个object的预测。
YOLOv2的loss计算公式如上:
在训练过程中,计算loss之前,先选定每一个object用哪个cell的哪个anchor负责。
每一个cell的不负责的anchor,计算 P r ( o b j e c t ) ∗ I O U ( b , o b j e c t ) Pr(object) * IOU(b, object) Pr(object)∗IOU(b,object),如果计算结果大于0.6,则这个不负责预测的anchor不计入置信度损失的计算。因为如果这类anchor负责,结果也准确,但是没有用它,因此这部分并不是重点需要优化的对象。
- 第一项:所有负责预测目标的anchor的坐标损失( b x 、 b y 、 b w 、 b h b_x、b_y、b_w、b_h bx、by、bw、bh)。label是 g x g_x gx、 g y g_y gy、 g w g_w gw、 g h g_h gh(注意计算loss之前要乘13)。衡量目标定位准确度。前面的系数设置为1
- 第二项:不负责预测目标的anchor的坐标损失( b x 、 b y 、 b w 、 b h b_x、b_y、b_w、b_h bx、by、bw、bh)。label是 c e l l 的 左 上 点 x 、 c e l l 的 左 上 点 y cell的左上点x、cell的左上点y cell的左上点x、cell的左上点y、 a n c h o r 的 w i d t h 、 a n c h o r 的 h e i g t h anchor的width、anchor的heigth anchor的width、anchor的heigth。因为这样的label对应的实际输出是零。前面的系数设置为1
- 第三项:负责预测目标的anchor的confidence损失 b o b_o bo。label是 P r ( o b j e c t ) ∗ I O U ( b , o b j e c t ) Pr(object) * IOU(b, object) Pr(object)∗IOU(b,object)。衡量可能有目标的准确度。前面的系数设置为5
- 第四项:不负责预测目标的anchor的confidece损失 b o b_o bo。计算 P r ( o b j e c t ) ∗ I O U ( b , o b j e c t ) Pr(object) * IOU(b, object) Pr(object)∗IOU(b,object),如果计算结果小于0.6,label是零。前面的系数设置为1
- 第五项:负责预测目标的anchor的类别损失。输出经过softmax后,分类label经过one-hot编码处理,计算的是L2损失。
注意前几个epoch不计算置信度损失
Darknet 框架实现的YOLOv2
https://pjreddie.com/darknet/yolov2/
深度解析YOLO-6D算法原理
什么是6D姿态估计
像素坐标系下的点,经过内参矩阵转换到相机坐标系。相机坐标系下的点,经过外参矩阵转换到世界坐标系。
YOLO-6D输出目标在像素坐标系下的9个key points,结合这9个key points在世界坐标系下的坐标,以及已知的相机内参矩阵,通过PnP算法,得到外参矩阵。为了方便得到key points在世界坐标系下的坐标,这9个key points选定为目标质心点和3Dbounding box的八个角点在二维图像上的投影。
网络结构
将YOLOv2的最后一个卷积层的输出channel数由,5(anchor数量) * [4(center_x, center_y, width, height) + 1(置信度) + num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)],改为:5(anchor数量) * [18(9个key points在像素坐标系下的坐标) + 1(置信度) + num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)],其余和YOLOv2的结构相同。
key point的编解码
和YOLOv2相同,以一个位置的一个anchor为例:
先验:
- 这个anchor的width和height分别是 p w p_w pw和 p h p_h ph,这是先验
- 这个位置的坐标(cell的左上点坐标)是 c x c_x cx和 c y c_y cy,这是先验
输出:
- 网络的输出18(9个key points在像素坐标系下的坐标)部分: t x c e n t e r t^{center}_x txcenter、 t y c e n t e r t^{center}_y tycenter、 t x 1 t^1_x tx1、 t y 1 t^1_y ty1、…、 t x 8 t^8_x tx8、 t y 8 t^8_y ty8
- 网络的输出 1(置信度)部分: t o t_o to
- 网络的输出num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)部分:分类结果,num_classes个值
label:
- 网络的输出18(9个key points在像素坐标系下的坐标)部分:归一化后的key points坐标 g x c e n t e r g^{center}_x gxcenter、 g y c e n t e r g^{center}_y gycenter、 g x 1 g^1_x gx1、 g y 1 g^1_y gy1、… g x 8 g^8_x gx8、 g y 8 g^8_y gy8
- 网络的输出 1(置信度)部分:置信度label的计算方式是YOLO-6D相比YOLOv2改动的很重要的部分。YOLO-6D的置信度计算函数图像如下:
对于九个key points中的一个: d t h d_{th} dth是设置的像素坐标的欧式距离阈值,默认为80。当这个key points的输出 t x t_x tx、 t y t_y ty经过编码后所表示的点的绝对坐标值(乘图像的width或heigth后)和 g x g_x gx、 g y g_y gy的绝对坐标值(乘图像的width或heigth后)的欧式距离 D T ( x ) D_{T(x)} DT(x),大于 d t h d_{th} dth,则置信度的label设置为0。如果 D T ( x ) D_{T(x)} DT(x)小于 d t h d_{th} dth,则置信度的label按如下公式计算求得:
e α ( 1 − D T ( x ) d t h ) − 1 e α − 1 \dfrac{e^{\alpha(1-\dfrac{D_{T(x)}}{d{th}})}-1}{e^\alpha - 1} eα−1eα(1−dthDT(x))−1, α \alpha α默认设置为 2 2 2。
最后这个cell的这个anchor的置信度label是9个key points的置信度label的平均值。
- 网络的输出num_classes(和SSD不同,这个num_classes不包括backgrund,SSD就是通过增加一个background起到YOLO中置信度的作用)部分:类别 ( 0 , n u m _ c l a s s e s − 1 ) (0, num\_classes-1) (0,num_classes−1),经过one-hot
box的编解码方式:
- b x c e n t e r = ( S i g m o i d ( t x c e n t e r ) + c x ) / 13 b^{center}_x = (Sigmoid(t^{center}_x) + c_x)/13 bxcenter=(Sigmoid(txcenter)+cx)/13,13是最后一个feature map的size
- b y c e n t e r = ( S i g m o i d ( t y c e n t e r ) + c y ) / 13 b^{center}_y = (Sigmoid(t^{center}_y) + c_y)/13 bycenter=(Sigmoid(tycenter)+cy)/13,center的点经过sigmoid,是为了将中心点控制在cell中
- b x 1 = ( t x 1 + c x ) / 13 b^1_x = (t^1_x + c_x)/13 bx1=(tx1+cx)/13
-
b
y
1
=
(
t
y
1
+
c
y
)
/
13
b^1_y=(t^1_y+c_y)/13
by1=(ty1+cy)/13
… - b x 8 = ( t x 8 + c x ) / 13 b^8_x=(t^8_x + c_x)/13 bx8=(tx8+cx)/13
- b y 8 = ( t y 8 + c y ) / 13 b^8_y=(t^8_y+c_y)/13 by8=(ty8+cy)/13
- b o = S i g m o i d ( t o ) b_o=Sigmoid(t_o) bo=Sigmoid(to)
损失函数
和YOLOv2的loss相同,在训练过程中,计算loss之前,先选定每一个object用哪个cell的哪个anchor负责。
选择方法和YOLOv2相同,这也是为什么YOLO-6D也需要二维ground truth box的label信息。
每一个cell的不负责的anchor,计算置信度label,如果计算结果大于0.6,则这个不负责预测的anchor不计入置信度损失的计算。因为如果这类anchor负责,结果也准确,但是没有用它,因此这部分并不是重点需要优化的对象。
不同的是,YOLO-6D的loss只有四项:
- 第一项:所有负责预测目标的anchor的key points损失( b x c e n t e r b^{center}_x bxcenter、 b y c e n t e r b^{center}_y bycenter、 b x 1 b^1_x bx1、 b y 1 b^1_y by1、…、 b x 8 b^8_x bx8、 b y 8 b^8_y by8)。label是 g x c e n t e r g^{center}_x gxcenter、 g y c e n t e r g^{center}_y gycenter、 g x 1 g^1_x gx1、 g y 1 g^1_y gy1、…、 g x 8 g^8_x gx8、 g y 8 g^8_y gy8。衡量目标定位准确度。前面的系数设置为1。仍然是L2损失。
torch.nn.MSELoss(size_average=False)
不负责预测目标的anchor不计入key points损失的计算。
- 第二项:负责预测目标的anchor的confidence损失 b o b_o bo。label是计算的置信度label。衡量可能有目标的准确度。前面的系数设置为5。仍然是L2损失。
torch.nn.MSELoss(size_average=False)
- 第三项:不负责预测目标的anchor的confidece损失 b o b_o bo。计算置信度label,如果计算结果小于0.6,label是零。前面的系数设置为1。仍然是L2损失。
torch.nn.MSELoss(size_average=False)
- 第四项:负责预测目标的anchor的类别损失。前面的系数设置为2。输出经过softmax后,分类label经过one-hot编码处理,计算的是交叉熵损失。
torch.nn.CrossEntropyLoss(size_average=False)
注意前几个epoch不计算置信度损失
单目标6D姿态估计精度统计指标
所谓单目标,是指分类label只有一类,且图像中只有一个目标的情况(注意这种情况不计算分类loss)
Mean Corner Error
测试集中的每一个样本,其9个key points的网络输出值和ground truth的像素级欧式距离求平均值。然后再将测试集所有样本求平均。
2D Projection
默认像素阈值:px_threshold=5
- 使用像素坐标系下9个key points的坐标(ground truth),相机内参,以及世界坐标系下9个key points的坐标,通过PnP算法,计算得到相机外参矩阵Rt_gt
- 使用像素坐标系下9个key points的坐标(网络输出),相机内参,以及世界坐标系下9个key points的坐标,通过PnP算法,计算得到相机外参矩阵Rt_pr
- 通过相机内参和Rt_gt计算所有世界坐标系中的点云点,在像素坐标系下的坐标proj_2d_gt
- 通过相机内参和Rt_pr计算所有世界坐标系中的点云点,在像素坐标系下的坐标proj_2d_pred
- 计算每一对转换过来的像素坐标系下的点的欧式距离,其均值小于px_threshold,则算估计正确
- 所有估计正确的数量 / 总数量
3D Transformation
默认体素阈值:vx_threshold=0.1 * 目标直径(米)
- 使用像素坐标系下9个key points的坐标(ground truth),相机内参,以及世界坐标系下9个key points的坐标,通过PnP算法,计算得到相机外参矩阵Rt_gt
- 使用像素坐标系下9个key points的坐标(网络输出),相机内参,以及世界坐标系下9个key points的坐标,通过PnP算法,计算得到相机外参矩阵Rt_pr
- 通过Rt_gt计算所有世界坐标系中的点云点,在相机坐标系下的坐标transform_3d_gt
- 通过Rt_pr计算所有世界坐标系中的点云点,在相机坐标系下的坐标transform_3d_pred
- 计算每一对转换过来的相机坐标系下的点的欧式距离,其均值小于vx_threshold,则算估计正确
- 所有估计正确的数量 / 总数量
Pytorch实现的YOLO-6D
https://github.com/Microsoft/singleshotpose/
结语
如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。