1. 旋转框目标检测基础概念第一次接触旋转框目标检测时我被那些倾斜的矩形框搞得一头雾水。传统的水平框检测就像用方盒子装球总有多余的空间而旋转框则是为每个目标量身定制的斜盒子能更精准地框住物体。这种技术在遥感图像分析、文档扫描、自动驾驶等场景特别有用——想象一下要检测斜停在路边的车辆或者卫星图像中各种角度的建筑物。旋转框的核心在于它的数学表示。不同于水平框简单的(x,y,w,h)旋转框多了一个角度参数θ。但就是这个θ让事情变得复杂起来。我踩过的第一个坑就是发现不同工具库对θ的定义完全不同有的以x轴为基准有的以长边为基准有的用角度制有的用弧度制甚至旋转方向都有顺时针和逆时针之分。2. OpenCV中的旋转框处理2.1 minAreaRect函数详解cv2.minAreaRect是我最早接触的旋转框生成函数。它的作用很简单给定一组点返回能包围这些点的最小面积旋转矩形。但实际使用时我发现这个函数在不同OpenCV版本中的表现差异很大import cv2 import numpy as np # 创建一个四边形点集 points np.array([[30,20], [70,10], [80,50], [40,60]]) # 获取最小外接旋转矩形 rect cv2.minAreaRect(points) print(rect) # 输出格式((cx,cy), (w,h), angle)在OpenCV 4.2版本中角度范围是[-90,0)宽度定义为x轴逆时针旋转最先碰到的边。而到了4.5版本角度范围变成了(0,90]宽度是x轴顺时针旋转最先碰到的边。这种差异导致我在数据转换时吃了不少苦头。2.2 版本兼容性解决方案为了解决版本差异问题我总结了一套转换方法def normalize_opencv_angle(rect, version4.5): (cx, cy), (w, h), angle rect if version 4.2: if w h: # 确保w总是长边 w, h h, w angle 90 angle -angle # 转换为正角度表示 else: # 4.5 if w h: # 确保h总是长边 w, h h, w angle 90 return ((cx, cy), (w, h), angle)实际项目中我建议统一使用一种定义法比如长边定义法并在数据预处理阶段做好转换。这样可以避免后续模型训练时的混乱。3. MMRotate框架实战3.1 环境配置与安装MMRotate是OpenMMLab系列中的旋转检测专用框架。安装时要注意与MMCV版本的兼容性# 推荐使用conda创建虚拟环境 conda create -n mmrotate python3.8 -y conda activate mmrotate # 安装PyTorch根据CUDA版本选择 pip install torch1.9.0cu111 torchvision0.10.0cu111 -f https://download.pytorch.org/whl/torch_stable.html # 安装MMRotate pip install mmrotate我遇到过因为MMCV版本不匹配导致RoIAlignRotated算子无法加载的问题解决方法是指定版本安装pip install mmcv-full1.4.0 -f https://download.openmmlab.com/mmcv/dist/cu111/torch1.9.0/index.html3.2 数据准备与转换MMRotate支持DOTA格式的数据集但实际项目中我们经常需要自定义数据。假设我们有VOC格式的旋转标注转换脚本如下from mmrotate.core.evaluation import dota_evaluation import xml.etree.ElementTree as ET def voc_to_dota(voc_path, dota_path): tree ET.parse(voc_path) root tree.getroot() with open(dota_path, 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 cv2.boxPoints(((cx,cy),(w,h),angle)) points_str .join([f{x:.2f},{y:.2f} for (x,y) in points]) # 写入DOTA格式 f.write(f{points_str} {obj.find(name).text} 0\n)3.3 模型训练技巧在MMRotate中训练自定义模型时配置文件是关键。以Rotated RetinaNet为例几个重要参数需要关注# 角度定义法与OpenCV版本匹配很重要 angle_version oc # oc for OpenCV, le90 for long-edge 90° # 锚点设置 anchors [ dict( typeRotatedAnchor, octave_base_scale4, scales_per_octave3, ratios[1.0, 0.5, 2.0], strides[8, 16, 32, 64, 128]) ] # 数据增强 train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeRResize, img_scale(1024, 1024)), dict( typeRRandomFlip, flip_ratio0.5, direction[horizontal, vertical]), dict( typePolyRandomRotate, rotate_ratio0.5, angles_range180, auto_boundFalse), dict(typeNormalize, mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375]), dict(typePad, size_divisor32), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_bboxes, gt_labels]) ]训练时常见的坑是角度越界问题。我建议在自定义数据集时先用可视化工具检查标注是否正确from mmrotate.core.visualization import imshow_det_bboxes # 可视化检查 img cv2.imread(image.jpg) gt_bboxes np.loadtxt(label.txt) # 加载DOTA格式标注 imshow_det_bboxes( img, gt_bboxes, labelsnp.zeros(len(gt_bboxes)), class_names[object], thickness2)4. 工业落地优化经验4.1 模型轻量化策略在实际部署中旋转检测模型往往需要优化。我常用的方法有知识蒸馏用大模型指导小模型训练# 在配置文件中添加蒸馏配置 distiller dict( typeDetectionDistiller, teacher_trainableFalse, components[ dict( student_modulebbox_head, teacher_modulebbox_head, losses[ dict( typeKnowledgeDistillationKLDivLoss, nameloss_kd, tau1, loss_weight0.5) ]) ])模型剪枝使用MMRazor工具包python tools/prune.py configs/pruning/rotated_faster_rcnn_pruning.pyTensorRT加速使用MMDeploy转换python tools/deploy.py \ configs/mmrotate/rotated-detection_tensorrt_dynamic-320x320-1024x1024.py \ rotated_faster_rcnn_r50_fpn_1x_dota_le90.py \ checkpoint.pth \ demo.jpg \ --work-dir trt_model \ --device cuda:04.2 多框架协同方案在复杂项目中我们可能需要组合多个框架。比如用MMRotate做旋转检测再用MMDetection做水平框后处理from mmrotate.apis import inference_detector, init_detector from mmdet.apis import inference_detector as inference_detector_h # 初始化旋转检测模型 rotate_model init_detector(rotated_config.py, rotate_checkpoint.pth) # 初始化水平检测模型 h_model init_detector(horizontal_config.py, h_checkpoint.pth) # 协同推理 rotate_results inference_detector(rotate_model, img) h_results inference_detector_h(h_model, img) # 结果融合...这种方案在遥感图像分析中特别有效先用旋转框检测大目标再用水平框检测小目标。
旋转框目标检测实战指南:从OpenCV定义到MMRotate应用
1. 旋转框目标检测基础概念第一次接触旋转框目标检测时我被那些倾斜的矩形框搞得一头雾水。传统的水平框检测就像用方盒子装球总有多余的空间而旋转框则是为每个目标量身定制的斜盒子能更精准地框住物体。这种技术在遥感图像分析、文档扫描、自动驾驶等场景特别有用——想象一下要检测斜停在路边的车辆或者卫星图像中各种角度的建筑物。旋转框的核心在于它的数学表示。不同于水平框简单的(x,y,w,h)旋转框多了一个角度参数θ。但就是这个θ让事情变得复杂起来。我踩过的第一个坑就是发现不同工具库对θ的定义完全不同有的以x轴为基准有的以长边为基准有的用角度制有的用弧度制甚至旋转方向都有顺时针和逆时针之分。2. OpenCV中的旋转框处理2.1 minAreaRect函数详解cv2.minAreaRect是我最早接触的旋转框生成函数。它的作用很简单给定一组点返回能包围这些点的最小面积旋转矩形。但实际使用时我发现这个函数在不同OpenCV版本中的表现差异很大import cv2 import numpy as np # 创建一个四边形点集 points np.array([[30,20], [70,10], [80,50], [40,60]]) # 获取最小外接旋转矩形 rect cv2.minAreaRect(points) print(rect) # 输出格式((cx,cy), (w,h), angle)在OpenCV 4.2版本中角度范围是[-90,0)宽度定义为x轴逆时针旋转最先碰到的边。而到了4.5版本角度范围变成了(0,90]宽度是x轴顺时针旋转最先碰到的边。这种差异导致我在数据转换时吃了不少苦头。2.2 版本兼容性解决方案为了解决版本差异问题我总结了一套转换方法def normalize_opencv_angle(rect, version4.5): (cx, cy), (w, h), angle rect if version 4.2: if w h: # 确保w总是长边 w, h h, w angle 90 angle -angle # 转换为正角度表示 else: # 4.5 if w h: # 确保h总是长边 w, h h, w angle 90 return ((cx, cy), (w, h), angle)实际项目中我建议统一使用一种定义法比如长边定义法并在数据预处理阶段做好转换。这样可以避免后续模型训练时的混乱。3. MMRotate框架实战3.1 环境配置与安装MMRotate是OpenMMLab系列中的旋转检测专用框架。安装时要注意与MMCV版本的兼容性# 推荐使用conda创建虚拟环境 conda create -n mmrotate python3.8 -y conda activate mmrotate # 安装PyTorch根据CUDA版本选择 pip install torch1.9.0cu111 torchvision0.10.0cu111 -f https://download.pytorch.org/whl/torch_stable.html # 安装MMRotate pip install mmrotate我遇到过因为MMCV版本不匹配导致RoIAlignRotated算子无法加载的问题解决方法是指定版本安装pip install mmcv-full1.4.0 -f https://download.openmmlab.com/mmcv/dist/cu111/torch1.9.0/index.html3.2 数据准备与转换MMRotate支持DOTA格式的数据集但实际项目中我们经常需要自定义数据。假设我们有VOC格式的旋转标注转换脚本如下from mmrotate.core.evaluation import dota_evaluation import xml.etree.ElementTree as ET def voc_to_dota(voc_path, dota_path): tree ET.parse(voc_path) root tree.getroot() with open(dota_path, 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 cv2.boxPoints(((cx,cy),(w,h),angle)) points_str .join([f{x:.2f},{y:.2f} for (x,y) in points]) # 写入DOTA格式 f.write(f{points_str} {obj.find(name).text} 0\n)3.3 模型训练技巧在MMRotate中训练自定义模型时配置文件是关键。以Rotated RetinaNet为例几个重要参数需要关注# 角度定义法与OpenCV版本匹配很重要 angle_version oc # oc for OpenCV, le90 for long-edge 90° # 锚点设置 anchors [ dict( typeRotatedAnchor, octave_base_scale4, scales_per_octave3, ratios[1.0, 0.5, 2.0], strides[8, 16, 32, 64, 128]) ] # 数据增强 train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue), dict(typeRResize, img_scale(1024, 1024)), dict( typeRRandomFlip, flip_ratio0.5, direction[horizontal, vertical]), dict( typePolyRandomRotate, rotate_ratio0.5, angles_range180, auto_boundFalse), dict(typeNormalize, mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375]), dict(typePad, size_divisor32), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_bboxes, gt_labels]) ]训练时常见的坑是角度越界问题。我建议在自定义数据集时先用可视化工具检查标注是否正确from mmrotate.core.visualization import imshow_det_bboxes # 可视化检查 img cv2.imread(image.jpg) gt_bboxes np.loadtxt(label.txt) # 加载DOTA格式标注 imshow_det_bboxes( img, gt_bboxes, labelsnp.zeros(len(gt_bboxes)), class_names[object], thickness2)4. 工业落地优化经验4.1 模型轻量化策略在实际部署中旋转检测模型往往需要优化。我常用的方法有知识蒸馏用大模型指导小模型训练# 在配置文件中添加蒸馏配置 distiller dict( typeDetectionDistiller, teacher_trainableFalse, components[ dict( student_modulebbox_head, teacher_modulebbox_head, losses[ dict( typeKnowledgeDistillationKLDivLoss, nameloss_kd, tau1, loss_weight0.5) ]) ])模型剪枝使用MMRazor工具包python tools/prune.py configs/pruning/rotated_faster_rcnn_pruning.pyTensorRT加速使用MMDeploy转换python tools/deploy.py \ configs/mmrotate/rotated-detection_tensorrt_dynamic-320x320-1024x1024.py \ rotated_faster_rcnn_r50_fpn_1x_dota_le90.py \ checkpoint.pth \ demo.jpg \ --work-dir trt_model \ --device cuda:04.2 多框架协同方案在复杂项目中我们可能需要组合多个框架。比如用MMRotate做旋转检测再用MMDetection做水平框后处理from mmrotate.apis import inference_detector, init_detector from mmdet.apis import inference_detector as inference_detector_h # 初始化旋转检测模型 rotate_model init_detector(rotated_config.py, rotate_checkpoint.pth) # 初始化水平检测模型 h_model init_detector(horizontal_config.py, h_checkpoint.pth) # 协同推理 rotate_results inference_detector(rotate_model, img) h_results inference_detector_h(h_model, img) # 结果融合...这种方案在遥感图像分析中特别有效先用旋转框检测大目标再用水平框检测小目标。