YOLOv5/YOLOv8/YOLOv10旋转框检测实战:从数据格式到训练评估全流程

发布时间:2026/10/23 8:10:42
YOLOv5/YOLOv8/YOLOv10旋转框检测实战:从数据格式到训练评估全流程
对于有旋转框需求的项目很多开发者第一个想到的就是 YOLOv5 / YOLOv8 / YOLOv10 全系列旋转框检测方案。遥感图像里的飞机、船舶工业质检里的芯片引脚、药瓶盖都是典型的目标角度任意、密集排列、水平框会把大量背景框进来的场景。这决定了单纯用水平框很难交差旋转框检测几乎成了刚需。这篇文章直接把我做旋转框项目时的完整思路写出来包括三个版本怎么选、数据集格式怎么处理、训练评估推理全流程怎么做、常见坑有哪些适合刚接触对向包围框检测的同学也适合已经在用水平框想迁移到旋转框的人参考。1. 拿到旋转框需求时先搞清楚你用 YOLO 的哪个版本1.1 为什么旋转框是刚需而不是锦上添花水平框检测在常规目标检测里没问题但目标一旦倾斜、密集、长宽比悬殊水平框的劣势就非常明显。以遥感影像里的停靠船舶为例一艘船的水平框可能比其他船的主体区域还宽如果场景里还有码头、栈桥等线性结构水平框会框进大量背景导致目标特征被稀释、后续跟踪匹配也容易出错。旋转框检测的本质是在回归目标中心点和宽高的基础上再增加一个角度参数。这样预测框不仅能确定“目标在哪”还能确定“目标的朝向”框住的是目标本身的最小外接矩形或靠近目标的旋转矩形。工程上带来的直接收益是两个一是检测精度提升因为框内背景干扰大幅减少二是下游任务友好比如车牌识别前需要把车牌矫正到水平旋转框结果直接给了方向信息。我自己的经验是在遥感场景做目标检测从水平框切到旋转框之后mAP50 普遍能涨 5 到 10 个点这并不夸张。但代价是数据标注、数据增强、模型推理和评估这套链路都要跟着调整直接套用水平框的代码通常跑不出效果。1.2 YOLOv5、YOLOv8、YOLOv10 在旋转框上的能力差别先说结论三个版本里YOLOv8 的旋转框支持最成熟官方原版集成了倒角框OBB训练、验证、推理和导出全链路YOLOv5 官方原版不支持旋转框需要借助社区分支YOLOv10 的情况有点特殊它官方仓库早期并没有直接集成 OBB 训练接口虽然有社区适配但稳定性一般。具体拆开看YOLOv5官方 ultralytics/yolov5 主仓库里没有 OBB 能力社区里有一些比较知名的分支比如把 YOLOv5 改造成旋转框检测的第三方实现这些分支数据集格式通常兼容 DOTA 或者 VOC 扩展格式。优点是继承了 v5 生态的成熟度缺点是依赖特定分支后续 bug 修复、模型导出都要跟进分支维护者的节奏。YOLOv8ultralytics 官方在 8.0.146 之后开始支持 OBB提供了yolov8n-obb.pt等预训练权重官方文档里专门有 Oriented Bounding Boxes 的说明。数据和标注格式有明确规定训练命令和水平检测一样简洁这是目前最推荐用于实际落地的主版本。YOLOv10官方仓库主推高精度去掉了 NMS但 OBB 支持并不算是开箱即用。我在实际项目中用过社区移植版本有一定效果但对比 v8 还是觉得踩坑成本高。标题里的“全系列”如果真要做我建议把 v10 视为“实验性尝试”把大量时间留给 v8。1.3 快速选型建议基于我踩过的坑选型可以按下面这个矩阵来对号入座场景推荐版本理由遥感目标检测需要稳定落地YOLOv8 OBB官方支持数据格式明确训练推理导出齐全已有 YOLOv5 代码基础只做算法预研YOLOv5 社区 OBB 分支上手快但不要投入生产想对比多版本效果、跟踪学术前沿YOLOv8 为主v10 少量实验两个版本读取同一份数据节省切图转换时间轻量部署嵌入式设备YOLOv8 OBB 导出 ONNX相比 v10 的 NMS-free 结构部署链路更成熟核心思路是不迷信“最新版”而是优先选“官方原生支持该能力的版本”。因为旋转框核心难点在时机的规划和处理版本本身带来的精度差异反而没那么大。2. 数据准备旋转框项目最容易被坑的一环2.1 旋转框标注格式的核心从“四点”到“中心点宽高角度”旋转框标注在实际项目中常遇到三种格式DOTA 四角点格式、YOLOv8 OBB 格式、长边定义下的角度格式。转换是躲不过的。DOTA 数据集的标注格式是一张图中每个目标用四组坐标表示形如x1 y1 x2 y2 x3 y3 x4 y4 class_name difficult四个点是按顺序围成一个凸四边形的但顺序的起点并不固定。你需要保证按顺序把点连接起来后沿着四边形转一圈能正确表示目标区域否则后续计算长边和中心点都会错。YOLOv8 OBB 格式则更像水平框标签的扩展class_id cx cy w h angle中心坐标cx cy是归一化到 [0,1] 的w h也是归一化的。角度angle则是最需要注意的地方在 ultralytics 的 OBB 设计中角度是弧度制范围通常是[-pi/2, 0)负号表示从 x 轴正方向按顺时针方向旋转到长边所在方向实际具体定义建议以你所用版本源码里的注释为准。这跟很多人熟悉的 DOTA 角度定义并不一样转换时一定要先把角度规范统一。2.2 长边定义与角度换算转换脚本里的核心逻辑为什么强调长边定义因为同一条线段朝向相差 180 度在旋转框里并不等价但目标本身的方向又是等价的这就涉及角度包装问题。举个例子一条水平边界线长边方向可以是 0 度也可以是 pi180 度。如果标注工具把 0 度和 pi 当成同一个“水平方向”转换后的角度会出现跳变。工程上稳妥的做法是统一采用“长边定义”——即计算矩形的四条边取长度较长的一组边作为朝向参考然后用该边的方向与参考轴夹角作为角度再归一化到目标格式期望的范围。我来给一段可以直接参考的转换思路输入是四点坐标输出是 YOLOv8 OBB 格式import math def poly_to_yolo(points, img_w, img_h): # 输入 points: [(x1,y1), (x2,y2), (x3,y3), (x4,y4)] # 先计算四条边的向量和长度 # 因为是矩形标注两条长边两条短边找到长边即可确定朝向 dx, dy points[1][0] - points[0][0], points[1][1] - points[0][1] lengths [] for i in range(4): j (i 1) % 4 vec_x points[j][0] - points[i][0] vec_y points[j][1] - points[i][1] lengths.append(math.hypot(vec_x, vec_y)) # 取最长边对应的向量视为目标长边朝向 max_idx max(range(4), keylambda i: lengths[i]) # 为避免起点刚好落在长边中部取最长边起点和终点的向量 sx, sy points[max_idx] ex, ey points[(max_idx 1) % 4] edge_x, edge_y ex - sx, ey - sy # 计算中心点 xs [p[0] for p in points] ys [p[1] for p in points] cx sum(xs) / 4.0 cy sum(ys) / 4.0 # 计算宽高宽短边长度高长边长度 w min(lengths[max_idx], lengths[(max_idx 1) % 4]) h max(lengths[max_idx], lengths[(max_idx 1) % 4]) # 计算角度弧度以 x 轴正方向为基准 angle math.atan2(edge_y, edge_x) # 不同框架对角度范围要求不同这里示例统一到 [-pi/2, 0) if angle 0: angle - math.pi # 例如 1.2 转 -1.94保留朝向不变 if angle -math.pi / 2: angle math.pi return (cx / img_w, cy / img_h, w / img_w, h / img_h, angle)这段代码不是一个完整可用的轮子但把核心逻辑讲清楚了。实际操作中还要处理四点是否按长边排布、各种工具导出的点顺序差异、长边计算取整误差等问题这些正是不同标注软件导出的数据互相踩坑的根源。2.3 数据增强的隐性陷阱角度必须同步旋转常规 YOLO 数据增强里用到随机旋转、镜像翻转、mosaic 拼图时水平框的标注是跟着框的中心点和宽高走的而旋转框的标注除了中心点、宽高外角度也要跟着变换。很多人在这一步直接复制水平框的 pipeline结果训练时 loss 迟迟不降最后发现增强后的角度标签全乱了。具体问题集中在两个方面镜像翻转水平翻转后角度的符号要取反垂直翻转也一样。如果你直接沿用水平框的hflip增强而不改角度标签模型会学到错误的方向映射。旋转增强图像旋转 alpha 度后角度标签需要加上旋转角度再做范围约束。如果用 OpenCV 做旋转增强还要注意旋转后的边界裁剪中心点偏移也要同步处理。最稳妥的做法是初始阶段先关闭所有图像旋转和镜像增强跑通训练链路再加入少量增强逐步验证。我在实际项目中就是这样先把准确率做到可接受再逐步开强化避免模型“看起来在训、实际上标签和图像对不上”。3. YOLOv5 / YOLOv8 / YOLOv10 的旋转框训练全流程实操3.1 环境搭建与依赖准备无论用哪个版本第一件事是准备 Python 环境。以 YOLOv8 为例ultralytics 提供了最省事的入口pip install ultralytics如果你用的是 YOLOv10 官方仓库需要手动克隆仓库并安装依赖git clone https://github.com/THU-MIG/yolov10.git cd yolov10 pip install -r requirements.txtYOLOv5 社区 OBB 分支的安装方式往往是依赖同一个仓库内的requirements.txt安装时注意 PyTorch 版本匹配。这只写建议统一使用 Python 3.8 或 3.10PyTorch 2.x 均可如果显卡是 RTX 30 系以上记得安装对应 CUDA 版本的 PyTorch不要省这一步否则训练速度会相差数倍。3.2 YOLOv8 OBB官方原生的正确打开方式YOLOv8 的 OBB 训练可以直接用命令行完成。数据集目录结构dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/每一张图片对应的标签文件是.txt每行格式为class_id cx cy w h angle。然后准备一个数据集配置文件内容类似path: /path/to/dataset train: images/train val: images/val names: 0: plane 1: ship训练命令yolo obb train datadataset.yaml modelyolov8n-obb.pt epochs100 imgsz1024 batch16这里的几个参数值得细化model要从yolov8n-obb.pt、yolov8s-obb.pt、yolov8m-obb.pt里选没有预训练权重时直接传入对应 yaml 文件从头训练也行。imgsz对遥感场景特别重要目标小、图像大建议至少 1024有条件直接上 1280 或 1536。图像太大时先用切图脚本把大图切成 1024x1024 或 512x512 的小图并保留重叠部分。batch要根据显存调整先用 16 做 baseline显存不足降到 8不要盲目开大 batchOBB 训练比水平框更容易显存爆炸。验证模型yolo obb val modelruns/detect/train/weights/best.pt datadataset.yaml推理yolo obb predict modelbest.pt sourcetest_images/ save_txtTrue推理输出的标签格式依然是class cx cy w h angle可以直接用 cv2 的minAreaRect思路绘制旋转矩形框。3.3 YOLOv10 OBB官方不支持时的实际处理方式YOLOv10 官方仓库早期没有内置 OBB 训练入口。我在实验时遇到的现实情况是直接把数据丢给yolov10的 train 脚本会报错因为没有angle输出分支。社区里有基于 v10 的 OBB 分支但训练流程不够统一依赖于分支作者对DetectionModel的改动。我的建议是如果项目主线用 YOLOv8 OBB只需要把 v10 当作对比实验可以借用 ultralytics 统一生态或社区分支尝试如果目标是生产落地绕开 v10直接用 v8 OBB。如果确实要跑 v10把数据文件和训练 yaml 准备好后通常的命令也是python train.py --data dataset.yaml --model yolov10s-obb.yaml这类分支定制化的写法需要认真阅读分支 README。这里我特别强调一点模型结构的复杂度并不能直接等价于旋转框效果。v10 的设计重心在消除 NMS、端到端推理加速对 OBB 这种需要特殊后处理的角度回归任务收益并不明确。我在几个遥感数据上对比过v10 分支版本在 mAP50 上并没超过 v8反而推理时角度输出的稳定性略弱。3.4 YOLOv5 OBB社区分支的实操记录YOLOv5 社区 OBB 分支的数据集格式通常面向 DOTA 或类 DOTA 的polygon格式。我在实验时发现大部分分支会提供自己的数据转换脚本一般也是把四点坐标转成类似 DOTA 的格式再生成训练列表。实操路径大致是把标注从标注软件导出为 XML、JSON 或 txt统一转换为四点坐标。运行分支提供的转换脚本把四点坐标转成训练所需的格式。修改data.yaml指定训练图片路径和类别。运行python train.py --data data.yaml --weights yolov5s.pt --img 1024 --batch 16 --epochs 100。这里最大的坑是分支与官方 v5 的配置差异。如果分支的术语是poly_gt、poly_shape就不是简单的 cls/box 回归逻辑你需要先读 README 确认格式定义。建议把官方 v5 和新权重版本记录好避免混用预训练权重导致维度不匹配。3.5 训练参数的通用经验参考不管哪个版本旋转框训练时有几个参数会被频繁调参数推荐值/范围说明imgsz1024 或 1280遥感大图切图后用小图训练这里是切图后小图尺寸epochs100 起步旋转框收敛略慢100 epoch 能看到趋势遥感数据多要 150 以上batch8~32以显存为准过大会引入太多空洞背景mosaic1.0 起步大图场景可以关闭或降低对角度回归干扰较大lr00.01和 YOLOv8 默认一致过小收敛慢、过大角度回归容易发散close_mosaic10最后 10 个 epoch 关闭 mosaic让模型在真实图片分布上收敛amp看情况如果 loss 出现 NaN优先关闭混合精度比较容易被忽略的是切图重叠与剔除策略。遥感大图切成 1024 小图时如果目标恰好被切在边缘标注框会被截断训练时模型会学到残缺目标。正确的做法是子图之间保留 25%~50% 重叠并过滤掉那些被截断比例超过阈值的目标样本。4. 评估与推理不只是看 mAP 数字4.1 旋转框的评估指标怎么算旋转框的评估核心是旋转 IoU而不是水平框 IoU。旋转 IoU 需要先求两个旋转矩形的交点多边形面积再除以并集面积计算量比水平框大得多但评估脚本通常已经实现了你不用自己写只要明白它为什么和水平框不同即可。一个经验是旋转框 mAP50 通常会比水平框 mAP50 高不少但 mAP50-95 的提升幅度可能没那么明显。因为 mAP50-95 对框位精度要求更高角度哪怕偏差 2-3 度IoU 就会从 0.8 掉到 0.7 以下。如果调参后 mAP50 很高但 mAP50-95 上不去优先检查角度回归分支的学习率或角度标签范围。4.2 推理脚本与后处理注意事项YOLOv8 的predict命令很省事会自动输出 OBB 格式的预测文件。但实际项目中我经常遇到的问题是把预测结果可视化后发现框确实贴着目标但角度方向反了。这不是模型错了而是可视化时你用了水平矩形的cv2.rectangle去画旋转框或者角度换算的坐标旋转公式写反了。正确的可视化思路是拿到cx, cy, w, h, angle后先用旋转矩阵把矩形的四个角点算出来再画四条线。可以复用这段思路import math import cv2 import numpy as np def draw_obb(img, cx, cy, w, h, angle): cos_a, sin_a math.cos(angle), math.sin(angle) # 矩形四个角点相对中心 corners np.array([ [-w / 2, -h / 2], [w / 2, -h / 2], [w / 2, h / 2], [-w / 2, h / 2], ]) # 旋转并平移到图像坐标 pts [] for x, y in corners: px x * cos_a - y * sin_a cx py x * sin_a y * cos_a cy pts.append([px, py]) pts np.array(pts, dtypenp.int32) cv2.polylines(img, [pts], isClosedTrue, color(0, 0, 255), thickness2)不要小看这几行我至少三次在可视化时把角度方向搞错最后都是靠对比标注框逐图排查出来的。4.3 推理结果和角度的后处理技巧推理阶段另一个容易出问题的是 NMS 对角度的处理。常规水平框 NMS 只比较 IoU而 OBB 的 NMS 要用旋转 IoU否则检测框之间重叠严重尤其对于密集的飞机机群误差会集中出现。使用 YOLOv8 官方代码时这是透明的但如果你自己部署到 ONNX就必须自己实现旋转框 NMS 或使用支持 OBB 的 NMS 算子这也是导出模型时最容易踩到的坑。导出 ONNX 时可以先导出普通检测模型再看输出层是否有 angle 分支或特殊的 NMS 输出定义。没有现成方案的话优先考虑把 OBB 后处理放在 Python 端完成即模型只输出cx, cy, w, h, angle的原始回归量再由 Python 做旋转 IoU NMS。虽然耗时但胜在可控。5. 常见问题与排查实录速查表做旋转框项目我几乎每一次都会被下面这类问题卡住这里整理成表格遇到时可以按图索骥。现象可能原因排查与解决训练 loss 长时间不降数据增强的旋转/翻转没有同步更新角度标签先关闭旋转和翻转增强跑 20 epoch 看 baseline预测框角度整体偏转 90 度角度定义范围不一致检查标签角度范围和预测解析统一到同一个周期约束检测框重叠严重、易漏检OBB NMS 被替换成了水平框 NMS确认后处理用的是旋转 IoU NMS训练时 loss 突然 NaN混合精度下角度回归发散关闭 amp或降低学习率验证集 mAP 高但可视化差可视化坐标转换公式错误对照标注框逐图角点排查多画几个点看角点位置大图推理严重丢小目标未切图或切图无重叠改用 1024 切图 25% 重叠过滤截断目标这里有一个我特别想强调的排查技巧第一次拿到旋转框模型不要只盯 mAP 曲线直接挑 20 张验证集图片做推理可视化然后把自己标注的结果和预测结果叠加对比。这一步能在五分钟内暴露格式转换、角度定义、可视化方向等关键问题否则你很可能在错误的基础上调了很多天参数。还有一点对遥感场景很实用就是切图和推理后处理要协同。训练时用了 1024 切图 重叠推理时也要对原图做同样的切图策略每张小图预测完后再把结果映射回原图坐标最后做一次全图的 OBB NMS。如果训练和推理的预处理不一致模型效果再好在真实场景也无法复现。6. 个人实操中的选型与心态建议从我自己的经验来看围绕 YOLOv5 / YOLOv8 / YOLOv10 的旋转框检测方案最终真正值得放进生产系统的往往不是最花哨的那个。YOLOv5 社区分支适合快速验证旋转框思路YOLOv8 OBB 是当前最稳妥的生产主力YOLOv10 的 OBB 更适合作为对比实验或学术探索。你要是刚起步我强烈建议直接把时间投给 YOLOv8 的 OBB它的官方文档、预训练权重和社区案例都最完备遇到问题能搜到大量答案。数据集处理阶段我会预留比模型训练更多的时间。四个点的顶点顺序、角度的范围定义、长边朝向的稳定性这三个环节如果处理不好后面再想修只会更痛苦。最后分享一个小细节在做旋转框标注数据的质量检查时不要只看图片上框贴不贴目标最好生成一份角度分布直方图。如果某些角度区间出现异常密集或异常稀疏往往意味着标注工具的自动化辅助把某些目标的角度卡在了固定值这种数据喂给模型会让模型在真实场景的角度回归能力明显变差。多做这一步很多时候比多调十个 epoch 更管用。