读一遍论文,认识一所大学,一家机构,让我们知道,世界很大,你不是一个人在研究某个领域。今天我们看到的是Universidade Federal do Esp´ırito Santo (UFES)的成果,圣埃斯皮里托联邦大学,位于巴西东南部。好的,以后有机会去巴西,一定去这所大学门口拍照留念一下。
其实是继LaneCNN之后,以anchor_based为核心的车道线检测方法,作者一直在强调global information对于车道线检测的影响,确实,如果基于这种思想的话,anchor其实本身就是对global information的提取或者说先验知识,车道线问题视为对anchor的回归问题,的确很有道理。
下图即为lane_ATT网络构成,先不用急的看网络,我们先理一下输入输出。
本质上,车道线就是图中一些点集,回归的本质就是找到每一行上最符合这条车道线的点的位置,最简单的方法就是去遍历每一行,在这里,我们把图像的左下角为坐标原点,向上为Y轴正方向,向右为X轴正方向。一般来说,为了减少计算量,我们一般会采用等间距采样的方法,其实拟合一条三次方程的车道线至少需要4个点,所以我们只需要大于4个点的采样即可。Npts就是我们要采样的个数。
这样我们相当于固定了Y轴,那么车道线只需要一个X轴上的数组就可以表示
我们得到了一套(x,y)坐标,可以与我们提出anchor进行回归,最简单的回归可以求pred和label的二范数。这里其实要比较慎重处理一下,是否除以点数进行归一化再反传这部分误差,后续再看一下。
Anchor其实类比目标检测,就是我们提出的标准的先验的车道线,最简单的anchor是一条直线,只需要一个起点和一个角度就可以表达,作者也是这样来处理的
δback 是 backbone’s global stride,我理解其实anchor和lane的回归可能不是在原图大小的层级上,可能是1/4或者1/16大小,因为直接接的backbone的输出。但是我们x和y是原图level的,所以需要转换到特征图层面,这样才能将anchor给投影到feature map。
为什么要用Attention,作者觉得由于池化下采样导致的采集的特征是局部的,不能很好的预测车道线的位置和相关属性,需要加入全局信息来解决。
首先将anchor[i] 投影到特征图上得到了这个anchor的局部特征 ailoc,我们需要通过一定的关系来求解这个anchor的全局特征 aiglob。现在问题到了如何求全局特征,作者的方法很简单,他讲这个anchor和其他任意的anchor通过全连接层加softmax,会获得一个概率,每一个anchor的投影特征和对应概率相乘,再SUM,得到了ailoc对应的全局的 aiglob。看一下这部分的代码
batch_features = self.backbone(x)['out']
这个是resnet34的主干,主干输出是8 * 512 * 12 * 20的feature map
batch_features = self.conv1(batch_features)
然后经过1 * 1的conv进行降channel处理,输出是8 * 64 * 12 * 20的feature map
batch_anchor_features = self.cut_anchor_features(batch_features)
这一步其实就是将预先设定好的anchor投影到feature map中,我们看一下作者提供的batch_anchor_features的维度信息,这是一个5维的信息,相比之前的特征图多了一个n_proposals,这个其实就是预先设置好的anchor数量,作者预留了1000条,n_fmaps是之前的channel,仍然是64。 featmap_h实际上是通过img_h // self.stride算出来的,360//32向下取整为11,代表1条车道线用11个点来量化。由于y轴上采样点固定的,所以描述一个点只需要一个维度就够了,因此最后一个维度为1。
cut_anchor_features这部分其实还涉及compute_anchor_cut_indices来获得x(w方向),y(h方向),z(channel方向)的index取值,作者通过这种方式,将1000个anchor下64channel 11个Width值同时访问操作,访问的对象就是batch_anchor_features,大大提高了处理速度。这里面涉及了很多数据维度的处理以及将不同维度信息保存在三个704 * 1000的数组里,分别存了三个方向的index,方便后期一次性对数据进行访问处理。
batch_anchor_features = torch.zeros((batch_size, n_proposals, n_fmaps, self.featmap_h, 1),
device=features.device)
然后作者又奖channel和h方向进行合并,也就是将一个anchor的所有信息压缩到一起。batch_anchor_features此时已经是8000 * 704,8000是batch和n_proposals压缩了,704是channel和h压缩了。一个anchor的ailoc其实就是704长度的tensor。
batch_anchor_features = batch_anchor_features.view(-1, self.anchor_feat_channels * self.featmap_h)
通过attention layer, 又得到了scores,维度为8000 * 999,它代表的是任意一anchor和其他所有的anchor的关系,再通过softmax就可以得到attention,维度为81000999
scores = self.attention_layer(batch_anchor_features)
attention = softmax(scores).reshape(x.shape[0], len(self.anchors), -1)
attention里面是一个anchor和其他anchor的权重,但是由于没有和自己的权重,导致维度上很难计算,所以作者用了attention_matrix 维度为8 * 1000 * 1000。
batch_anchor_features = batch_anchor_features.reshape(x.shape[0], len(self.anchors), -1)
attention_features = torch.bmm(torch.transpose(batch_anchor_features, 1, 2),
torch.transpose(attention_matrix, 1, 2)).transpose(1, 2)
batch_anchor_features之前是8000 * 704,现在重新将维度变为8 * 1000 * 704,通过bmm矩阵乘法的方法,最后我们获得的attention_features为8 * 1000 * 704。
此时,将attention_features其实是任何一个anchor都已经与其他anchor进行了一次交互,包含了全局的信息。将attention_features和batch_anchor_features进行向量cat拼接就可以得到最终的特征