MMRotate遥感目标检测实战:从数据预处理到模型调优的全流程指南

遥感图像中的目标检测一直是计算机视觉领域的重要研究方向。与传统水平框检测不同,旋转目标检测能够更精确地定位和识别任意方向排列的物体,在卫星图像分析、城市规划、农业监测等领域具有广泛应用价值。本文将基于MMRotate框架,详细介绍如何从原始遥感数据出发,构建完整的旋转目标检测流程。

1. 环境配置与数据准备

1.1 系统环境搭建

MMRotate作为OpenMMLab生态系统的一部分,对运行环境有特定要求。以下是推荐的配置方案:

# 创建并激活conda环境
conda create -n mmrotate python=3.8 -y
conda activate mmrotate

# 安装PyTorch与CUDA(根据显卡驱动选择版本)
conda install pytorch==1.10.0 torchvision==0.11.0 cudatoolkit=11.3 -c pytorch

# 安装MMCV和MMDetection
pip install mmcv-full==1.4.7 -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html
pip install mmdet==2.25.0

# 安装MMRotate
git clone https://github.com/open-mmlab/mmrotate.git
cd mmrotate
pip install -r requirements/build.txt
pip install -v -e .

提示:安装过程中常见问题包括CUDA版本不匹配、MMCV与PyTorch版本冲突等。建议严格按照官方文档的版本对应关系进行配置。

1.2 数据集格式转换

遥感数据通常以非标准格式存储,需要转换为DOTA格式才能被MMRotate处理。典型转换流程包括:

  1. 原始标注转换 :将VOC/COCO格式标注转为DOTA格式
  2. 图像格式统一 :确保所有图像为PNG格式
  3. 坐标系统一 :确认旋转角度表示方法(le90或oc)

以下是一个将roLabelImg生成的XML标注转为DOTA格式的Python脚本核心部分:

def xml_to_dota(xml_path, img_path, output_dir):
    tree = ET.parse(xml_path)
    root = tree.getroot()
    
    with open(os.path.join(output_dir, 'label.txt'), 'w') as f:
        for obj in root.findall('object'):
            robndbox = obj.find('robndbox')
            cx = float(robndbox.find('cx').text)
            cy = float(robndbox.find('cy').text)
            w = float(robndbox.find('w').text)
            h = float(robndbox.find('h').text)
            angle = float(robndbox.find('angle').text)
            
            # 计算旋转后的四个顶点坐标
            points = calculate_rotated_points(cx, cy, w, h, angle)
            line = ' '.join([str(p) for p in points]) + ' ' + obj.find('name').text + ' 0\n'
            f.write(line)

2. 数据预处理与增强策略

2.1 图像裁剪与分块

大尺寸遥感图像通常需要分割以适应GPU内存限制。MMRotate提供了内置的裁剪工具,可通过修改 split_configs 中的JSON文件进行配置:

{
    "image_spliter": {
        "type": "sliding_window",
        "window_size": [1024, 1024],
        "stride": [512, 512],
        "padding": false,
        "save_dir": "split_results"
    },
    "data_root": "/path/to/your/data",
    "ann_file": "trainval/annfiles",
    "img_dir": "trainval/images"
}

执行裁剪命令:

python tools/data/dota/split/img_split.py --base_json split_configs/custom.json

2.2 数据增强配置

针对遥感数据特点,推荐在配置文件中添加以下增强策略:

train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='RResize', img_scale=(1024, 1024)),
    dict(type='RRandomFlip', flip_ratio=0.5),
    dict(type='RandomRotate', rate=0.5, angles=[30, 60, 90, 120, 150]),
    dict(type='BrightnessTransform', level=5),
    dict(type='ContrastTransform', level=5),
    dict(type='Normalize', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375]),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]

3. 模型选择与配置优化

3.1 主流旋转检测模型对比

模型类型 优势 适用场景 训练效率
Rotated Faster R-CNN 检测精度高,稳定性好 中等规模数据集 中等
R3Det 对小目标检测效果好 密集小目标场景 较低
S2ANet 计算效率高 实时检测需求 较高
KFIoU 旋转框回归更准确 高精度定位要求 较低

3.2 关键参数配置

configs/rotated_faster_rcnn 目录下的配置文件中,需要特别关注以下参数:

model = dict(
    type='RotatedFasterRCNN',
    backbone=dict(
        type='ResNet',
        depth=50,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        norm_eval=True,
        style='pytorch'),
    neck=dict(
        type='FPN',
        in_channels=[256, 512, 1024, 2048],
        out_channels=256,
        num_outs=5),
    rpn_head=dict(
        type='RotatedRPNHead',
        in_channels=256,
        feat_channels=256,
        anchor_generator=dict(
            type='AnchorGenerator',
            scales=[8],
            ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64]),
        bbox_coder=dict(
            type='DeltaXYWHABBoxCoder',
            target_means=[0.0, 0.0, 0.0, 0.0, 0.0],
            target_stds=[1.0, 1.0, 1.0, 1.0, 1.0]),
        loss_cls=dict(
            type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
        loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)),
    roi_head=dict(
        type='RotatedStandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='RotatedShared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=1,  # 修改为实际类别数
            bbox_coder=dict(
                type='DeltaXYWHABBoxCoder',
                target_means=[0.0, 0.0, 0.0, 0.0, 0.0],
                target_stds=[0.1, 0.1, 0.2, 0.2, 0.1]),
            reg_class_agnostic=True,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0))),
    train_cfg=dict(
        rpn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.7,
                neg_iou_thr=0.3,
                min_pos_iou=0.3,
                match_low_quality=True,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=256,
                pos_fraction=0.5,
                neg_pos_ub=-1,
                add_gt_as_proposals=False),
            allowed_border=0,
            pos_weight=-1,
            debug=False),
        rpn_proposal=dict(
            nms_pre=2000,
            max_per_img=2000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            assigner=dict(
                type='MaxIoUAssigner',
                pos_iou_thr=0.5,
                neg_iou_thr=0.5,
                min_pos_iou=0.5,
                match_low_quality=False,
                ignore_iof_thr=-1),
            sampler=dict(
                type='RandomSampler',
                num=512,
                pos_fraction=0.25,
                neg_pos_ub=-1,
                add_gt_as_proposals=True),
            pos_weight=-1,
            debug=False)),
    test_cfg=dict(
        rpn=dict(
            nms_pre=2000,
            max_per_img=2000,
            nms=dict(type='nms', iou_threshold=0.7),
            min_bbox_size=0),
        rcnn=dict(
            nms_pre=2000,
            max_per_img=2000,
            score_thr=0.05,
            nms=dict(type='nms', iou_threshold=0.1),
            max_per_img=2000)))

4. 训练优化与问题排查

4.1 内存不足解决方案

当遇到"CUDA out of memory"错误时,可尝试以下调整:

  1. 减小batch size :修改 dotav1.py 中的 samples_per_gpu
  2. 优化数据加载 :调整 workers_per_gpu 为2-4
  3. 使用混合精度训练 :在配置中添加 fp16 = dict(loss_scale=512.)

4.2 训练参数调优

关键训练参数推荐设置:

参数名称 推荐值范围 作用说明
base_lr 0.001-0.01 基础学习率
warmup_iters 500-1000 学习率预热步数
optimizer.momentum 0.9-0.99 优化器动量
lr_config.step [8, 11] 学习率衰减时机
total_epochs 12-24 总训练轮数

4.3 模型评估与测试

训练完成后,使用以下命令进行测试:

python tools/test.py \
    configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.py \
    work_dirs/rotated_faster_rcnn/latest.pth \
    --eval mAP

评估指标解读:

  • mAP : 平均精度均值,主要评估指标
  • AP50 : IoU阈值为0.5时的AP值
  • AP75 : IoU阈值为0.75时的AP值

5. 部署与性能优化

5.1 模型导出为ONNX格式

from mmdet.apis import init_detector, export_model

config_file = 'configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.py'
checkpoint_file = 'work_dirs/rotated_faster_rcnn/latest.pth'
export_model(config_file, checkpoint_file, 'model.onnx')

5.2 推理速度优化技巧

  1. 模型剪枝 :移除冗余卷积层
  2. 量化压缩 :将FP32转为INT8
  3. TensorRT加速 :转换模型为TensorRT引擎
  4. 多尺度测试优化 :合理设置测试尺度
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1024, 1024),
        flip=False,
        transforms=[
            dict(type='RResize'),
            dict(type='Normalize'),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img'])
        ])
]

在实际项目中,我们发现将输入尺寸从1024x1024降至800x800可使推理速度提升约40%,而mAP仅下降2-3个百分点,这种权衡在实时性要求高的场景中非常实用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐