尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于YOLOv8的道路标线识别实战:小数据集训练与部署经验

基于YOLOv8的道路标线识别实战:小数据集训练与部署经验 简介目标检测作为计算机视觉的核心任务旨在定位并分类图像中的物体。其原理基于深度学习特征提取与边界框回归YOLO系列凭借端到端的单阶段设计在实时性与精度之间取得了良好平衡。在自动驾驶、智慧交通等场景中准确识别车道线、斑马线等道路标线是路径规划与安全预警的基础。然而通用目标检测模型难以直接适配这些细长、小尺寸的特定目标需要借助高质量的数据集标注与针对性训练。本文基于YOLOv8探讨仅用1449张人工标注图像完成道路标线识别的完整流程包括数据构建、标注规范、训练调参及后处理优化为小样本条件下的目标检测工程实践提供参考。 这两年做过不少目标检测类的项目从工厂质检到小区安防都碰过但要说最有意思的还是上个月刚收尾的道路标线识别。这个项目的数据集规模不大1449张图全部人工标注完成但里面踩的坑、总结的经验足够写一篇实实在在的复盘。先交代一下背景。道路标线识别简单说就是让算法能认出画面里的车道线、斑马线、导向箭头这些东西。它不是什么新鲜任务但却是自动驾驶和高级辅助驾驶的基础能力之一高精度地图采集车要用城市交通管理系统要用甚至一些特种车辆比如清扫车也在用。为什么专门强调数据集只有1449张因为很多人一听到做目标检测第一反应就是要搞几万甚至几十万张图实际上对道路标线这种类别相对固定、场景相对受限的任务来说小数据集如果处理得好完全能出一个可用的模型。这篇就当是给想入坑目标检测、尤其是想自己动手标数据训模型的朋友一份完整的过程记录。我这次用的技术栈是YOLOv8配合自定义数据集前前后后折腾了三周。文章里会把数据从采集、清洗、标注到训练的完整链路都过一遍包括标注规范怎么定、模型参数怎么调、推理的时候哪些后处理技巧最管用以及我踩过的几个典型坑。不管你是刚接触目标检测的初学者还是已经在做类似项目想找点参考的同行应该都能从中捞到点东西。1. 项目整体思路与数据集构建方案1.1 为什么选“道路标线”这个小切口目标检测的公开数据集其实很多COCO、VOC这些随便下但真拿到实际项目里用你会发现一个问题通用模型对“道路标线”这类目标的识别效果并不理想。原因不复杂COCO里虽然有“stop sign”“traffic light”这种交通标志但几乎没有专门把车道线、菱形预告标志、导向箭头这些地面标线当作独立类别去标注的样本。换句话说用通用数据集能训出一个“认识世界”的模型但训不出一个“认识路面细节”的模型。所以这个项目从一开始就锁定了垂直场景只做道路标线识别。核心类别定了五个车道线白色实线/虚线、停止线、斑马线、导向箭头直行、左转、右转等、菱形预告标志。这几个类别覆盖了城市道路中最常见的标线类型也足够支撑后续业务方的需求——他们需要一个能在路口场景下实时框出标线位置的模块。至于为什么是1449张而不是更多其实是个务实的选择。数据来源主要是公开的街景影像和车载记录仪视频截帧人工筛选后有1449张可用图。这个量级对于五类目标来说不算充裕但如果分布控制得好再配合适当的数据增强完全有能力训出mAP 50在0.85以上的模型。1.2 数据从哪里来怎么筛数据采集这块要特别说一句很多人第一步就栽了。图像不是越多越好关键是多样性和代表性。我见过有人一口气拉了几千张图结果全是晴天正午、同一条路、同一个视角的这样的数据训练出来的模型换个天气、换个时间段就废了。我的做法是先定采集维度天气晴/阴/雨、时间段白天/黄昏/夜间、道路类型双向两车道/四车道/路口、拍摄角度略俯视为主允许少量平视。然后按这些维度去筛选保证每个维度都有一定样本量。1449张图里白天晴天大概占60%阴天20%雨天和黄昏各10%夜间少量保留但单独处理因为夜间标线反光特性不一样混在一起容易把模型搞糊涂。筛选的时候有个小技巧用视频抽帧会比直接找图更高效。记录仪视频每秒30帧但相邻帧之间高度相似直接全抽出来会造成大量冗余。我按每隔10帧抽1帧的方式初筛再人工剔除模糊、过曝、遮挡严重的帧最终定下1449张。另外所有图像统一缩放到不高于1280分辨率处理但标注时保留原图坐标训练时再让YOLO自动做letterbox。1.3 类别体系与标注规范宁可细不可粗标注这件事最怕的是标准不统一。我一开始犯过这个毛病——前200张图标注的时候车道线到底是画一个框还是画一整条完全看心情结果后面模型训练loss怎么都降不下去回头检查发现是标注风格太乱。后来我把标注规范重新梳理了一遍核心原则是车道线白色实线、虚线、黄线按“最小独立线段”标注虚线按每一段虚线框实线按视野内连续可见部分框。整条路从头框到尾是不对的因为YOLO的框必须贴合目标实际边界把不连续的虚线框成一个整体会让网络学到一个错误的“完整目标”概念。停止线横向停止线单独框不要把路口的所有线混在一起。斑马线按整条斑马线区域框一个大的外接矩形不逐条框白色条带。这个比较反直觉但实测效果更好因为斑马线的辨识特征是“一组平行条带”网络学习整体形状比学习单根条带更容易。导向箭头每个箭头单独框即使是连续两个直行箭头也分开框。菱形预告标志整体框包括里面的图形符号。每个类别都建立了“尽量框紧、不包背景”的标注习惯边界框和目标的间隙控制在2到3个像素以内。这样做的原因是YOLO的回归头对IoU非常敏感框得越紧算出的IoU才越真实模型训出来定位精度才会高。2. 标注工具选型与实操细节2.1 工具对比LabelImg、Labelme、X-AnyLabeling怎么选标注工具我前后试了三款最后定下来用X-AnyLabeling作为主力。简单说下我的对比感受工具是否适合矩形框目标检测自动辅助标注学习成本备注LabelImg适合但界面老旧不支持低轻量适合快速小规模标注Labelme适合多边形更擅长不支持中导出格式是JSON要转X-AnyLabeling非常适合支持YOLOv8预标注中低自带多种模型效率高LabelImg是老牌工具胜在简单但它的交互逻辑还停留在十年前的风格框一多就卡标注效率上不来。Labelme功能全但更多是为实例分割设计的导出的是JSON格式转成YOLO的txt还得写脚本。X-AnyLabeling是我最近比较喜欢的一款内置了多种推理模型可以先用一个初步训练过的模型做预标注然后人工微调对1449张图这种量级来说能节约至少三分之一的时间。2.2 标注实操从预标注到人工精修这次项目的标注流程分了两个阶段。第一阶段是手工标注了大约300张覆盖了五类目标的所有形态。这批图不是为了直接用而是为了训一个“种子模型”。模型不需要太好能框出大概位置就算成功反正后面还得人改。第二阶段用种子模型对剩余约1100张图做预标注然后我一张一张过重点检查三件事漏框模型没找到的目标、错框框的位置明显不对、类别错箭头方向识别错了。这个过程很枯燥但必须做预标注的作用是减少画框的时间而不是减少思考的时间最终标注质量还是得靠人把控。说到标注文件格式YOLO系用的是txt文件每行代表一个目标格式是类别id 中心点x 中心点y 框宽 框高。注意这四个坐标值都是归一化到0到1之间的基于图片宽度和高度分别计算。我见过不少新手在这一步翻车把像素坐标直接写了进去训练的时候loss直接起飞。举个例子一张1280x720的图有一个目标的像素坐标是左上角(400, 300)右下角(700, 500)。那么中心点x是(400700)/2550除以1280约等于0.4297中心点y是(300500)/2400除以720约等于0.5556框宽是700-400300除以1280约等于0.2344框高是500-300200除以720约等于0.2778。最终txt里写的就是“0 0.4297 0.5556 0.2344 0.2778”这样一行。2.3 标注质量检查一个人怎么保证标注质量单兵作战最容易出现的问题是标注前后风格漂移前面框得松后面框得紧这类问题靠人眼很难发现。我的解决方法是训练前做一轮基于统计的检查用脚本扫描所有txt文件统计每个类别的标注框宽度、高度分布如果发现某个类别的宽度方差特别大多半就是标注标准松动了。另外还要做一次“影像与标签叠加”的抽检随机抽50张图把标注框画回图上可视化人工过一遍。别省这步1449张图看起来不多但如果不做抽检你可能在训练完一轮之后才发现问题回头改标注再重训浪费的时间远比抽检多。3. 模型选型与训练参数调优3.1 为什么选YOLOv8而不是YOLOv5或其他模型这部分我纠结过一阵。YOLOv5太旧了YOLOv7代码风格我不太喜欢YOLOv8出来之后API设计比较干净文档也全就直接用了。并不是说YOLOv8在所有任务上都是最优解但作为基线模型来说它胜在工程友好、调参有章可循、推理部署方便。具体选了yolov8m这个中等规格的版本。为什么不用s或n因为道路标线在图像里经常是细长条形状比如车道线宽度可能只有十几个像素而长度能横跨大半张图这对特征提取网络的细粒度表达能力有一定要求太小的骨干网络容易丢失这些细节。为什么不用l或x因为后续要部署到边缘设备Jetson或者工控机推理速度同样重要m版本在精度和速度之间平衡得最好。如果对YOLO家族还不熟悉可以简单理解为nano是最轻量精度低速度快small适中medium是平衡点large和xlarge精度高但速度慢适合服务器端离线处理。3.2 数据划分与增强策略数据划分这里有个必须注意的点不要随机划分要按“视频来源”划分。我的数据是从多段视频里抽帧来的如果同一段视频的画面既出现在训练集又出现在验证集那验证集的分数会虚高因为模型见过这些画面的“邻居”本质上算数据泄露。最后我按视频段分组训练集1100张、验证集200张、测试集149张保证测试集和训练集完全没有同源画面。YOLOv8默认开启的马赛克增强对道路标线任务有好处因为它让模型看到不同场景的拼接增强对上下文的理解。但有一个坑要注意马赛克增强本质上会把若干个图拼在一起这就导致细长的车道线在拼接处被切断网络可能学会“看到断线就以为目标结束”。解决方法是开启mosaic的同时把copy_paste参数关掉然后适当增加scale和flip的强度让模型对不同尺度下的标线形态更鲁棒。我最终用的增强配置是mosaic1.0mixup0.0对细长目标不太友好hsv_h0.015hsv_s0.7hsv_v0.4degrees10translate0.1scale0.5fliplr0.5。基本思路是颜色的适度扰动有助于适应不同光照但不要过分旋转角度控制在10度以内因为真实道路标线不会出现大幅倾斜水平翻转可以用但要意识到它会把“左转箭头”变成“右转箭头”所以类别必须在语义上支持这种翻转。3.3 训练参数、损失曲线与验证结果训练这块我用的输入分辨率是640x640batch size 16初始学习率0.01优化器SGD共训了200个epoch。硬件是单张RTX 3090显存24Gbatch size 16跑yolov8m没有压力如果你显存不够可以适当减小batch size并同步降低学习率。模型在150个epoch左右开始在验证集上收敛最终结果mAP50约0.88mAP50-95约0.61。需要注意mAP50-95这个指标对细长目标非常不友好因为IoU阈值升高后细长框的轻微偏移就会导致IoU大幅下降所以0.61这个数字对于车道线任务来说已经算正常水平。训练过程中我特别关注了每个类别的单独AP值。分类别看斑马线和菱形标志的AP最高都超过了0.92因为它们在图像中面积大、形状固定导向箭头次之约0.87难点主要在远小目标车道线AP相对低约0.83主要是虚线段的独立检测偶尔会出现漏检或者合并这是后续需要针对性优化的方向。4. 推理与后处理模型训完之后才是真麻烦的开始4.1 从YOLO输出到业务可用的坐标模型训完很多人直接就把原始输出拿去做业务了这是一个常见的误区。YOLO的原始输出是归一化的中心点坐标和宽高而且是在letterbox填充后的坐标系里的需要经过还原才能映射回原图否则画框位置会整体偏移。YOLOv8在推理时会自动做letterbox也就是说原图会被等比缩放多余部分用灰色填充。因此拿到输出框之后必须把坐标从填充后的坐标系还原到原图坐标系。计算公式不复杂关键是搞清楚缩放比例和填充偏移量。以下是我在推理时使用的核心代码逻辑import cv2 import numpy as np def letterbox(im, new_shape(640, 640), color(114, 114, 114)): shape im.shape[:2] r min(new_shape[0] / shape[0], new_shape[1] / shape[1]) new_unpad int(round(shape[1] * r)), int(round(shape[0] * r)) dw, dh new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] dw / 2 dh / 2 im cv2.resize(im, new_unpad, interpolationcv2.INTER_LINEAR) top, bottom int(round(dh - 0.1)), int(round(dh 0.1)) left, right int(round(dw - 0.1)), int(round(dw 0.1)) im cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return im, r, dw, dh # 推理后的坐标还原 def restore_coords(pred_boxes, r, dw, dh, orig_shape): boxes pred_boxes.copy() boxes[:, [0, 2]] (boxes[:, [0, 2]] - dw) / r boxes[:, [1, 3]] (boxes[:, [1, 3]] - dh) / r boxes[:, [0, 2]] np.clip(boxes[:, [0, 2]], 0, orig_shape[1]) boxes[:, [1, 3]] np.clip(boxes[:, [1, 3]], 0, orig_shape[0]) return boxes.astype(int)这段代码把letterbox的细节封装成了函数推理后在主流程里调用restore_coords就能把归一化坐标还原成原图坐标。每次写推理脚本都把这部分复制过去省得反复踩坑。4.2 针对细长目标的NMS参数调整YOLO默认的NMS阈值是0.45对一般目标没问题但用在车道线上会出现一个现象一条长虚线被网络输出多个高置信度的局部框NMS又因为IoU不够高而没有把它们合并最后画出来就变成了一条虚线框了好几个框。调整方法有两个一是把NMS的IoU阈值调高到0.6甚至0.65让互相重叠较多的框更容易被合并二是在NMS之前增加一个基于类别和位置的后处理逻辑如果是车道线类别的框再额外检查它们之间的距离距离过近且中心点偏移小于某个阈值的框直接合并。这里我用了第二种方式效果更好。核心思路是车道线本质上是一个连续目标被截断成多段检测框之间其实有很强的空间关联可以写一个基于贪心的合并函数把“近乎共线”的框合并成更大的框或者保留响应最强的一个。这种小技巧官方文档不会教你但对道路标线这个场景非常有效。4.3 推理提速与边缘设备部署体验项目后期我把模型转成了TensorRT的engine格式在Jetson Orin Nano上跑推理速度从原始的约50ms一帧降到了大约18ms一帧基本能满足15到20帧每秒的实时处理需求。转换过程不算复杂用官方提供的ultralytics库直接导出即可但要注意TensorRT的engine文件是和GPU架构强绑定的在RTX 3090上导出的engine不能直接拿到Jetson上跑必须在目标设备上重新导出一次。如果部署端的算力更紧还有一个思路把输入分辨率从640降到480实测mAP50下降约1.5个百分点但推理速度能进一步降到12ms左右。对于部分只需要“大概知道哪里有标线”的场景这个精度损耗是可以接受的。5. 常见问题与排查技巧实录5.1 训练loss不下降先查标注再查超参如果训练到50个epoch左右loss还稳定在一个高位震荡别急着调学习率先去检查自建的标注数据。最常见的问题有三个一是标注框没有归一化坐标值大于1二是类别id和类别名称对不上比如txt里写的是1但data.yaml里第1个类别是“stop_line”而实际想标的是“zebra_crossing”三是某些图片没有对应的txt文件YOLO会把这张图当作全背景样本参与训练如果这类图特别多模型会倾向输出“无目标”导致漏检严重。我写了一个快速检查脚本统计每个txt的行数、坐标范围、类别id分布跑一遍基本能发现90%以上的标注问题。5.2 小目标漏检注意力要放在“感受野”上导向箭头如果成像较小比如在图像上方远处yolov8m的检测效果不太理想。提升小目标能力有几种常见方案把输入分辨率从640提高到960或1280相当于让目标在图像里占更大的像素区域或者在网络结构上增加一个小目标检测头P2层或者采用SAHI这类切片推理工具将大图切块后分别检测再合并结果。我用的是提高输入分辨率方案。代价是训练和推理时间都明显上升但对小目标的召回率提升非常明显箭头类别的AP从0.87提到了0.91。如果你的部署设备性能有限建议优先尝试SAHI它只在推理阶段多花时间训练完全不受影响。5.3 夜间和雨天泛化差适度增加数据增强不如定向补充数据前文提到夜间的图我单独处理了实际上最终模型在夜间测试集上的表现确实不如白天mAP50约0.78 vs 0.88。一开始我尝试通过加强色彩增强来拟补但效果有限。后来补了大概80张带车灯光晕的雨天夜间图重新标注后混合训练夜间AP有了明显回升。这个经验的底层逻辑是数据增强只能在现有数据分布内做插值而如果训练集中根本没有“车灯光晕下的标线”这个特征组合增强算法也无法凭空创造。所以如果你的项目同样面临极端天气泛化问题我的建议是与其花大量时间调增强策略不如去定向采集那类场景的数据哪怕只有一两百张效果也比盲目堆数据增强参数要好得多。6. 一些绕不开的工程化心得写到这里基本把项目从头到尾串了一遍。想再强调一个观点数据集只有1449张听起来是个劣势但换个角度看这恰恰逼着我把数据质量打磨得更细。很多做目标检测的团队开口就要十万张图但真正交付的时候标注质量参差不齐训练出来的模型反而不如用少量高质量数据训出来的。模型不是“喂得多”就聪明“喂得对”才是关键。实际操作中我的体会是标注标准的制定和严格执行是项目成败的分水岭尤其是多类别目标时一只箭头是“直行箭头”还是“直行左转箭头”如果标注阶段不统一网络学到的类别边界就是模糊的后处理阶段再怎么优化也救不回来。另一个很重要的习惯是每个关键节点都做可视化检查标注完成后抽检、训练完成后跑一批真实场景视频来观察效果这些看起来“不产生数据”的步骤反而能省下最多的返工时间。最后再说一个小技巧目标检测模型在测试集上的分数再好看跟真实场景还是有一段距离。我的习惯是每次训练完把模型接到一段30分钟的实拍行车视频上跑一遍人工观察漏检和误检的情况再根据观察结果决定是补数据、调后处理还是换模型结构。这个过程听起来原始但确实比任何评估指标都更能发现问题。本文还有配套的精品资源点击获取
返回列表