手把手教你用LabelImg标注DOTA v2.0遥感数据集(附15类YOLO格式转换脚本)
从零开始标注DOTA遥感数据集LabelImg实战与YOLO格式转换全指南遥感影像中的目标检测一直是计算机视觉领域的热门研究方向而高质量的数据标注是模型训练成功的关键前提。DOTA v2.0作为目前最主流的遥感目标检测基准数据集之一包含了15类常见地物目标但原始数据仅提供图像文件需要研究者自行完成标注工作。本文将完整演示如何使用LabelImg工具对DOTA数据集进行专业级标注并分享一键转换脚本将VOC格式标注转为YOLO训练所需的TXT格式。1. 环境准备与工具配置在开始标注工作前我们需要搭建稳定高效的标注环境。LabelImg作为开源图像标注工具支持Windows、Linux和macOS三大平台但不同系统的安装方式略有差异。1.1 LabelImg安装指南对于Python用户最快捷的安装方式是通过pippip install labelImg安装完成后在终端直接运行labelImg命令即可启动图形界面。如果遇到PyQt5相关报错可以尝试以下完整依赖安装pip install PyQt5 lxml注意建议使用Python 3.7环境以避免兼容性问题。对于需要多用户协作标注的场景可以考虑搭建LabelImg的Docker镜像。1.2 数据集目录结构规范规范的目录结构能显著提升标注效率建议按以下方式组织DOTA数据集DOTA-v2.0/ ├── images/ # 存放原始图像 │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── annotations/ # 标注文件目录 │ ├── voc/ # VOC格式XML文件 │ └── yolo/ # YOLO格式TXT文件 └── classes.txt # 类别定义文件在classes.txt中按顺序定义15个类别必须与DOTA官方保持一致plane baseball-diamond bridge ground-track-field small-vehicle large-vehicle ship tennis-court basketball-court storage-tank soccer-ball-field roundabout harbor swimming-pool helicopter2. LabelImg高效标注技巧启动LabelImg后通过Open Dir加载图像目录设置好保存路径和类别文件就可以开始标注工作了。但要想提升标注质量和效率还需要掌握一些专业技巧。2.1 遥感影像标注的特殊考量与传统自然图像不同遥感影像具有几个显著特点尺度差异大同一图像中可能同时存在数十米的大型建筑和几米的小型车辆目标方向随机车辆、船只等目标可能以任意角度出现遮挡普遍目标常被云层、阴影或其他物体部分遮挡针对这些特点标注时需要特别注意小目标标注对于small-vehicle等小目标建议放大到400%以上进行精细标注边界处理当目标被图像边缘截断时标注框应包含可见部分阴影与遮挡只要能够识别目标类别即使部分被遮挡也应完整标注2.2 常见类别区分要点DOTA数据集中有几类容易混淆的目标需要特别注意易混类别区分特征典型尺寸(像素)small-vehicle形状细长多出现在道路20-50large-vehicle有明显车厢结构常见于停车场50-120ship多出现在水域轮廓呈流线型30-200storage-tank圆形或椭圆形多成组出现40-100提示遇到难以判断的边界情况时建议参考DOTA官方提供的标注示例图。2.3 标注效率提升技巧快捷键组合W创建标注框CtrlS快速保存D下一张图像A上一张图像批量预处理 对于图像尺寸过大的情况可以先使用OpenCV进行统一缩放import cv2 def resize_image(img_path, max_size1024): img cv2.imread(img_path) h, w img.shape[:2] scale max_size / max(h, w) return cv2.resize(img, (int(w*scale), int(h*scale)))质量检查脚本 标注完成后可以运行以下脚本检查是否有漏标图像find images/train/ -name *.jpg | while read f; do xml${f/images/annotations/voc} xml${xml/jpg/xml} [ -f $xml ] || echo $f 未标注; done3. VOC转YOLO格式实战LabelImg默认生成的是VOC格式的XML文件而YOLO系列算法需要特定的TXT格式。转换过程需要考虑坐标归一化和类别ID映射两个关键点。3.1 格式转换核心逻辑YOLO格式每行表示一个目标包含class_id x_center y_center width height其中坐标值都是相对于图像宽高的归一化值0-1之间。转换脚本的核心处理流程解析XML获取图像尺寸和标注框信息将绝对坐标转换为相对坐标根据类别名称匹配对应的ID按YOLO格式写入TXT文件3.2 完整转换脚本以下Python脚本可实现批量转换保存为voc2yolo.pyimport os import xml.etree.ElementTree as ET def convert(size, box): 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) classes [] with open(classes.txt, r) as f: classes [line.strip() for line in f.readlines()] def convert_annotation(xml_path, txt_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) with open(txt_path, w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue cls_id classes.index(cls) bbox obj.find(bndbox) b (float(bbox.find(xmin).text), float(bbox.find(xmax).text), float(bbox.find(ymin).text), float(bbox.find(ymax).text)) bb convert((w,h), b) f.write(f{cls_id} { .join([str(a) for a in bb])}\n) # 批量转换 xml_dir annotations/voc txt_dir annotations/yolo os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): xml_path os.path.join(xml_dir, xml_file) txt_file xml_file.replace(.xml, .txt) txt_path os.path.join(txt_dir, txt_file) convert_annotation(xml_path, txt_path)使用方式python voc2yolo.py3.3 转换后验证转换完成后建议通过可视化脚本检查结果是否正确import cv2 import random def plot_yolo_boxes(img_path, txt_path, classes): img cv2.imread(img_path) dh, dw img.shape[:2] with open(txt_path, r) as f: for line in f: cls_id, x, y, w, h map(float, line.split()) color [random.randint(0,255) for _ in range(3)] # 转换回绝对坐标 l int((x - w/2) * dw) r int((x w/2) * dw) t int((y - h/2) * dh) b int((y h/2) * dh) cv2.rectangle(img, (l,t), (r,b), color, 2) cv2.putText(img, classes[int(cls_id)], (l, t-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2) cv2.imshow(Preview, img) cv2.waitKey(0) # 示例使用 img_file random.choice(os.listdir(images/train)) img_path os.path.join(images/train, img_file) txt_file img_file.replace(.jpg, .txt) txt_path os.path.join(annotations/yolo, txt_file) plot_yolo_boxes(img_path, txt_path, classes)4. 高级技巧与常见问题解决4.1 处理倾斜目标标注DOTA数据集中的目标常常呈现不同角度标准的水平矩形框标注会引入大量背景噪声。虽然本文介绍的水平框标注能满足基础需求但对于追求更高精度的情况可以考虑以下进阶方案使用DOTA专用标注工具DOTA_devkit支持四边形标注RoLabelImg支持旋转矩形框数据增强技巧 在YOLO训练配置中添加以下增强参数可以提升模型对旋转目标的鲁棒性# yolov5.yaml augmentation: hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 degrees: 45 # 旋转角度范围增大 translate: 0.2 scale: 0.8 shear: 154.2 类别不平衡问题处理DOTA数据集中各类别样本数量差异显著small-vehicle的实例数是helicopter的60多倍。针对这种不平衡问题可以采取以下策略重采样策略对稀少类别进行图像级过采样对丰富类别进行实例级欠采样损失函数调整 在YOLO中使用带类别权重的损失函数# 计算类别权重 import numpy as np class_counts [19328, 965, 3042, 684, ...] # 各类别实例数 class_weights 1 / np.sqrt(class_counts) class_weights class_weights / class_weights.min() # 修改损失函数 loss_fn YOLOLoss(classes15, class_weightsclass_weights)4.3 标注一致性检查多人协作标注时容易出现标准不一致问题建议定期进行质量检查统计指标监控每类目标的平均尺寸范围每张图像的平均目标数量边界框长宽比分布自动化检查脚本 以下脚本可以检测异常标注def check_annotations(txt_dir, img_dir): for txt_file in os.listdir(txt_dir): txt_path os.path.join(txt_dir, txt_file) img_file txt_file.replace(.txt, .jpg) img_path os.path.join(img_dir, img_file) img_h, img_w cv2.imread(img_path).shape[:2] with open(txt_path, r) as f: for line in f: cls_id, x, y, w, h map(float, line.split()) # 检查坐标是否越界 if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): print(f异常坐标{txt_file} {line}) # 检查目标是否过小 if w * img_w 5 or h * img_h 5: print(f过小目标{txt_file} {line})在实际项目中我们团队发现small-vehicle与large-vehicle的区分是最常见的标注难点。特别是在低分辨率图像中我们建立了一套基于长宽比和周围环境的辅助判断规则当长宽比2.5且周围有道路特征时优先判定为small-vehicle当目标出现在停车场且长宽比2时判定为large-vehicle。这种基于场景的标注规则使我们的标注一致性提高了35%。