ultra fast lane detection提供了很好的源码,根据演示视频来看,效果似乎不赖,很有必要试一试该算法。
一、基本情况
作者知乎:https://zhuanlan.zhihu.com/p/157530787
简单来说,作者认为卷积层形式的输出,导致局部感受野小,很明显车道线识别需要结合全局特征来分析。而全连接层形式的输出,运用了全局特征,也就没有感受野的问题。
另外,作者把这个问题转化为了分类问题。也就是如下图所示。
C是车道线数目,h和w则是图像长宽相关的cell,类似于锚点。w方向上的通道是w+1个,最后一位输出的是有无该通道。
大概意思,就是用h和w预测第c个车道的大概位置,然后按照下面的公式,算具体位置:
损失函数还考量到相邻的位置附近,因此有了,后两个损失函数就是邻近位置的二阶差分,邻近概率的差分。
二、使用情况
作者在github上已经给出了开源库。要求先设置configs下的py,我选了culane数据集(比较大,大概50多个G),因此修改了culane.py。
需要注意的是,culane数据集有黑夜的数据集,但貌似没有特殊天气的数据。
之后就在pycharm进行了训练。
以下是笔者使用的libtorch C++端的代码,另外需要提醒一点(如果对数据的存储与运算不熟悉的话)
CPU类型下大多数函数都可以使用,但是推理速度比较慢。CUDA类型下并行速度快,但是有些函数不能用,遍历计算速度很慢,需要转为CPU类型。
#include
#include
#include
#include
#include
#include
using namespace torch::indexing;
using namespace std;
int tusimple_row_anchor[56] = {64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112,
116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160, 164,
168, 172, 176, 180, 184, 188, 192, 196, 200, 204, 208, 212, 216,
220, 224, 228, 232, 236, 240, 244, 248, 252, 256, 260, 264, 268,
272, 276, 280, 284};
int culane_row_anchor[18] = {121, 131, 141, 150, 160, 170, 180, 189, 199, 209, 219, 228, 238, 248, 258, 267, 277, 287};
void tusimple_lane(at::Tensor& input, cv::Mat& img)
{
int lane_num = 4;
int length_cell = 56;
int width = 100;
int topk_n = 3;
double w_scale = img.cols / (double)width;
double h_scale = img.rows / (double)(length_cell*2);
input = input.to(torch::kCPU);//CPU for Ergodic computation,CUDA for Parallel computation
for (int i = 0; i < length_cell; i++)
for (int j = 0; j < lane_num; j++)
{
double lane_loc = 0;
double sum_w = 0;
double sum = 0;
auto cut = input.index({ Slice(torch::indexing::None),Slice(torch::indexing::None), Slice(i,i + 1), Slice(j,j + 1) });
cut = torch::softmax(cut, 1).view(-1);
if (cut[width].item() > 0.5)
{
continue;
}
std::vector tesnor2vec(cut.data_ptr(), cut.data_ptr() + cut.numel());
for (int s = 0; s < width; s++)
{
sum += tesnor2vec[s];
sum_w += tesnor2vec[s] * s;
}
lane_loc = (sum_w / sum) * w_scale;
//std::tuple result = cut.topk(2, -1);
//auto top_scores = std::get<0>(result).view(-1);//{1,10} 变成 {10}
//auto top_idxs = std::get<1>(result).view(-1);
/*if (cut[width].item() > 0.5)
{
continue;
}
if(top_idxs[0].item().toInt()!=width)
{
lane_loc = top_idxs[0].item().toInt() * w_scale;
}
else
{
lane_loc= top_idxs[1].item().toInt() * w_scale;
}*/
cv::Scalar temp;
switch (j)
{
case 0:
temp = cv::Scalar(0, 0, 0);
break;
case 1:
temp = cv::Scalar(255, 0, 0);
break;
case 2:
temp = cv::Scalar(0, 255, 0);
break;
case 3:
temp = cv::Scalar(0, 0, 255);
break;
}
cv::circle(img, cv::Point(lane_loc, tusimple_row_anchor[i]), 5, temp, -1);
}
}
//
void culane_lane(at::Tensor& input, cv::Mat& img)
{
int lane_num = 4;
int length_cell = 18;
int width = 200;
int topk_n =3;
double w_scale = img.cols/(double)width;
double h_scale = img.rows/(double)length_cell;
input = input.to(torch::kCPU);//CPU for Ergodic computation,CUDA for Parallel computation
for(int i=0;i() > 0.5)
{
continue;
}
std::vector tesnor2vec(cut.data_ptr(), cut.data_ptr() + cut.numel());
for (int s = 0; s < width; s++)
{
sum += tesnor2vec[s];
sum_w += tesnor2vec[s] * s;
}
lane_loc = (sum_w / sum )* w_scale;
cv::Scalar temp;
switch (j)
{
case 0:
temp = cv::Scalar(0, 0, 0);
break;
case 1:
temp = cv::Scalar(255, 0, 0);
break;
case 2:
temp = cv::Scalar(0, 255, 0);
break;
case 3:
temp = cv::Scalar(0, 0, 255);
break;
}
cv::circle(img, cv::Point(lane_loc, culane_row_anchor[i]), 5, temp, -1);
}
}
int main()
{
torch::jit::script::Module module = torch::jit::load("C:\\Users\\lvdon\\source\\repos\\C_test\\lane_det\\lane.torchscript_gpu.pt");
torch::DeviceType device_type = torch::kCUDA;
module.to(device_type);
module.eval();
cv::VideoCapture cap("G:\\视频\\弯道\\cv2_curve.mp4");
//cv::VideoWriter vw;
//vw.open("C:\\Users\\lvdon\\source\\repos\\C_test\\test.mp4", //路径
// CV_FOURCC('m', 'p', '4', 'v'), //编码格式
// 30, //帧率
// cv::Size(800,
// 288));//尺寸
cv::Mat img;
cv::Mat ori;
cap.read(img);
int width = img.size().width;
int height = img.size().height;
int count = 0;
while (true)
{
clock_t start = clock();
count++;
cap >> img;
if (img.empty())
{
cout << "play end" << endl;
break;
}
else if (count % 1 != 0)
{
continue;
}
//img = cv::imread("E:\\train_lane\\driver_182_30frame\\05312333_0003.MP4\\00000.jpg");
cv::resize(img, img, cv::Size(600, 288));
cv::copyMakeBorder(img, img,0, 0, 100, 100, cv::BORDER_CONSTANT, cv::Scalar(0, 0,0));
cv::cvtColor(img, img, cv::COLOR_BGR2RGB); // BGR -> RGB
img.convertTo(img, CV_32FC3, 1.0f / 255.0f); // normalization 1/255
auto tensor_img = torch::from_blob(img.data, { 1, img.rows, img.cols, img.channels() },torch::kFloat).to(device_type);
tensor_img = tensor_img.permute({ 0, 3, 1, 2 }).contiguous(); // BHWC -> BCHW (Batch, Channel, Height, Width)
std::vector inputs;
inputs.emplace_back(tensor_img);
auto output = module.forward(inputs).toTensor();
culane_lane(output,img);
cout << "cost time:" << clock() - start << endl;
//cv::imshow("result", img);
img.convertTo(img, CV_8UC3,255);//need to convert to CV_8UC3 for save the video
cv::imshow("result", img);
//vw << img;
//cv::imshow("result", img);
cv::waitKey(1);
}
return NULL;
}
模型链接: https://pan.baidu.com/s/1MZhkt87aWFb771gwUBxH6A?pwd=LDHL 提取码: LDHL
转载前,请注明来源。