CODrone 无人机航拍车辆检测
基于 YOLO11-OBB(Oriented Bounding Box,旋转目标检测) 的无人机航拍图像车辆要素检测工程。
本文档根据工程源码、训练日志(
console_print.txt)与数据集结构整理,覆盖数据预处理、模型训练、推理预测与可视化全流程。
目录
1. 项目概述
1.1 项目目标
从无人机航拍图像中检测 旋转目标(OBB) ,识别 12 类交通要素,其中以 车辆(car) 为核心目标。相比传统水平框(HBB)检测,旋转框能够更贴合地框选出倾斜停放的车辆,减少背景冗余、提高定位精度。
1.2 技术栈
| 组件 | 版本/说明 |
|---|---|
| 深度学习框架 | PyTorch 2.4.0 + CUDA 12.4 |
| 检测框架 | Ultralytics 8.3.252(YOLO11) |
| 模型 | YOLO11n-obb(预训练权重迁移) |
| 编程语言 | Python 3.12.12 |
| 图像处理 | OpenCV(cv2) |
| 训练硬件 | NVIDIA RTX A4000(16GB 显存) |
| 进度显示 | tqdm |
1.3 整体流程
#mermaid-svg-p2dXg8xlGQONvLwM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p2dXg8xlGQONvLwM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p2dXg8xlGQONvLwM .error-icon{fill:#552222;}#mermaid-svg-p2dXg8xlGQONvLwM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p2dXg8xlGQONvLwM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p2dXg8xlGQONvLwM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p2dXg8xlGQONvLwM .marker.cross{stroke:#333333;}#mermaid-svg-p2dXg8xlGQONvLwM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p2dXg8xlGQONvLwM p{margin:0;}#mermaid-svg-p2dXg8xlGQONvLwM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-p2dXg8xlGQONvLwM .cluster-label text{fill:#333;}#mermaid-svg-p2dXg8xlGQONvLwM .cluster-label span{color:#333;}#mermaid-svg-p2dXg8xlGQONvLwM .cluster-label span p{background-color:transparent;}#mermaid-svg-p2dXg8xlGQONvLwM .label text,#mermaid-svg-p2dXg8xlGQONvLwM span{fill:#333;color:#333;}#mermaid-svg-p2dXg8xlGQONvLwM .node rect,#mermaid-svg-p2dXg8xlGQONvLwM .node circle,#mermaid-svg-p2dXg8xlGQONvLwM .node ellipse,#mermaid-svg-p2dXg8xlGQONvLwM .node polygon,#mermaid-svg-p2dXg8xlGQONvLwM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p2dXg8xlGQONvLwM .rough-node .label text,#mermaid-svg-p2dXg8xlGQONvLwM .node .label text,#mermaid-svg-p2dXg8xlGQONvLwM .image-shape .label,#mermaid-svg-p2dXg8xlGQONvLwM .icon-shape .label{text-anchor:middle;}#mermaid-svg-p2dXg8xlGQONvLwM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-p2dXg8xlGQONvLwM .rough-node .label,#mermaid-svg-p2dXg8xlGQONvLwM .node .label,#mermaid-svg-p2dXg8xlGQONvLwM .image-shape .label,#mermaid-svg-p2dXg8xlGQONvLwM .icon-shape .label{text-align:center;}#mermaid-svg-p2dXg8xlGQONvLwM .node.clickable{cursor:pointer;}#mermaid-svg-p2dXg8xlGQONvLwM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-p2dXg8xlGQONvLwM .arrowheadPath{fill:#333333;}#mermaid-svg-p2dXg8xlGQONvLwM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p2dXg8xlGQONvLwM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p2dXg8xlGQONvLwM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2dXg8xlGQONvLwM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-p2dXg8xlGQONvLwM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2dXg8xlGQONvLwM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-p2dXg8xlGQONvLwM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-p2dXg8xlGQONvLwM .cluster text{fill:#333;}#mermaid-svg-p2dXg8xlGQONvLwM .cluster span{color:#333;}#mermaid-svg-p2dXg8xlGQONvLwM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-p2dXg8xlGQONvLwM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-p2dXg8xlGQONvLwM rect.text{fill:none;stroke-width:0;}#mermaid-svg-p2dXg8xlGQONvLwM .icon-shape,#mermaid-svg-p2dXg8xlGQONvLwM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2dXg8xlGQONvLwM .icon-shape p,#mermaid-svg-p2dXg8xlGQONvLwM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-p2dXg8xlGQONvLwM .icon-shape .label rect,#mermaid-svg-p2dXg8xlGQONvLwM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2dXg8xlGQONvLwM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-p2dXg8xlGQONvLwM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-p2dXg8xlGQONvLwM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} convert_xml2yolo.py
train.py
predict.py
visual_points.py
原始XML标注
robndbox
YOLO OBB格式
归一化txt
数据集配置
codrone_obb.yaml
YOLO11n-obb
模型训练
best.pt
最优权重
批量推理
测试集/事故照片
可视化标注
图像+txt
单图手动可视化
polyline绘制
2. 目录结构
codrone/
├── codrone_obb.yaml # 数据集配置(12类)
├── train.py # 训练入口
├── predict.py # 批量推理入口(主/备用两套方法)
├── convert_xml2yolo.py # XML → YOLO OBB 格式转换
├── visualize_obb_annotations.py # 标注可视化(验证转换正确性)
├── visual_points.py # 检测结果手动可视化(绘制多边形)
├── detect.py # 占位(空文件)
├── evaluate.py # 占位(空文件)
├── yolo11n-obb.pt # OBB 预训练权重
├── yolo11n.pt # 通用检测预训练权重
├── console_print.txt # 训练完整日志
├── CODrone/ # 数据集根目录
│ ├── train/{images,labels,annfile}/ # 训练集 5002 张
│ ├── val/{images,labels,annfile}/ # 验证集 2000 张
│ └── test/{images,labels,annfile}/ # 测试集 3002 张
├── original_labels/{train,val}/ # 原始 XML 标注
├── actual_accidents_photo/ # 实际事故航拍照片(13张,推理输入)
├── video_model_images/ # 视频抽帧图片(1.png,推理输入)
├── visualization/ # 标注可视化输出
├── runs/ # 训练/推理输出
│ ├── obb/codrone_obb_exp1/ # 训练结果(weights/、曲线图、混淆矩阵)
│ └── detect/actual_accidents_photo/predict/ # 事故照片推理结果
└── obb_visualization_manual.jpg # 手动可视化示例图
3. 数据集说明
3.1 CODrone 数据集
数据集为无人机航拍图像,命名规则体现了采集场景信息:
{场景}_{日夜}_{高度}_{角度}_{序号}_frame_{帧号}
例:chenhuachengpark_day_100m_30c_frame_1500
└─ 陈化成公园 / 白天 / 100米高度 / 30°俯角 / 第1500帧
| 字段 | 含义 | 取值示例 |
|---|---|---|
| 场景 | 拍摄地点 | chenhuachengpark、city_road、village_road、feitianyingbin... |
| 日夜 | day / night | day、night |
| 高度 | 无人机飞行高度 | 30m、60m、100m |
| 角度 | 云台俯仰角 | 30c、90c |
| 序号 | 同参数多段视频 | 1、2、3... |
| frame | 视频帧号 | 0 ~ 9000 |
3.2 数据划分
| 划分 | 图片数 | 标签数 | 用途 |
|---|---|---|---|
| train | 5002 | 5002 | 训练 |
| val | 2000 | 2000 | 验证(含 89468 个目标实例) |
| test | 3002 | 3002 | 测试 |
日志显示训练集、验证集各有 1 个背景图(无目标),1 张图存在重复标签被自动去除。
3.3 类别定义(12 类)
来自 codrone_obb.yaml:
| ID | 类别 | 英文名 | 说明 |
|---|---|---|---|
| 0 | 小汽车 | car | 核心目标 |
| 1 | 卡车 | truck | |
| 2 | 交通标志 | traffic-sign | |
| 3 | 行人 | people | |
| 4 | 摩托车 | motor | |
| 5 | 自行车 | bicycle | |
| 6 | 交通信号灯 | traffic-light | |
| 7 | 三轮车 | tricycle | |
| 8 | 桥 | bridge | |
| 9 | 公交车 | bus | |
| 10 | 小船 | boat | |
| 11 | 轮船 | ship |
4. 标注格式与数据预处理
4.1 三种标注格式
工程中存在三种标注文件,对应不同的处理阶段:
| 格式 | 目录 | 内容示例 | 坐标 |
|---|---|---|---|
| XML(robndbox) | original_labels/ |
<cx><cy><w><h><angle> + 4 角点 |
绝对像素 |
| annfile | CODrone/{split}/annfile/ |
x0 y0 x1 y1 x2 y2 x3 y3 class difficulty |
绝对像素 |
| YOLO OBB | CODrone/{split}/labels/ |
class_id x0 y0 x1 y1 x2 y2 x3 y3 |
归一化 0,1 |
原始 XML 结构(robndbox)
xml
<annotation verified="no">
<size>
<width>3840</width>
<height>2160</height>
<depth>3</depth>
</size>
<object>
<type>robndbox</type>
<name>car</name>
<difficult>0</difficult>
<bndbox>
<cx>1378.549</cx>
<cy>1705.8327</cy>
<w>37.8945</w>
<h>77.2586</h>
<angle>0.42</angle>
<x0>1376</x0><y0>1662</y0><x1>1411</x1><y1>1678</y1>
<x2>1380</x2><y2>1748</y2><x3>1345</x3><y3>1733</y3>
</bndbox>
</object>
</annotation>
每个 robndbox 同时包含:
- 中心点 + 宽高 + 角度 :
cx, cy, w, h, angle(用于计算,本工程未直接使用) - 四个角点坐标 :
x0,y0 ~ x3,y3(实际用于转换,工程取此四角点)
4.2 YOLO OBB 标签格式
转换后的每行标签为 9 列:
class_id x0 y0 x1 y1 x2 y2 x3 y3
- 8 个坐标值为归一化后的 4 个角点(
x/width、y/height) - 角点顺序:绕框顺时针或逆时针排列(顺序在训练中不敏感)
4.3 转换流程(convert_xml2yolo.py)
#mermaid-svg-cYu7nQgDMA2JNeH9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cYu7nQgDMA2JNeH9 .error-icon{fill:#552222;}#mermaid-svg-cYu7nQgDMA2JNeH9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cYu7nQgDMA2JNeH9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .marker.cross{stroke:#333333;}#mermaid-svg-cYu7nQgDMA2JNeH9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cYu7nQgDMA2JNeH9 p{margin:0;}#mermaid-svg-cYu7nQgDMA2JNeH9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster-label text{fill:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster-label span{color:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster-label span p{background-color:transparent;}#mermaid-svg-cYu7nQgDMA2JNeH9 .label text,#mermaid-svg-cYu7nQgDMA2JNeH9 span{fill:#333;color:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .node rect,#mermaid-svg-cYu7nQgDMA2JNeH9 .node circle,#mermaid-svg-cYu7nQgDMA2JNeH9 .node ellipse,#mermaid-svg-cYu7nQgDMA2JNeH9 .node polygon,#mermaid-svg-cYu7nQgDMA2JNeH9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .rough-node .label text,#mermaid-svg-cYu7nQgDMA2JNeH9 .node .label text,#mermaid-svg-cYu7nQgDMA2JNeH9 .image-shape .label,#mermaid-svg-cYu7nQgDMA2JNeH9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cYu7nQgDMA2JNeH9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .rough-node .label,#mermaid-svg-cYu7nQgDMA2JNeH9 .node .label,#mermaid-svg-cYu7nQgDMA2JNeH9 .image-shape .label,#mermaid-svg-cYu7nQgDMA2JNeH9 .icon-shape .label{text-align:center;}#mermaid-svg-cYu7nQgDMA2JNeH9 .node.clickable{cursor:pointer;}#mermaid-svg-cYu7nQgDMA2JNeH9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .arrowheadPath{fill:#333333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cYu7nQgDMA2JNeH9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cYu7nQgDMA2JNeH9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cYu7nQgDMA2JNeH9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster text{fill:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 .cluster span{color:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cYu7nQgDMA2JNeH9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cYu7nQgDMA2JNeH9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cYu7nQgDMA2JNeH9 .icon-shape,#mermaid-svg-cYu7nQgDMA2JNeH9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cYu7nQgDMA2JNeH9 .icon-shape p,#mermaid-svg-cYu7nQgDMA2JNeH9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cYu7nQgDMA2JNeH9 .icon-shape .label rect,#mermaid-svg-cYu7nQgDMA2JNeH9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cYu7nQgDMA2JNeH9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cYu7nQgDMA2JNeH9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cYu7nQgDMA2JNeH9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
是
否
否
是
遍历 XML 目录
后缀为 .xml?
解析 XML
ET.parse
存在 size 节点?
读取 width/height
从对应图片
cv2.imread 获取尺寸
遍历每个 object
name == ignored?
跳过该目标
name 在类别映射中?
打印警告并跳过
提取 bndbox
x0~x3, y0~y3
坐标归一化
coord / img_width
裁剪到 0,1 区间
拼接为 9 列标签行
写入同名 .txt 文件
统计 success/total/objects
关键逻辑说明:
- 类别映射 :
class_mapping = {name: idx},与codrone_obb.yaml顺序一致。 - 忽略类别 :
name == 'ignored'的目标直接跳过(不参与训练)。 - 未知类别:不在映射中的类别打印警告并跳过。
- 归一化:每个角点分别除以图像宽、高。
- 边界保护 :
max(0.0, min(1.0, coord))将坐标裁剪到[0,1]。 - 图像尺寸获取优先级 :XML
<size>节点 → 图片文件 →(都失败则跳过该文件)。
4.4 转换正确性验证(visualize_obb_annotations.py)
#mermaid-svg-rsJYN9ARfO9z05sF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rsJYN9ARfO9z05sF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rsJYN9ARfO9z05sF .error-icon{fill:#552222;}#mermaid-svg-rsJYN9ARfO9z05sF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rsJYN9ARfO9z05sF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rsJYN9ARfO9z05sF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rsJYN9ARfO9z05sF .marker.cross{stroke:#333333;}#mermaid-svg-rsJYN9ARfO9z05sF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rsJYN9ARfO9z05sF p{margin:0;}#mermaid-svg-rsJYN9ARfO9z05sF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rsJYN9ARfO9z05sF .cluster-label text{fill:#333;}#mermaid-svg-rsJYN9ARfO9z05sF .cluster-label span{color:#333;}#mermaid-svg-rsJYN9ARfO9z05sF .cluster-label span p{background-color:transparent;}#mermaid-svg-rsJYN9ARfO9z05sF .label text,#mermaid-svg-rsJYN9ARfO9z05sF span{fill:#333;color:#333;}#mermaid-svg-rsJYN9ARfO9z05sF .node rect,#mermaid-svg-rsJYN9ARfO9z05sF .node circle,#mermaid-svg-rsJYN9ARfO9z05sF .node ellipse,#mermaid-svg-rsJYN9ARfO9z05sF .node polygon,#mermaid-svg-rsJYN9ARfO9z05sF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rsJYN9ARfO9z05sF .rough-node .label text,#mermaid-svg-rsJYN9ARfO9z05sF .node .label text,#mermaid-svg-rsJYN9ARfO9z05sF .image-shape .label,#mermaid-svg-rsJYN9ARfO9z05sF .icon-shape .label{text-anchor:middle;}#mermaid-svg-rsJYN9ARfO9z05sF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rsJYN9ARfO9z05sF .rough-node .label,#mermaid-svg-rsJYN9ARfO9z05sF .node .label,#mermaid-svg-rsJYN9ARfO9z05sF .image-shape .label,#mermaid-svg-rsJYN9ARfO9z05sF .icon-shape .label{text-align:center;}#mermaid-svg-rsJYN9ARfO9z05sF .node.clickable{cursor:pointer;}#mermaid-svg-rsJYN9ARfO9z05sF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rsJYN9ARfO9z05sF .arrowheadPath{fill:#333333;}#mermaid-svg-rsJYN9ARfO9z05sF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rsJYN9ARfO9z05sF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rsJYN9ARfO9z05sF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rsJYN9ARfO9z05sF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rsJYN9ARfO9z05sF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rsJYN9ARfO9z05sF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rsJYN9ARfO9z05sF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rsJYN9ARfO9z05sF .cluster text{fill:#333;}#mermaid-svg-rsJYN9ARfO9z05sF .cluster span{color:#333;}#mermaid-svg-rsJYN9ARfO9z05sF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rsJYN9ARfO9z05sF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rsJYN9ARfO9z05sF rect.text{fill:none;stroke-width:0;}#mermaid-svg-rsJYN9ARfO9z05sF .icon-shape,#mermaid-svg-rsJYN9ARfO9z05sF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rsJYN9ARfO9z05sF .icon-shape p,#mermaid-svg-rsJYN9ARfO9z05sF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rsJYN9ARfO9z05sF .icon-shape .label rect,#mermaid-svg-rsJYN9ARfO9z05sF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rsJYN9ARfO9z05sF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rsJYN9ARfO9z05sF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rsJYN9ARfO9z05sF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
读取标签目录 .txt 列表
取前 max_images 张
按 base_name 匹配
对应图片文件
图片存在?
打印提示并跳过
读取图片
逐行解析标签
校验 9 列
归一化坐标
还原为像素坐标
构造 4 角点数组
cv2.polylines 绘制
在首角点绘制
类别名+ID 文本
保存
- 每类分配固定颜色(12 色循环表)。
- 输出到
visualization/,用于人工核对转换前后标注是否一致。
5. 模型训练
5.1 训练脚本(train.py)
python
model = YOLO('yolo11n-obb.pt') # 加载 OBB 预训练权重
model.train(
data='codrone_obb.yaml',
epochs=100,
imgsz=640,
batch=16,
device=0,
workers=4,
optimizer='AdamW',
lr0=0.001,
mosaic=1.0,
close_mosaic=10,
cache=False,
patience=15,
save=True,
val=True,
name='codrone_obb_exp1'
)
5.2 关键超参数说明
| 参数 | 值 | 说明 |
|---|---|---|
model |
yolo11n-obb.pt | 加载 ImageNet/COCO 预训练的 OBB 权重迁移 |
epochs |
100 | 总训练轮数 |
imgsz |
640 | 输入分辨率(训练时 letterbox 到 640×640) |
batch |
16 | 批大小(受显存限制) |
optimizer |
AdamW | 自适应学习率优化器 |
lr0 |
0.001 | 初始学习率 |
lrf |
0.01 | 最终学习率系数(余弦退火) |
momentum |
0.937 | 动量 |
weight_decay |
0.0005 | 权重衰减 |
warmup_epochs |
3.0 | 预热轮数 |
mosaic |
1.0 | Mosaic 数据增强概率(100%) |
close_mosaic |
10 | 最后 10 轮关闭 Mosaic |
patience |
15 | 早停耐心(15 轮无提升则停止) |
device |
0 | 使用第 0 块 GPU |
workers |
4 | 数据加载子进程数 |
5.3 模型结构
日志中打印的模型概况:
YOLO11n-obb summary: 196 layers, 2,663,847 parameters, 2,663,831 gradients, 6.7 GFLOPs
- 加载预训练权重后:
Transferred 535/541 items from pretrained weights - 覆盖输出类别:
Overriding model.yaml nc=80 with nc=12 - 冻结 DFL 卷积层:
Freezing layer 'model.23.dfl.conv.weight' - 关键模块:
Conv、C3k2、SPPF、C2PSA、Upsample、Concat、OBB检测头
5.4 训练流程
#mermaid-svg-lQxxlQkQHe5SUW6i{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lQxxlQkQHe5SUW6i .error-icon{fill:#552222;}#mermaid-svg-lQxxlQkQHe5SUW6i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lQxxlQkQHe5SUW6i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lQxxlQkQHe5SUW6i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lQxxlQkQHe5SUW6i .marker.cross{stroke:#333333;}#mermaid-svg-lQxxlQkQHe5SUW6i svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lQxxlQkQHe5SUW6i p{margin:0;}#mermaid-svg-lQxxlQkQHe5SUW6i .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster-label text{fill:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster-label span{color:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster-label span p{background-color:transparent;}#mermaid-svg-lQxxlQkQHe5SUW6i .label text,#mermaid-svg-lQxxlQkQHe5SUW6i span{fill:#333;color:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i .node rect,#mermaid-svg-lQxxlQkQHe5SUW6i .node circle,#mermaid-svg-lQxxlQkQHe5SUW6i .node ellipse,#mermaid-svg-lQxxlQkQHe5SUW6i .node polygon,#mermaid-svg-lQxxlQkQHe5SUW6i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lQxxlQkQHe5SUW6i .rough-node .label text,#mermaid-svg-lQxxlQkQHe5SUW6i .node .label text,#mermaid-svg-lQxxlQkQHe5SUW6i .image-shape .label,#mermaid-svg-lQxxlQkQHe5SUW6i .icon-shape .label{text-anchor:middle;}#mermaid-svg-lQxxlQkQHe5SUW6i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lQxxlQkQHe5SUW6i .rough-node .label,#mermaid-svg-lQxxlQkQHe5SUW6i .node .label,#mermaid-svg-lQxxlQkQHe5SUW6i .image-shape .label,#mermaid-svg-lQxxlQkQHe5SUW6i .icon-shape .label{text-align:center;}#mermaid-svg-lQxxlQkQHe5SUW6i .node.clickable{cursor:pointer;}#mermaid-svg-lQxxlQkQHe5SUW6i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lQxxlQkQHe5SUW6i .arrowheadPath{fill:#333333;}#mermaid-svg-lQxxlQkQHe5SUW6i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lQxxlQkQHe5SUW6i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lQxxlQkQHe5SUW6i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lQxxlQkQHe5SUW6i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lQxxlQkQHe5SUW6i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lQxxlQkQHe5SUW6i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster text{fill:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i .cluster span{color:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lQxxlQkQHe5SUW6i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lQxxlQkQHe5SUW6i rect.text{fill:none;stroke-width:0;}#mermaid-svg-lQxxlQkQHe5SUW6i .icon-shape,#mermaid-svg-lQxxlQkQHe5SUW6i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lQxxlQkQHe5SUW6i .icon-shape p,#mermaid-svg-lQxxlQkQHe5SUW6i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lQxxlQkQHe5SUW6i .icon-shape .label rect,#mermaid-svg-lQxxlQkQHe5SUW6i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lQxxlQkQHe5SUW6i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lQxxlQkQHe5SUW6i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lQxxlQkQHe5SUW6i :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
加载 yolo11n-obb.pt
覆盖 nc=80 → nc=12
迁移预训练权重
535/541 items
AMP 混合精度检查
扫描数据集
train 5002 / val 2000
构建 labels.cache
生成标签统计图 labels.jpg
epoch 循环
1..100
训练前向/反向
box_loss + cls_loss + dfl_loss
每轮验证
计算 P/R/mAP50/mAP50-95
早停判断
15轮无提升?
EarlyStopping 触发
保存 best.pt / last.pt
剥离优化器状态
对 best.pt 做最终验证
输出各类别指标
5.5 损失函数
YOLO11-OBB 训练使用三部分损失(日志中的 results.csv 列):
| 损失 | 含义 |
|---|---|
box_loss |
旋转框回归损失(CIoU 变体 + 角度) |
cls_loss |
分类损失(BCE) |
dfl_loss |
Distribution Focal Loss(分布焦点损失,精确定位) |
6. 推理预测
6.1 预测脚本(predict.py)
提供两套方法:主方法 (目录级批量预测)与备用方法(逐图处理)。
主方法 predict_test_set()
#mermaid-svg-bpOpGJ6CpJUdWjri{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bpOpGJ6CpJUdWjri .error-icon{fill:#552222;}#mermaid-svg-bpOpGJ6CpJUdWjri .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bpOpGJ6CpJUdWjri .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bpOpGJ6CpJUdWjri .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bpOpGJ6CpJUdWjri .marker.cross{stroke:#333333;}#mermaid-svg-bpOpGJ6CpJUdWjri svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bpOpGJ6CpJUdWjri p{margin:0;}#mermaid-svg-bpOpGJ6CpJUdWjri .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster-label text{fill:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster-label span{color:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster-label span p{background-color:transparent;}#mermaid-svg-bpOpGJ6CpJUdWjri .label text,#mermaid-svg-bpOpGJ6CpJUdWjri span{fill:#333;color:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri .node rect,#mermaid-svg-bpOpGJ6CpJUdWjri .node circle,#mermaid-svg-bpOpGJ6CpJUdWjri .node ellipse,#mermaid-svg-bpOpGJ6CpJUdWjri .node polygon,#mermaid-svg-bpOpGJ6CpJUdWjri .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bpOpGJ6CpJUdWjri .rough-node .label text,#mermaid-svg-bpOpGJ6CpJUdWjri .node .label text,#mermaid-svg-bpOpGJ6CpJUdWjri .image-shape .label,#mermaid-svg-bpOpGJ6CpJUdWjri .icon-shape .label{text-anchor:middle;}#mermaid-svg-bpOpGJ6CpJUdWjri .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bpOpGJ6CpJUdWjri .rough-node .label,#mermaid-svg-bpOpGJ6CpJUdWjri .node .label,#mermaid-svg-bpOpGJ6CpJUdWjri .image-shape .label,#mermaid-svg-bpOpGJ6CpJUdWjri .icon-shape .label{text-align:center;}#mermaid-svg-bpOpGJ6CpJUdWjri .node.clickable{cursor:pointer;}#mermaid-svg-bpOpGJ6CpJUdWjri .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bpOpGJ6CpJUdWjri .arrowheadPath{fill:#333333;}#mermaid-svg-bpOpGJ6CpJUdWjri .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bpOpGJ6CpJUdWjri .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bpOpGJ6CpJUdWjri .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bpOpGJ6CpJUdWjri .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bpOpGJ6CpJUdWjri .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bpOpGJ6CpJUdWjri .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster text{fill:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri .cluster span{color:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bpOpGJ6CpJUdWjri .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bpOpGJ6CpJUdWjri rect.text{fill:none;stroke-width:0;}#mermaid-svg-bpOpGJ6CpJUdWjri .icon-shape,#mermaid-svg-bpOpGJ6CpJUdWjri .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bpOpGJ6CpJUdWjri .icon-shape p,#mermaid-svg-bpOpGJ6CpJUdWjri .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bpOpGJ6CpJUdWjri .icon-shape .label rect,#mermaid-svg-bpOpGJ6CpJUdWjri .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bpOpGJ6CpJUdWjri .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bpOpGJ6CpJUdWjri .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bpOpGJ6CpJUdWjri :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
解析命令行参数
weights/test_dir/output_dir/conf/iou/img_size
创建输出目录
加载模型 YOLO weights_path
测试目录存在?
返回错误
model.predict 批量推理
source=test_images_dir
参数:
imgsz=640 conf=0.25 iou=0.7
save=True save_txt=True save_conf=True
异常?
调用备用方法
predict_alternative_method
输出预测完成
备用方法 predict_alternative_method()
#mermaid-svg-nIEEg5ufCs7YU3C8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nIEEg5ufCs7YU3C8 .error-icon{fill:#552222;}#mermaid-svg-nIEEg5ufCs7YU3C8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nIEEg5ufCs7YU3C8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .marker.cross{stroke:#333333;}#mermaid-svg-nIEEg5ufCs7YU3C8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nIEEg5ufCs7YU3C8 p{margin:0;}#mermaid-svg-nIEEg5ufCs7YU3C8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster-label text{fill:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster-label span{color:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster-label span p{background-color:transparent;}#mermaid-svg-nIEEg5ufCs7YU3C8 .label text,#mermaid-svg-nIEEg5ufCs7YU3C8 span{fill:#333;color:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .node rect,#mermaid-svg-nIEEg5ufCs7YU3C8 .node circle,#mermaid-svg-nIEEg5ufCs7YU3C8 .node ellipse,#mermaid-svg-nIEEg5ufCs7YU3C8 .node polygon,#mermaid-svg-nIEEg5ufCs7YU3C8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .rough-node .label text,#mermaid-svg-nIEEg5ufCs7YU3C8 .node .label text,#mermaid-svg-nIEEg5ufCs7YU3C8 .image-shape .label,#mermaid-svg-nIEEg5ufCs7YU3C8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-nIEEg5ufCs7YU3C8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .rough-node .label,#mermaid-svg-nIEEg5ufCs7YU3C8 .node .label,#mermaid-svg-nIEEg5ufCs7YU3C8 .image-shape .label,#mermaid-svg-nIEEg5ufCs7YU3C8 .icon-shape .label{text-align:center;}#mermaid-svg-nIEEg5ufCs7YU3C8 .node.clickable{cursor:pointer;}#mermaid-svg-nIEEg5ufCs7YU3C8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .arrowheadPath{fill:#333333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nIEEg5ufCs7YU3C8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nIEEg5ufCs7YU3C8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nIEEg5ufCs7YU3C8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster text{fill:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 .cluster span{color:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nIEEg5ufCs7YU3C8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nIEEg5ufCs7YU3C8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-nIEEg5ufCs7YU3C8 .icon-shape,#mermaid-svg-nIEEg5ufCs7YU3C8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nIEEg5ufCs7YU3C8 .icon-shape p,#mermaid-svg-nIEEg5ufCs7YU3C8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nIEEg5ufCs7YU3C8 .icon-shape .label rect,#mermaid-svg-nIEEg5ufCs7YU3C8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nIEEg5ufCs7YU3C8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nIEEg5ufCs7YU3C8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nIEEg5ufCs7YU3C8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
遍历目录收集图片
.jpg/.jpeg/.png/.bmp/.tiff
创建 images/labels 子目录
逐图循环 tqdm
model 单图推理
imgsz/conf/iou
r.plot 获取标注图
cv2.imwrite 保存
存在检测框?
逐框写入标签
cls + xywhn + conf
跳过
写入同名 .txt
说明 :备用方法中标签保存为 cls x_center y_center w h conf(水平框 xywh 归一化格式),与主方法的 OBB 输出(cls x0..y3 conf)格式不同,需注意区分。
命令行接口
bash
python predict.py \
--weights runs/obb/codrone_obb_exp1/weights/best.pt \
--test_dir video_model_images \
--output_dir runs/detect/actual_accidents_photo \
--conf 0.25 --iou 0.7 --img_size 640
6.2 预测输出格式
主方法输出的标签文件为 10 列(OBB + 置信度):
class_id x0 y0 x1 y1 x2 y2 x3 y3 confidence
例:0 0.675779 0.599432 0.733697 0.59453 0.731994 0.558751 0.674075 0.563653 0.777829
同时输出带框的标注图像(*.jpg)。
6.3 推理输入
| 目录 | 说明 |
|---|---|
actual_accidents_photo/ |
13 张真实事故航拍照片(DJI 大疆系列 + 现场照片) |
video_model_images/ |
视频抽帧图片(1.png,1800×尺寸) |
推理输出目录:runs/detect/actual_accidents_photo/predict/,包含标注图片与 labels/ 子目录。
7. 结果可视化
7.1 单图手动可视化(visual_points.py)
用于对单张图片的检测结果做精细可视化控制:
#mermaid-svg-czTrcvJn6cU2kk0Q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-czTrcvJn6cU2kk0Q .error-icon{fill:#552222;}#mermaid-svg-czTrcvJn6cU2kk0Q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-czTrcvJn6cU2kk0Q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-czTrcvJn6cU2kk0Q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-czTrcvJn6cU2kk0Q .marker.cross{stroke:#333333;}#mermaid-svg-czTrcvJn6cU2kk0Q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-czTrcvJn6cU2kk0Q p{margin:0;}#mermaid-svg-czTrcvJn6cU2kk0Q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster-label text{fill:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster-label span{color:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster-label span p{background-color:transparent;}#mermaid-svg-czTrcvJn6cU2kk0Q .label text,#mermaid-svg-czTrcvJn6cU2kk0Q span{fill:#333;color:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q .node rect,#mermaid-svg-czTrcvJn6cU2kk0Q .node circle,#mermaid-svg-czTrcvJn6cU2kk0Q .node ellipse,#mermaid-svg-czTrcvJn6cU2kk0Q .node polygon,#mermaid-svg-czTrcvJn6cU2kk0Q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-czTrcvJn6cU2kk0Q .rough-node .label text,#mermaid-svg-czTrcvJn6cU2kk0Q .node .label text,#mermaid-svg-czTrcvJn6cU2kk0Q .image-shape .label,#mermaid-svg-czTrcvJn6cU2kk0Q .icon-shape .label{text-anchor:middle;}#mermaid-svg-czTrcvJn6cU2kk0Q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-czTrcvJn6cU2kk0Q .rough-node .label,#mermaid-svg-czTrcvJn6cU2kk0Q .node .label,#mermaid-svg-czTrcvJn6cU2kk0Q .image-shape .label,#mermaid-svg-czTrcvJn6cU2kk0Q .icon-shape .label{text-align:center;}#mermaid-svg-czTrcvJn6cU2kk0Q .node.clickable{cursor:pointer;}#mermaid-svg-czTrcvJn6cU2kk0Q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-czTrcvJn6cU2kk0Q .arrowheadPath{fill:#333333;}#mermaid-svg-czTrcvJn6cU2kk0Q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-czTrcvJn6cU2kk0Q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-czTrcvJn6cU2kk0Q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-czTrcvJn6cU2kk0Q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-czTrcvJn6cU2kk0Q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-czTrcvJn6cU2kk0Q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster text{fill:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q .cluster span{color:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-czTrcvJn6cU2kk0Q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-czTrcvJn6cU2kk0Q rect.text{fill:none;stroke-width:0;}#mermaid-svg-czTrcvJn6cU2kk0Q .icon-shape,#mermaid-svg-czTrcvJn6cU2kk0Q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-czTrcvJn6cU2kk0Q .icon-shape p,#mermaid-svg-czTrcvJn6cU2kk0Q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-czTrcvJn6cU2kk0Q .icon-shape .label rect,#mermaid-svg-czTrcvJn6cU2kk0Q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-czTrcvJn6cU2kk0Q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-czTrcvJn6cU2kk0Q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-czTrcvJn6cU2kk0Q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
加载 best.pt 模型
对单图推理
actual_accidents_photo/DJI_0005.JPG
存在 OBB 结果?
输出 未检测到任何目标
提取四角点
归一化坐标 × width/height
还原像素坐标
astype int32
reshape 为 n,1,2
cv2.polylines 绘制
绿色闭合多边形
首顶点绘制
类别名 + 置信度文本
保存 obb_visualization_manual.jpg
可选 imshow 显示
关键坐标处理:
python
all_vertices = results[0].obb.xyxyxyxy.cpu().numpy() # 形状 (N, 4, 2)
vertices_denorm = vertices * np.array([width, height]) # 反归一化
vertices_reshaped = vertices_int.reshape((-1, 1, 2)) # cv2.polylines 要求 (n,1,2)
cv2.polylines(result_img, [vertices_reshaped], isClosed=True,
color=(0,255,0), thickness=2, lineType=cv2.LINE_AA)
7.2 训练过程可视化产物
runs/obb/codrone_obb_exp1/ 自动生成的图表:
| 文件 | 说明 |
|---|---|
results.png / results.csv |
训练-验证损失与指标曲线 / 原始数据 |
BoxP_curve.png |
Precision-Confidence 曲线 |
BoxR_curve.png |
Recall-Confidence 曲线 |
BoxPR_curve.png |
PR 曲线 |
BoxF1_curve.png |
F1-Confidence 曲线 |
confusion_matrix.png |
混淆矩阵 |
confusion_matrix_normalized.png |
归一化混淆矩阵 |
labels.jpg |
标签分布统计 |
train_batch*.jpg |
训练批次增强效果 |
val_batch*_pred.jpg / _labels.jpg |
验证集预测/真值对比 |
8. 关键技术细节
8.1 OBB(旋转框)vs HBB(水平框)
| 维度 | HBB | OBB |
|---|---|---|
| 框表示 | x_center y_center w h |
4 个角点 x0y0..x3y3(或 cx,cy,w,h,angle) |
| 标签列数 | 5 | 9 |
| 目标贴合度 | 倾斜目标会含大量背景 | 紧密贴合目标轮廓 |
| 适用场景 | 正视角目标 | 航拍倾斜车辆/舰船 |
8.2 数据增强(训练日志配置)
yaml
hsv_h: 0.015 # HSV 色相扰动
hsv_s: 0.7 # 饱和度扰动
hsv_v: 0.4 # 明度扰动
degrees: 0.0 # 旋转角度(OBB 场景设为 0)
translate: 0.1 # 平移
scale: 0.5 # 缩放
fliplr: 0.5 # 水平翻转概率
mosaic: 1.0 # Mosaic 增强
erasing: 0.4 # 随机擦除
auto_augment: randaugment # 自动增强策略
8.3 训练硬件与性能
GPU: NVIDIA RTX A4000 (15971MiB)
训练耗时: 3.791 hours(100 epochs)
单轮时间: 约 2 分 20 秒(313 个 batch)
验证耗时: 约 19 秒 / 轮(125 个 batch)
推理速度: 0.1ms 预处理 + 1.0ms 推理 + 2.6ms 后处理 / 图
8.4 类别不平衡问题
从最终验证指标可见,类别分布严重不均衡:
| 类别 | 实例数 | mAP50 |
|---|---|---|
| car | 46396 | 0.693 |
| people | 15457 | 0.217 |
| motor | 14986 | 0.375 |
| bridge | 77 | 0.124 |
| ship | 58 | 0.263 |
car 类占据绝对主导,稀有类别(bridge/ship)样本极少导致指标偏低,这是后续可优化的方向(如类别均衡采样、加权损失)。
9. 运行方式
9.1 环境要求
bash
pip install ultralytics==8.3.252 opencv-python tqdm
# PyTorch: torch 2.4.0 + CUDA 12.4
9.2 数据转换
bash
python convert_xml2yolo.py
9.3 标注可视化验证
bash
python visualize_obb_annotations.py
9.4 训练
bash
python train.py
9.5 批量推理
bash
python predict.py \
--weights runs/obb/codrone_obb_exp1/weights/best.pt \
--test_dir <图片目录> \
--output_dir <输出目录>
9.6 单图可视化
bash
python visual_points.py
10. 训练结果与性能指标
10.1 最终指标(best.pt 在验证集)
| 类别 | Images | Instances | Precision(B) | Recall(B) | mAP50 | mAP50-95 |
|---|---|---|---|---|---|---|
| all | 2000 | 89468 | 0.449 | 0.313 | 0.310 | 0.188 |
| car | 1822 | 46396 | 0.684 | 0.645 | 0.693 | 0.473 |
| truck | 1143 | 5058 | 0.531 | 0.310 | 0.346 | 0.235 |
| traffic-sign | 797 | 2425 | 0.634 | 0.221 | 0.281 | 0.163 |
| people | 1296 | 15457 | 0.471 | 0.180 | 0.217 | 0.0913 |
| motor | 1363 | 14986 | 0.501 | 0.361 | 0.375 | 0.175 |
| bicycle | 227 | 813 | 0.370 | 0.080 | 0.0891 | 0.0344 |
| traffic-light | 335 | 1498 | 0.228 | 0.0407 | 0.0578 | 0.0231 |
| tricycle | 329 | 599 | 0.433 | 0.122 | 0.136 | 0.0904 |
| bridge | 54 | 77 | 0.277 | 0.182 | 0.124 | 0.0523 |
| bus | 424 | 1021 | 0.481 | 0.442 | 0.418 | 0.289 |
| boat | 92 | 1080 | 0.557 | 0.741 | 0.724 | 0.464 |
| ship | 26 | 58 | 0.222 | 0.431 | 0.263 | 0.167 |
10.2 训练收敛
早停触发:第 100 轮(15 轮无提升),最佳轮次为 epoch 85
best.pt 保存于 epoch 85 的最优指标
最终损失: box=1.116 cls=0.856 dfl=1.229
10.3 核心结论
- 核心目标 car 检测效果良好(mAP50=0.693,mAP50-95=0.473),满足车辆要素提取需求。
- boat 类表现最佳(mAP50=0.724),但样本量仅 1080。
- 小目标 / 稀有类(traffic-light、bicycle)指标偏低,主要受限于航拍尺度小、样本不均衡。
- 整体 mAP50=0.310 受类别不平衡拖累,若只关注车辆类,模型已具备实用价值。
附:文件清单与职责
| 文件 | 职责 | 状态 |
|---|---|---|
codrone_obb.yaml |
数据集路径 + 12 类定义 | 正常 |
train.py |
加载 OBB 预训练权重并训练 | 正常 |
predict.py |
批量推理(主/备用两套) | 正常 |
convert_xml2yolo.py |
XML → YOLO OBB 转换 | 正常 |
visualize_obb_annotations.py |
标注可视化验证 | 正常 |
visual_points.py |
单图检测结果可视化 | 正常 |
detect.py |
空占位文件 | 未实现 |
evaluate.py |
空占位文件 | 未实现 |