遥感CV实战:从SpaceNet上海数据集到建筑物分割模型全流程解析
第一次接触遥感图像处理时,我被那些五彩斑斓的多光谱图像弄得晕头转向——明明看起来像普通照片,却藏着肉眼看不见的信息维度。直到亲手用代码拆解了SpaceNet的上海数据集,才真正理解为什么遥感CV被称为"地理空间的显微镜"。本文将带你从零开始,用PyTorch搭建一个能自动识别建筑物的分割模型,过程中我会分享那些官方文档里找不到的实战技巧。
1. 认识SpaceNet上海数据集
SpaceNet挑战赛提供的上海区域数据集(AOI_4_Shanghai_Train)堪称遥感CV领域的"MNIST"。这套数据最迷人的特点是它的多维度信息:
- 8波段WorldView-3卫星影像:包含海岸蓝(400-450nm)、蓝(450-510nm)、绿(510-580nm)、黄(585-625nm)、红(630-690nm)、红边(705-745nm)、近红外1(770-895nm)和近红外2(860-1040nm)
- 0.3米超高分辨率:能清晰识别小型建筑结构
- 多类型数据版本:
AOI_4_Shanghai_Train/ ├── geojson/ # 建筑物多边形标注 ├── MUL/ # 原始8波段数据(1.2m) ├── MUL-PanSharpen/ # 全色锐化后的8波段(0.3m) ├── PAN/ # 全色波段(0.3m) └── RGB-PanSharpen/ # 真彩色RGB(0.3m)
实战建议:初学者建议从RGB-PanSharpen开始,熟悉后再挑战8波段数据。处理多光谱数据时需要特别注意波段顺序,WorldView-3的波段排列可能与库函数默认假设不同。
2. 数据预处理的关键步骤
2.1 图像与标注的对齐难题
遥感图像处理第一个拦路虎就是坐标系统转换。GeoJSON标注使用WGS84坐标系,而图像像素坐标需要经过仿射变换才能对应。这个转换过程常见问题包括:
- 投影变形导致的建筑物轮廓偏移
- 图像旋转后的标注匹配错误
- 不同分辨率数据间的尺度差异
import rasterio from shapely.geometry import shape # 读取图像地理信息 with rasterio.open('MUL-PanSharpen.tif') as src: transform = src.transform # 将GeoJSON坐标转为像素坐标 def geo_to_pixel(geom): return [~transform * point for point in geom.exterior.coords]2.2 多波段图像处理技巧
8波段数据虽然信息丰富,但直接输入模型会导致计算量暴增。有效的波段组合策略:
| 波段组合 | 适用场景 | 优势 |
|---|---|---|
| 近红外+红边+红 | 植被识别 | 增强植被特征 |
| 海岸蓝+蓝+绿 | 水体检测 | 突出水质差异 |
| 全色+红+绿 | 通用场景 | 保持空间细节 |
# 提取特定波段组合 def extract_band_combination(img, bands=[5,3,2]): # 近红外、红、绿 return np.stack([img.getdata(b) for b in bands], axis=-1)3. 构建遥感专用分割模型
3.1 改进UNet的输入输出层
标准UNet设计用于3通道RGB图像,我们需要针对遥感特点进行改造:
import torch.nn as nn class RemoteUNet(nn.Module): def __init__(self, in_channels=8, out_channels=1): super().__init__() # 修改第一层卷积输入通道数 self.inc = DoubleConv(in_channels, 64) # 保持后续结构不变... def forward(self, x): # 输出添加sigmoid激活 return torch.sigmoid(self.unet(x))3.2 针对遥感数据的增强策略
不同于自然图像,遥感增强需要遵守地理空间一致性原则:
- 禁止单独增强:图像与标注必须同步变换
- 特殊增强方式:
- 波段随机丢弃(BandDropout)
- 辐射度畸变模拟
- 云层遮挡合成
import albumentations as A transform = A.Compose([ A.RandomRotate90(), A.RandomCrop(512, 512), A.OneOf([ A.RandomGamma(), A.RandomBrightnessContrast(), ], p=0.3), A.BandDropout(p=0.1) # 随机丢弃1-2个波段 ], additional_targets={'mask': 'mask'})4. 训练技巧与评估指标
4.1 解决类别不平衡问题
建筑物在整幅图像中占比通常不足10%,需要特殊处理:
加权损失函数:
pos_weight = torch.tensor([10.0]) # 正样本权重 criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)难例挖掘:在训练过程中动态调整样本权重
4.2 遥感专用评估指标
除了常规的IoU,遥感任务还需关注:
| 指标 | 公式 | 意义 |
|---|---|---|
| F1-Score | 2*(P*R)/(P+R) | 综合精确率与召回率 |
| SpaceNet Metric | (F1 + 4*AP)/5 | 官方竞赛指标 |
| Boundary F1 | 边界像素F1值 | 评估边缘精度 |
def boundary_iou(y_true, y_pred, threshold=0.5): # 计算预测和真实掩膜的边界 pred_boundary = find_boundaries((y_pred > threshold).astype(np.uint8)) true_boundary = find_boundaries(y_true.astype(np.uint8)) # 计算边界IoU intersection = np.logical_and(pred_boundary, true_boundary) union = np.logical_or(pred_boundary, true_boundary) return np.sum(intersection) / np.sum(union)5. 部署优化与生产建议
在实际项目中,我们发现几个关键优化点:
分块推理策略:大尺寸图像需分块处理,但要处理边缘效应
def tile_inference(model, image, tile_size=512, overlap=64): # 实现带重叠的分块推理 ...后处理优化:原始输出需经过:
- 小区域过滤
- 边缘平滑
- 矢量化优化
多时相分析:利用时间序列数据提升识别稳定性
处理上海陆家嘴区域时,模型在玻璃幕墙建筑上表现不佳。后来我们通过添加近红外波段反射率特征,将这部分建筑的识别率提升了27%。另一个教训是:雨季拍摄的图像中,积水区域经常被误判为建筑物,需要特别训练数据增强时加入水面反射模拟。