ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

高分遥感语义分割实战:GF2影像Pytorch全流程与避坑指南

高分遥感语义分割实战:GF2影像Pytorch全流程与避坑指南 简介这份资源面向遥感图像处理方向的研究者、工程师及具备一定深度学习基础的学习者提供基于Pytorch实现高分辨率遥感图像语义分割的完整教程与配套数据集帮助解决地物信息提取中从数据预处理到模型训练评估的全流程问题。压缩包共1029个文件约577.48MB以819个png图像样本、35个py源码脚本为主辅以zbak备份、csv标注索引、jpg效果图与md说明文档覆盖数据读取、网络搭建、训练预测与结果可视化等环节。教程从遥感图像基本概念讲起逐步深入到预处理方法、语义分割网络结构选择与优化策略并演示如何操作数据集、设计训练模型及评估分割结果还涉及标注工具制作像素级标签的思路。目前已有94人学习下载适合希望快速上手遥感语义分割项目、对照源码复现实验并积累实战经验的中高级读者参考。1. 高分遥感语义分割资源包从 GF2 影像到可复现训练流程高分遥感图像的语义分割难的不是把模型跑起来而是把一整条链路串通原始 GF2 多光谱影像怎么读、标签怎么对齐、切片尺寸怎么定、训练完怎么验证。这份资源包给的就是一条已经跑通的链路——基于 Pytorch 的高分遥感图像语义分割完整教程附带 GF2_PMS2 影像切片、类别样例图、预测结果图和对比可视化图。它解决的是「有数据、有代码、但拼不起来」的问题适合已经会写 Pytorch 训练循环、但对遥感数据特性不熟的工程师和研究生。下面按「资源是什么 → 怎么用 → 坑在哪」的顺序拆开讲。2. 遥感语义分割的数据底座GF2 切片、标签与目录结构2.1 为什么遥感分割不能直接套自然图像流程自然图像语义分割比如 VOC、Cityscapes的输入是 8 位 RGB尺寸规整标签是人工逐像素标注。遥感图像完全是另一回事。GF2_PMS2 这类高分多光谱影像单景覆盖范围大、波段多蓝、绿、红、近红外像素值范围往往超过 8 位直接送进网络会出现两个问题一是动态范围不匹配导致训练震荡二是地物尺度差异极大——一条道路可能横跨几千像素一栋建筑只有几十像素。所以遥感语义分割的第一步不是选网络而是做「切片 归一化 标签对齐」。资源包里的GF2_PMS2_E116.0_N39.1_20170302_L1A0002214760-MSS2_380.png、_381.png、_382.png、_142.png就是同一景影像切出来的多个 patch命名规则里保留了原始景号和切片编号方便回溯。classes_sample.jpg是类别样例图用来确认标签体系img_gt_pre.png是原图、真值、预测三联对比图blend.png是预测结果叠加到原图上的效果predict.png是纯预测掩膜。这套文件命名不是随便起的它对应的是「原图 → 标签 → 预测 → 叠加」四步验证链。常见做法是先把整景影像按固定窗口切片窗口之间保留重叠避免边缘地物被切断然后对每个 patch 做逐波段归一化而不是全局归一化。我一般会把近红外波段单独拿出来看分布因为植被和建筑在近红外上的差异比 RGB 大得多归一化参数设错植被类直接全错。2.2 目录结构与文件对应关系拿到资源包后先别急着跑训练。把目录理清楚后面排错能省一半时间。资源包里的文件大致分四类文件/文件组类型用途GF2_PMS2_...-MSS2.csv元数据记录影像景号、成像时间、波段信息GF2_PMS2_...-MSS2_380/381/382/142.png影像切片训练/推理输入classes_sample.jpg类别样例确认标签类别与颜色映射predict.png/blend.png/img_gt_pre.png结果可视化验证分割效果README.md说明环境依赖与运行入口这里有个容易忽略的点CSV 元数据里的成像时间20170302和景号L1A0002214760要跟切片文件名对得上。如果后面你要自己扩数据命名规则最好保持一致否则写 dataloader 时得额外维护一张映射表纯属给自己找麻烦。2.3 从切片到 Dataset一个可抄的 Pytorch 实现下面这段代码是遥感分割 Dataset 的常见写法核心是「按文件名配对影像和标签、逐波段归一化、返回 tensor」。资源包里没有直接给 dataloader 源码但按这个结构写能直接对接后面的训练脚本。import os import numpy as np import torch from torch.utils.data import Dataset, DataLoader from PIL import Image class RemoteSensingSegDataset(Dataset): def __init__(self, img_dir, mask_dir, img_suffix_MSS2, mask_suffix_label, transformNone): # 按文件名前缀配对避免影像和标签错位 self.img_dir img_dir self.mask_dir mask_dir self.transform transform self.ids [] for f in os.listdir(img_dir): if f.endswith(.png) and img_suffix in f: # 去掉后缀得到唯一 id例如 GF2_PMS2_..._380 uid f.replace(.png, ) mask_name uid mask_suffix .png if os.path.exists(os.path.join(mask_dir, mask_name)): self.ids.append(uid) self.ids.sort() def __len__(self): return len(self.ids) def __getitem__(self, idx): uid self.ids[idx] img_path os.path.join(self.img_dir, uid .png) mask_path os.path.join(self.mask_dir, uid _label.png) # 遥感影像可能是 16 位用 I;16 读再转 float32 img Image.open(img_path) img np.array(img).astype(np.float32) # 逐波段归一化每个波段减均值除标准差均值方差建议从训练集统计 if img.ndim 3: mean img.mean(axis(0, 1), keepdimsTrue) std img.std(axis(0, 1), keepdimsTrue) 1e-6 img (img - mean) / std else: img (img - img.mean()) / (img.std() 1e-6) mask np.array(Image.open(mask_path)).astype(np.int64) if self.transform: img, mask self.transform(img, mask) # 转成 CHW if img.ndim 2: img img[None, :, :] else: img np.transpose(img, (2, 0, 1)) return torch.from_numpy(img).float(), torch.from_numpy(mask).long() # 使用示例 train_ds RemoteSensingSegDataset( img_dir./data/images, mask_dir./data/masks, img_suffix_MSS2, mask_suffix_label ) train_loader DataLoader(train_ds, batch_size4, shuffleTrue, num_workers2)逻辑说明__init__里用文件名前缀配对是为了防止影像和标签数量不一致时静默错位——遥感数据里这种错位很常见训练 loss 会正常下降但预测全是噪声。__getitem__里逐波段归一化而不是全局归一化是因为多光谱波段量纲不同全局归一化会让近红外波段被 RGB 压制。mask用int64是因为交叉熵损失要求标签是 long 类型用 uint8 会报类型错误。参数说明img_suffix和mask_suffix要按你实际标签命名改batch_size4是高分影像显存占用大时的保守值16G 显存可以试 8num_workers在 Windows 上建议设 0否则容易卡在共享内存。提示如果你的标签是单通道灰度图类别值必须是 0、1、2… 连续整数中间不能跳号。跳号会导致CrossEntropyLoss报 index out of range。3. 模型选型与训练从 FCN 到 DeepLabV3 的落地取舍3.1 遥感分割为什么偏爱编码器-解码器结构遥感语义分割的网络选型绕不开 FCN、U-Net、DeepLab 系列。FCN 是开山之作但上采样太粗暴边缘地物糊成一片U-Net 的跳跃连接对建筑、道路这类有明确边界的类别很友好DeepLabV3 的空洞卷积能扩大感受野适合大范围地物。资源包的可视化结果img_gt_pre.png里如果预测边缘比较锐利大概率用的是带跳跃连接的结构。我一般会先跑 U-Net 做 baseline因为它在小数据集上收敛快、调参少。如果类别里有大面积同类地物比如大片农田再换 DeepLabV3 或加 ASPP 模块。别一上来就上 Transformer 分割模型遥感数据标注成本高样本量往往撑不起大模型过拟合比欠拟合更常见。3.2 训练脚本的关键参数与损失函数下面是一个最小可跑的训练循环重点看损失函数和优化器参数。遥感分割里类别极不平衡背景类可能占 80% 以上直接用交叉熵会让模型学会「全预测背景」。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader # 假设 model 是 U-Net 或 DeepLabV3num_classes 按 classes_sample.jpg 确认 num_classes 6 # 示例值按实际类别数改 model build_model(num_classes).cuda() # 类别权重按训练集统计的频率倒数缓解不平衡 class_weights torch.tensor([1.0, 2.0, 3.0, 5.0, 4.0, 2.0]).cuda() criterion nn.CrossEntropyLoss(weightclass_weights, ignore_index255) # 遥感分割常用 AdamW 余弦退火初始 lr 不要太大 optimizer optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) for epoch in range(50): model.train() total_loss 0 for img, mask in train_loader: img, mask img.cuda(), mask.cuda() optimizer.zero_grad() out model(img) loss criterion(out, mask) loss.backward() optimizer.step() total_loss loss.item() scheduler.step() print(fepoch {epoch}, loss {total_loss / len(train_loader):.4f})逻辑说明class_weights是手动设的示例实际应该用sklearn.utils.class_weight.compute_class_weight从训练集统计。ignore_index255是遥感标签里的常见约定未标注区域标 255不参与 loss 计算。AdamW比Adam多了正确的权重衰减遥感数据小样本下更稳。参数说明lr1e-4是保守起点如果 loss 前几个 epoch 不降可以试 3e-4T_max50要和总 epoch 数一致否则余弦退火会在训练中途把 lr 降到接近 0weight_decay1e-4是常规正则数据量特别小时可以加到 1e-3。3.3 评估指标别只看像素准确率遥感分割的评估像素准确率PA是最容易骗人的指标。如果背景占 90%模型全预测背景也能拿 90% PA。真正要看的是 mIoU平均交并比和各类别的 IoU。import numpy as np def compute_iou(pred, target, num_classes): # pred, target: numpy array, shape [H, W] ious [] for cls in range(num_classes): pred_mask (pred cls) target_mask (target cls) intersection np.logical_and(pred_mask, target_mask).sum() union np.logical_or(pred_mask, target_mask).sum() if union 0: continue # 该类别在图中不存在跳过 ious.append(intersection / union) return np.mean(ious), ious # 推理后调用 model.eval() with torch.no_grad(): for img, mask in val_loader: img img.cuda() out model(img) pred out.argmax(dim1).cpu().numpy() mask mask.numpy() for p, m in zip(pred, mask): miou, per_cls compute_iou(p, m, num_classes) print(fmIoU {miou:.4f}, per-class {per_cls})逻辑说明union 0时跳过是因为某些 patch 里可能没有某一类地物强行算 IoU 会得到 0 或 nan拉低整体指标。argmax(dim1)取每个像素最大概率的类别这是分割推理的标准操作。参数说明num_classes必须和训练时一致如果标签里有 255评估前要先把它过滤掉否则会被当成一个额外类别。4. 避坑与排查遥感分割训练中最容易翻车的五件事4.1 现象loss 正常下降但预测全是同一类原因类别极不平衡 损失函数没加权。背景类占比过高时模型发现「全预测背景」就能让 loss 降得很快于是陷入局部最优。解决先统计训练集各类别像素占比用compute_class_weight生成权重传给CrossEntropyLoss。如果某类占比低于 1%考虑用 Focal Loss 或对少数类做重采样。资源包里的classes_sample.jpg就是用来确认类别分布的先看图再定权重。4.2 现象训练 loss 震荡剧烈几个 epoch 后突然变 nan原因遥感影像像素值范围大归一化没做好或者学习率设太大。16 位影像直接除以 255 会得到 0~257 的值送进网络必然炸。解决读图时确认位深用np.array(img).astype(np.float32)后做逐波段标准化而不是简单除以 255。学习率从 1e-4 起步如果前 100 个 iteration 就 nan降到 1e-5 再试。另外检查标签里有没有 255 以外的异常值。4.3 现象验证集 mIoU 比训练集低很多且持续下降原因过拟合。遥感标注数据少模型参数量大时很容易记住训练集。解决加数据增强随机翻转、旋转、色彩抖动加 dropout 或 weight decay减小模型宽度。如果用了预训练权重确认预训练数据域和遥感影像别差太远——ImageNet 预训练对遥感分割有帮助但不如在类似遥感数据上自监督预训练。4.4 现象推理结果边缘有锯齿建筑轮廓不完整原因上采样方式太粗暴或者切片时边缘地物被切断。解决把双线性上采样换成转置卷积或 PixelShuffle或者在解码器里加跳跃连接。切片时设置重叠区域比如 64 像素推理时对重叠区做加权平均。资源包里的blend.png就是用来检查边缘融合效果的。4.5 现象dataloader 报错「cannot identify image file」或读出来全黑原因影像格式不是标准 PNG或者标签是调色板模式P 模式直接转 numpy 会得到索引而不是类别值。解决用Image.open(path).convert(RGB)或.convert(L)显式转换模式。如果是 16 位 PNGPIL 默认可能读成 I;16要确认img.mode。标签图如果是调色板模式用np.array(img)前先img.convert(L)。5. 进阶验证用可视化三联图反推模型问题资源包里的img_gt_pre.png是最有价值的文件之一它把原图、真值、预测放在同一张图里。很多人训练完只看 mIoU 数字但数字不告诉你错在哪。我的习惯是每跑完一个 epoch抽 3~5 个验证样本生成三联图肉眼过一遍。具体做法是取原图、真值掩膜、预测掩膜按相同颜色映射上色横向拼接。如果预测在某一类上系统性偏移比如把道路预测成建筑说明类别权重或标签定义有问题如果边缘普遍模糊说明上采样或感受野不够如果某些 patch 全错回去检查那个 patch 的标签是不是标错了。import matplotlib.pyplot as plt def visualize_triplet(img, gt, pred, save_path): # img: [C,H,W] or [H,W], gt/pred: [H,W] if img.ndim 3: img np.transpose(img, (1, 2, 0)) img (img - img.min()) / (img.max() - img.min() 1e-6) fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(img) axes[0].set_title(Image) axes[1].imshow(gt, cmaptab20) axes[1].set_title(Ground Truth) axes[2].imshow(pred, cmaptab20) axes[2].set_title(Prediction) for ax in axes: ax.axis(off) plt.tight_layout() plt.savefig(save_path, dpi150) plt.close()逻辑说明cmaptab20适合类别数少于 20 的分割掩膜颜色区分度高。原图归一化到 0~1 是为了显示正常不影响推理。保存而不是show()是为了批量跑验证时不用人工关窗口。参数说明dpi150在论文插图里够用如果类别超过 20换nipy_spectral或自定义颜色表。三联图建议按 epoch 存到独立目录方便对比不同训练阶段的变化。从那以后我每次训练遥感分割模型都强制在验证集上抽三联图看一遍数字达标但图不对的情况太多了。希望这份拆解能帮你少走几个弯路。本文还有配套的精品资源点击获取
返回列表