资讯动态

道路标线数据集处理与YOLO训练实战:从标注清洗到部署避坑

发布时间:2026/10/2 5:11:30 来源:尧图企业网站定制
简介这份资源面向计算机视觉与自动驾驶方向的学习者和研究者提供一套已完成标注的道路标线识别数据集可用于目标检测模型的训练与验证。数据集共1449个文件以png图像和txt标注文件为主另含少量m脚本文件压缩包约250.25MB图像与标注一一对应便于直接接入YOLO、Faster R-CNN等检测框架或U-Net类分割网络。标注覆盖实线、虚线、双黄线、停车线等常见标线类型适合开展道路理解、智能交通与机器人导航相关实验。使用者可按需划分训练集、验证集与测试集并结合随机翻转、旋转、缩放等数据增强手段提升模型鲁棒性同时借助mAP、IoU、准确率与召回率等指标评估效果。目前已有1122人学习下载适合希望快速获得高质量标注数据、缩短数据准备周期并推进标线检测研究的中高级开发者。1. 1449 张道路标线数据集到手后先别急着喂给 YOLO拿到一个「1449 张、标注完成」的道路标线数据集很多人的第一反应是解压、改 data.yaml、开训。我见过太多人这么干然后 mAP 卡在 0.4 上不去回头怀疑模型、怀疑显卡、怀疑人生就是不怀疑数据本身。道路标线识别这个任务和普通的目标检测有个本质区别标线是细长、低对比度、强透视、大面积遮挡的类别它的难点不在「有没有框」而在「框得准不准、类别分得对不对、场景覆不覆盖得住」。这个标题里的 1449 张说多不多说少不少。它够跑通一个 YOLOv8 或 YOLOv11 的基线但绝对不够你直接上产线。真正决定这套数据能不能用的是标注格式是否统一、类别定义是否互斥、以及有没有把「虚线/实线/导流线/停止线」这类容易混的类别拆清楚。这篇文章就是把我拿到这类数据集后的完整处理链路讲一遍从格式核对、类别清洗、划分策略到训练参数、评估口径再到那些只有踩过才知道的坑。适合手上已经有一份标注数据、准备做道路标线检测的工程师也适合刚接触目标检测、想拿一个真实任务练手的人。2. 道路标线数据集到底长什么样先做三件事再谈训练2.1 用脚本盘一遍标注格式和类别分布标注完成不等于标注规范。市面上常见的道路标线数据集标注格式无非几种Pascal VOC 的 XML、COCO 的 JSON、YOLO 的 txt还有 Labelme 的 JSON。你拿到的 1449 张很可能是一个混合体或者标注工具换过、类别名改过。第一步不是训练是盘点。我一般先写一个统计脚本把每张图的标注文件读出来统计类别名、框数量、框的宽高比。道路标线的框有个明显特征宽高比极端要么特别扁横向停止线要么特别长纵向车道线。如果统计出来某个类别的框接近正方形那大概率是标错了。import os import glob from collections import Counter # 假设是 YOLO 格式每行 class_id x_center y_center w h归一化 label_dir labels/train class_names [] # 从 classes.txt 或 data.yaml 读入 cls_counter Counter() aspect_ratios [] for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式异常: {txt} - {line.strip()}) continue cid, x, y, w, h parts cls_counter[int(cid)] 1 w, h float(w), float(h) if h 0: aspect_ratios.append(w / h) # 记录宽高比 print(类别分布:, cls_counter) print(宽高比样本数:, len(aspect_ratios)) print(宽高比中位数:, sorted(aspect_ratios)[len(aspect_ratios)//2])这段脚本干三件事校验每行是不是标准的 5 字段、统计每个类别的框数量、收集宽高比。逻辑很直白但能立刻暴露问题。参数上label_dir指向你的标注目录class_names要和 data.yaml 里的顺序严格对应YOLO 的 class_id 是从 0 开始的整数一旦顺序错位训练出来的模型会把「实线」认成「虚线」而且 loss 还降得很正常这就是最坑的玄学来源。如果统计出来某个类别只有几十个框而另一个类别有几千个那这个数据集就是典型的长尾分布。道路标线里「停止线」「导流线」通常少「车道线」通常多。这时候你要么做重采样要么在 loss 里给稀有类别加权不能直接硬训。2.2 类别定义必须互斥别让模型猜你的心思道路标线识别最常见的翻车点是类别定义重叠。比如你定义了「白实线」「白虚线」「黄实线」「黄虚线」但标注的时候有人把「白实线」和「车道线」混着标有人把「导流线」标成「白实线」。模型面对这种数据学到的是一团浆糊。我的做法是先画一张类别定义表把每个类别的视觉特征、出现场景、和其他类别的边界写清楚。下面这张表是我处理道路标线时常用的类别拆分方式你可以对照自己的数据集改。类别名视觉特征典型场景易混类别处理建议车道线-实线连续、单色、长条车道边界、禁止变道导流线按连续性区分车道线-虚线间断、等间距允许变道实线按间断判断停止线横向、粗、路口前红绿灯路口人行横道按位置和宽度导流线斜纹、V 形、区域匝道口、分流处实线按纹理区分人行横道多条平行粗线路口、学校停止线按数量和方向这张表的作用不是给你抄而是逼你把「模型要学什么」想清楚。如果两个类别在视觉上你自己都分不清那就合并成一个类别为难模型。1449 张的规模类别数控制在 5 到 8 个比较合理超过 10 个类每个类的样本量就撑不住了。2.3 划分训练验证集别用随机划分骗自己很多人划分数据集就是train_test_split(random_state42)然后验证集 mAP 很高一上真实视频就崩。原因很简单道路标线数据往往来自连续视频帧相邻帧几乎一样。随机划分会把同一段路的相邻帧分到训练集和验证集验证集等于在考训练集见过的场景指标虚高。正确做法是按场景或按视频源划分。如果你的 1449 张来自若干段采集视频那就把其中几段整段划到验证集训练集用剩下的段。如果数据来源单一那就按时间段或按路口划分。下面是一个按文件名前缀分组的划分脚本思路。import os import glob import random from collections import defaultdict # 假设文件名格式road_001_frame_0001.jpg按 road_001 分组 img_dir images groups defaultdict(list) for img in glob.glob(os.path.join(img_dir, *.jpg)): key os.path.basename(img).split(_frame_)[0] # 提取路段标识 groups[key].append(img) keys list(groups.keys()) random.seed(42) random.shuffle(keys) val_ratio 0.2 val_count int(len(keys) * val_ratio) val_keys set(keys[:val_count]) with open(train.txt, w) as f_train, open(val.txt, w) as f_val: for k, imgs in groups.items(): target f_val if k in val_keys else f_train for img in imgs: target.write(img \n)这里的关键参数是val_ratio道路标线任务我一般留 15% 到 20% 的验证集但前提是验证集要覆盖不同光照、不同路段。如果验证集全是白天直路那它测不出模型在夜间弯道上的表现。划分完之后最好再人工翻一遍验证集的图确认场景多样性。提示如果数据集没有路段标识可以用感知哈希或简单的图像相似度做聚类把相似帧聚成一组再划分避免相邻帧泄漏。3. 用 YOLOv8 跑通道路标线基线配置、训练与评估3.1 data.yaml 的五个字段错一个就白训YOLO 系列的 data.yaml 看起来简单但道路标线任务里最容易错的是names的顺序和nc的数量。nc必须等于names列表长度且 class_id 必须和 names 的索引一一对应。我见过有人改了 names 但忘了改 nc训练不报错但类别全乱。# data.yaml path: /data/road_marking # 数据集根目录 train: train.txt # 训练集图片列表相对 path val: val.txt # 验证集图片列表 nc: 6 # 类别数必须等于 names 长度 names: 0: lane_solid 1: lane_dashed 2: stop_line 3: guide_line 4: crosswalk 5: other_markingpath是根目录train和val可以是 txt 列表也可以是图片目录。用 txt 列表的好处是你可以精确控制哪些图进训练。nc和names必须同步改这是硬性约束。另外names 里的类别名不要用中文不要用空格用下划线避免后续导出 ONNX 或部署时出编码问题。3.2 训练参数道路标线该调哪几个YOLOv8 的默认训练参数是给 COCO 调的直接拿来训道路标线效果通常一般。下面是我在 1449 张规模上常用的参数组合基于 ultralytics 的 CLI 或 Python API。yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ warmup_epochs3 \ mosaic1.0 \ mixup0.1 \ copy_paste0.1 \ degrees5.0 \ translate0.1 \ scale0.5 \ fliplr0.5 \ flipud0.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ patience30 \ save_period10 \ projectruns/road_marking逐个说关键参数。model选 yolov8s 而不是 yolov8n是因为道路标线是细长目标n 版的感受野和特征分辨率不够小目标召回率会明显低。imgsz640是起点如果你的标线在图中占比很小比如远距离车道线可以提到 960 或 1280但显存和速度要权衡。mosaic1.0默认开但道路标线的透视关系很强mosaic 拼接会破坏透视我一般会在最后 20 个 epoch 关掉或者降到 0.5。mixup和copy_paste对稀有类别有帮助但 copy_paste 在标线任务里要小心粘贴的标线可能和背景透视不匹配反而引入噪声。degrees5.0是旋转增强道路标线对旋转敏感角度别开太大否则水平停止线转成斜的语义就变了。flipud0.0是因为上下翻转会让道路场景完全失真不要开。patience30是早停1449 张数据量不大容易过拟合早停能省时间。3.3 评估指标怎么看mAP 之外还要看什么训练完看 results.csv别只盯 mAP50。道路标线任务里我更关注三个东西每个类别的 mAP、混淆矩阵、以及验证集上的可视化预测。from ultralytics import YOLO model YOLO(runs/road_marking/weights/best.pt) metrics model.val(datadata.yaml, imgsz640, conf0.25, iou0.5) # 打印每个类别的指标 for i, name in enumerate(metrics.names.values()): print(f{name}: mAP50{metrics.box.ap50[i]:.3f}, mAP50-95{metrics.box.ap[i]:.3f}) # 混淆矩阵会保存到 runs 目录重点看稀有类别是否被吞conf0.25和iou0.5是评估时的阈值和训练时的 NMS 阈值不是一回事。看混淆矩阵时重点看「导流线」是不是被大量预测成「实线」或者「停止线」和「人行横道」有没有互相混。如果某个稀有类别的 mAP 低于 0.3先别调模型回去看这个类别的标注框是不是太少、是不是标错了。另外道路标线的评估不能只看图片级 mAP。实际部署时你关心的是「一条车道线有没有被完整检出来」而不是「框的 IoU 是不是 0.5」。所以我会额外做一件事把验证集的预测结果按图可视化人工看漏检和误检的模式。漏检集中在远处小目标还是集中在遮挡处这决定了你下一步是加数据还是改模型。4. 道路标线识别的五个血泪坑从标注到部署4.1 坑一标注框把整条车道线框成一个大框现象训练 loss 正常下降但推理时模型对长车道线只输出一个巨大的框或者干脆不输出。原因标注时有人图省事把一条贯穿整图的虚线用一个大框框住而不是按虚线段分别标注。模型学到的「虚线」是一个超长目标和实际推理时的局部特征不匹配。解决回到标注规范明确虚线按段标注每段一个框。如果已经标完可以用脚本按框的宽高比过滤把宽高比超过某个阈值比如 20:1的框挑出来人工复核。道路标线的框单段长度一般不会超过图像宽度的三分之一。4.2 坑二类别不平衡导致稀有类别被完全忽略现象训练完发现「导流线」的 mAP 接近 0混淆矩阵里全被预测成背景或实线。原因1449 张里「导流线」可能只有几十个框YOLO 的默认 loss 对每个框权重相同稀有类别梯度被淹没。解决三个手段叠加。一是用copy_paste增强稀有类别把稀有类别的实例粘贴到其他图上二是在 data.yaml 里给稀有类别更高的cls权重YOLOv8 支持通过cls_pw或自定义 loss 调整三是过采样包含稀有类别的图片在 train.txt 里重复列出这些图。我一般先做过采样简单有效。4.3 坑三验证集指标高实车视频一塌糊涂现象验证集 mAP50 到 0.85但拿行车记录仪视频一跑漏检严重。原因验证集和训练集同分布都是白天、干燥、直路。实车视频有夜间、逆光、雨天、弯道这些场景训练集里没有。解决划分验证集时强制按场景分层确保夜间、雨天、弯道都有样本进验证集。如果数据里根本没有这些场景那就要么补采集要么在训练时加更强的光照增强hsv_v 调大、加随机亮度对比度但增强不能替代真实数据。这一点没有后悔药数据覆盖度是硬门槛。4.4 坑四图像预处理和训练时不一致现象训练时 mAP 正常部署到 C 或 ONNX Runtime 后精度掉一大截。原因训练时 ultralytics 默认做了 letterbox 填充推理时如果直接 resize 到 640x640长宽比变了标线的形状和位置全偏。解决部署时严格复现 letterbox保持长宽比填充灰色。下面是一个 letterbox 的参考实现。import cv2 import numpy as np def letterbox(img, new_shape640, color(114, 114, 114)): h, w img.shape[:2] r min(new_shape / h, new_shape / w) new_unpad (int(round(w * r)), int(round(h * r))) dw, dh new_shape - new_unpad[0], new_shape - new_unpad[1] dw, dh dw // 2, dh // 2 img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) top, bottom dh, new_shape - new_unpad[1] - dh left, right dw, new_shape - new_unpad[0] - dw img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img, r, (dw, dh)r是缩放比例(dw, dh)是填充偏移后处理还原框坐标时必须用这两个值反算。很多人只做了 letterbox 但忘了反算框的位置就整体偏移了。4.5 坑五把标注工具的输出格式当成唯一真理现象换了一个标注工具重新标或者合并两个来源的数据训练直接崩。原因不同标注工具的坐标定义不同。Labelme 是 polygon 点集LabelImg 是 VOC 的 xmin/ymin/xmax/ymaxYOLO 是归一化的中心点加宽高。合并时如果不统一框会错位甚至超出图像。解决所有数据先统一转成 YOLO 格式再训练。转换时注意 VOC 的坐标是左上角和右下角YOLO 是中心点和宽高且要除以图像宽高做归一化。转换完随机抽 20 张可视化核对确认框和标线对齐。这一步不能省我见过太多人栽在坐标没对齐上还以为是模型问题。5. 让 1449 张数据发挥更大价值几个我常用的进阶技巧数据量固定的时候提升空间主要来自三处更好的增强策略、更聪明的训练调度、以及把检测结果做后处理。先说增强。道路标线任务里我最近常用的是「透视变换增强」模拟不同视角下的标线形变。做法是用 OpenCV 对图像做随机透视变换同时同步变换标注框的四个角点再重新计算 YOLO 格式。这个增强比单纯的旋转缩放更贴近真实车载视角变化但实现时要小心框变换后可能变成非矩形需要取外接矩形。import cv2 import numpy as np import random def perspective_aug(img, boxes, jitter0.05): h, w img.shape[:2] # 原始四角 src np.float32([[0, 0], [w, 0], [w, h], [0, h]]) # 随机扰动四角 dst src np.float32([[random.uniform(-jitter, jitter) * w, random.uniform(-jitter, jitter) * h] for _ in range(4)]) M cv2.getPerspectiveTransform(src, dst) warped cv2.warpPerspective(img, M, (w, h)) new_boxes [] for box in boxes: x1, y1, x2, y2 box pts np.float32([[x1, y1], [x2, y1], [x2, y2], [x1, y2]]).reshape(-1, 1, 2) pts cv2.perspectiveTransform(pts, M).reshape(-1, 2) nx1, ny1 pts.min(axis0) nx2, ny2 pts.max(axis0) new_boxes.append([nx1, ny1, nx2, ny2]) return warped, new_boxesjitter控制扰动幅度0.05 表示四角最多偏移图像宽高的 5%太大标线会变形到不真实。这个增强我一般以 0.3 的概率施加不是每张都做。第二个技巧是「分阶段训练」。先用 mosaic 和强增强训 100 个 epoch让模型学到鲁棒特征然后关掉 mosaic、降低学习率用干净数据再训 30 到 50 个 epoch 做微调。这样模型在验证集上的框会更贴合标线边缘而不是飘忽不定。YOLOv8 里可以通过close_mosaic参数控制最后多少个 epoch 关闭 mosaic我一般设close_mosaic20。第三个技巧是后处理里的「线段合并」。道路标线检测出来的框同一条虚线会被检成多个小框。实际应用时你可能需要把它们合并成一条完整的车道线。做法是把相邻的、方向一致的框按 x 或 y 排序后合并合并的阈值根据透视关系动态调整。这个逻辑不复杂但能显著提升下游任务比如车道偏离预警的可用性。最后说一个验证习惯每次改完数据或参数不要只看最终 mAP而是固定抽 10 张验证集图片把预测结果画出来存成对比图。我自己的习惯是建一个debug_vis目录每次训练完跑一遍可视化脚本肉眼过一遍。模型指标会骗人但漏检和误检的图不会。这套流程我用了很久1449 张数据在 YOLOv8s 上跑到 mAP50 0.88 左右是合理的再往上就要靠补数据和更精细的标注了。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑