ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv11-seg道路护栏实例分割实战:从数据集构建到部署全流程

YOLOv11-seg道路护栏实例分割实战:从数据集构建到部署全流程 道路护栏检测这类任务用目标检测只能给个框真要做病害定位、形变分析、养护评估必须得做到像素级分割。去年我在做道路设施巡检项目时护栏分割这块踩了不少坑正好YOLOv11-seg发布后性能提升明显我拿它重新训练了道路护栏实例分割数据集跑通了一套完整流程。这篇内容不只讲命令怎么敲还会把数据怎么采、标注怎么避坑、训练参数为什么这么调、推理结果如何保存这些关键细节全部拆开打算自己训练护栏分割模型的可以直接照抄。1. 道路护栏实例分割的项目定位与整体设计思路1.1 道路护栏实例分割到底在解决什么问题道路护栏是公路基础设施里非常特殊的一类目标。它不像车辆、行人那样有相对固定的长宽比和姿态而是一种典型的细长结构在画面中往往呈现为连续的、弯曲的、部分遮挡的带状区域。传统的目标检测算法比如标准YOLO检测模型能把护栏用一个矩形框框出来但框里面既包含护栏本身又包含背景路面、绿化带、对面车道这些干扰信息对后续的病害判断和尺寸测量完全没有帮助。实例分割任务解决的是“每一个像素属于哪个目标实例”的问题。对于道路护栏来说实例分割输出的是护栏轮廓的掩膜mask能精确到像素级别地把护栏和背景分开。有了这个掩膜下游可以做几件很有价值的事一是护栏变形检测通过比对掩膜几何形状与标准波形梁的差异判断有没有弯折或错位二是净距测量护栏边缘到车道线的距离直接影响行车安全三是养护面积统计需要涂装或更换的护栏长度、面积可以直接从掩膜像素数换算出来。1.2 为什么选YOLOv11-seg而不是其他分割方案做实例分割的可选方案其实不少Mask R-CNN、SOLO、YOLOv8-seg、YOLOv11-seg都在考虑范围内。我在这个项目里最终选型YOLOv11-seg主要基于三点考量。第一是速度与精度的平衡。道路巡检的场景通常是在车辆行驶过程中实时采集视频流对推理帧率有硬性要求。Mask R-CNN这类两阶段方法虽然精度上限高但在边缘设备或普通GPU上的实时性满足不了巡检需求。YOLOv11-seg作为单阶段方法在保持高精度的同时能跑到很高的FPS实测在RTX 4060上处理1080P视频帧能达到55-60 FPS完全满足实时处理。第二是部署友好度。Ultralytics生态的模型从训练到导出ONNX、TensorRT的链路非常成熟一行命令搞定。对于工程落地来说这条链路的价值比模型涨零点几个mAP重要得多。第三是YOLOv11本身的架构改进。相比YOLOv8-segv11引入了C3k2模块替代部分C2f结构在保持轻量化的同时提升了特征提取效率backbone中加入了C2PSA模块通过注意力机制增强了对长条状小目标的特征表达能力。道路护栏恰恰就是长条状目标这种改进对护栏分割任务非常契合。1.3 整体技术链路设计整个项目的技术链路可以拆成四个阶段数据采集与标注、数据集构建与增强、模型训练与验证、推理部署与输出。每个阶段都有自己的关键决策点我根据实际经验把整体流程图整理如下后面每一节都会详细拆解。数据采集行车记录仪、无人机、路测专业相机三种来源覆盖不同场景数据标注LabelMe或X-AnyLabeling手工标注多边形掩膜统一导出为JSON数据集构建转换为YOLO分割格式TXT掩膜坐标文件划分训练集、验证集、测试集做数据增强模型训练YOLOv11-seg预训练权重配置数据集YAML调节超参数验证评估mAP50、mAP50-95、mask精度等指标分析推理部署视频流推理、结果可视化、掩膜信息导出JSON/CSV2. 道路护栏数据集的采集、标注与预处理实操2.1 数据采集的场景覆盖和采集要点道路护栏数据集的质量直接决定分割模型的上限这一步不能省。理想的数据集应该覆盖不同道路等级、不同护栏类型、不同天气光照条件。护栏类型方面国内最常见的三种是波形梁护栏高速公路和一级公路的主流配置特征是有波浪形起伏的钢板、混凝土护栏桥面和中央分隔带常见表面平整或有纹理、组合式护栏混凝土底座加金属横梁。训练数据必须包含这三种类型否则模型在实际场景中遇到没见过的护栏形态分割效果会明显下降。采集场景的多样性也很关键。我当时的采集渠道有三个行车记录仪视频抽帧、无人机航拍、手机随拍。行车记录仪的画面视角低、距离近能覆盖护栏的细节纹理无人机航拍能获得俯视视角对护栏顶部轮廓和整体走向的标注很有价值手机随拍则可以补充服务区、收费站、匝道等特殊场景。不同来源的视频抽帧时要注意相邻帧不能太相似一般每隔5到10帧抽一张避免训练集中出现大量近乎重复的图像导致过拟合。采集时的光照条件尽量覆盖白天的顺光、逆光、阴天、晴天夜间数据可以暂时不放因为夜间护栏反光特性完全不同混入夜间数据反而会干扰模型学习。如果项目后续确实需要夜间检测能力建议单独采集夜间数据单独训练一个模型。2.2 标注规范怎么让掩膜质量对得起训练代价标注这一步是整个项目最耗时的环节也是决定分割精度的最大变量。标注规范定得是否合理直接影响标注效率和最终效果。第一明确“护栏”的边界定义。波形梁护栏包含横梁面板、立柱和连接件建议把连续连接的横梁面板作为一个完整的实例来标注立柱是否包含可以根据后续业务需求决定。如果下游关注的是护栏面板变形检测就应该标注面板部分立柱单独标注反而会干扰面板轮廓的准确性。我当时的做法是只标注横梁面板区域立柱不标这样模型学到的轮廓更纯粹。第二遮挡情况的标注策略。护栏被车辆、植被遮挡时标注软件里画出来的多边形会穿过遮挡物把护栏被遮挡的部分框出来。这样做对实例分割来说问题不大因为模型在推理时本身就会尽量还原可见部分的轮廓标注完整轮廓反而能帮助模型学习护栏的延展形态。但如果遮挡部分过大、超过护栏总长度的40%就建议直接跳过这张图避免引入过多噪声。第三标注工具的选择。我推荐X-AnyLabeling它支持自动分割模型辅助标注先用SAM或YOLOv8-seg模型做预标注然后人工修正多边形顶点。这样能大幅提升标注效率纯手工画一个细长护栏的多边形要1到2分钟有预标注辅助后30秒就能完成一张图。如果对工具有特殊偏好LabelMe也是可以的只是没有预标注辅助功能。2.3 标注结果转成YOLO分割格式YOLO实例分割训练需要的标签格式是每个TXT文件对应一张图片每行代表一个实例格式为“类别ID 多边形x1 y1 x2 y2 ... xn yn”所有坐标值都归一化到0到1之间用图片宽度和高度分别归一化。从LabelMe导出的JSON转成YOLO格式需要写个转换脚本核心逻辑如下import json import os def convert_labelme_to_yolo(json_path, output_dir, class_dict, img_width, img_height): with open(json_path, r, encodingutf-8) as f: data json.load(f) output_txt os.path.join(output_dir, os.path.basename(json_path).replace(.json, .txt)) with open(output_txt, w) as out_f: for shape in data[shapes]: label shape[label] if label not in class_dict: continue class_id class_dict[label] points shape[points] normalized_points [] for x, y in points: nx min(max(x / img_width, 0.0), 1.0) ny min(max(y / img_height, 0.0), 1.0) normalized_points.append(f{nx:.6f} {ny:.6f}) line f{class_id} .join(normalized_points) out_f.write(line \n)转换时要注意三点一是归一化坐标必须夹紧到0到1之间否则训练时Mosaic增强和随机仿射变换会产生越界坐标导致报错二是多边形的顶点顺序要保持一致顺时针或逆时针均可但不宜混用这会影响损失函数的收敛稳定性三是每个实例至少要有6个顶点太粗糙的多边形会让分割边缘锯齿感明显。2.4 训练集、验证集、测试集划分与数据增强策略数据集划分我建议按“场景”而不是按“图片”来分。如果同一路段的连续帧分别出现在训练集和验证集中验证指标会虚高模型相当于提前见过了场景。正确做法是先把不同路段、不同采集来源的视频整理成多个“场景组”按7:2:1的比例在场景层面做划分保证验证集和测试集中的场景与训练集不完全重叠。数据增强策略采用Ultralytics默认配置基本够用但有几个点值得调整。Mosaic增强在训练初期非常有效它能把四张图拼接在一起增加小目标的数量和多样性建议保持开启。对于护栏这类纹理较弱的目标HSV颜色增强的饱和度扰动值可以从默认的0.7稍微降低到0.5避免护栏的色彩在增强后发生过度偏移。随机翻转要考虑护栏的语义对称性左右翻转问题不大但上下翻转在真实行车视角中几乎不会出现建议关闭或仅在少量轮次开启。最终我用的数据集规模是训练集约3500张图像、标注实例约9800个验证集约800张、实例约2200个测试集约300张、实例约900个。这个量级对于单类别实例分割任务已经足够训练出泛化能力不错的模型继续增加数据带来的精度提升会逐渐趋于饱和。3. YOLOv11-seg环境配置与训练配置详解3.1 环境配置与依赖安装YOLOv11的环境配置和v8基本一致核心依赖是PyTorch和Ultralytics。我的推荐配置如下# 创建Python虚拟环境 conda create -n yolov11 python3.10 conda activate yolov11 # 安装PyTorch以CUDA 11.8为例根据实际GPU驱动版本调整 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装Ultralyticsv11需要8.3.0及以上版本 pip install ultralytics8.3.0安装完成后可以验证一下GPU是否正常可用python -c import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))需要提醒一个问题不要图新鲜把Ultralytics升级到最新版本因为最新版偶尔会调整部分默认参数。建议用8.3.x或8.4.x的稳定小版本我这套流程验证下来的版本是ultralytics 8.3.77整体运行稳定。3.2 数据集YAML配置与目录结构在工程目录下建一个数据集配置文件data_guardrail.yaml内容如下# 数据集根路径 path: /home/user/datasets/guardrail_seg train: images/train val: images/val test: images/test # 类别配置单类别场景就一个护栏类 nc: 1 names: 0: guardrail对应的目录结构应该是guardrail_seg/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 训练集标签TXT │ ├── val/ # 验证集标签TXT │ └── test/ # 测试集标签TXT └── data_guardrail.yaml一个常见坑是images和labels目录下的文件名必须完全一致扩展名不同没关系ultralytics会自动替换扩展名查找对应文件。如果训练时报大量“image without labels”的警告优先检查文件名是否对得上。3.3 训练命令与超参数逐项解析训练命令看起来简单但里面的参数选择决定了最终精度和训练效率。我用的完整训练命令如下yolo segment train datadata_guardrail.yaml modelyolo11n-seg.pt epochs150 imgsz640 batch16 device0 optimizerAdamW lr00.002 lrf0.01 warmup_epochs3 patience20 workers8逐个参数说下依据model用yolo11n-seg.pt作为预训练权重。n是nano版本参数量最小先跑通流程如果最终精度不足再换s或m版本加大模型容量。也可以用yolov8n-seg.pt作为初始化但实测直接用yolo11n-seg.pt收敛更快效果更好。imgsz640训练输入图像尺寸。护栏是细长目标如果原始图像中护栏占比很小建议用768甚至896但显存消耗会增加很多。我平衡后选640配合Mosaic增强基本够用。batch16显存16GB的RTX 4060/4080可以跑到16到24。显存不足时优先减batch而不是减imgsz因为图像分辨率对分割精度的影响比batch size更显著。optimizerAdamWUltralytics默认的SGD在中小数据集上收敛速度偏慢。实测AdamW在护栏这类结构纹理相对简单的目标上150轮就能达到SGD约250轮的效果。lr00.002AdamW对学习率更敏感0.002是经验值太小收敛慢太大会震荡。配合warmup_epochs3做预热可以在前几轮用较小的学习率稳定初始化。patience20早停策略验证集mAP连续20轮不提升就提前终止训练。护栏任务50到120轮之间基本收敛超过150轮后提升很小提早停可以避免无效等待。训练日志建议开启wandb或用Ultralytics自带的runs目录下的metrics曲线图重点观察mask_loss的下降趋势它比box_loss更能反映分割掩膜的学习情况。3.4 训练过程监控与最优权重选择训练过程中的监控指标主要看四类box_loss、seg_loss、cls_loss和验证集mAP。前三个是训练损失趋势应该是稳步下降并最终收敛到相对平稳的区间mAP则应该逐步上升最终在某个区间震荡。关于最优权重的选择Ultralytics会在每个epoch结束时自动保存best.pt和last.pt其中best.pt是验证集mAP最优的权重。有两点要注意第一验证集mAP最优的epoch不一定等于训练损失最低的epoch因为存在过拟合风险所以直接信best.pt就行第二如果训练后期验证集mAP出现持续下降说明模型开始过拟合此时应该提前停止而不是等patience触发。4. 常见踩坑记录与推理结果输出技巧4.1 训练报错的四类高频问题和排查思路跑护栏分割训练时最常遇到四类问题我把排查思路和解决方案整理成了速查表。问题现象可能原因解决方案CUDA out of memorybatch过大或imgsz过大先从batch8开始逐项降低或用auto参数自动调整batch“image 1/1 ... 0 labels”警告图片和标签文件名不一致检查文件名确保一一对应不要有中文或空格训练loss异常增高后不降学习率过高或数据集有坏样本降低lr0到0.001用data可视化工具检查是否有标注严重错误的图片seg_loss盘旋不降掩膜标注质量差坐标未归一化检查TXT中坐标是否在0-1范围内抽查10%标注是否有明显轮廓错误排查问题最有效的手段不是看控制台日志而是直接把带标注的图像可视化出来。Ultralytics提供了plot训练样本的函数也可以用下面这段代码快速检查标签是否挂在正确位置from ultralytics import YOLO model YOLO(yolo11n-seg.pt) model.val(datadata_guardrail.yaml, plotsTrue)执行后会在runs/segment/val目录下生成带掩膜叠加的可视化图一眼就能看出标注和图像是否对齐。4.2 分割效果不佳的优化方向优先级如果训练完成后验证集mAP50已经到了0.85以上但实际推理效果不理想大概率是数据分布偏差而不是模型问题。优化方向按优先级排序第一优先是补充“难例数据”。很多实际场景中护栏会被路边树木的阴影遮盖、被泥水污染、被广告牌部分遮挡这些场景如果没有进训练集模型自然表现不佳。针对性地采集补充这类难例比盲目增加整体数据量更有效。第二优先是调大输入分辨率。验证时用imgsz960推理往往比训练分辨率更高的输入能显著提升细长目标的掩膜质量代价只是推理变慢。第三优先是增加模型容量。把模型从nano级别换成small或medium级别即yolo11s-seg.pt或yolo11m-seg.pt在推理设备算力允许的情况下mAP通常能提升2到4个点。第四优先才是调整数据增强策略。比如将mosaic从默认的1.0降到0.5有时能避免小目标被过度拼接导致的信息损失。但数据增强调整的收益通常不如前三个方向明显。4.3 推理结果保存视频、图片和掩膜数据导出模型训练完成后实际工程应用中需要把推理结果保存下来。Ultralytics提供了几种保存方式根据场景选择。最简单的保存图片和视频结果# 保存带掩膜叠加的图片推理结果 yolo segment predict modelruns/segment/train/weights/best.pt source/path/to/test_images saveTrue # 保存视频推理结果同时导出JSON格式的检测信息 yolo segment predict modelruns/segment/train/weights/best.pt source/path/to/test_video.mp4 saveTrue save_jsonTrue如果需要把掩膜输出成可直接用于业务分析的格式用Python脚本做后处理更灵活from ultralytics import YOLO import numpy as np import json model YOLO(runs/segment/train/weights/best.pt) results model.predict(sourcepath/to/image.jpg, imgsz960, conf0.35) for result in results: if result.masks is not None: masks result.masks.data.cpu().numpy() # 每个实例的布尔掩膜 boxes result.boxes.xyxy.cpu().numpy() # 每个实例的检测框 for i, mask in enumerate(masks): mask_area int(mask.sum()) # 像素面积 x1, y1, x2, y2 boxes[i] print(f检测到护栏实例框体范围: ({x1:.1f}, {y1:.1f}) - ({x2:.1f}, {y2:.1f})掩膜像素面积: {mask_area})掩膜的像素面积结合相机标定参数可以换算成护栏的实际长度或表面积这是后续护栏养护评估的关键输入。4.4 模型部署时的精度与速度取舍部署阶段最大的取舍是精度和速度的平衡。YOLOv11-seg从导出到TensorRT的流程很方便实测在TensorRT FP16模式下推理速度能比PyTorch原生提升约3倍。操作分两步# 第一步导出为ONNX yolo export modelruns/segment/train/weights/best.pt formatonnx opset12 # 第二步转换到TensorRT需要安装tensorrt库 yolo export modelruns/segment/train/weights/best.pt formatengine device0如果目标是边缘设备如Jetson Orin建议用FP16精度如果目标设备是普通办公GPU如RTX 3060FP16和FP32差距不大。实际部署时发现一个常见坑TensorRT引擎和导出时的batch size绑定如果导出时没有加batch参数默认batch1推理时也必须是batch1。如果要动态batch导出时要加dynamicTrue参数。做道路护栏实例分割这一年多来我最大的体会是这种长条形细目标的标注质量比模型结构更决定效果上限。结构上YOLOv11-seg已经足够好但形状细节是否能准确还原很大程度取决于掩膜多边形顶点的标注密度。顶点太少轮廓粗糙顶点太多标注耗时爆炸我的经验是每条护栏边缘线段上至少保持10到15个顶点能让最终分割边缘既平滑又不失真。另外一个小技巧训练时不要把所有护栏类型混在一个类别里如果项目经费和标注人力允许按波形梁、混凝土、组合式分开标注成三个类别后续做护栏类型性能统计和故障分析起来会省很多事。这套流程你拿自己的数据跑一遍遇到瓶颈再对照排查就行道路护栏分割这个方向当前开源工具链的成熟度远超实际落地需求。
返回列表