车道线检测是自动驾驶与智能驾驶辅助系统的基础感知能力——通过计算机视觉或深度学习技术识别道路图像中的车道线位置、形状与走向,为车道保持、路径规划与安全行驶提供关键输入。

与云端方案相比,端侧部署能降低数据传输延迟、减少对云端计算与网络连接的依赖,并增强道路图像数据的隐私安全性——避免因网络延迟、信号不稳定或通信中断导致检测结果无法及时获取,保障车辆在弱网或无网环境下仍能持续进行车道线检测。这正是自动驾驶及智能驾驶辅助系统对实时性、可靠性与环境适应性的硬要求。

灵眸科技在 EASY-EAI-PI2(RV1126B) 上完成了 lane-detect 算法的端侧适配,单次推理 100ms(含预处理及后处理),并通过封装好的 EASY-EAI API 提供标准化调用接口。本文记录从源码下载、模型部署、例程编译到 API 调用的完整链路,并逐段拆解例程代码的四个环节。

车道线检测网络架构示意图

01——为什么车道线检测要跑在端侧

把车道线检测放在端侧,不止"算力够不够",还有三个云端方案绕不开的结构性问题:

  • 延迟不可控 —— 网络往返时间随信号质量波动,而车道保持对时延有硬约束
  • 弱网即失效 —— 隧道、偏远路段信号中断时,云端方案直接不可用
  • 数据出域 —— 道路图像持续上传,涉及隐私与合规风险

对量产车型与商用车队而言,这三项都属于无法通过优化云端算力解决的约束,只能靠端侧部署绕开。

02——快速上手:从环境到结果

开发环境准备:

若初次使用,需先按《入门指南/开发环境准备/Easy-Eai编译环境准备与更新》完成编译环境部署。在 PC 端 Ubuntu 中执行 run.sh 进入 EASY-EAI 编译环境。

⚠️ 版本一致性要求: 开发环境版本必须与开发板正在运行的系统版本一致。若开发板是其它版本固件,可修改 ./run.sh 参数(如 2204)进入对应编译环境,或将开发板烧录为与编译环境匹配的固件。

通过串口或 SSH 登录开发板,执行 lsb_release -a 可查看系统版本。

源码下载:

在编译环境下创建管理目录并克隆仓库:

cd /opt
mkdir EASY-EAI-Toolkit
cd EASY-EAI-Toolkit
git clone https://github.com/EASY-EAI/EASY-EAI-Toolkit-1126B.
📌 注意: 若需手动下载,请下载整个仓库,不能只下载本实例对应的目录——例程依赖仓库中的公共库与头文件。

模型部署:

下载车道线检测模型(网盘链接见文末),并将模型文件复制粘贴到Release/目录下。

例程编译:

cd EASY-EAI-Toolkit-1126B/Demos/algorithm-lane_detect/
./build.sh cpres
⚠️ 两个前提: ① 依赖库部署在板卡上,交叉编译过程中必须保持 /mnt 挂载;② build.sh 带 cpres 参数时,会把 Release/ 目录下的所有资源拷贝到开发板。

例程运行:

在板卡后台执行:

cd /userdata/Demo/algorithm-lane_detect/
./test-lane-detect lane_detect.model test.jpg

第一个参数指定模型路径,第二个参数指定推理图像。Demo 会进行 10 次推理计算平均延迟,然后绘制推理结果并保存为result.jpg。

03——API 说明:五个函数与四组数据结构

为便于在本地工程中直接调用,EASY-EAI 将车道线检测封装为标准 API 库。

引用方式:

其中lane_detect.h声明初始化、运行与释放接口;utils.h声明结果转换与车道线绘制等辅助接口。

接口注释:

数据结构:

理解这几个结构体的关系,是正确调用 API 的关键。

lane_detect_context—— 运行周期内使用的模型上下文,含rknn_ctx、io_num、输入/输出张量属性、以及 inference_width / inference_height(模型的推理宽高,可用于坐标归一化)。

detect_result—— 模型输出的原生数据:

lane —— 转换后的车道线,核心成员 points 为 std::vector<cv::Point>,存储一条车道线上的有效采样点(原图坐标系)。

postprocess_info —— 由 lane_detect_run() 内部预处理流程生成的辅助信息,含 ratio_y、ratio_x、croped_y(图像纵向从上往下被裁掉的行数),作为参数传给 results_to_lanes()。

📌 两个设计意图值得注意:
  • results_to_lanes() 未集成进 lane_detect_run(),是为了保留模型的原始推理结果,便于开发者按实际需求做进一步处理。
  • 调用 results_to_lanes() 时应使用与该次推理结果相匹配的 postprocess_info,且不要在调用前手动修改结构体成员值,否则会影响检测结果到原图坐标系的转换。

04——算法说明:预处理为什么裁顶部

例程test-lane-detect.cpp分为四个环节:模型初始化 → 图像读取 → 模型运行与释放 → 结果转换与绘制。

在了解代码前,需要说明一个容易被忽略但直接影响效果的预处理策略。

算法默认摄像头安装时,天空或无车道区域位于采集图像上方。模型推理采用 2:1 宽高比,并根据输入图像的宽高比采用不同预处理方式:

  • 宽高比 < 2:1(更接近正方形):先将图像宽度放大至预设推理尺寸(当前版本 384),再裁去高度方向超出推理范围的区域。裁切从图像顶部进行。以 350×192 为例,宽度缩放至 384 后高度约 210 像素,超出部分约 18 像素被裁掉。
  • 宽高比 > 2:1(更狭长):直接将图像 Resize 至预设推理尺寸。

以 1920×1080 输入为例,其宽高比约 1.78:1,触发第一种逻辑:等比缩放宽度至 384 像素并裁剪顶部,在确保水平方向图像信息完整保留且不产生形变的前提下,去除主要由天空构成的顶部冗余区域。

💡 工程价值: 这个策略的巧妙之处在于——被裁掉的恰恰是对车道线检测无贡献的天空区域,既满足了模型输入尺寸要求,又不会对检测效果造成负面影响。这也是为什么算法能接受任意宽高比输入,而无需调用方自己做 letterbox 填充。

05——例程代码逐段拆解

模型初始化:

创建并清零lane_detect_context,调用 lane_detect_init()加载模型:

lane_detect_context ctx;
memset(&ctx, 0, sizeof(ctx));
ret = lane_detect_init(&ctx, model_path);
if (ret != 0) {
    printf("Failed to initialize lane detection model.\n");
    return -1;
}
📌 提示:inference_width 和 inference_height 仅用于特殊需求下的坐标归一化,运行期间修改这两个值不会改变模型底层的实际推理尺寸。

图像读取:

cv::Mat input_image = cv::imread(image_path, cv::IMREAD_COLO

输入图像必须严格满足三项格式约束(这是最常见的调用失败原因):

lane_detect_run() 内部已包含必要的数值预处理,因此调用前无需额外做数值归一化;但若业务图像不满足上述约束,必须在传入前完成格式转换。

模型运行:

std::vector<detect_result> model_results;
postprocess_info info;
float score_threshold = 0.41f;  // 默认置信度阈值
int   nms_threshold   = 50;     // 默认临近距离阈值

for (int i = 0; i < 10; ++i) {
    ret = lane_detect_run(&ctx, input_image, model_results, info,
                          score_threshold, nms_threshold);
    ...
}

性能统计采用 10 次推理取平均的方式。


⚠️ 一个容易踩的坑:lane_detect_run()每次调用会自动清空传入的 results 容器。因此务必在下一次推理调用前,完成当前推理结果的提取或存储(例如调用 results_to_lanes() 转换)。容器内仅保留有效检测结果,可直接通过 model_results.size() 获取当前帧检测到的车道线总数。

可调参数:

结果转换与绘制:

std::vector<lane> lanes;
ret = results_to_lanes(model_results, lanes, info);

cv::Mat output_image = input_image.clone();
draw_line(output_image, lanes);
cv::imwrite("result.jpg", output_image);

转换后lanes与results严格一对一映射、顺序完全一致(model_results.size() == lanes.size())。

lane.points是cv::Point容器,可直接调用 OpenCV 的 cv::polylines 高效绘制:

cv::polylines(image, lane.points, false, cv::Scalar(0,255,0),
⚠️ 同样需注意:results_to_lanes() 也会自动清空传入的 lanes 容器,请在下一次调用前妥善保存或已使用已转换数据。

模型释放:

ret = lane_detect_release(&ctx);

调用后上下文被彻底销毁,此后不可再调用任何依赖该上下文的推理函数。

结语

对正在评估车道线检测方案的团队而言,100ms 的端侧推理耗时(含预处理与后处理)意味着该算法可以嵌入实时视频流水线;而标准化的-llane_detect库链接方式,则意味着集成成本主要集中在图像格式转换,而非算法本身。

灵眸科技旗下产品 EASY-EAI-PI2(RV1126B)已内置 lane-detect 算法支持,本文所用源码、模型与例程均可通过社区获取。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐