0.前言:
最近在学习自动驾驶方向的东西,简单整理一些学习笔记,学习过程中发现宝藏up 手写AI
1. 概述
Laneaf思想是把后处理放在模型里面。重点在于理解vaf, haf,就是横向聚类:中心点,纵向聚类:利用vaf学到的单位向量去预测下一行中心点与haf预测到的当前中心点做匹配,根据距离error阈值判断是否属于同一个lane id。主要了解标签和decode,decode就是标签制作的逆过程,decode部分主要是cost代价矩阵理解,loss针对正负样本不平衡,可以使用OHEM或者focal loss。
2. 算法结构
使用DLA-34作为Backbone,网络输出二值的分割结果、Vertical Affinity Field(VAF)和Horizontal Affinity Field(HAF)。其中:Affinity Field. 亲和域
使用HAF、VAF,结合二值分割结果(三个头可以产生一个实例),能够在后处理中对任意数量的车道线进行聚类,得到多个车道线实例。
3. Affinity Field 构建
给定图像中的每个位置 ( x , y ) (x,y) (x,y),HAF和VAF为每个位置分配一个向量,将HAF记作 H → ( ⋅ , ⋅ ) \overset{\rightarrow}H(\cdot,\cdot) H→(⋅,⋅),将VAF记作 V → ( ⋅ , ⋅ ) \overset{\rightarrow}V(\cdot,\cdot) V→(⋅,⋅)。
AF的生成都是从最下面一行往上面扫描
使用ground truth构建HAF和VAF,将ground truth到HAF和VAF的映射函数分别记作 H → g t ( ⋅ , ⋅ ) \overset{\rightarrow}H_{gt}(⋅,⋅) H→gt(⋅,⋅)和 V → g t ( ⋅ , ⋅ ) \overset{\rightarrow}V_{gt}(⋅,⋅) V→gt(⋅,⋅)。
对于图像第 y y y行中车道线 l l l所包含的每个点 ( x i l , y ) (x_i^l, y) (xil,y),HAF由下式得到:
H → g t ( x i l , y ) = ( x − y l − x i l ∣ x − y l − x i l ∣ , y − y ∣ y − y ∣ ) T = ( x − y l − x i l ∣ x − y l − x i l ∣ , 0 ) T \overset{\rightarrow}H_{gt}(x^l_i , y) = (\frac{{\overset{-} x}^l_y − x^l _i} {|{\overset{-} x}^l_ y − x^ l_ i | }, \frac{y − y}{ |y − y|})^T = (\frac{{\overset{-} x}^l_ y − x^l_i} {|{\overset{-} x}^ l_ y − x ^l _i | }, 0 )^T H→gt(xil,y)=(∣x−yl−xil∣x−yl−xil,∣y−y∣y−y)T=(∣x−yl−xil∣x−yl−xil,0)T
上式中的 x − y l \overset{-}x^l_y x−yl表示第 y y y行中属于车道线 l l l的所有点的横坐标平均值,求解HAF的过程如下图所示:
上图中绿色框表示属于车道线 l l l的点,蓝色框表示属于车道线 l + 1 l+1 l+1的点。箭头表示某个位置处HAF中的向量。
对于图像第 y y y行中属于车道线 l l l的每个点 ( x i l , y ) (x^l_i,y) (xil,y),VAF由下式得到:
V → g t ( x i l , y ) = ( x − y − 1 l − x i l ∣ x − y − 1 l − x i l ∣ , y − 1 − y ∣ y − 1 − y ∣ ) T = ( x − y − 1 l − x i l ∣ x − y − 1 l − x i l ∣ , − 1 ) T \overset{\rightarrow}V_{gt}(x^l_i , y) = (\frac{{\overset{-} x}^l_{y-1} − x^l _i} {|{\overset{-} x}^l_ {y-1} − x^ l_ i | }, \frac{y -1− y}{ |y -1− y|})^T = (\frac{{\overset{-} x}^l_ {y-1} − x^l_i} {|{\overset{-} x}^ l_ {y-1} − x ^l _i | }, -1)^T V→gt(xil,y)=(∣x−y−1l−xil∣x−y−1l−xil,∣y−1−y∣y−1−y)T=(∣x−y−1l−xil∣x−y−1l−xil,−1)T
上式中的 x − y − 1 l \overset{-}x^l_{y-1} x−y−1l示第 y − 1 y-1 y−1行中属于车道线 l l l的所有点的横坐标平均值。求解VAF的过程如下图所示:
需要注意的是,VAF中每行的向量指向上一行中属于该车道线实例的点的平均位置。
def generateAFs(label, viz=False):
# 创建透视场数组
num_lanes = np.amax(label) # 获取车道线的数量
VAF = np.zeros((label.shape[0], label.shape[1], 2)) # 垂直透视场
HAF = np.zeros((label.shape[0], label.shape[1], 1)) # 水平透视场
# 对每条车道线进行循环处理
for l in range(1, num_lanes+1):
# 初始化先前的行和列值
prev_cols = np.array([], dtype=np.int64)
prev_row = label.shape[0]
# 从下到上解析每一行
for row in range(label.shape[0]-1, -1, -1):
# [0] :np.where 返回一个元组,其每一维都是一个数组,表示该维度上满足条件的索引。
# 在这里,我们只关心列索引,所以我们取出这个元组的第一个元素
cols = np.where(label[row, :] == l)[0] # 获取当前行的前景列值(即车道线位置)
# 为每个列值生成水平方向向量
for c in cols:
if c < np.mean(cols):
HAF[row, c, 0] = 1.0 # 向右指示
elif c > np.mean(cols):
HAF[row, c, 0] = -1.0 # 向左指示
else:
HAF[row, c, 0] = 0.0 # 保持不变
# 检查先前的列和当前的列是否都非空
if prev_cols.size == 0: # 如果没有先前的行/列,更新并继续
prev_cols = cols
prev_row = row
continue
if cols.size == 0: # 如果当前没有列,继续
continue
col = np.mean(cols) # 计算列的均值
# 为先前的列生成垂直方向向量
for c in prev_cols:
# 计算方向向量的位置
vec = np.array([col - c, row - prev_row], dtype=np.float32)
# 单位标准化
vec = vec / np.linalg.norm(vec) # 标准化为单位向量 # 模
VAF[prev_row, c, 0] = vec[0]
VAF[prev_row, c, 1] = vec[1] # 具有像两方向的增值
# 使用当前的行和列值更新先前的行和列值
prev_cols = cols
prev_row = row
decode code
cost矩阵:
当提到“建立每条线与头坐标与当前行聚类点之间的cost矩阵”,这很有可能是在一个场景中,例如图像或传感器数据处理,你想要在平面上追踪或匹配多个线对象。让我为你详细解释一下。
背景概念