news 2026/4/18 14:40:44

从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/4/18 0:26:38

Java代码优化效率提升:如何用插件系统解决80%的反编译烦恼?

Java代码优化效率提升&#xff1a;如何用插件系统解决80%的反编译烦恼&#xff1f; 【免费下载链接】Recaf Col-E/Recaf: Recaf 是一个现代Java反编译器和分析器&#xff0c;它提供了用户友好的界面&#xff0c;便于浏览、修改和重构Java字节码。 项目地址: https://gitcode.…

作者头像 李华
网站建设 2026/4/18 0:21:24

mPLUG视觉问答行业落地:零售货架分析、物流单据图文核验实战案例

mPLUG视觉问答行业落地&#xff1a;零售货架分析、物流单据图文核验实战案例 1. 本地化视觉问答工具&#xff1a;让图片自己“开口说话” 你有没有遇到过这样的场景&#xff1a; 一张超市货架的照片发到工作群&#xff0c;同事问“第三排左边第二个是什么商品&#xff1f;保…

作者头像 李华
网站建设 2026/4/18 0:26:40

零代码企业级测试自动化实战指南

零代码企业级测试自动化实战指南 【免费下载链接】testsigma A powerful open source test automation platform for Web Apps, Mobile Apps, and APIs. Build stable and reliable end-to-end tests DevOps speed. 项目地址: https://gitcode.com/gh_mirrors/te/testsigma …

作者头像 李华
网站建设 2026/4/18 2:01:25

如何彻底解决ComfyUI扩展功能缺失问题?专家级排查指南

如何彻底解决ComfyUI扩展功能缺失问题&#xff1f;专家级排查指南 【免费下载链接】ComfyUI-Impact-Pack 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-Impact-Pack 在使用ComfyUI进行AI图像生成时&#xff0c;扩展功能的正常加载是实现复杂效果的关键。许多用…

作者头像 李华
网站建设 2026/4/18 2:04:38

InstructPix2Pix惊艳效果:‘Turn day into night’光照重建真实感分析

InstructPix2Pix惊艳效果&#xff1a;‘Turn day into night’光照重建真实感分析 1. 不是滤镜&#xff0c;是会听指令的修图师 你有没有试过在深夜加班时&#xff0c;突然被老板甩来一张白天拍的街景照片&#xff0c;要求“马上改成夜晚效果&#xff0c;今晚就要发稿”&…

作者头像 李华