保姆级教程:手把手教你将DIOR遥感数据集转为YOLOv5/v8训练格式(附完整代码)

保姆级教程:手把手教你将DIOR遥感数据集转为YOLOv5/v8训练格式(附完整代码) 从DIOR到YOLO遥感目标检测数据集格式转换实战指南遥感影像中的目标检测一直是计算机视觉领域的重要研究方向。DIOR数据集作为当前主流的遥感目标检测基准数据集之一包含20个类别、超过2万张高分辨率遥感图像。但对于刚接触该领域的研究者而言如何将DIOR官方提供的XML标注格式转换为YOLO系列模型所需的TXT格式往往成为第一个技术门槛。1. 环境准备与数据理解在开始转换前我们需要先理解DIOR数据集的基本结构和YOLO格式的核心要求。DIOR数据集通常包含以下关键目录JPEGImages存放所有遥感图像文件.jpg格式Annotations存放XML格式的标注文件ImageSets/Main包含train.txt、val.txt和test.txt指定了训练集、验证集和测试集的划分而YOLO格式要求每个图像对应一个同名的TXT文件其中每行表示一个目标对象格式为class_id x_center y_center width height这些坐标值都是相对于图像宽度和高度的归一化值0-1之间。为顺利进行转换我们需要准备以下环境# 必需Python库 pip install lxml pip install tqdm # 用于显示进度条2. 数据集目录结构调整正确的目录结构是避免后续报错的关键。按照YOLO的惯例我们需要对DIOR原始结构进行调整DIOR/ ├── images/ # 原JPEGImages目录改名为images │ ├── train/ # 训练集图像 │ ├── val/ # 验证集图像 │ └── test/ # 测试集图像 └── labels/ # 新建labels目录 ├── train/ # 训练集标签 ├── val/ # 验证集标签 └── test/ # 测试集标签重要提示必须使用全小写的images和labels作为目录名这是YOLO模型的硬性要求。大小写不一致会导致No labels错误。3. XML到TXT的完整转换流程以下是完整的格式转换代码我们逐部分解析其工作原理import xml.etree.ElementTree as ET import os from tqdm import tqdm # DIOR的20个类别 CLASSES [ airplane, airport, baseballfield, basketballcourt, bridge, chimney, dam, Expressway-Service-area, Expressway-toll-station, golffield, groundtrackfield, harbor, overpass, ship, stadium, storagetank, tenniscourt, trainstation, vehicle, windmill ] def convert(size, box): 将边界框坐标转换为YOLO格式的归一化坐标 :param size: 图像宽高 (w, h) :param box: 边界框坐标 (xmin, xmax, ymin, ymax) :return: 归一化的(x_center, y_center, width, height) dw 1. / size[0] dh 1. / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] x x * dw w w * dw y y * dh h h * dh return (x, y, w, h) def process_dior_dataset(dior_root, output_dir): 主处理函数 :param dior_root: DIOR数据集根目录 :param output_dir: 输出目录 # 创建必要的目录结构 os.makedirs(os.path.join(output_dir, labels, train), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, val), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, test), exist_okTrue) # 处理每个划分集 for split in [train, val, test]: # 获取图像ID列表 with open(os.path.join(dior_root, ImageSets, Main, f{split}.txt)) as f: image_ids f.read().strip().split() # 创建图像路径列表文件 with open(os.path.join(output_dir, f{split}.txt), w) as list_file: for image_id in tqdm(image_ids, descfProcessing {split}): # 写入图像绝对路径 img_path os.path.join(dior_root, images, f{image_id}.jpg) list_file.write(f{img_path}\n) # 转换对应的标注文件 convert_annotation(dior_root, image_id, output_dir, split) def convert_annotation(dior_root, image_id, output_dir, split): 转换单个XML标注文件为YOLO格式 in_file open(os.path.join(dior_root, Annotations, f{image_id}.xml)) out_file open(os.path.join(output_dir, labels, split, f{image_id}.txt), w) tree ET.parse(in_file) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) for obj in root.iter(object): cls obj.find(name).text if cls not in CLASSES: continue cls_id CLASSES.index(cls) xmlbox obj.find(bndbox) b ( float(xmlbox.find(xmin).text), float(xmlbox.find(xmax).text), float(xmlbox.find(ymin).text), float(xmlbox.find(ymax).text) ) # 边界检查 b ( max(0, b[0]), min(w, b[1]), max(0, b[2]), min(h, b[3]) ) bb convert((w, h), b) out_file.write(f{cls_id} { .join([str(a) for a in bb])}\n) if __name__ __main__: dior_path /path/to/DIOR # 修改为你的DIOR路径 output_path /path/to/output # 修改为输出路径 process_dior_dataset(dior_path, output_path)4. 常见问题与解决方案在实际转换过程中可能会遇到以下典型问题4.1 No labels错误排查当训练时出现No labels in xxx/train.cache错误时请按以下步骤检查目录结构验证确认存在labels/train目录且与images/train平行检查目录名称是否为全小写标签文件验证确保每个图像都有对应的TXT标签文件标签文件内容格式正确class_id x y w h路径验证在生成的train.txt中路径是否指向正确的图像位置路径中不应包含中文或特殊字符4.2 坐标越界处理遥感图像中的目标有时会部分超出图像边界。我们的代码已经包含边界检查# 边界检查代码段 b ( max(0, b[0]), min(w, b[1]), # xmin和xmax限制在[0, w]之间 max(0, b[2]), min(h, b[3]) # ymin和ymax限制在[0, h]之间 )4.3 类别映射问题如果DIOR的类别与你的需求不完全一致可以修改CLASSES列表# 自定义类别示例只保留部分类别 CLASSES [ airplane, ship, vehicle, bridge ]注意修改类别后class_id会相应变化务必确保训练时的类别顺序与此一致。5. 创建YOLO配置文件转换完成后需要创建YAML配置文件供YOLO模型使用# DIOR.yaml train: /path/to/output/train.txt val: /path/to/output/val.txt test: /path/to/output/test.txt # 类别数 nc: 20 # 类别名称 names: [ airplane, airport, baseballfield, basketballcourt, bridge, chimney, dam, Expressway-Service-area, Expressway-toll-station, golffield, groundtrackfield, harbor, overpass, ship, stadium, storagetank, tenniscourt, trainstation, vehicle, windmill ]6. 验证转换结果为确保转换正确建议进行可视化验证import cv2 import random def visualize_yolo_label(img_path, label_path, classes): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: lines f.readlines() for line in lines: parts line.strip().split() cls_id int(parts[0]) x, y, w_, h_ map(float, parts[1:]) # 转换回像素坐标 x int(x * w) y int(y * h) w_ int(w_ * w) h_ int(h_ * h) # 计算边界框坐标 x1 int(x - w_ / 2) y1 int(y - h_ / 2) x2 int(x w_ / 2) y2 int(y h_ / 2) # 绘制边界框和类别 color (random.randint(0,255), random.randint(0,255), random.randint(0,255)) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, classes[cls_id], (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2) return img # 示例使用 img_path /path/to/output/images/train/000001.jpg label_path /path/to/output/labels/train/000001.txt img visualize_yolo_label(img_path, label_path, CLASSES) cv2.imwrite(visualization.jpg, img)7. 高效处理大规模数据集的技巧当处理完整的DIOR数据集约23,463张图像时可以考虑以下优化策略并行处理 使用Python的multiprocessing模块加速处理from multiprocessing import Pool def process_image(image_id): # 包装转换逻辑 pass with Pool(processes8) as pool: # 使用8个进程 pool.map(process_image, image_ids)增量处理 记录已处理的图像ID便于中断后恢复processed_file processed_ids.txt # 加载已处理的ID if os.path.exists(processed_file): with open(processed_file) as f: processed set(f.read().splitlines()) else: processed set() for image_id in image_ids: if image_id not in processed: # 处理逻辑 with open(processed_file, a) as f: f.write(f{image_id}\n)数据校验 转换完成后进行完整性检查def validate_conversion(output_dir): for split in [train, val, test]: with open(os.path.join(output_dir, f{split}.txt)) as f: img_paths f.read().splitlines() for img_path in img_paths: img_id os.path.splitext(os.path.basename(img_path))[0] label_path os.path.join(output_dir, labels, split, f{img_id}.txt) if not os.path.exists(label_path): print(fMissing label: {label_path}) if not os.path.exists(img_path): print(fMissing image: {img_path})