自制Tusimple格式车道线数据集:从标注到训练全流程实践

发布时间:2026/10/22 14:24:49
自制Tusimple格式车道线数据集:从标注到训练全流程实践 1. 自制Tusimple格式数据集的核心思路1.1 为什么值得自己造一份数据集很多做车道线检测的同学第一步都会去找开源的Tusimple数据集来训练。但实际跑通以后你会发现一个很尴尬的问题别人采集的是国外高速公路场景车道线清晰、视野开阔、路面平整而你自己要做的场景可能是城市窄路、乡镇道路、甚至园区内部道路直接把开源数据训出来的模型放到这些场景里检测率会明显下滑。我做了几个车道线检测项目之后逐渐养成了一个习惯不管用什么模型先做一份贴合自己应用场景的Tusimple格式数据集。原因很简单模型能不能用很大程度上不取决于网络结构有多新而取决于训练数据和你真实场景有多接近。与其在模型上反复调参不如先把数据地基打牢。Tusimple数据集格式之所以被广泛使用是因为它的标注形式简单直观又贴近真实道路感知需求。它把一条车道线表示为一组离散的点坐标正好适合深度学习模型直接回归输出。自制数据集的过程本质上就是按照这套格式把你采集到的道路图片变成模型能学习的标注文件。1.2 Tusimple格式到底在描述什么Tusimple格式的核心不是图片本身而是一个JSON文件。每一行JSON对应一张图片描述这张图片里的所有车道线信息。我之前第一次看这个格式的时候也绕了一会儿后来用一个生活化的类比才彻底搞明白可以把车道线想象成一条弯曲的铁丝JSON里的坐标点就是铁丝上等间距打的小孔。具体到字段最关键的有四个raw_file图片的相对路径模型训练时靠它找到对应的图。lanes一个二维数组每一行代表一条车道线的所有x坐标。h_samples一组固定的y坐标和lanes里的每一行一一对应。lane_annotations每条车道线的属性标注包括是否有效、是否是实线/虚线等。这里最容易被忽视的是坐标系的约定。Tusimple数据集的图像尺寸通常为1280×720h_samples是从y240开始、每隔10个像素取一个点一直到y710一共48个采样高度。lanes里的每条线就是在这些y值位置上的x坐标。如果某条车道线在某些位置不存在就填-2而不是0。这是格式的坑之一填0会被模型当成x0位置的线结果完全错乱。理解了这个结构自制数据集的方向就清晰了先采集图片然后标注每条车道线在48个高度上的x坐标最后打包成JSON。听起来步骤不多但里面有很多细节值得展开说。2. 素材采集与标注规范2.1 采集素材的条件和注意点制作数据集的第一步不是标注而是采集。素材质量直接决定标注效率和最终模型效果。我踩过的最大一个坑是第一版数据集用的是不同角度、不同高度的手机随手拍的照片结果模型训练完以后在同一段路上测试有时检测得到有时检测不到。后来才知道是相机安装高度和俯仰角不一致导致消失点位置飘忽模型学到的几何规律不统一。如果你想做一份靠谱的Tusimple格式数据集采集阶段建议满足下面几个条件相机固定安装不要拿在手里随意拍。可以使用行车记录仪、固定支架的手机或专用摄像头。安装高度和角度尽量一次定好所有素材保持一致。前视摄像头通常安装在后视镜附近高度1.3米左右俯仰角让画面地平线大致处于图像中部偏上。覆盖不同场景直道、弯道、上坡下坡、光线充足和逆光、地面清晰和略脏污都要有一定比例。如果要做多车道线检测尽可能拍到两条到五条车道线这样标注出来的数据更有训练价值。采集完的图片建议统一缩放成1280×720。不要偷懒直接使用原始分辨率因为Tusimple格式的h_samples就是基于1280×720设定的如果你用其他分辨率要么自己重新定义采样区间要么模型输出和格式对不上。2.2 车道线类型的语义约定与标注点采样Tusimple格式里有一个lane_annotations字段里面每条车道线的属性分为五类分别用数字表示。实际项目里最常用的是前四种0表示当前车道左线、1表示当前车道右线、2表示相邻车道左线、3表示相邻车道右线。这个语义和车辆当前行驶位置强相关不是简单地按图片从左到右排序。我见过不少初学者在这里栽跟头把所有车道线都标成0或1模型训练出来以后完全分不清车道归属关系。其实自己标注的时候心里要有一个基准以车辆当前所在车道为中心最靠近车身的左右两条线是0和1再往外扩展是2和3。标注点的采样密度官方格式是每10个像素取一个点。实际操作中尽量不要为了省事减少采样点数量。采样点太少弯道部分的曲线拟合会严重失真采样点太密标注工作量和标注文件体积又会增加。折中方案是直道上可以隔20个像素标一个控制点弯道上隔5到10个像素标一个控制点最后统一在48个h_samples位置插值生成坐标。这里还有一个很重要但常被忽略的点无效点填充值为-2。比如某条车道线在图像远端已经超出视野那么超出部分的x坐标就全部填-2。模型在训练时会通过mask机制忽略这些位置如果你填0等于人为制造了一条从图像边缘穿过的假车道线会严重干扰训练。3. 标注实操全流程3.1 标注工具怎么选Tusimple本身没有公开完整的官方标注工具所以自制数据集通常有两种路线一是用现成的通用标注工具标控制点再写脚本转换二是自己写一个简单的标注小工具。我个人更推荐第一种因为通用标注工具更成熟缩放、拖动、撤销这些基础功能都有比自己从零写省时间得多。常见的做法是用LabelMe这类工具在图片上标点位每个点对应车道线的一个控制点。标注时按一条线的顺序依次打点点完一条标下一条最后导出成LabelMe的JSON格式再通过转换脚本生成Tusimple需要的格式。如果你项目周期比较长需要反复标注多批数据也可以自己写一个轻量标注工具。界面不需要花哨能展示图片、鼠标点击取点、撤销上一个点、按一组预定义的y值自动插值并保存标注结果就够了。我实际使用后发现一个顺手的小工具能把标注效率提升两倍以上尤其是批量标注时能省掉很多机械重复操作。3.2 一条车道线从标注到采样的完整过程以标一条完整车道线为例具体流程是这样的打开图片先整体观察有几条车道线需要标注确定每条线的类型编号。从距离车辆最近的位置开始打点沿着车道线中心线往远处标打点间隔尽量均匀。车道线如果有断裂或者被车辆遮挡不要跳过整个区域而是尽量按车道线的走向趋势补点。补不了的后续插值采样时这部分自然会被填成-2。一条线标完后切换到下一条线注意类型编号按左右交替来不要重复。全部标完后检查一遍每条线的起止范围和车道线类别确认无误再保存。这里面最关键的是插值步骤。因为你在标注时打的点不可能恰好都在h_samples采样高度上所以需要根据标注控制点拟合出一条折线或曲线再在48个固定y值位置取对应的x坐标。直线段可以直接线性插值弯道建议用样条插值效果会平滑很多。后续的脚本部分我会给出具体实现。3.3 标注质量控制与一致性检查标注质量比标注速度重要得多。我自己有一条经验与其一口气标100张粗制滥造的图不如分两轮标100张高质量的图。模型训练对标注错误非常敏感哪怕只有5%的标注点偏了几个像素最终检测出来的车道线在弯道上也可能出现明显的锯齿或抖动。标完一批图之后建议做一个简单的一致性检查把所有标注结果叠到原图上生成一张带车道线覆盖的可视化结果图。肉眼扫一遍重点关注三条车道线是否贴合实际道路线远处是否有明显飘移。各条线的类型编号顺序是否正确有没有出现两条线都是0的情况。断点处是否有异常跳变比如某条线在某个y位置x坐标突然从100跳到300。这些检查看起来很基础但能挡住大部分低质量数据。可视化检查这一步我在后面也会给出一个极简的脚本配合OpenCV几行代码就能完成。4. 生成Tusimple格式JSON的脚本实现4.1 目录结构与命名规范在实际项目中一份标准的数据集目录通常长这样dataset/ ├── train/ │ ├── clips/ │ │ ├── 0000/ │ │ │ ├── 00000.jpg │ │ │ ├── 00001.jpg │ │ │ └── ... │ │ ├── 0001/ │ │ │ └── ... │ └── label.json ├── val/ │ ├── clips/ │ │ └── ... │ └── label.jsonlabel.json里的raw_file路径要和目录结构一致。比如训练集第一张图raw_file写成clips/0000/00000.jpg。路径问题看着小实际坑不少。我用绝对路径写过一次结果换了机器训练路径全部失效重新改配置浪费了整整一个下午。4.2 从标注点到Tusimple JSON的转换脚本下面是我实际用的一个转换脚本输入是手动标注的控制点文件输出为Tusimple格式的JSON。控制点文件我用的是极简的自定义格式每行一个点坐标用逗号分隔多条车道线用空行隔开。import json import os import re from scipy.interpolate import splprep, splev import numpy as np H_SAMPLES list(range(240, 720, 10)) # 48个采样y坐标 def interpolate_lane(points): 对一条车道线的控制点做样条插值。 返回一个数组每个元素对应H_SAMPLES位置上的x坐标。 pts np.array(points, dtypenp.float64) # 控制点少于2个就返回全-2 if len(pts) 2: return [-2] * len(H_SAMPLES) # 用样条插值s0表示强制通过所有控制点 tck, u splprep([pts[:, 0], pts[:, 1]], s0) result_x [] for y in H_SAMPLES: # 计算当前y值是否在控制点覆盖范围内 if y pts[:, 1].min() or y pts[:, 1].max(): result_x.append(-2) else: # 二分查找最近的u值区间再插值x ratio (y - pts[:, 1].min()) / (pts[:, 1].max() - pts[:, 1].min()) u_target u[0] (u[-1] - u[0]) * ratio x_interp, y_interp splev(u_target, tck) result_x.append(round(float(x_interp), 1)) return result_x def convert_annotation(annotation_file, image_path, lane_types, output_json): annotation_file: 手动标注的控制点文件 image_path: 对应的图片路径写入raw_file字段 lane_types: 每条车道线的类型列表如[0, 1, 2] output_json: 输出的Tusimple格式JSON文件路径 with open(annotation_file, r, encodingutf-8) as f: content f.read().strip() # 按空行拆分得到每条车道线的控制点列表 lane_blocks re.split(r\n\s*\n, content) lanes [] lane_annotations [] for i, block in enumerate(lane_blocks): if not block.strip(): continue points [] for line in block.strip().split(\n): x_str, y_str line.strip().split(,) points.append((float(x_str), float(y_str))) lane_x interpolate_lane(points) lanes.append(lane_x) lane_annotations.append({ lane_id: i, lane_type: lane_types[i] if i len(lane_types) else 0, valid: True, attributes: [solid if (i % 2 0) else dashed] }) entry { raw_file: image_path, lanes: lanes, h_samples: H_SAMPLES, lane_annotations: lane_annotations } with open(output_json, a, encodingutf-8) as f: json.dump(entry, f) f.write(\n)这个脚本的核心逻辑是interpolate_lane函数。它把标注控制点作为样条控制点在每个h_samples位置求x坐标。这个思路学名叫“按行采样”就是Tusimple格式的本质——一条车道线不再是一串任意密度点而是固定y间隔下的等间距点。脚本里需要特别注意的是控制点覆盖范围的判断。如果某条车道线在y240位置根本还没出现比如虚线起始点比较晚那这个位置的x就直接填-2绝对不能往前面硬插值。4.3 数据划分与可视化验证JSON生成完以后不要急着丢给模型训练先做两件事划分数据集、可视化验证。划分数据集的原则是尽量按场景分不要按图片随机分。因为连续视频帧之间有很强的相似性随机划分会导致训练集和验证集出现同一条道路甚至同一帧的情况验证指标虚高。我习惯把不同时间、不同路段采集的素材分别编号按编号整体划分。比如采集了10个片段8个进训练集、2个进验证集比例大概80%和20%。可视化验证代码很简单import cv2 import json def visualize_label(image_path, lanes, h_samples): img cv2.imread(image_path) for lane in lanes: for x, y in zip(lane, h_samples): if x 0: cv2.circle(img, (int(x), int(y)), 3, (0, 0, 255), -1) cv2.imshow(check, img) cv2.waitKey(0) with open(label.json, r) as f: for line in f: entry json.loads(line) visualize_label(entry[raw_file], entry[lanes], entry[h_samples])看到红色点在图上连成几条清晰的车道线数据基本就没问题了。如果某条线的点在弯道上明显偏离车道线中心位置回到标注文件里修正那几个控制点再重新生成。5. 常见问题与排查技巧实录5.1 高频问题速查表做数据集制作和转换的这段时间我整理了一份问题排查表按出现频率排序。问题现象可能原因解决方案训练时Loss爆炸lanes里有大量0值检查无效点是否填成-2而非0检测出的车道线在远端消失h_samples范围超过图像高度确认图片尺寸统一为1280×720车道线类型全部一样标注时未按左右顺序编号重新核对lane_annotations的lane_type弯道处线形扭曲控制点太少或插值方式不对弯道加密标注点改用样条插值图片和标注错位raw_file路径写错统一使用相对路径目录结构保持一致模型在验证集分数高但实际效果差训练集和验证集场景重叠按视频片段整体划分不按帧随机划分这个表里每一行都是我实际踩过的坑尤其是第一行影响最大也最容易犯。很多人在生成JSON时习惯性地把缺失点填0结果模型把图像最边缘当成车道线训练根本收敛不了。5.2 独家避坑技巧关于标注密度、相机参数和类别平衡标注密度这块我想多说几句。Tusimple格式的h_samples是每10像素一个y值但这不代表你标注控制点时也要每10像素一个点。控制点可以比采样点少很多直线上两头各一个点就够了弯道上适当加密。这里的关键是插值质量建议弯道控制点间距控制在15像素以内否则样条插值容易在曲率大的地方出现过冲。相机参数方面不同摄像头采集的图片消失点位置差异很大。Tusimple格式本身不要求标定参数但为了让模型适应不同安装角度最好在数据集中自然涵盖一些轻微的角度变化。比如有的图片略微下俯有的略微上抬这样训练的模型才会有一定鲁棒性。不要所有图片都在同一个固定角度下采集那样模型学到的几何规律过拟合得很严重。类别平衡也是一个容易被忽视的问题。如果采集的素材大量是直道弯道数据很少模型对弯道的检测能力会明显偏弱。我在实际项目中就吃过亏训练集里直道占了85%模型在弯道上几乎每次都在远端把车道线丢掉。后来单独补采了一批弯道素材把弯道比例提升到30%左右效果立竿见影。做数据采集规划的时候一定要先盘算好场景分布而不是想到什么拍什么。5.3 大批量标注时的效率提升思路如果你需要标注的数据量很大比如几百张甚至上千张纯手动标注会很痛苦。我的做法是先手动标注20张左右跑通整个流程然后用这些标注数据做一个简单的模型再对剩余图片进行预标注最后人工修正预标注结果。预标注的做法并不复杂把已标注的图片送入一个预训练模型或自己刚训练的小模型得到初步的车道线点坐标以叠加可视化形式保存成图片标注工具加载这些图片时自动带上预标注点人工只需要拖动错误点位。这能把标注时间压缩到原来的三分之一左右。这个思路其实和主动学习有点类似先用少量高质量标注获取初始能力再用模型辅助扩展数据集规模。对比纯手动标注单张图的标注时间从3分钟缩短到1分钟以内总耗时能降低一半以上。6. 从数据到训练的无缝衔接经验6.1 模型读取Tusimple格式时容易忽略的细节很多车道线检测模型都提供了Tusimple数据集的加载接口但直接加载自制数据时有几个细节需要和模型代码对齐。首先是h_samples是否要动态读取。有的模型在DataLoader里写死了h_samples为默认值240到710如果你改了自己的采样区间模型不会自动适配。我建议在训练脚本中从JSON第一行读取h_samples动态传入模型避免硬编码。其次是lanes的归一化处理。有的模型把x坐标除以图像宽度缩放到[0,1]有的直接使用像素坐标。自定义数据集时要确认模型的预处理方式和标注格式匹配。否则模型输入输出与标签坐标维度不统一训练过程不会报错但指标会非常诡异检测出来的点全部偏移。最后是mask机制。Tusimple格式中-2的无效点在模型里通常对应一个mask矩阵训练时这些位置的loss会被忽略。有些模型的实现只支持全图mask有些支持逐点mask。如果你的场景里车道线经常被车辆遮挡无效点很多务必确认模型的mask处理能力否则这些位置会被强行参与loss计算产生明显的拖拽效应模型会被遮挡物带偏。6.2 训练效果验证的一个实用方法训练完模型后不要只看验证集指标建议找一个和训练集完全不同的真实场景做一次快速实测。挑一段没有出现在数据集里的路录一段视频跑一遍检测观察车道线在直道、弯道、光线变化下的表现。我自己常用的快速验证方式是在车内副驾位置放一台笔记本把摄像头画面实时送入模型处理输出重叠了车道线检测结果的画面。这种实时反馈比任何离线指标都更能暴露问题。比如车道线在强逆光下大面积闪烁、弯道远端频繁抖动这些在验证集指标里看不出来但在真实场景里一测就能发现。如果实测效果不理想不要急着调模型超参优先检查数据。我会重新做一次标注质量抽样检查随机抽10张训练图逐条看车道线和实际线形的贴合度。大多数情况下问题出在标注误差较大的那几张图上。6.3 一次完整的数据集迭代周期参考根据我的经验一份可用的Tusemple格式数据集从零开始制作可以按这样的节奏推进比如有一周时间第一天完成素材采集和筛选第二天标注第一批图片并写好转换脚本第三天做可视化检查和质量修正第四天补充弯道和特殊场景素材第五天完成全部标注并生成最终JSON第六天训练模型跑一遍完整流程第七天实测并根据结果决定是否多采一批数据补进去。这个节奏的关键是第三天必须做可视化检查。很多人在转换脚本写完、JSON生成之后就以为万事大吉了直接开始训练结果模型训了一宿第二天一看效果不对回头才发现是标注或转换环节出了问题浪费了大量时间。可视化检查只要半小时却能避免一整夜无效训练这笔账怎么算都值。7. 关于这份这份数据集的几点心得结合实际项目经验我想说自制一份Tusimple格式数据集价值不只是得到一批标注数据。整个流程走下来你会对格式的每个字段、每个坐标点的含义有十分深刻的理解。以后再调试模型遇到检测结果异常你会更快地判断出到底是不是数据问题。我个人在操作中的体会是标注质量远比数据量更重要。你用500张高质量标注数据训练出来的模型效果往往比2000张粗标注数据更好。因为车道线检测本质上是几何回归任务标注点位的准确性和一致性直接影响模型学到的车道线分布规律。另外自动化脚本和数据规范化这部分花一点时间做得完整一些后面能省很多麻烦。我一开始图省事用临时脚本处理标注后来换场景加数据时发现脚本完全没法复用又重新整理了一遍目录结构和转换逻辑。如果能在一开始就规划好目录、脚本和检查流程后续扩展数据集会非常顺畅。最后再分享一个小技巧第一次做完一份数据集后一定要完整地跑一遍训练和验证流程记录下各项指标比如准确率、误检率、每张图的推理耗时。这些数据可以作为基线保存下来之后每次给数据集新增样本、调整标注策略或更换场景时都用同一套模型和参数重新训练然后和基线对比。这样你能非常直观地看到每次变化的收益而不是拍脑袋觉得“好像数据多了效果应该会变好”。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询