
简介这份资源面向遥感图像处理与计算机视觉方向的开发者、学生及研究人员聚焦卫星图像分割这一核心任务帮助读者理解从图像预处理、特征提取到分割算法选择与后处理的完整流程。包内共3个文件均为Python脚本压缩包约3KB涵盖主程序入口、屋顶检测示例与数据准备模块便于快速搭建实验环境并对照代码理解分割逻辑。内容涉及GDAL/OGR、OpenCV、Scikit-image及TensorFlow、PyTorch等常用库的典型用法并延伸至U-Net等深度学习分割模型在遥感场景中的应用思路。已有252人学习下载适合希望以轻量脚本入门卫星图像分割、掌握地物分类与土地覆盖分析基础实现的中级学习者参考。1. 卫星图像分割从遥感大图里把每一类地物抠出来拿到一份标注好的遥感影像数据集想把建筑、水体、植被、道路逐像素分开这件事比想象中麻烦。卫星图幅面动辄几千乘几千像素直接塞进网络显存扛不住地物边界模糊建筑和道路灰度接近标注样本还少。这个项目就是冲着这些痛点来的用 Python 搭一套完整的卫星图像分割流程从数据读取、切图增强到模型训练、推理拼接最后输出可视化的分割掩膜。适合做遥感方向的学生、想入门语义分割的开发者以及需要把分割能力集成到自己系统里的工程同学。读完你能跑通全流程也能按自己的数据改类别和网络结构。2. 项目结构与技术选型为什么是这套组合2.1 目录结构一览项目按数据、模型、训练、推理、工具五个维度切分避免所有代码堆在一个文件里。常见做法是这样组织satellite-seg/ ├── configs/ # 配置集中管理 │ ├── default.yaml # 默认超参、路径 │ └── unet_r50.yaml # 具体实验配置 ├── data/ # 数据相关 │ ├── dataset.py # Dataset 与 DataLoader │ ├── transforms.py # 增强与归一化 │ └── split.py # 训练/验证集划分 ├── models/ │ ├── unet.py # UNet 主干 │ ├── deeplab.py # DeepLabV3 备选 │ └── losses.py # Dice BCE 组合损失 ├── engine/ │ ├── trainer.py # 训练循环 │ ├── validator.py # 验证与指标 │ └── inference.py # 滑窗推理与拼接 ├── utils/ │ ├── metrics.py # IoU、F1、混淆矩阵 │ ├── visualize.py # 掩膜叠加可视化 │ └── logger.py # 日志与 TensorBoard ├── scripts/ │ ├── train.sh │ └── predict.sh └── requirements.txt这个结构的好处是配置和代码分离换数据集只改configs换模型只动models训练逻辑不用碰。新手容易把所有东西写进一个train.py改一个参数要翻半天后期维护成本极高。2.2 技术栈与依赖核心依赖如下表版本按项目实际锁定避免环境漂移依赖用途备注Python 3.8运行环境建议 3.8/3.9PyTorch 1.10深度学习框架支持 CUDA 11.xtorchvision预训练主干ResNet 权重OpenCV图像读写与几何变换处理大图切片NumPy数组运算掩膜拼接albumentations数据增强比 torchvision 更丰富rasterio遥感影像读取支持 GeoTIFFmatplotlib可视化结果叠加PyYAML配置解析管理超参选 PyTorch 而不是 TensorFlow主要因为动态图调试方便自定义损失和滑窗推理写起来直观。遥感影像常用 GeoTIFF 格式rasterio能保留地理坐标和波段信息比cv2.imread更合适。增强用albumentations它支持同步变换图像和掩膜避免旋转后标签错位。2.3 数据流与训练流程整个流程分四步读取大图并切片、增强、送入网络、计算损失反向传播。验证阶段用滑窗推理拼回原图尺寸再算 IoU。推理阶段加载权重对单张大图做重叠滑窗最后用加权平均消除拼接缝。提示切片尺寸和重叠比例直接影响显存占用与边界效果配置里要显式写清楚。3. 核心模块拆解数据管道与模型实现3.1 数据集与切片策略卫星图太大必须切成小块训练。切片时保留重叠避免边界地物被切断。下面是一个简化的 Dataset 实现import cv2 import numpy as np import torch from torch.utils.data import Dataset class SatelliteDataset(Dataset): def __init__(self, image_paths, mask_paths, transformNone, patch_size512, stride384): self.image_paths image_paths self.mask_paths mask_paths self.transform transform self.patch_size patch_size self.stride stride self.patches [] # 预计算所有切片坐标训练时按索引取 for img_path, msk_path in zip(image_paths, mask_paths): img cv2.imread(img_path, cv2.IMREAD_COLOR) h, w img.shape[:2] for y in range(0, h - patch_size 1, stride): for x in range(0, w - patch_size 1, stride): self.patches.append((img_path, msk_path, x, y)) def __len__(self): return len(self.patches) def __getitem__(self, idx): img_path, msk_path, x, y self.patches[idx] img cv2.imread(img_path, cv2.IMREAD_COLOR) msk cv2.imread(msk_path, cv2.IMREAD_GRAYSCALE) img img[y:yself.patch_size, x:xself.patch_size] msk msk[y:yself.patch_size, x:xself.patch_size] img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if self.transform: augmented self.transform(imageimg, maskmsk) img, msk augmented[image], augmented[mask] img img.transpose(2, 0, 1).astype(np.float32) / 255.0 msk (msk 0).astype(np.float32) # 二分类示例 return torch.from_numpy(img), torch.from_numpy(msk).unsqueeze(0)patch_size控制单块大小512 是常见起点显存不够降到 256。stride小于patch_size就产生重叠重叠区域在推理时加权融合。预计算切片坐标而不是每次读整图能大幅减少 IO。注意掩膜要转成 0/1 浮点多类别时改成类别索引并配合 CrossEntropyLoss。3.2 UNet 模型定义UNet 的编码器-解码器加跳跃连接适合分割任务。这里用 ResNet 做编码器解码器逐级上采样import torch import torch.nn as nn import torchvision class DecoderBlock(nn.Module): def __init__(self, in_ch, skip_ch, out_ch): super().__init__() self.up nn.Upsample(scale_factor2, modebilinear, align_cornersTrue) self.conv nn.Sequential( nn.Conv2d(in_ch skip_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), ) def forward(self, x, skip): x self.up(x) x torch.cat([x, skip], dim1) return self.conv(x) class UNetResNet(nn.Module): def __init__(self, num_classes1, pretrainedTrue): super().__init__() backbone torchvision.models.resnet34(pretrainedpretrained) self.encoder0 nn.Sequential(backbone.conv1, backbone.bn1, backbone.relu) # 1/2 self.encoder1 nn.Sequential(backbone.maxpool, backbone.layer1) # 1/4 self.encoder2 backbone.layer2 # 1/8 self.encoder3 backbone.layer3 # 1/16 self.encoder4 backbone.layer4 # 1/32 self.decoder4 DecoderBlock(512, 256, 256) self.decoder3 DecoderBlock(256, 128, 128) self.decoder2 DecoderBlock(128, 64, 64) self.decoder1 DecoderBlock(64, 64, 64) self.final nn.Conv2d(64, num_classes, 1) def forward(self, x): e0 self.encoder0(x) e1 self.encoder1(e0) e2 self.encoder2(e1) e3 self.encoder3(e2) e4 self.encoder4(e3) d4 self.decoder4(e4, e3) d3 self.decoder3(d4, e2) d2 self.decoder2(d3, e1) d1 self.decoder1(d2, e0) return self.final(d1)编码器用 ResNet34 预训练权重收敛更快。DecoderBlock先上采样再拼接对应层特征最后两层卷积融合。num_classes二分类设 1多分类设类别数。输出分辨率是输入的 1/2因为encoder0后没有下采样实际使用时可在final前再加一次上采样或调整编码器结构对齐。3.3 损失函数与指标分割任务常用 Dice BCE 组合前者缓解类别不平衡后者稳定梯度import torch import torch.nn as nn class DiceBCELoss(nn.Module): def __init__(self, weight0.5): super().__init__() self.weight weight self.bce nn.BCEWithLogitsLoss() def forward(self, logits, targets): bce_loss self.bce(logits, targets) probs torch.sigmoid(logits) intersection (probs * targets).sum() dice_loss 1 - (2 * intersection 1e-6) / (probs.sum() targets.sum() 1e-6) return self.weight * bce_loss (1 - self.weight) * dice_lossweight调节两者比例0.5 是常用起点。Dice 对前景少的情况更敏感BCE 提供逐像素监督。训练时记录 IoUdef compute_iou(pred, target, threshold0.5): pred (torch.sigmoid(pred) threshold).float() intersection (pred * target).sum() union pred.sum() target.sum() - intersection return (intersection 1e-6) / (union 1e-6)阈值 0.5 是默认实际可按验证集调。IoU 比准确率更能反映分割质量尤其背景占多数时。4. 训练与推理从配置到出图4.1 配置文件与训练入口配置用 YAML 管理训练脚本读取后组装数据、模型、优化器# configs/unet_r50.yaml data: train_dir: data/train/images val_dir: data/val/images patch_size: 512 stride: 384 batch_size: 4 num_workers: 4 model: name: unet_resnet34 num_classes: 1 pretrained: true train: epochs: 50 lr: 0.0001 weight_decay: 0.0001 save_dir: checkpoints训练循环核心import yaml import torch from torch.utils.data import DataLoader from models.unet import UNetResNet from data.dataset import SatelliteDataset from models.losses import DiceBCELoss with open(configs/unet_r50.yaml) as f: cfg yaml.safe_load(f) train_ds SatelliteDataset(...) train_loader DataLoader(train_ds, batch_sizecfg[data][batch_size], shuffleTrue, num_workerscfg[data][num_workers]) model UNetResNet(num_classescfg[model][num_classes]).cuda() optimizer torch.optim.Adam(model.parameters(), lrcfg[train][lr], weight_decaycfg[train][weight_decay]) criterion DiceBCELoss() for epoch in range(cfg[train][epochs]): model.train() for imgs, masks in train_loader: imgs, masks imgs.cuda(), masks.cuda() logits model(imgs) loss criterion(logits, masks) optimizer.zero_grad() loss.backward() optimizer.step() # 每个 epoch 后在验证集算 IoU 并保存最优权重batch_size受显存限制512 切片下 4 是常见值。lr用 1e-4 配合 Adam太大容易震荡。num_workers按 CPU 核数调IO 瓶颈时加大。4.2 滑窗推理与拼接大图推理不能直接整图送入要滑窗后拼回。重叠区域用高斯权重融合消除拼接缝import numpy as np import torch def sliding_window_predict(model, image, patch_size512, stride384, num_classes1): model.eval() h, w image.shape[:2] prob_map np.zeros((num_classes, h, w), dtypenp.float32) count_map np.zeros((h, w), dtypenp.float32) # 高斯权重中心高边缘低 weight np.outer(np.hanning(patch_size), np.hanning(patch_size)).astype(np.float32) for y in range(0, h - patch_size 1, stride): for x in range(0, w - patch_size 1, stride): patch image[y:ypatch_size, x:xpatch_size] tensor torch.from_numpy(patch.transpose(2,0,1)).float().unsqueeze(0).cuda() / 255.0 with torch.no_grad(): out torch.sigmoid(model(tensor)).cpu().numpy()[0] prob_map[:, y:ypatch_size, x:xpatch_size] out * weight count_map[y:ypatch_size, x:xpatch_size] weight prob_map / np.maximum(count_map, 1e-6) return prob_mapstride越小重叠越多融合越平滑但耗时增加。weight用汉宁窗中心贡献大边缘贡献小避免边界突变。多类别时num_classes改成类别数取 argmax 得到最终掩膜。4.3 结果可视化把预测掩膜叠加到原图方便检查import cv2 import numpy as np def overlay_mask(image, mask, color(0, 255, 0), alpha0.5): mask_bin (mask 0.5).astype(np.uint8) colored np.zeros_like(image) colored[mask_bin 1] color return cv2.addWeighted(image, 1 - alpha, colored, alpha, 0)alpha控制透明度0.5 能同时看到原图和掩膜。颜色按类别区分多类别时循环赋值。5. 避坑与常见问题排查5.1 显存溢出现象训练几个 batch 后报 CUDA out of memory。原因切片太大或 batch_size 过高。解决把patch_size从 512 降到 256batch_size降到 2或开启混合精度torch.cuda.amp。5.2 掩膜与图像错位现象增强后标签和图像对不上。原因用了不同步的变换库。解决统一用albumentations它保证 image 和 mask 同步旋转、翻转。5.3 验证 IoU 远低于训练现象训练 loss 降但验证 IoU 上不去。原因过拟合或数据分布不一致。解决加数据增强、早停、检查训练验证集是否来自同一区域。5.4 拼接缝明显现象推理结果有网格状接缝。原因重叠区域直接平均或没有重叠。解决用高斯权重融合stride设为patch_size的 0.75 倍左右。5.5 类别不平衡导致全预测背景现象模型输出几乎全为背景。原因前景像素太少BCE 被背景主导。解决换 Dice BCE 组合损失或对前景采样加权。6. 二次开发换数据、换模型、加类别拿到这套代码后最常改的是数据接口和类别数。换数据集时把SatelliteDataset里的读取逻辑换成你的格式GeoTIFF 用rasterio读普通图片用cv2。类别数从 1 改成 N损失换成CrossEntropyLoss或Dice CE输出层num_classesN可视化按类别配色。换模型更简单在models/下加新文件实现forward返回[B, C, H, W]的 logits配置里改model.name即可。想试 DeepLabV3直接调torchvision.models.segmentation.deeplabv3_resnet50改输出层通道数。验证改造是否有效我一般固定三件事先在少量样本上过拟合确认模型能学会再跑完整验证集看 IoU 曲线最后拿几张没见过的图做滑窗推理肉眼检查边界。这三步走完基本能判断改动是正向还是翻车。有个习惯我每次都强制走一遍改完数据管道后先可视化一个 batch 的原图和掩膜确认对齐、归一化、类别映射都没问题再开训练。这个后悔药成本极低能省下几小时白跑。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。