最近在研究视觉语义地图,需要进行车道线检测,发现这篇车道线检测论文效果蛮好的 (Ultra Fast Structure-aware Deep Lane Detection)。论文作者在知乎上已经介绍过了:https://zhuanlan.zhihu.com/p/157530787,本文中我简单介绍下论文原理,重点介绍如何使用原作者开源的模型检测自己采集的数据。
1.Method介绍
目前大多数车道线检测算法中都是将车道线检测看作是像素分割问题
。但是这会存在两个问题,一是计算速度慢,二是在一些有挑战的场景中(遮挡、夜晚、极端光照条件)由于没有视觉线索
很难进行检测。因此在本文中,作者提出了一个按行检测车道线的方法
。
作者首先将原始图像按照一定像素间隔划分成
h
h
h 个anchors,同时又将每一个anchor划分为
w
w
w 个网格。若图像全局特征用
X
X
X 表示,车道线数量用
C
C
C 表示,车道线分类器为
f
i
j
f^{ij}
fij,则车道线预测概率
为:
P
i
,
j
,
:
=
f
i
j
(
X
)
,
s.t.
i
∈
[
1
,
C
]
,
j
∈
[
1
,
h
]
P_{i, j,:}=f^{i j}(X), \text { s.t. } i \in[1, C], j \in[1, h]
Pi,j,:=fij(X), s.t. i∈[1,C],j∈[1,h]
概率 P i , j , : P_{i, j,:} Pi,j,: 是 ( w + 1 ) (w+1) (w+1) 维的向量。
为什么本文提出的方法可以快速检测车道线呢?如下图所示,基于像素分割的车道线检测要进行
H
×
W
×
(
C
+
1
)
H \times W \times (C+1)
H×W×(C+1) 次分类,而本文提出的方法只需要进行
C
×
h
×
(
w
+
1
)
C \times h \times (w+1)
C×h×(w+1) 次分类,由于
h
<
<
H
,
w
<
<
W
h<<H,w<<W
h<<H,w<<W,因此整体计算量大幅减小。
同时在本文中作者又提出了两个新的结构损失函数。
- 由于车道线一般是连续的,所有同一车道线在相邻anchor的预测概率应该是接近的,作者提出了
车道线相似损失函数
:
L s i m = ∑ i = 1 C ∑ j = 1 h − 1 ∥ P i , j , : − P i , j + 1 , : ∥ 1 L_{s i m}=\sum_{i=1}^{C} \sum_{j=1}^{h-1}\left\|P_{i, j,:}-P_{i, j+1,:}\right\|_{1} Lsim=i=1∑Cj=1∑h−1∥Pi,j,:−Pi,j+1,:∥1 - 另一个是
车道线形状损失函数
,作者使用softmax函数得到车道线的概率: Prob i , j , : = softmax ( P i , j , 1 : w ) \operatorname{Prob}_{i, j,:}=\operatorname{softmax}\left(P_{i, j, 1: w}\right) Probi,j,:=softmax(Pi,j,1:w),同时使用期望来表示车道线所处位置
: L o c i , j = ∑ k = 1 w k ⋅ Prob i , j , k L o c_{i, j}=\sum_{k=1}^{w} k \cdot \operatorname{Prob}_{i, j, k} Loci,j=∑k=1wk⋅Probi,j,k。最后作者使用二阶微分方程来约束车道线位置:
L s h p = ∑ i = 1 C ∑ j = 1 h − 2 ∥ ( L o c i , j − L o c i , j + 1 ) − ( L o c i , j + 1 − L o c i , j + 2 ) ∥ 1 \begin{aligned} L_{s h p}=\sum_{i=1}^{C} \sum_{j=1}^{h-2} \| &\left(L o c_{i, j}-L o c_{i, j+1}\right) -\left(L o c_{i, j+1}-L o c_{i, j+2}\right) \|_{1} \end{aligned} Lshp=i=1∑Cj=1∑h−2∥(Loci,j−Loci,j+1)−(Loci,j+1−Loci,j+2)∥1
2.自采数据集车道线检测
下面使用作者训练好的模型来检测车道线,代码链接为:https://github.com/cfzd/Ultra-Fast-Lane-Detection。
下载完源码后,创建一个lane_detection.py
,首先导入需要的库:
import torch
import scipy.special
import os, cv2
from PIL import Image
from model.model import parsingNet
import numpy as np
import torchvision.transforms as transforms
from data.constant import tusimple_row_anchor
然后加载预训练模型,作者在两个车道线数据集上进行了训练,我这里选择是的在TuSimple数据集
训练的模型,代码如下:
net = parsingNet(pretrained = False, backbone='18',cls_dim = (101,56,4),
use_aux=False) # we dont need auxiliary segmentation in testing
test_model = 'tusimple_18.pth'
state_dict = torch.load(test_model, map_location='cpu')['model']
compatible_state_dict = {}
for k, v in state_dict.items():
if 'module.' in k:
compatible_state_dict[k[7:]] = v
else:
compatible_state_dict[k] = v
net.load_state_dict(compatible_state_dict, strict=False)
net.eval()
然后是一些参数设置,如图片大小尺寸(我这里图片大小为 1280 × 560 1280\times560 1280×560),row anchor 尺寸(这里使用的是作者设置的anchor尺寸),图片预处理等(将图片大小resize为 800 × 288 800\times288 800×288):
img_w, img_h = 1280, 560
size = (img_w,img_h)
cls_num_per_lane = 56
row_anchor = tusimple_row_anchor
img_transforms = transforms.Compose([
transforms.Resize((288, 800)),
transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
])
下一步就是车道线检测与可视化:
with torch.no_grad():
out = net(img)
col_sample = np.linspace(0, 800-1, 100)
col_sample_w = col_sample[1] - col_sample[0]
out_j = out[0].data.cpu().numpy()
out_j = out_j[:, ::-1, :]
prob = scipy.special.softmax(out_j[:-1, :, :], axis=0)
idx = np.arange(100) + 1
idx = idx.reshape(-1, 1, 1)
loc = np.sum(prob * idx, axis=0)
out_j = np.argmax(out_j, axis=0)
loc[out_j == 100] = 0
out_j = loc
for i in range(out_j.shape[1]):
if np.sum(out_j[:, i] != 0) > 2:
for k in range(out_j.shape[0]):
if out_j[k, i] > 0:
ppp = (int(out_j[k,i] * col_sample_w * img_w / 800) - 1, int(img_h *
(row_anchor[cls_num_per_lane-1-k]/288))-1)
cv2.circle(vis,ppp,4,(0,0,255),-1)
Image.fromarray(vis).show()
最后检测结果如下: