Lane检测是自动驾驶和高级驾驶员辅助系统(ADAS)的基本组成部分,用于区分和定位道路上的车道线。尽管深度学习模型已经取得了巨大成功,但仍有一些重要且具有挑战性的问题需要解决。
第一个问题是效率问题。在实践中,车道线检测算法被大量执行,以利用受约束的车辆计算设备为下游任务提供即时感知结果,这需要快速的检测速度。
传统方法通常使用低级图像处理技术来解决车道检测问题。通过使用低级别的图像处理,传统方法本质上是以自下而上的方式工作的。他们的主要想法是通过HSI颜色模型和边缘提取算法等图像处理来利用视觉线索。Gold是使用立体视觉系统的边缘提取算法检测车道和障碍物的最早尝试之一。
除了使用不同颜色模型的特征和边缘提取方法外,有方法还提出使用投影几何和逆透视映射来利用车道线在现实世界中通常平行的先验信息。尽管许多方法尝试了不同的车道线传统特征,但在复杂的场景中,来自低级图像处理的语义信息仍然相对不足。通过这种方式,跟踪是另一种流行的后处理解决方案以增强鲁棒性。除了跟踪,马尔可夫和条件随机场也被用作后处理方法。此外,还提出了一些采用学习机制的方法(如模板匹配、决策树和支持向量机)。
随着深度学习的发展,一些基于深度神经网络的方法在车道检测方面显示出了优越性。这些方法通常使用指示车道存在和位置的Headtmap来处理车道线检测任务。
在这些早期的尝试之后,主流方法开始将车道检测视为一个分割问题。例如,VPGNet提出了一种由消失点引导的多任务分割网络,用于车道和道路标记检测。为了扩大逐像素分割的感受野并提高性能,SCNN在分割模块中使用了一种特殊的卷积运算。它通过处理切片特征并将其逐个相加来聚合来自不同维度的信息,这类似于递归神经网络。RONELD提出了一种SCNN的增强方法,通过分别寻找和构建直线和曲线主动车道。RESA还提出了一种类似的方法,通过反复的特征转移来扩大感受野。
由于分割方法的计算量很大,一些工作试图探索用于实时应用的轻量级方法。自注意力蒸馏(SAD)采用了一种注意力蒸馏机制,将高层次和低层次的注意力分别作为教师和学生对待。IntRA KD还使用inter-region affifinity蒸馏来提高学生网络的性能。通过注意力提取,浅层网络可以具有与深层网络类似的性能。CurveLane NAS引入了神经结构搜索技术,用于搜索为车道线检测量身定制的分割网络。在LaneAF中,这项工作提出通过在基于分割的affinity fields中进行投票来检测车道线。FOLO-Lane提出对局部模式进行建模,并通过全局几何解码器以自下而上的方式实现全局结构的全局预测。
python
import cv2
import numpy as npfrom rknn.api import RKNN
import osif __name__ == '__main__':platform = 'rk3568'exp = 'fastpose'Width = 640Height = 640MODEL_PATH = 'ufld.onnx'NEED_BUILD_MODEL = True# NEED_BUILD_MODEL = Falseim_file = './bus.jpg'# Create RKNN objectrknn = RKNN()OUT_DIR = "rknn_models"RKNN_MODEL_PATH = './{}/{}_kk.rknn'.format(OUT_DIR,exp+'-'+str(Width)+'-'+str(Height))if NEED_BUILD_MODEL:DATASET = './data.txt'rknn.config(mean_values=[[123, 116, 103]], std_values=[[58, 57, 57]], target_platform="rk3568")# Load modelprint('--> Loading model')ret = rknn.load_onnx(MODEL_PATH)if ret != 0:print('load model failed!')exit(ret)print('done')# Build modelprint('--> Building model')ret = rknn.build(do_quantization=True, dataset=DATASET)if ret != 0:print('build model failed.')exit(ret)print('done')# Export rknn modelif not os.path.exists(OUT_DIR):os.mkdir(OUT_DIR)print('--> Export RKNN model: {}'.format(RKNN_MODEL_PATH))ret = rknn.export_rknn(RKNN_MODEL_PATH)if ret != 0:print('Export rknn model failed.')exit(ret)print('done')else:ret = rknn.load_rknn(RKNN_MODEL_PATH)rknn.release()
main.c
// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License./*-------------------------------------------Includes
-------------------------------------------*/
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>#define _BASETSD_H#include "RgaUtils.h"
#include "im2d.h"
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "rga.h"
#include "rknn_api.h"
#include "detface.h"#define PERF_WITH_POST 1
/*-------------------------------------------Functions
-------------------------------------------*/double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); }// ================= 程序入口===============//
int main(int argc, char** argv)
{struct timeval start_time, stop_time;char* model_name = NULL;model_name = (char*)argv[1];char* image_name = argv[2];printf("Read %s ...\n", image_name);cv::Mat orig_img = cv::imread(image_name, 1);if (!orig_img.data) {printf("cv::imread %s fail!\n", image_name);return -1;}cv::Mat img;cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);int img_width = img.cols;int img_height = img.rows;std::string nameModel = "tusimple";detface facedet;facedet.detfaceinit(model_name,nameModel); // 初始化gettimeofday(&start_time, NULL); // 计算时间int test_count = 1;for(int i =0;i<test_count;i++){detectufld1 detect_result_group;facedet.detfaceinference(img,detect_result_group,0.25,0.45);for (auto& line : detect_result_group.line_list){for (auto& p : line){cv::circle(orig_img, p, 3, cv::Scalar(0, 255, 0), -1);}}}gettimeofday(&stop_time, NULL);printf("loop count = %d , average run %f ms\n", test_count,(__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count);imwrite("./out.jpg", orig_img);facedet.detinit();// 释放
}