
遥感目标检测数据集预处理实战以 LEVIR 和 HRSC2016 为例的 5 步标准化流程当你在GitHub上兴奋地下载完LEVIR或HRSC2016数据集后看着满文件夹的JPEG和零散的标注文件是否感觉无从下手别担心这正是每个计算机视觉工程师都会经历的数据消化不良症。本文将带你用手术刀般的Python代码解剖这两个典型遥感数据集完成从原始数据到模型可消化格式的完整转化。遥感图像与自然图像的最大区别在于——它们是从上帝视角拍摄的平面谜题。DOTA数据集的研究显示航空图像中目标具有任意方向性传统水平框会引入30%以上的冗余背景噪声。而HRSC2016采用的旋转框标注方式正是解决这一痛点的关键。1. 环境配置与数据解构工欲善其事必先利其器。我们需要配置一个专门处理地理空间图像的Python环境conda create -n rsod python3.8 conda activate rsod pip install opencv-contrib-python4.5.5.64 rasterio shapely albumentations数据集目录结构解析是预处理的第一步。以HRSC2016为例其原始压缩包解压后通常呈现这样的混乱状态HRSC2016/ ├── Images/ │ ├── 1000001.jpg │ └── ... ├── Annotations/ │ ├── 1000001.xml │ └── ... └── train_val_test.txt而LEVIR数据集更贴心地提供了MATLAB格式的标注需要额外处理import h5py with h5py.File(LEVIR.mat, r) as f: bboxes f[boxes][:] # 奇怪的MATLAB格式转换建议立即建立标准化目录结构这是后续所有操作的基础import shutil def reorganize_dataset(root): os.makedirs(f{root}/images, exist_okTrue) os.makedirs(f{root}/labels, exist_okTrue) # 移动并重命名所有图像文件 for img in glob(f{root}/Images/*.jpg): shutil.move(img, f{root}/images/{os.path.basename(img)})2. 图像标准化处理遥感图像常见的三大顽疾尺寸不一、色偏严重、位深差异。我们的处理策略如下问题类型典型表现解决方案代码实现尺寸变异4000×4000到800×600混存分块或填充cv2.copyMakeBorder色彩异常过曝/欠曝区域CLAHE均衡化cv2.createCLAHE位深差异8位/16位混杂统一归一化img.astype(np.float32)/65535对于大尺寸图像推荐使用滑动窗口分块策略def tile_image(img, window_size1024, stride512): h, w img.shape[:2] patches [] for y in range(0, h - window_size 1, stride): for x in range(0, w - window_size 1, stride): patch img[y:ywindow_size, x:xwindow_size] patches.append(patch) return patches坐标转换是遥感目标检测的核心难点。当图像被resize或分块时标注框需要同步变换。以下是旋转框的处理公式新坐标 原坐标 × (目标尺寸/原尺寸) - 偏移量用OpenCV实现旋转框的仿射变换def rotate_box(box, angle, center): rot_mat cv2.getRotationMatrix2D(center, angle, 1.0) points np.array(box).reshape(-1,2) homg_points np.hstack([points, np.ones((points.shape[0],1))]) rotated np.dot(rot_mat, homg_points.T).T return rotated.reshape(-1)3. 标注格式转换实战主流标注格式的三国演义COCO格式嵌套JSON结构适合实例分割YOLO格式归一化txt文件简洁高效PASCAL VOCXML描述兼容性好以HRSC2016的旋转框XML转YOLO格式为例def parse_hrsc_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() boxes [] for obj in root.findall(.//HRSC_Object): # 解析旋转框的四个顶点坐标 x1 float(obj.find(box_x1).text) y1 float(obj.find(box_y1).text) # ... 其他三个顶点 boxes.append([(x1,y1), (x2,y2), (x3,y3), (x4,y4)]) return boxes def rotated_box_to_yolo(box, img_w, img_h): # 将旋转框转换为YOLO格式的cx,cy,width,height,angle center np.mean(box, axis0) width np.linalg.norm(box[0] - box[1]) height np.linalg.norm(box[1] - box[2]) angle np.arctan2(box[1][1]-box[0][1], box[1][0]-box[0][0]) # 归一化 cx, cy center[0]/img_w, center[1]/img_h w, h width/img_w, height/img_h return [cx, cy, w, h, angle]注意旋转角度在YOLO格式中通常用弧度表示范围在[-π/2, π/2]4. 数据集划分与增强策略数据集划分不是简单的随机切分要考虑以下因素场景分布确保训练/验证集包含所有场景类型目标密度保持各集合中目标数量分布一致时间连续性时序遥感图像需按时间划分推荐使用分层抽样方法from sklearn.model_selection import train_test_split def stratified_split(image_list, label_list, test_size0.2): # 根据场景类型分层 scene_types [get_scene_type(img) for img in image_list] X_train, X_val, y_train, y_val train_test_split( image_list, label_list, test_sizetest_size, stratifyscene_types ) return X_train, X_val, y_train, y_val遥感数据增强需要特殊处理几何变换允许大角度旋转0-360°因为航拍没有固定上下色彩扰动模拟不同天气、季节的光照变化模拟遮挡添加云层、烟雾等合成噪声使用Albumentations库的增强管道示例import albumentations as A transform A.Compose([ A.RandomRotate90(p0.5), A.RandomBrightnessContrast(p0.2), A.HueSaturationValue(p0.2), A.Cutout(max_h_size32, max_w_size32, p0.3), A.OneOf([ A.GaussNoise(var_limit(10.0, 50.0)), A.ISONoise(), ], p0.2) ], bbox_paramsA.BboxParams(formatyolo_r))5. 校验与可视化最后的质量控制环节常被忽视却至关重要。我们需要检查标注框是否超出图像边界图像与标注是否匹配目标尺寸分布是否符合预期可视化校验工具代码片段def visualize_yolo_labels(img_path, label_path): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: cls, cx, cy, bw, bh, angle map(float, line.split()) # 转换到绝对坐标 cx, cy int(cx*w), int(cy*h) bw, bh int(bw*w), int(bh*h) # 绘制旋转矩形 rect ((cx,cy), (bw,bh), angle*180/np.pi) box cv2.boxPoints(rect) box np.int0(box) cv2.drawContours(img, [box], 0, (0,255,0), 2) cv2.imshow(Verify, img) cv2.waitKey(0)常见陷阱排查表问题现象可能原因解决方案训练时Loss震荡大标注坐标未归一化检查YOLO格式是否在[0,1]范围验证集指标异常高数据泄露重新检查图像分割策略模型不收敛旋转角定义不一致统一使用OpenCV的angle定义标准完成所有步骤后你的数据集目录应该呈现这样的标准结构processed_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── dataset.yaml # 包含类别信息和路径配置最终的dataset.yaml示例path: ../processed_dataset train: images/train val: images/val test: images/test nc: 3 # 类别数 names: [airplane, ship, storage_tank] # 类别名称在第一次跑通全部流程时建议在小样本如50张图像上完整测试整个pipeline。我们团队曾因跳过这一步在处理完8000张图像后才发现坐标转换公式有误不得不全部返工——这种教训值得每个计算机视觉工程师铭记。