news 2026/6/13 22:59:31

从DOTA到YOLO:HBB水平框遥感数据集的转换实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从DOTA到YOLO:HBB水平框遥感数据集的转换实战指南

1. 从DOTA到YOLO:为什么需要转换数据集格式

第一次接触遥感图像目标检测时,我对着DOTA数据集里密密麻麻的四边形标注框发了好一会儿呆。这些被称为OBB(Oriented Bounding Box)的旋转框虽然能精准框住斜向停放的飞机或船舶,但主流的YOLOv5/v8等检测器默认只支持HBB(Horizontal Bounding Box)水平框。这就好比你想用标准螺丝刀拧三角螺丝——工具和零件不匹配。

DOTA数据集的特点确实令人印象深刻:

  • 图像分辨率普遍在4000×4000像素以上
  • 标注采用8个坐标点的多边形格式(x1,y1,x2,y2,...,x8,y8)
  • 包含15个典型遥感目标类别(船舶、储油罐、运动场等)
  • 单个图像可能包含数百个密集目标

而YOLO需要的却是简约的5参数格式:

类别索引 x_center y_center width height

这个转换过程就像把复杂的立体折纸展开成平面图纸。我最初尝试手动转换时,发现直接取旋转框的外接矩形会导致大量无效背景区域被包含,特别是对于长宽比悬殊的桥梁、船舶等目标。后来通过分析DOTA_devkit源码才找到正确解法——应该计算所有顶点坐标的最小外接水平矩形。

2. 数据准备与环境配置

2.1 获取原始数据集

建议直接从DOTA官网下载基准数据集,目前主流版本有:

  • DOTA-v1.0(2.8GB,15类别)
  • DOTA-v1.5(6.2GB,新增集装箱起重机类别)
  • DOTA-v2.0(35.4GB,18类别)

如果网络条件受限,可以使用我预处理过的HBB版本(包含已转换的YOLO格式标签):

# 百度云下载(密码:iw3w) wget https://pan.baidu.com/s/1UX7oX3_x5CrP_SxSA7XKXQ

2.2 安装关键工具包

处理过程中需要以下Python包:

# 基础环境 pip install numpy opencv-python pillow # 专用工具 pip install dota-utils shapely

特别提醒:建议使用Shapely 1.7.1版本,新版本在计算多边形几何时可能有API变动。我在Colab上测试时遇到过这样的报错:

AttributeError: 'Polygon' object has no attribute '_get_coords'

就是版本兼容性问题导致的。

3. 核心转换流程详解

3.1 标注文件解析实战

DOTA的标注文件是这样的文本格式:

imagesource:GoogleEarth gsd:0.146 ... 1 128 256 384 512 ... large-vehicle 0

我们需要提取的是每行末尾的8个坐标点和类别信息。用Python处理时要注意:

def parse_dota_label(label_path): with open(label_path) as f: lines = [l.strip() for l in f.readlines()] objects = [] for line in lines: if line.startswith('imagesource'): continue parts = line.split() if len(parts) < 9: continue # 提取8个坐标点(x1,y1,...,x4,y4) points = list(map(float, parts[:8])) # 获取类别和difficult标志 cls = parts[8] difficult = int(parts[9]) if len(parts) > 9 else 0 objects.append({'points': points, 'class': cls, 'difficult': difficult}) return objects

3.2 坐标转换关键算法

将旋转框转为水平框的核心是计算最小外接矩形。使用Shapely库的MultiPoint可以优雅实现:

from shapely.geometry import MultiPoint def obb_to_hbb(points): # 将8个坐标点转为4个顶点 vertices = [(points[i], points[i+1]) for i in range(0, 8, 2)] multipoint = MultiPoint(vertices) # 获取最小外接矩形 hbb = multipoint.minimum_rotated_rectangle # 返回矩形四个顶点 return list(hbb.exterior.coords)[:4]

但YOLO需要的是归一化的中心坐标和宽高,还需要进行二次转换:

def hbb_to_yolo(vertices, img_width, img_height): # 计算边界 x_coords = [p[0] for p in vertices] y_coords = [p[1] for p in vertices] x_min, x_max = min(x_coords), max(x_coords) y_min, y_max = min(y_coords), max(y_coords) # 计算中心点和宽高(归一化) x_center = ((x_min + x_max) / 2) / img_width y_center = ((y_min + y_max) / 2) / img_height width = (x_max - x_min) / img_width height = (y_max - y_min) / img_height return x_center, y_center, width, height

3.3 图像分块处理技巧

DOTA图像尺寸过大(平均4000×4000),直接输入网络会显存爆炸。我推荐使用滑动窗口分块:

def split_image(img, window_size=1024, overlap=200): height, width = img.shape[:2] patches = [] for y in range(0, height, window_size - overlap): for x in range(0, width, window_size - overlap): # 计算实际裁剪区域 x1 = max(0, x) y1 = max(0, y) x2 = min(width, x + window_size) y2 = min(height, y + window_size) patch = img[y1:y2, x1:x2] patches.append((patch, (x1, y1, x2, y2))) return patches

注意重叠区域(overlap)要设置合理,我测试发现200像素能较好避免目标被切割。

4. 实战中的常见问题解决

4.1 类别映射问题

DOTA的类别名称带有连字符(如"small-vehicle"),而YOLO通常用数字索引。建议创建映射文件:

# dota_classes.yaml names: 0: plane 1: ship 2: storage-tank ... 14: helicopter

4.2 小目标丢失问题

在转换过程中,有些小目标(<10像素)可能因坐标取整被过滤。可以通过以下方式缓解:

# 在转换前添加过滤条件 if width * img_width < 10 or height * img_height < 10: print(f"忽略小目标:{cls} at ({x_center},{y_center})") continue

4.3 图像格式兼容性

YOLO对PNG支持不如JPG稳定,建议批量转换:

# 使用Imagemagick批量转换 mogrify -format jpg -quality 90 *.png

5. 完整转换脚本示例

以下是经过实战检验的完整转换脚本:

import os import cv2 from tqdm import tqdm from shapely.geometry import MultiPoint class DOTA2YOLO: def __init__(self, src_img_dir, src_label_dir, dst_dir): self.src_img_dir = src_img_dir self.src_label_dir = src_label_dir self.dst_dir = dst_dir os.makedirs(os.path.join(dst_dir, 'images'), exist_ok=True) os.makedirs(os.path.join(dst_dir, 'labels'), exist_ok=True) def convert(self): img_files = [f for f in os.listdir(self.src_img_dir) if f.lower().endswith(('.png', '.jpg'))] for img_file in tqdm(img_files): # 处理图像 img_path = os.path.join(self.src_img_dir, img_file) img = cv2.imread(img_path) h, w = img.shape[:2] # 处理对应标注 label_file = img_file.replace('.png', '.txt').replace('.jpg', '.txt') label_path = os.path.join(self.src_label_dir, label_file) if not os.path.exists(label_path): continue yolo_labels = [] objects = self.parse_dota_label(label_path) for obj in objects: hbb = self.obb_to_hbb(obj['points']) xc, yc, bw, bh = self.hbb_to_yolo(hbb, w, h) yolo_labels.append(f"{obj['class']} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") # 保存结果 dst_label_path = os.path.join(self.dst_dir, 'labels', label_file) with open(dst_label_path, 'w') as f: f.write('\n'.join(yolo_labels)) # 转换并保存图像 dst_img_path = os.path.join(self.dst_dir, 'images', img_file.replace('.png', '.jpg')) cv2.imwrite(dst_img_path, img) # 其他工具方法同上...

使用时只需初始化并执行:

converter = DOTA2YOLO('DOTA/train/images', 'DOTA/train/labels', 'YOLO_DOTA') converter.convert()

6. 验证转换结果

转换完成后,强烈建议可视化检查:

import matplotlib.pyplot as plt import matplotlib.patches as patches def visualize(img_path, label_path, class_map): img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB) h, w = img.shape[:2] fig, ax = plt.subplots(1, figsize=(12, 8)) ax.imshow(img) with open(label_path) as f: lines = f.readlines() for line in lines: cls_idx, xc, yc, bw, bh = map(float, line.strip().split()) # 转换回像素坐标 x = (xc - bw/2) * w y = (yc - bh/2) * h width = bw * w height = bh * h rect = patches.Rectangle((x,y), width, height, linewidth=2, edgecolor='r', facecolor='none') ax.add_patch(rect) plt.text(x, y, class_map[int(cls_idx)], color='white', bbox=dict(facecolor='red', alpha=0.5)) plt.show()

7. 高效训练技巧

转换后的数据集可以这样配置YOLOv5训练:

# dota.yaml train: ../YOLO_DOTA/images/train val: ../YOLO_DOTA/images/val nc: 15 names: ['plane', 'ship', 'storage-tank', 'baseball-diamond', 'tennis-court', 'basketball-court', 'ground-track-field', 'harbor', 'bridge', 'large-vehicle', 'small-vehicle', 'helicopter', 'roundabout', 'soccer-ball-field', 'swimming-pool']

启动训练时建议调整锚点参数:

python train.py --img 1024 --batch 8 --epochs 100 --data dota.yaml \ --weights yolov5s.pt --hyp data/hyps/hyp.scratch-low.yaml

我在RTX 3090上测试发现,使用--img 1024配合--batch 8能在显存占用和检测效果间取得平衡。对于小目标密集的场景,可以尝试以下改进:

  • 增加--img-size到1536
  • 使用更密集的锚点配置
  • 添加小目标检测层
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 13:05:39

OpenGL实战:利用glReadPixels实现动态区域像素分析与BMP截图

1. 理解glReadPixels的核心机制 第一次接触glReadPixels时&#xff0c;我盯着那个包含7个参数的函数原型看了足足十分钟。这个OpenGL函数就像个精密的瑞士军刀&#xff0c;能直接从显存中挖出一块像素数据。它的标准调用形式是这样的&#xff1a; void glReadPixels(GLint x,…

作者头像 李华
网站建设 2026/6/13 1:55:53

Windows运行库修复完全指南:告别程序启动失败的终极解决方案

Windows运行库修复完全指南&#xff1a;告别程序启动失败的终极解决方案 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 作为Windows系统依赖修复工具&#xff0…

作者头像 李华
网站建设 2026/6/13 7:11:47

XySubFilter字幕渲染技术解析:从原理到实践的高清解决方案

XySubFilter字幕渲染技术解析&#xff1a;从原理到实践的高清解决方案 【免费下载链接】xy-VSFilter xy-VSFilter variant with libass backend 项目地址: https://gitcode.com/gh_mirrors/xyv/xy-VSFilter 引言&#xff1a;字幕渲染的技术挑战 在视频播放过程中&#…

作者头像 李华
网站建设 2026/6/13 12:10:05

WeKnora镜像免配置部署教程:Docker一键拉取,开箱即用Web问答界面

WeKnora镜像免配置部署教程&#xff1a;Docker一键拉取&#xff0c;开箱即用Web问答界面 1. 为什么你需要一个“不胡说”的知识问答工具&#xff1f; 你有没有遇到过这样的情况&#xff1a;把一份产品说明书丢给AI&#xff0c;问它某个参数&#xff0c;结果它自信满满地编了个…

作者头像 李华
网站建设 2026/6/12 19:47:31

Z-Image-Turbo_UI界面带来的视觉冲击太强了

Z-Image-Turbo_UI界面带来的视觉冲击太强了 1. 初见即震撼&#xff1a;这不是传统WebUI&#xff0c;而是一次视觉体验升级 第一次打开Z-Image-Turbo_UI界面时&#xff0c;我下意识停顿了两秒——不是因为加载慢&#xff0c;而是被它干净、锐利、富有呼吸感的视觉设计击中了。…

作者头像 李华
网站建设 2026/6/13 19:26:36

5个步骤打造高效文献管理工作流:Zotero-MDNotes全攻略

5个步骤打造高效文献管理工作流&#xff1a;Zotero-MDNotes全攻略 【免费下载链接】zotero-mdnotes A Zotero plugin to export item metadata and notes as markdown files 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-mdnotes 在信息爆炸的学术环境中&#x…

作者头像 李华