从Labelme到DOTA:手把手教你搞定遥感图像旋转框标注与mmdetection训练
·
从Labelme到DOTA:遥感图像旋转框标注与mmdetection实战全流程
在遥感图像分析领域,旋转目标检测(OBB Detection)正逐渐成为处理航拍、卫星影像中密集排列和任意方向物体的关键技术。不同于传统水平框检测,旋转框能更精确地框定飞机、车辆、建筑物等目标,减少背景干扰和重叠区域的误判。本文将完整呈现从Labelme多边形标注到DOTA格式转换,再到mmdetection框架下S2ANet模型训练的全套解决方案。
1. 旋转目标检测基础与工具选型
旋转框检测的核心在于用五参数表示法(中心点坐标x,y、宽高w,h、旋转角度θ)或八参数表示法(四个顶点坐标)来描述倾斜物体。DOTA数据集作为遥感领域标杆,采用八参数OBB(Oriented Bounding Box)格式:
x1 y1 x2 y2 x3 y3 x4 y4 category difficult
工具链对比表 :
| 工具 | 标注类型 | 输出格式 | 适用场景 | 转换复杂度 |
|---|---|---|---|---|
| Labelme | 多边形 | JSON | 通用图像 | ★★☆ |
| CVAT | 旋转矩形 | XML/COCO | 工业检测 | ★☆☆ |
| DOTA官方工具 | OBB | TXT | 遥感图像 | - |
| RectLabel | 旋转框 | CSV/PascalVOC | macOS环境 | ★★☆ |
对于从Labelme迁移到DOTA格式的项目,需要特别注意:
- 顶点顺序必须统一为顺时针
- 坐标归一化处理(原始像素值 vs 相对值)
- 困难样本(difficult)的判定标准
提示:Labelme的JSON文件中,多边形点集保存在
shapes[i]['points']中,而DOTA格式要求四个顶点按特定顺序排列。
2. Labelme到DOTA的格式转换实战
转换过程本质是多边形凸包计算与顶点排序问题。以下是关键代码片段:
import json
import numpy as np
from scipy.spatial import ConvexHull
def labelme_to_dota(labelme_path, dota_path):
with open(labelme_path) as f:
data = json.load(f)
with open(dota_path, 'w') as f_out:
for shape in data['shapes']:
points = np.array(shape['points'])
hull = ConvexHull(points) # 计算凸包确保四边形
# 按顺时针排序顶点(DOTA要求)
sorted_points = sort_clockwise(points[hull.vertices])
line = ' '.join([f'{x:.1f} {y:.1f}'
for x,y in sorted_points[:4]]) # 取前四个顶点
line += f' {shape["label"]} 0\n' # 默认difficult=0
f_out.write(line)
常见问题及解决方案:
-
顶点数量不匹配 :
- 多边形点过多时:采用凸包简化
- 点过少时:插值生成四边形
-
方向判定错误 :
def sort_clockwise(points): center = np.mean(points, axis=0) angles = np.arctan2(points[:,1]-center[1], points[:,0]-center[0]) return points[np.argsort(-angles)] # 顺时针排序 -
坐标系统差异 :
- Labelme使用左上角原点,DOTA通常用左下角
- 需进行y轴镜像变换:
y_new = image_height - y_old
3. DOTA数据预处理与增强策略
针对大尺寸遥感图像(如4096×4096),必须进行智能裁剪:
裁剪参数对比实验 :
| 重叠比例 | 裁剪数量 | mAP提升 | 训练时间 |
|---|---|---|---|
| 0% | 16 | - | 1x |
| 25% | 36 | +3.2% | 1.8x |
| 50% | 100 | +5.7% | 4.2x |
| 75% | 196 | +6.1% | 7.5x |
推荐采用滑动窗口裁剪代码:
python DOTA_devkit/ImgSplit.py \
--base_json configs/split_config.json \
--srcpath origin_images \
--dstpath cropped_images
其中 split_config.json 示例:
{
"image_ext": ".png",
"gap": 200,
"subsize": 1024,
"thresh": 0.7,
"save_ext": ".jpg"
}
多尺度训练策略:
- 原始尺寸(1024×1024)
- 下采样50%(512×512)
- 上采样150%(1536×1536)
注意:裁剪后的标注文件需要同步处理,使用DOTA_devkit中的
SplitOnlyImage_multi_process.py可自动完成
4. mmdetection旋转检测模型配置详解
以S2ANet为例,关键配置项修改:
# configs/s2anet/s2anet_r50_fpn_1x_dota.py
model = dict(
bbox_head=dict(
num_classes=15, # 对应DOTA的15个类别
anchor_generator=dict(
strides=[8, 16, 32, 64, 128],
ratios=[1.0],
scales=[1, 2, 4]),
loss_cls=dict(
type='FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))
)
data = dict(
samples_per_gpu=4, # 根据GPU显存调整
workers_per_gpu=2,
train=dict(
type='DOTADataset',
ann_file='data/dota/train/labelTxt/',
img_prefix='data/dota/train/images/',
pipeline=train_pipeline),
val=dict(
type='DOTADataset',
ann_file='data/dota/val/labelTxt/',
img_prefix='data/dota/val/images/',
pipeline=test_pipeline)
)
训练启动命令:
CUDA_VISIBLE_DEVICES=0,1 tools/dist_train.sh \
configs/s2anet/s2anet_r50_fpn_1x_dota.py \
2 --validate
性能优化技巧 :
- 使用
RotatedAugmentation增强:train_pipeline = [ dict(type='RotatedRandomFlip', flip_ratio=0.5), dict(type='RotatedRandomRotate', angle_range=180), dict(type='RotatedResize', img_scale=(1024, 1024)) ] - 开启FP16训练:
optimizer_config = dict(type="Fp16OptimizerHook", loss_scale=512.) - 采用
CheckpointHook定期保存:checkpoint_config = dict(interval=3)
5. 模型部署与生产化实践
Docker镜像构建关键点:
FROM nvidia/cuda:11.1-base
# 安装依赖
RUN apt-get update && apt-get install -y \
libgl1-mesa-glx \
libsm6 \
libxext6
# 固定版本防止兼容问题
RUN pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 -f https://download.pytorch.org/whl/torch_stable.html
RUN pip install mmcv-full==1.3.9 -f https://download.openmmlab.com/mmcv/dist/cu111/torch1.8.0/index.html
# 复制预编译的mmdetection
COPY mmdetection /app/mmdetection
ENV PYTHONPATH=/app/mmdetection:$PYTHONPATH
# 示例推理命令
CMD ["python", "tools/test.py", "config.py", "checkpoint.pth"]
性能监控方案:
- 使用
nvtop实时查看GPU利用率 - 通过
mmdet.utils.logger记录训练指标 - 添加Prometheus监控端点:
from prometheus_client import start_http_server, Gauge
train_loss = Gauge('training_loss', 'Current training loss')
val_mAP = Gauge('validation_map', 'Current validation mAP')
def log_metrics(runner):
train_loss.set(runner.log_buffer.output['loss'])
val_mAP.set(runner.log_buffer.output['mAP'])
更多推荐




所有评论(0)