ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

遥感影像语义分割实战指南:从数据预处理到边界优化

遥感影像语义分割实战指南:从数据预处理到边界优化 简介一份面向固定翼无人机系统辨识与仿真研究的Matlab代码资源特别适合电子信息工程、计算机、数学等专业学生用于课程设计、期末大作业与毕业设计也适合需要快速理解UAV建模流程的初学者。压缩包共包含5个文件其中2个M脚本为核心程序配合PDF说明文档以及txt/md文本说明分别用注释解释代码思路、参数含义与运行方式整体rar包仅911KB非常轻量。代码采用参数化编程参数可方便修改路径和模块注释清晰并附赠案例数据下载解压后即可直接运行省去环境配置与调试时间。工程围绕UAV_sysID_project展开覆盖固定翼无人机系统辨识项目的主要框架有助于读者从数据准备、脚本执行到结果分析形成完整认知。已有144人浏览学习适合希望借助成熟示例快速上手的用户。1. 做遥感影像语义分割先想清楚“逐像素”这三个字语义分割在遥感影像里从来不是“把图分个类”那么简单。同样一张城市影像目标检测给出几个框语义分割要回答的是每一个像素到底是道路、建筑、植被还是水体。一个 2 万乘 2 万像素的正射影像检测模型只需要输出几百个目标框分割模型却要做四亿次分类判断。这个数量级差异决定了整个技术路线不是选一个网络就能跑通而是数据组织、模型结构、损失函数和推理策略都要围绕“逐像素”重新设计。边界模糊是另一个绕不开的问题。建筑阴影投射到路面上树冠遮挡了部分屋顶云影扫过农田——这些地方连人工标注都会犹豫。模型在这种像素上做出的判断边界必然是锯齿状或粘连的。所以遥感语义分割的工程难点从来不是“准确率再高一个点”而是“边界更干净一点、小目标不漏掉、跨区域泛化不崩”。本文直接从数据预处理、模型选型、训练调参到评估部署串一遍重点放在参数和踩坑上新手能照着跑熟手能对照检查自己的流程。2. 数据预处理遥感影像的分块、波段与标签对齐2.1 滑动窗口切块尺寸、重叠率与步长的配合遥感影像不能整图直接进网络。显存装不下是表面原因更深层的原因是分割模型对输入尺寸敏感太大则下采样倍数不够导致感受野不足太小则上下文信息丢失。常见做法是切成固定尺寸的 patch 送入训练而 patch 的尺寸和步长决定了样本的有效性和数量。我习惯用 512×512 作为默认 patch 尺寸。这个大小在 ResNet 系列编码器下采样 32 倍下特征图是 16×16既不会太小导致语义信息丢失也不会因为过大降低 batch size。如果显卡显存只有 8GB可以降到 448×448 或 384×384但不要低于 320×320否则道路、河流这类长条状地物的语义连续性会被截断。步长的选择更讲究。步长等于 patch 尺寸时相邻 patch 之间没有重叠样本量最小训练速度快但物体正好被切在 patch 边缘时模型永远看不到完整目标。比如一栋 60 米长的建筑被 512 像素假设地面分辨率 0.5 米即 256 米的窗口切分大概率有建筑跨越两个 patch每个 patch 里只有半栋楼标注也只剩一半——模型在训练时会把“半栋楼”当成完整特征学进去。推理时会输出残缺的目标。解决办法是重叠切块。训练时用 stride patch_size // 2 的步长滑动配合随机数据增强随机翻转、随机旋转 90 度、随机色彩抖动可以从一张标准影像中生成大量有效的训练样本。推理时同样用重叠窗口但输出的重叠区域需要特殊处理这个放到第 5 章讲。提示切块之前先做一次全局统计分析记录每张影像的均值与标准差后续归一化要用。2.2 多波段输入RGB 之外的通道到底用不用大多数预训练模型ImageNet 系列的输入是 3 通道 RGB直接套用到遥感影像上有两个明显问题。一是很多遥感影像是多波段的至少包含 R、G、B、NIR 四个波段甚至还有红边、短波红外、热红外。二是影像的数值范围与自然图像完全不同可能已经是地表反射率产品而非原始 DN 值。所以输入通道设计是一个需要单独决策的工程问题不是简单地“随便拼凑”。我的做法是先用 RGB 三通道跑通基线再评估是否有必要添加额外波段。添加波段时优先选 NDVI 和 NDWI 这类归一化指数而非原始波段。原因很简单归一化指数是比值运算的结果像素值范围稳定受光照和大气影响小模型的输入分布更平稳。NDVI 的计算公式import numpy as np def compute_ndvi(nir_band, red_band, epsilon1e-10): 计算归一化植被指数 nir_band: 近红外波段形状 HxWfloat32 red_band: 红光波段形状 HxWfloat32 返回 NDVI范围约在 -1 到 1 之间 ndvi (nir_band.astype(np.float32) - red_band.astype(np.float32)) / ( nir_band.astype(np.float32) red_band.astype(np.float32) epsilon ) # 超过 99.5% 分位数视为异常值通常由传感器噪声或云阴影导致 clip_max np.percentile(ndvi, 99.5) clip_min np.percentile(ndvi, 0.5) ndvi np.clip(ndvi, clip_min, clip_max) return ndviNDVI 的数值本身被限制在 -1 到 1 之间这一步就已经做了归一化异常值裁剪是为了避免个别传感器坏点在训练时放大梯度。如果直接用 NIR、SWIR 等原始波段需要各自做 min-max 或 z-score 归一化否则数值范围差异会导致训练早期振荡。通道拼接时还要考虑预训练权重的问题。ResNet 第一层卷积的权重是 3 通道预训练好的换成 4 通道或 5 通道后常规做法是复制 RGB 通道的均值权重到新通道上把新增通道的权重初始化为极小的随机值比如均值为 0、标准差 0.01 的正态分布。这样前向传播初期新通道对特征的影响是微弱的不会破坏已有的高层语义特征但又能让梯度正常回传到新通道上。2.3 标签对齐影像偏移与标注滞后的处理遥感语义分割中标签质量是决定 mIoU 上限的最大因素。网络结构再新颖面对一个错位的标签也无能为力。影像可能存在几何校正误差多时相影像之间存在配准偏差标注人员手工勾画的建筑物边界与真实屋顶轮廓通常有几像素的偏移。对于精度要求高的任务比如 0.3 米分辨率的建筑轮廓提取这个偏移量足以造成显著的边缘误差。针对标签噪声我的处理流程是三级审核。第一步是路径归零检查将标签二值化后与原始影像做半透明叠加肉眼检查边界错位。第二步是面积分布统计如果某一类在训练集里占 40%在验证集里却占 5%这个标签很可能存在系统性错标。第三步是训练后的预测一致性对比用训练好的模型对训练集做预测将预测结果与 ground truth 不一致的区域可视化。这个区域往往是标签噪声最集中的地方人工抽查修正后重新训练通常能带来 1~2 个点的 mIoU 提升。提示做标签清洗时不要直接修改原始标注文件保留一份原始标签另外生成一份 clean 版本。后续训练脚本通过配置文件选择使用哪个版本避免反复备份和误操作。3. 模型选型从 FCN 到 DeepLabV3再到分割 Transformer3.1 编码器-解码器架构为什么遥感分割普遍不直接用 FCNFCNFully Convolutional Network是语义分割的开山之作其核心贡献是把分类网络最后的全连接层替换为卷积层使得网络可以接受任意大小的输入。但 FCN 有一个致命的弱点下采样 32 倍后直接上采样回原尺寸分割结果不仅粗糙而且边缘位置依赖低层特征细节丢失严重。在遥感影像这种纹理丰富、目标密集的场景里FCN 的表现远不如预期。于是编码器-解码器结构成为事实标准。编码器负责提取语义特征通过逐步下采样增大感受野解码器通过上采样和跳跃连接恢复空间细节。两者配合兼顾了“是什么”和“在哪里”两个维度。遥感影像通常目标密集同一场景中有大量相似的小物体比如一排按同样式建的房子编码器的语义特征能区分“屋顶”与“道路”解码器的空间特征能区分“这个屋顶”与“那个屋顶”。选型时首先要确定编码器也就是 backbone。ResNet-50、ResNet-101、EfficientNet 是常见选择。ResNet-101 比 ResNet-50 更深的优势在遥感影像上能体现出来因为影像中的物体尺度跨度极大——一棵树可能只有几十个像素一片森林却覆盖整个画面深层网络对尺度变化的适应性更强。但也要意识到编码器占了模型总参数量的绝大部分ResNet-101 的模型体量在部署时可能是个负担。我个人的经验先跑通 ResNet-50 找明显问题再用 ResNet-101 验证上限提升最后根据部署要求决定是否做轻量化。3.2 DeepLabV3 与 UNet解码器设计的核心差异DeepLabV3 和 UNet 是遥感分割实践中最常用的两个框架它们在解码器设计上走了两条完全不同的路。DeepLabV3 的核心组件是 ASPPAtrous Spatial Pyramid Pooling。ASPP 用不同的空洞率dilation rate常见是 6、12、18对同一特征图做并行卷积相当于在同一层上捕获多尺度上下文信息。空洞卷积在保持特征图分辨率的同时扩大感受野这意味着编码器输出的特征图空间细节更好解码器不需要做复杂的上采样融合。DeepLabV3 的解码器结构简洁先对编码器输出做 4 倍上采样再与编码器低层特征拼接最后再做 4 倍上采样没有复杂的密集连接。UNet 的核心是嵌套的密集跳跃连接每一层解码器都融合了不同层级的编码器特征。这样的设计对细节恢复更有利但也有代价模型参数量显著增大训练时间更长。遥感影像中的目标边缘质量在 UNet 体系下通常优于 DeepLabV3尤其是锯齿状边缘更少。对比维度DeepLabV3UNet多尺度建模能力强ASPP 显式建模中依赖多级特征融合边缘细节恢复中等更好训练速度更快较慢显存占用较低较高适用场景道路/水体等大目标建筑/车辆等中小目标遥感影像分割如果以道路、水体、农田等大面积地物为主DeepLabV3 的 ASPP 能更好地建模全局上下文如果目标是建筑物、小型水体、独立树木等中小目标UNet 的密集跳跃连接对边缘细节更有帮助。3.3 引入注意力机制与分割 Transformer什么时候值得换遥感语义分割近年一个明显变化是分割 Transformer如 SETR、SegFormer、Mask2Former从论文走向工程实践。这类模型的核心是用自注意力机制替代卷积在感受野方面天然优势明显一个卷积核的局部感受野只有几像素到几十像素而 Transformer 的注意力模块在高级特征层可以关联整张图。但在遥感分割任务里我观察到 Transformer 的优势是有条件的。当我处理的是高分辨率影像中密集小目标时CNN 仍然能打当我处理的是跨区域泛化、需要关联全局上下文的任务时Transformer 的效果值得尝试。SegFormer 这类混合架构分层 Transformer 编码器加轻量 MLP 解码器在遥感场景下效果稳定模型可解释性也不差。需要注意的是 Transformer 对训练数据量更敏感在标注样本较少的情况下更容易欠拟合或过拟合。我的建议是数据规模在几千张 patch 以内先不换 Transformer有几万张 patch 时再来对比。4. 训练工程优化器、学习率与不平衡损失4.1 优化器与学习率分割任务为什么和分类任务不一样图像分类任务中模型输出的是一个全局概率分布梯度计算简单直接。语义分割任务中模型的输出是一个 H×W×C 的张量每个像素都贡献一份梯度。这意味着梯度的方差更大更新方向波动更明显对优化器的稳定性要求更高。Adam 是分类任务的主流选择但在分割任务中SGD with Momentum 有时效果更好。原因在于 SGD 的更新方向在长期看更平稳适合分割这种需要精细空间特征的任务而 Adam 在前期收敛快后期容易出现震荡导致边界细节反复变化。我的默认配置如下import torch import torch.nn as nn import torch.optim as optim model build_segmentation_model(backboneresnet50, num_classes6) criterion nn.CrossEntropyLoss(ignore_index255) # 255 为 ignore 类 # 分割任务中更推荐的优化器配置SGD Momentum 多项式衰减 # 初始学习率不宜取太大0.01 是常见默认值配合 poly 策略衰减 optimizer optim.SGD( model.parameters(), lr0.01, # 8 卡训练时可适当加大到 0.02 momentum0.9, weight_decay1e-4, # 遥感影像类别多权重衰减不宜过大 ) # 多项式衰减iter 越大学习率越小比 step 衰减更适合分割 # power0.9 是常用配置让模型后期稳定精修 def poly_lr(epoch, max_epoch, initial_lr, power0.9): return initial_lr * (1 - epoch / max_epoch) ** power scheduler optim.lr_scheduler.LambdaLR( optimizer, lr_lambdalambda epoch: poly_lr(epoch, 50, 0.01) )SGDMomentum 在训练后期比 Adam 更容易收敛到更平滑的局部最优解在 mIoU 上通常能高出 1~3 个点。Adam 的优势在超参数较少、调试方便适合做快速 baseline。因此我通常的模式是先用 Adam 跑通流程再用 SGD 精调获得最终指标。学习率的初值单卡 0.01 是保守起点若使用混合精度训练或分布式训练可适当提高但超过 0.05 基本必崩。4.2 类别不平衡交叉熵、Dice、Focal 的取舍遥感分割的类别分布天然不均匀。以土地利用分类为例植被可能占 50%水体占 5%建筑物占 15%道路占 10%剩下的类别零零散散。直接用交叉熵训练模型会把大量参数资源用于拟合占比最高的类别小类别的像素梯度淹没在大类别中。实际工程中最有效的方案不是换损失函数而是先做类别权重。在交叉熵中按类别样本量的倒数设置权重是最简单也最稳的办法。torch 的 CrossEntropyLoss 自带 weight 参数# 先统计训练集中每个类别的像素占比 # 假设 class_ratios [0.45, 0.05, 0.15, 0.10, 0.15, 0.10] # 权重与占比成反比训练前根据全量数据统计得到 class_weights torch.tensor([0.5, 5.0, 1.5, 2.0, 1.5, 2.0], devicedevice) criterion nn.CrossEntropyLoss(weightclass_weights, ignore_index255)权重设置的核心逻辑是“让稀有类别贡献更大的梯度”但也要防止权重过大导致的类别失衡逆转。比如某个小类别占比不足 1%如果将其权重设到 20 以上训练中会出现该类别过拟合验证集上那类的 IoU 先升后降其他类别的精度被拉垮。Dice Loss 对类别不平衡问题天然鲁棒它基于区域重叠度而非逐像素交叉熵对小目标的梯度占比更合理。但纯 Dice Loss 训练容易不稳定因为它的梯度在数值上不饱和前期剧烈后期无力。常见的方案是交叉熵Dice 的组合比如def combined_loss(pred, target, weights): ce_loss nn.functional.cross_entropy(pred, target, weightweights, ignore_index255) # 对 pred 做 softmax 后计算 dice probs torch.softmax(pred, dim1) target_onehot torch.nn.functional.one_hot( target, num_classespred.shape[1] ).permute(0, 3, 1, 2).float() dice dice_loss(probs, target_onehot) return ce_loss 0.5 * dice这个组合方案中交叉熵负责稳定优化路径Dice 负责把小目标的梯度托起来。超参数 0.5 是经验值通常无需频繁调整。Focal Loss 侧重难例挖掘但对遥感这种目标尺度差异大的场景容易出现困难样本过于集中导致训练回合延长收益不如 DiceCE。4.3 用 OHEM 提升边界质量在线难例挖掘的具体写法边界区域像素数量少却是模型能力差距最大的地方。交叉熵损失会让大多数背景像素主导梯度边界像素贡献被淹没。在线难例挖掘OHEM的思路是每次迭代中只挑选损失值最高的前 k% 像素反向传播。这部分像素正好对应边界、小目标和易混淆区域。def ohem_cross_entropy(pred, target, keep_ratio0.3): keep_ratio: 保留损失最高的前 30% 像素参与梯度回传 loss_per_pixel nn.functional.cross_entropy( pred, target, ignore_index255, reductionnone ) # 将所有非 ignore 的像素展平 valid_mask (target ! 255).view(-1) losses loss_per_pixel.view(-1) valid_losses losses[valid_mask] if valid_losses.numel() 0: return torch.tensor(0.0, devicepred.device) # 按损失值降序排序取前 keep_ratio k max(1, int(valid_losses.numel() * keep_ratio)) topk_losses, _ torch.topk(valid_losses, k) return topk_losses.mean()该函数返回的损失值只来自损失最高的 30% 像素梯度回传也集中在这部分像素上。实际操作中keep_ratio 从 0.2 到 0.4 之间是合理区间调太低训练不稳定调太高就退化成普通交叉熵。OHEM 在训练后期的收益更明显前期所有像素都在学后期只学难点精度曲线会更平稳。5. 评估与部署mIoU 之外遥感分割还要看什么5.1 验证集划分与指标解读OA 和 mIoU 的差距在哪里遥感分割中 OAOverall Accuracy是最常见的指标但也最容易被“灌水”。如果一张影像中有 60% 的像素是植被模型只需要把所有像素都预测为植被OA 就有 60%。所以真正应该看的是 mIoU 和各类别的 IoU 加权平均。验证集的划分比指标选择更重要。不能把所有 patch 全部随机打乱因为同一个大影像分出来的相邻 patch 有高度相似的特征模型在训练中见过类似的 patch验证结果必然虚高。正确的做法是在影像级别上划分按地理位置或影像编号分组保证训练集、验证集、测试集来自不同的影像区域。否则模型的泛化能力会被严重高估换一张新影像后 mIoU 可能暴跌 5 个点以上。5.2 重叠推理切片拼接时重叠区到底保留谁的输出推理时同样要切块但重叠区域的拼接策略会直接影响最终结果。直接丢弃重叠区会导致目标被切断模型输出在 patch 边界处存在明显的“缝”。我常用的方法是设置重叠率 50%推理步长为 patch_size // 2重叠区的每个像素做多次预测最终结果采用多数投票或预测概率平均。Softmax 概率平均比多数投票更平滑。具体操作是为整幅大图维护一个 H×W×C 的概率累加矩阵和一个 H×W 的计数矩阵。每次滑动窗口推理后将该窗口输出的 softmax 概率累加到对应位置并将计数加 1。全部窗口推理完后用累加概率除以计数得到每个像素的平均概率import numpy as np def overlap_inference(model, image, window512, stride256, num_classes6, devicecuda): h, w image.shape[:2] probability_map np.zeros((h, w, num_classes), dtypenp.float32) count_map np.zeros((h, w, 1), dtypenp.float32) # 从(0,0)开始按stride滑动最后一圈不足512像素时向外padding for y in range(0, h - window 1, stride): for x in range(0, w - window 1, stride): patch image[y:ywindow, x:xwindow] # 归一化与通道维度转换BCHW patch_tensor transform_to_tensor(patch).unsqueeze(0).to(device) with torch.no_grad(): output torch.softmax(model(patch_tensor), dim1) probability_map[y:ywindow, x:xwindow] output[0].cpu().numpy().transpose(1, 2, 0) count_map[y:ywindow, x:xwindow] 1 # 边缘区域如果没被覆盖比如最右/最下的残余区域单独推理一次 probability_map / np.maximum(count_map, 1) final_prediction np.argmax(probability_map, axis-1).astype(np.uint8) return final_prediction这段代码的 key point 是 stride 小于 window重叠区域被多次预测后取均值。它的效果是让分割结果的空间连续性显著优于单次推理尤其在道路、河流这类长条形基元上边界更平直。5.3 提升边缘质量的三个实用技巧最后分享三个具体技巧它们不改变模型结构但能显著提升边界质量。第一个技巧是条件随机场CRF后处理。早期语义分割几乎标配 CRF现在用得少了但在遥感分割中CRF 对消除细碎孤立点依然有效。OpenCV 的cv2.xtra模块里有现成的实现。它对有小块噪点的影像改善最直观但注意 CRF 作用是局部的不要指望它修复大面积的错误区域。第二个技巧是边缘增强训练。在标注阶段额外生成一张边界权重图让边界像素在损失函数中的权重高于非边界像素。具体操作是对标签做 Canny 边缘检测或形态学梯度得到边缘像素的 mask然后将这些像素的交叉熵损失乘上 5~10 倍。这个方式的成本很低生成边缘图是离线操作不拖慢训练速度但边界 IoU 的提升通常在 2 个点以上。第三个技巧是用测试时增强对预测结果做整合。推理时对同一张大图做多尺度的推理比如 1.0 倍和 1.25 倍将两种尺度的概率平均后取 argmax。这个操作能让尺度差异大的目标既有小房屋又有大厂房同时获得更好的表现。代价是推理时间成倍增加更适合离线制图场景而不是在线服务的实时推理。这三个技巧叠加之后再去诊断模型的边界问题就会清晰很多如果加了 CRF 边界还是碎问题大概率在标签本身如果大目标边界完整但小目标缺失问题在损失函数的权重分配。此时回顾 4.3 节的 OHEM 设置和 4.2 节的类别权重会比继续调 backbone 更有效。本文还有配套的精品资源点击获取
返回列表