从零到一:手把手教你用MMRotate构建专属旋转目标检测模型
1. 为什么选择MMRotate做旋转目标检测第一次接触旋转目标检测是在处理遥感图像项目时遇到的。当时用常规的矩形框检测方法发现对于密集排列的倾斜目标效果很差检测框要么重叠严重要么漏检率高。后来发现了MMRotate这个专门针对旋转框检测的工具箱实测下来效果提升非常明显。MMRotate是OpenMMLab生态中的重要成员它基于PyTorch框架开发支持R3Det、Rotated Faster R-CNN等主流旋转检测算法。相比普通目标检测它的核心优势在于能够精确捕捉目标的旋转角度特别适合处理以下场景遥感图像中的建筑物、车辆等倾斜目标文档扫描件中的文字区域检测工业检测中的零件定位自动驾驶场景中的特殊角度车辆识别我最近在一个卫星图像分析项目中就用到了MMRotate。项目需要检测港口区域的集装箱堆放情况这些集装箱通常呈45度角排列。使用传统检测方法时相邻集装箱的检测框IOU值经常超过0.7导致大量误判。改用旋转框检测后准确率直接从72%提升到了89%。2. 环境配置与安装指南2.1 硬件与基础软件要求建议使用Linux系统进行开发实测Ubuntu 18.04/20.04兼容性最好。Windows系统也能运行但在多卡训练时可能会遇到问题。硬件配置方面GPU至少8GB显存GTX 1080 Ti及以上内存建议16GB以上存储SSD硬盘能显著提升数据加载速度先确保系统已安装CUDA 10.1/10.2/11.1cuDNN 7.6NCCL多卡训练需要2.2 详细安装步骤创建并激活conda环境conda create -n mmrotate python3.7 -y conda activate mmrotate安装PyTorch注意版本匹配conda install pytorch1.7.0 torchvision0.8.0 cudatoolkit10.1 -c pytorch安装MMCV关键依赖pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.7.0/index.html安装MMDetection和MMRotatepip install mmdet git clone https://github.com/open-mmlab/mmrotate.git cd mmrotate pip install -r requirements/build.txt pip install -v -e .验证安装是否成功python demo/image_demo.py demo/demo.jpg \ configs/rotated_retinanet/rotated_retinanet_r50_fpn_1x_dota_le90.py \ checkpoints/rotated_retinanet_r50_fpn_1x_dota_le90-0393aa5c.pth \ --out-file result.jpg如果能看到生成的result.jpg文件说明环境配置正确。我在第一次安装时遇到过mmcv版本不兼容的问题后来发现是torch和cuda版本不匹配导致的。建议严格按照官方推荐的版本组合安装。3. 制作自定义旋转框数据集3.1 使用roLabelImg标注工具roLabelImg是专门为旋转目标设计的标注工具安装方法git clone https://github.com/cgvict/roLabelImg cd roLabelImg pip install -r requirements.txt make qt5py3 python roLabelImg.py标注时几个实用技巧按A/D键旋转框体Z/X键微调角度保持标注一致性所有目标都采用长边标注法角度范围控制在[-90°,90°]之间复杂目标可以先标大框再细化标注完成后会生成XML文件包含目标类别、旋转角度和矩形框坐标。我标注过的一个船舶数据集平均每张图需要3-5分钟熟练后能缩短到2分钟左右。3.2 转换为DOTA格式MMRotate使用DOTA数据集格式转换脚本核心逻辑def convert_rotation(x_center, y_center, width, height, angle): 将旋转框转换为四个角点坐标 angle_rad np.deg2rad(angle) cos_val np.cos(angle_rad) sin_val np.sin(angle_rad) # 计算四个角点相对中心点的偏移 x1 -width/2 * cos_val - (-height/2) * sin_val y1 -width/2 * sin_val (-height/2) * cos_val x2 width/2 * cos_val - (-height/2) * sin_val y2 width/2 * sin_val (-height/2) * cos_val x3 width/2 * cos_val - height/2 * sin_val y3 width/2 * sin_val height/2 * cos_val x4 -width/2 * cos_val - height/2 * sin_val y4 -width/2 * sin_val height/2 * cos_val # 加上中心点坐标 points np.array([ [x1 x_center, y1 y_center], [x2 x_center, y2 y_center], [x3 x_center, y3 y_center], [x4 x_center, y4 y_center] ]) return points转换后的标签格式示例1024 768 1462 768 1462 1024 1024 1024 ship 0 512 256 768 256 768 512 512 512 plane 13.3 数据集划分与预处理建议按7:2:1的比例划分训练集、验证集和测试集。使用以下脚本自动划分import os import random from shutil import copyfile def split_dataset(data_dir, output_dir, ratios(0.7, 0.2, 0.1)): images [f for f in os.listdir(data_dir) if f.endswith(.jpg)] random.shuffle(images) total len(images) train_end int(total * ratios[0]) val_end train_end int(total * ratios[1]) os.makedirs(os.path.join(output_dir, train), exist_okTrue) os.makedirs(os.path.join(output_dir, val), exist_okTrue) os.makedirs(os.path.join(output_dir, test), exist_okTrue) for i, img in enumerate(images): base_name os.path.splitext(img)[0] txt_file base_name .txt if i train_end: dest train elif i val_end: dest val else: dest test copyfile(os.path.join(data_dir, img), os.path.join(output_dir, dest, img)) copyfile(os.path.join(data_dir, txt_file), os.path.join(output_dir, dest, txt_file))对于大尺寸图像还需要使用MMRotate提供的split工具进行裁剪python tools/data/dota/split/img_split.py \ --base_json tools/data/dota/split/split_configs/ss_train.json \ --save_dir data/split4. 模型训练与调优实战4.1 配置文件详解以Rotated Faster R-CNN为例主要修改三个配置文件模型配置configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.pymodel dict( roi_headdict( bbox_headdict( num_classes5, # 修改为你的类别数 loss_bboxdict(typeRotatedIoULoss, loss_weight1.0) ) ) )数据集配置configs/base/datasets/dotav1.pydata dict( samples_per_gpu4, # 根据显存调整 workers_per_gpu4, # 根据CPU核心数调整 traindict( typeDOTADataset, ann_filedata/train/annfiles/, img_prefixdata/train/images/ ), valdict( typeDOTADataset, ann_filedata/val/annfiles/, img_prefixdata/val/images/ ), testdict( typeDOTADataset, ann_filedata/test/annfiles/, img_prefixdata/test/images/ ) )训练策略configs/base/schedules/schedule_1x.pyoptimizer dict(typeSGD, lr0.005, momentum0.9, weight_decay0.0001) lr_config dict( policystep, warmuplinear, warmup_iters500, warmup_ratio0.001, step[8, 11]) runner dict(typeEpochBasedRunner, max_epochs12)4.2 启动训练与监控单卡训练命令python tools/train.py configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.py \ --work-dir work_dirs/rotated_faster_rcnn多卡训练例如4卡./tools/dist_train.sh configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.py 4 \ --work-dir work_dirs/rotated_faster_rcnn使用TensorBoard监控训练过程tensorboard --logdir work_dirs/rotated_faster_rcnn --port 6006常见的训练问题与解决方案显存不足减小samples_per_gpu增加workers_per_gpu训练震荡降低初始学习率增加warmup迭代次数过拟合增加数据增强添加Dropout层4.3 模型测试与评估测试单张图像python demo/image_demo.py demo/demo.jpg \ configs/rotated_faster_rcnn/rotated_faster_rcnn_r50_fpn_1x_dota_le90.py \ work_dirs/rotated_faster_rcnn/latest.pth \ --out-file result.jpg批量测试并生成评估报告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评估指标解读mAP50IoU阈值为0.5时的平均精度mAP75IoU阈值为0.75时的平均精度mAPIoU阈值从0.5到0.95的平均精度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)使用TensorRT加速推理trtexec --onnxmodel.onnx \ --saveEnginemodel.engine \ --fp16 \ --workspace40965.2 性能优化技巧输入尺寸优化原始图像尺寸1024x1024 → 推理时间45ms优化后尺寸800x800 → 推理时间32ms精度下降约1.2%模型量化model.qconfig torch.quantization.get_default_qat_qconfig(fbgemm) torch.quantization.prepare_qat(model, inplaceTrue)后处理优化原始NMS耗时8ms优化后NMS耗时3ms使用CUDA加速5.3 常见问题解决方案漏检问题增加训练时的数据增强旋转、缩放调整RPN阶段的anchor尺寸降低分类阈值误检问题增加难例挖掘调整NMS的IoU阈值增加验证集样本角度预测不准检查标注是否统一使用长边法增加角度预测的损失权重使用KFIoU等更精确的损失函数在一个实际项目中我们通过以下调整将mAP从78.3%提升到了83.7%将学习率从0.005调整为0.0025增加随机旋转增强角度范围从[-15,15]扩大到[-30,30]使用KFIoU替代原始的SmoothL1 Loss