ARTICLE DETAIL

资讯详情

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

YOLOv5旋转框检测与语义分割实战:基于WoodScape数据集的自动驾驶感知

YOLOv5旋转框检测与语义分割实战:基于WoodScape数据集的自动驾驶感知 简介本资源是一套基于YOLOv5在WoodScape数据集上实现旋转框目标检测与语义分割分割任务的完整Python项目面向计算机、人工智能、自动化等专业的在校学生、教师及初学者适用于课程设计、毕业设计、科研入门与算法实践。项目代码经实测可正常运行包含76个文件46个Python脚本、8个YAML配置、5个JSON定义、4个PNG/JPG可视化图及3个Markdown文档总大小6.14MB其中models目录含改进的yolov5s_seg.yaml等分割专用模型结构utils与datasets模块支持旋转框标注解析与多任务联合训练scripts中提供数据划分、XML转YOLO格式、语义图生成等实用工具。目前已有126人学习下载配套README.md详述环境配置、训练流程与推理演示所有内容仅供学习参考不建议用于商业用途。1. 项目概述当YOLOv5遇上WoodScape我们能做什么如果你正在计算机视觉领域特别是自动驾驶感知方向寻找一个既有挑战性又足够“硬核”的练手项目那么“基于YOLOv5WoodScape数据集的旋转框目标检测语义分割”这个组合绝对值得你投入时间。这不仅仅是一个简单的模型训练任务它更像是一个微缩版的、面向真实世界的感知系统集成开发。YOLOv5以其极致的速度和友好的工程化特性著称而WoodScape数据集则提供了来自真实车辆的多任务标注包括我们需要的边界框和像素级分割标签尤其是其中大量需要旋转框才能精确定位的物体比如斜向停放的车辆、路边的交通标志杆等。这个项目的核心目标就是改造经典的YOLOv5让它不仅能完成常规的水平框检测还能输出带角度的旋转矩形框并同时进行像素级的语义分割实现“一图两吃”在一个网络中完成对场景中物体的定位、定向和轮廓理解。我之所以花大力气折腾这个项目是因为在实际的自动驾驶、遥感影像分析甚至工业质检中水平框的局限性太大了。一个水平框会把斜着的卡车框进大量背景像素导致后续的测距、轨迹预测都不准。而语义分割能告诉你“哪里是道路哪里是天空”但对于“那辆车具体是哪一辆”的实例级信息又有所欠缺。将旋转框检测与语义分割结合正是为了取长补短构建更鲁棒的环境感知能力。这个项目适合有一定PyTorch和深度学习基础想深入理解目标检测模型改造、多任务学习以及如何处理复杂标注数据的开发者。接下来我会带你从思路到代码完整走一遍这个项目的构建过程其中会包含大量官方文档里不会写的工程细节和踩坑记录。2. 核心思路与方案选型为什么是YOLOv5和旋转框2.1 骨干网络与多任务框架的选择选择YOLOv5作为基础框架几乎是必然的。相较于更早期的YOLO版本或一些学术性更强的检测器YOLOv5在易用性、训练速度和社区支持上拥有巨大优势。它的代码结构清晰配置系统*.yaml文件让模型结构调整变得非常简单并且内置了从数据加载、增强到模型导出如ONNX、TensorRT的一整套流水线。我们的任务是在此基础上增加两个头一个用于预测旋转框一个用于语义分割。一个关键的设计决策是两个任务共享主干特征还是使用独立的主干为了效率和轻量化我们选择共享主干。YOLOv5的NeckFPNPAN结构已经能很好地融合多尺度特征我们只需在Head部分进行扩展。具体来说保留原有的检测头分支用于分类和旋转框回归新增一个分割头分支通常接在Neck输出的某个或某几个特征层之后通过一系列卷积上采样最终输出与输入图像同分辨率的分割图。2.2 旋转框的表示法与损失函数设计这是项目的第一个技术难点。水平框通常用(x_center, y_center, width, height)表示而旋转框需要增加一个角度参数。业界常见的表示法有几种OpenCV格式(x_center, y_center, width, height, angle)其中angle是度数width是框旋转前的第一条边。长边定义法(x_center, y_center, width, height, angle)但约定width为长边angle为长边与x轴的夹角。五点法直接预测矩形四个角点的坐标共8个值但顺序需要一致。八参数法预测旋转矩形到水平外接矩形的四个偏移量计算复杂但更稳定。为了与YOLOv5的架构更好地兼容并考虑到后续损失计算的稳定性我选择了长边定义法。在数据预处理时将WoodScape标注的旋转框统一转换为(x, y, w, h, θ)格式其中θ ∈ [-90° 90°) 或 [0° 180°)并确保w为长边。这样做可以避免因为角度周期性和边交换带来的歧义。损失函数方面分类损失沿用YOLOv5的二元交叉熵BCE或Focal Loss。框回归损失则不能再用传统的IoU Loss如GIoU、DIoU因为对于旋转框计算IoU本身就是一个复杂的多边形相交问题。这里我采用了旋转框IoU损失Rotated IoU Loss的近似或直接使用Smooth L1 Loss回归角度参数结合改进的框回归损失。一种实用的方法是将旋转框的损失分解为中心点损失L1或L2、尺度损失对w, h使用L1或GIoU风格的损失和角度损失。角度损失需要特殊处理因为179°和-179°实际上只差2°但直接L2损失会很大。我使用了基于正弦或余弦的损失L_angle 1 - cos(θ_pred - θ_gt)这样能正确处理角度的周期性。2.3 语义分割头的设计分割任务是对每个像素进行分类。我们通常在Neck输出的具有丰富语义信息的特征层例如下采样8倍或16倍的特征图后接分割头。分割头一般是一个轻量级的解码器由几个卷积层和上采样层如双线性插值或转置卷积组成逐步将特征图恢复到输入图像尺寸。最后通过一个卷积层将通道数映射为类别数对于WoodScape包括道路、车辆、行人、天空等类别并用Softmax或Sigmoid针对多标签激活。这里的一个技巧是多尺度特征融合。不仅使用Neck最后输出的特征还可以将中间层的特征也通过跳跃连接Skip Connection引入分割头以结合更多细节信息这对于精确分割物体边缘至关重要。损失函数则选择在分割领域常用的交叉熵损失CrossEntropy Loss如果存在严重的类别不平衡如天空像素远多于行人像素可以引入带权重的交叉熵或Dice Loss。2.4 WoodScape数据集特性与预处理WoodScape是一个环视鱼眼镜头数据集这带来了额外的挑战。图像存在严重的畸变特别是边缘区域。对于检测任务直接在原图上训练旋转框模型可能效果不佳因为物体的形状和方向受到了畸变影响。常见的处理方案有两种去畸变预处理在加载图像后先利用相机标定参数进行去畸变校正将鱼眼图像转换为针孔相机模型下的图像然后在校正后的图像上进行标注和训练。这样模型学习到的是更“正常”的物体形态但会损失边缘区域的信息并引入插值伪影。模型适应畸变直接在原始鱼眼图像上训练让模型学习这种畸变下的物体表现。这要求模型有更强的泛化能力但能保留全部视野信息。考虑到我们使用的YOLOv5骨干是在ImageNet等标准数据集上预训练的方案一去畸变后训练在初期更容易收敛也是本项目采用的方案。预处理流程包括读取原始图像和JSON标注文件解析旋转框通常是多边形四点或旋转矩形参数和分割图路径应用去畸变变换同时对标注的框坐标和分割图进行相同的几何变换最后将数据转换为YOLOv5训练所需的格式如将旋转框归一化并转换为长边表示法将分割图调整为对应尺寸。3. 项目实战从环境搭建到模型训练3.1 环境配置与YOLOv5源码改造首先从官方仓库克隆YOLOv5的代码。我强烈建议锁定一个稳定的版本如v6.0或v7.0因为不同版本间接口可能有变化。git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt核心的改造集中在以下几个文件models/yolo.py修改Detect类或创建新的RotatedDetect类。我们需要修改输出通道数。原来每个anchor预测(x, y, w, h, obj, cls1, cls2...)现在需要预测(x, y, w, h, angle, obj, cls1, cls2...)。同时在forward函数中解码部分需要加入角度解码。models/common.py可能需要添加新的模块例如用于分割头上采样的Upsample模块组合或者一个独立的SegmentationHead类。loss.py这是重灾区。需要重写或新增计算旋转框损失的函数compute_rotated_loss。这个函数需要接收预测的旋转框参数和真实框参数并计算中心点损失、尺度损失可以考虑用旋转框GWD损失或KLD损失初期可用Smooth L1、角度损失和置信度/分类损失。datasets.py重写数据加载部分。需要编写一个专门的load_woodscape函数来读取去畸变后的图像、旋转框标签和分割标签。旋转框标签需要被转换为YOLO格式归一化的中心坐标、长宽和角度。分割标签需要被下采样到与分割头输出相匹配的尺寸或者作为监督信号在损失计算时再上采样。utils/metrics.py评估指标也需要适配。对于旋转框检测mAP的计算依赖于旋转框IoU。你需要集成一个能计算旋转框IoU的函数库比如rbox_iou可以从一些旋转框检测项目中借鉴如R3Det、DOTA_devkit中的代码。对于分割任务则需要计算mIoU平均交并比、Pixel Accuracy等指标。注意直接修改YOLOv5源码存在风险可能会破坏原有功能。一个更稳妥的做法是继承原有的类并重写方法或者将新增的功能模块化通过配置文件动态加载。3.2 数据准备与配置文件编写假设你已经完成了WoodScape数据集的下载和去畸变预处理得到了一个规整的文件夹结构如下WoodScape_processed/ ├── images/ │ ├── train/ │ └── val/ ├── labels_rotated/ # 旋转框标签每个txt文件对应一张图 │ ├── train/ │ └── val/ └── segmentation_masks/ # 语义分割标签图 ├── train/ └── val/每个旋转框标签txt文件的内容格式为class_id x_center y_center width height angle confidence。其中坐标和宽高均已归一化角度采用长边定义法例如0-180度。接下来创建数据配置文件woodscape.yaml# WoodScape dataset config path: /path/to/WoodScape_processed train: images/train val: images/val # 类别数 (根据WoodScape的检测类别例如Car, Pedestrian, Cyclist, Traffic Sign等) nc: 10 # 类别名称 names: [Car, Pedestrian, Cyclist, Traffic Sign, Traffic Light, Truck, Bus, Motorcycle, Animal, Other Vehicle] # 旋转框标签路径 (可选如果和images在同一目录不同文件夹) label_dir: labels_rotated # 分割标签路径 seg_dir: segmentation_masks # 输入图像尺寸 img_size: 640然后创建模型配置文件yolov5s_rotated_seg.yaml。这里以YOLOv5s为基础进行修改# YOLOv5 by Ultralytics, GPL-3.0 license # Parameters nc: 10 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, C3, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, C3, [256, False]], # 17 (P3/8) # 旋转框检测头 [-1, 1, Conv, [256, 3, 2]], [[-1, 14], 1, Concat, [1]], # cat head P4 [-1, 3, C3, [512, False]], # 20 (P4/16) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, C3, [1024, False]], # 23 (P5/32) # 分割头分支从P3/8特征引出 [17, 1, Conv, [128, 3, 1]], # 24 [24, 1, nn.Upsample, [None, 2, nearest]], # ... 可以继续上采样并与更底层特征融合最终输出通道数为分割类别数 [[-1], 1, Segment, [nc_seg19]], # 假设分割有19类这是一个自定义的分割头模块 ]3.3 训练策略与超参数调优训练启动命令大致如下python train.py --img 640 --batch 16 --epochs 100 --data woodscape.yaml --cfg yolov5s_rotated_seg.yaml --weights yolov5s.pt --device 0 --name woodscape_rotated_seg但直接运行很可能失败或效果很差因为多任务学习需要精心平衡。损失权重平衡检测损失分类旋转框回归置信度和分割损失交叉熵的量级可能相差很大。你需要在loss.py中引入可调节的权重系数λ_det和λ_seg。初期可以设置λ_det1.0,λ_seg0.5然后根据验证集上两个任务的表现动态调整。如果分割任务一直学不好就提高λ_seg如果检测任务退化就提高λ_det。学习率与优化器沿用YOLOv5默认的SGD优化器通常没问题。学习率可以稍微调低一点因为任务更复杂了。使用余弦退火或带热重启的调度器有助于模型跳出局部最优。数据增强YOLOv5自带强大的Mosaic和MixUp增强但对于旋转框需要确保这些增强操作特别是旋转、缩放能正确同步地作用于图像、旋转框坐标和分割标签图。这需要你对datasets.py中的增强函数进行改造确保几何变换矩阵能一致地应用到所有标注上。多尺度训练YOLOv5支持多尺度训练这对提升模型鲁棒性有好处。但要注意分割标签图在缩放时需要使用最近邻插值以保持标签值的整数性避免产生无效的类别值。4. 关键问题排查与实战心得4.1 旋转框损失震荡或不收敛这是最常见的问题。症状是训练初期旋转框的损失特别是角度损失剧烈震荡或者回归的框尺寸爆炸。排查角度周期性问题确保你的角度表示法在整个训练和推理 pipeline 中是一致的。比如在数据加载时统一到[-90, 90)在损失计算时也按此范围处理。使用sin(2θ)和cos(2θ)作为回归目标有时比直接回归角度更稳定因为它将180度周期性问题转化为了360度避免了在±90度附近的不连续。检查损失函数实现手动计算几个样本的损失值与一个简单的参考实现如使用cv2.minAreaRect计算旋转框再求损失进行对比确保代码无误。降低初始学习率旋转框回归是一个更精细的任务过大的学习率容易导致梯度爆炸。尝试将初始学习率降低一个数量级。归一化问题确保回归目标(x, y, w, h, θ)的数值范围是合理的。x, y, w, h应在0-1之间θ如果以弧度为单位范围应在[-π/2, π/2)。过大的数值范围会导致梯度不稳定。4.2 分割任务“淹没”检测任务在多任务训练中经常出现一个任务学得很好另一个任务完全学不到的情况。如果分割任务很快收敛损失下降而检测任务损失居高不下说明分割梯度主导了优化方向。调整损失权重这是最直接的杠杆。显著提高检测损失的权重λ_det例如从1.0调到5.0甚至10.0直到两个任务的损失曲线开始同步下降。任务特定特征考虑在共享主干的基础上为检测和分割任务设计一些独立的低层网络层。这可以让两个任务在提取特征时有更大的灵活性减少相互干扰。例如分割头可以从更早的骨干层引出特征而检测头依赖更深层的特征。梯度手术Gradient Surgery这是一种高级技巧当检测和分割任务的梯度方向冲突时对梯度进行投影或裁剪以减少任务间的负迁移。实现起来较复杂但对于严重冲突的任务组合可能有效。4.3 评估指标不对旋转框mAP极低训练损失在下降但用验证集计算旋转框mAP时结果低得离谱。确认评估代码的IoU计算方式务必确保评估时计算旋转框IoU的函数与损失函数中使用的如果有是兼容的或者至少是正确实现的。旋转框IoU计算很容易因顶点顺序、角度定义不同而出错。使用一个公认的测试用例进行验证。检查NMS非极大值抑制推理后处理中的NMS也需要适配旋转框。水平框NMS使用水平IoU而旋转框NMS需要使用旋转框IoU。你需要将标准NMS函数中的IoU计算替换为旋转框IoU计算。这一步遗漏会导致大量重叠框无法被抑制严重拉低mAP。验证解码过程模型预测的输出是经过编码的相对于anchor的偏移量在评估前需要解码回图像尺度的绝对坐标。仔细检查解码公式确保与训练时编码公式互逆。4.4 内存溢出与速度优化同时进行检测和分割尤其是分割头输出高分辨率特征图时显存消耗和计算量会大幅增加。降低分割图分辨率不一定非要输出原图分辨率的分割结果。可以输出下采样4倍或8倍的特征图然后在后处理时用双线性插值上采样回原图大小。这能极大减少分割头最后一层的通道数和计算量。使用更轻量的分割头避免使用复杂的解码器结构如DeepLabV3的ASPP。尝试用简单的“卷积上采样”堆叠或者使用深度可分离卷积。梯度检查点如果显存仍然紧张可以考虑使用PyTorch的梯度检查点功能以时间换空间。模型剪枝与量化训练完成后可以对模型进行剪枝移除不重要的权重和量化将FP32转为INT8这对部署到边缘设备如你提到的RV1106、RK3568至关重要。YOLOv5官方支持导出为ONNX进而可以使用TensorRT或OpenVINO等工具进行推理优化。4.5 WoodScape鱼眼畸变带来的泛化问题在去畸变图像上训练好的模型如果直接应用到原始鱼眼图像上性能会显著下降。数据增强中加入模拟畸变在训练的数据增强pipeline中随机对图像施加轻微的非线性畸变让模型学会适应一定程度的形状扭曲。多尺度输入训练除了常规的缩放可以加入一些仿射变换模拟不同视角下的物体形态。考虑模型架构改进有研究工作尝试将可变形卷积Deformable Convolution引入检测网络使其能自适应地调整感受野理论上能更好地处理畸变。但这会进一步增加模型复杂度和训练难度。这个项目就像搭积木但每一块积木都可能需要你自己打磨。从数据准备、模型改造、损失设计到训练调优每一步都有坑。我的建议是采用增量开发策略先确保旋转框检测单任务能正常训练和评估然后再加入分割头并固定检测部分的权重只训练分割部分最后再解冻全部网络进行端到端的微调。这样能有效隔离问题让调试过程更清晰。当你看到模型既能准确地框出斜停的汽车又能清晰地分割出道路和天空的边界时那种成就感是对所有折腾最好的回报。本文还有配套的精品资源点击获取
返回列表