CODrone 无人机航拍车辆检测

CODrone 无人机航拍车辆检测

基于 YOLO11-OBB(Oriented Bounding Box,旋转目标检测) 的无人机航拍图像车辆要素检测工程。

本文档根据工程源码、训练日志(console_print.txt)与数据集结构整理,覆盖数据预处理、模型训练、推理预测与可视化全流程。


目录

  1. 项目概述
  2. 目录结构
  3. 数据集说明
  4. 标注格式与数据预处理
  5. 模型训练
  6. 推理预测
  7. 结果可视化
  8. 关键技术细节
  9. 运行方式
  10. 训练结果与性能指标

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/widthy/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

关键逻辑说明:

  1. 类别映射class_mapping = {name: idx},与 codrone_obb.yaml 顺序一致。
  2. 忽略类别name == 'ignored' 的目标直接跳过(不参与训练)。
  3. 未知类别:不在映射中的类别打印警告并跳过。
  4. 归一化:每个角点分别除以图像宽、高。
  5. 边界保护max(0.0, min(1.0, coord)) 将坐标裁剪到 [0,1]
  6. 图像尺寸获取优先级 :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'
  • 关键模块:ConvC3k2SPPFC2PSAUpsampleConcatOBB 检测头

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 空占位文件 未实现
相关推荐
会飞的拖把1 小时前
Python序列详解:列表、元组、字符串的通用操作(超详细版)
开发语言·windows·python
2601_962283881 小时前
如何通过python控制nbiot
python·串口通信·数据传输·nb-iot·at命令
盼小辉丶2 小时前
PyTorch计算机视觉(9)——变分自编码器(VAE)详解与实现
人工智能·pytorch·深度学习·计算机视觉
AC赳赳老秦2 小时前
环保监测公开数据应用:OpenClaw 抓取空气与水质公开监测数据,开展区域环境质量趋势分析
大数据·数据库·人工智能·python·php·deepseek·openclaw
RSABLOCKCHAIN2 小时前
打造工业级多智能体量化交易闭环:基于 Alpaca、LangGraph 与双轨执行的智能投资与持仓治理系统
人工智能·python·ai·aigc
用户019027581612 小时前
如何用 Python 做多因子打分选股?
python
烂蜻蜓2 小时前
Flask入门教程(三十一):实用函数与类API——全局工具函数速查
后端·python·flask
晓窗科技2 小时前
AI基座哪家服务好
大数据·人工智能·python
Zane19942 小时前
257 is 257 为什么是 True?小整数缓存背后还藏着一个更容易被忽略的机制
后端·python