简介这份资源面向具备一定Python与遥感基础的开发者聚焦卫星图像分割这一遥感图像处理核心任务帮助读者理解从预处理、特征提取到分割算法与后处理的完整流程并借助Python生态完成落地实践。压缩包共3个文件均为py脚本整体约3KB体量轻便涵盖主流程入口、屋顶检测与数据准备等模块便于快速阅读与二次修改。已有253人学习下载具备一定参考热度。内容围绕GDAL/OGR读写遥感影像、OpenCV图像增强与边缘检测、Scikit-image多种分割算法以及TensorFlow、PyTorch构建U-Net等深度学习模型展开可帮助读者掌握阈值分割、区域生长、聚类与CNN端到端分割的实现思路并了解其在土地覆盖变化评估、灾害监测与农业估产等场景中的应用适合作为入门与实验参考。1. 卫星图像分割从一张遥感大图到可用的地物掩膜第一次拿到卫星影像做分割任务时我盯着那张 8000×8000 像素的 GeoTIFF 愣了半天——显存装不下直接 resize 又会把道路、建筑边缘糊成一团。卫星图像分割和常规的语义分割 Demo 完全是两回事单张图动辄上亿像素、类别极度不均衡水体占大半小目标建筑零星分布、标注成本高得离谱。这个项目要解决的就是把大幅遥感影像切成可训练的瓦片、跑通分割模型、再把预测结果拼回原图这套完整链路。适合谁手里有遥感数据想做地物提取的算法同学以及想把 U-Net 那套东西真正落到工程上的 Python 开发者。下面按「能干什么 → 怎么跑 → 怎么改」的顺序拆。2. 项目定位与技术选型为什么是 Python 瓦片化管线2.1 这个项目到底在做什么一句话概括输入一张带地理坐标的卫星影像输出同尺寸的类别掩膜水体、植被、建筑、道路、裸地这类。听起来和普通语义分割一样但工程上的差异全在「大幅」两个字上。常规数据集是 512×512 的小图直接喂给网络就行卫星影像单景覆盖几十平方公里像素量是常规图的几百倍必须做瓦片化tiling切分训练时按瓦片采样推理时滑窗预测再拼接。项目的核心链路我拆成四段影像读取与预处理含坐标参考系处理、瓦片切分与数据集构建、分割模型训练、滑窗推理与结果拼接。每一段都有独立的坑后面逐段讲。技术栈上Python 是绝对主力影像 IO 用 rasterio比 GDAL 的 Python 绑定好用能直接读地理变换参数数组运算和切片靠 numpy模型部分用 PyTorch数据增强用 albumentations评估指标用自定义的 IoU/mIoU 实现。这套组合是遥感分割场景里最常见的做法没有花哨的依赖装起来不折腾。为什么不用 OpenCV 读图因为 cv2.imread 会丢掉地理坐标信息而拼接预测结果时需要原始影像的 transform仿射变换参数才能把瓦片坐标映射回原图坐标。rasterio 读进来能同时拿到数组和 profile这是后续拼接的关键。我一般会在项目入口就把 profile 存下来后面所有坐标换算都靠它。2.2 目录结构长什么样一个能跑的分割项目目录大致是这样组织的不同实现细节会有出入但骨架大同小异satellite-seg/ ├── configs/ │ └── train.yaml # 数据路径、瓦片大小、超参 ├── data/ │ ├── raw/ # 原始影像与标注 │ └── tiles/ # 切好的瓦片缓存 ├── src/ │ ├── dataset.py # 瓦片数据集与增强 │ ├── tiling.py # 切分与拼接 │ ├── model.py # 分割网络定义 │ ├── train.py # 训练入口 │ ├── infer.py # 滑窗推理 │ └── metrics.py # IoU 等指标 ├── scripts/ │ ├── prepare_tiles.py # 预处理脚本 │ └── evaluate.py # 评估脚本 └── requirements.txt这个结构的好处是「切分/拼接」和「模型」解耦——换模型不用动 tiling 逻辑换数据源不用动训练代码。我见过不少项目把切图逻辑塞进 Dataset 的__getitem__里训练时每次采样都重新切一遍IO 直接成为瓶颈血泪经验切分一定要做成离线预处理落盘缓存。2.3 依赖与运行环境依赖清单不长但版本要对齐尤其是 rasterio 和 GDAL 的底层绑定装错版本会报一堆找不到 proj 库的错。常见做法是先用 conda 装 rasterio它会顺带把 GDAL 的二进制依赖处理好再 pip 装其余包conda create -n satseg python3.10 -y conda activate satseg conda install -c conda-forge rasterio -y pip install torch torchvision numpy albumentations pyyaml tqdm参数说明python3.10是稳妥选择太新的版本某些科学计算包还没轮子-c conda-forge指定频道rasterio 的预编译包在这里最全。装完跑一句python -c import rasterio; print(rasterio.__version__)验证能打印版本号就说明 GDAL 链路通了。如果这一步就报错八成是系统里残留了旧版 GDAL清掉重装比修依赖快。3. 核心模块拆解瓦片切分、数据集与滑窗推理3.1 瓦片切分带重叠的滑窗是标配切分是整个管线的地基。最朴素的做法是按固定大小无重叠切但这样会在瓦片边界产生「割裂」——一个建筑正好跨在两块瓦片中间两块各看到一半模型学到的都是残缺目标。所以工程上几乎都用带重叠overlap的切分训练时重叠比例可以小一点比如 25%推理时重叠要大50%给拼接留足冗余。import numpy as np import rasterio from rasterio.windows import Window def tile_image(src_path, tile_size512, overlap128, out_dirdata/tiles): 把大幅影像切成带重叠的瓦片并落盘 with rasterio.open(src_path) as src: H, W src.height, src.width stride tile_size - overlap # 步长 瓦片边长 - 重叠 idx 0 for row in range(0, H, stride): for col in range(0, W, stride): # 边界瓦片向内收避免出现不足 tile_size 的碎块 r min(row, H - tile_size) c min(col, W - tile_size) window Window(c, r, tile_size, tile_size) data src.read(windowwindow) # (C, tile, tile) profile src.profile.copy() profile.update(widthtile_size, heighttile_size, transformsrc.window_transform(window)) with rasterio.open(f{out_dir}/tile_{idx:05d}.tif, w, **profile) as dst: dst.write(data) idx 1 return idx逻辑说明stride控制相邻瓦片的位移overlap 越大瓦片数越多、冗余越高。min(row, H - tile_size)这行是处理右边缘和下边缘的关键——如果直接按 stride 走最后一块可能只剩几十像素网络输入尺寸对不上。向内收的代价是边缘区域被重复覆盖但换来的是所有瓦片尺寸统一训练时不用做 padding 分支。window_transform把瓦片在原图中的位置写进它自己的 transform后面拼接时靠这个反推坐标。参数上tile_size 一般取 512 或 1024取决于显存overlap 训练用 64~128推理用 256 更稳。3.2 数据集与增强类别不均衡是第一道坎瓦片切好后Dataset 的职责就是读瓦片、配对掩膜、做增强。这里最大的问题是类别不均衡——水体可能占 60%建筑只占 3%。直接训练模型会倾向于全预测成水体IoU 看着还行但小目标全丢。常见做法有两层数据层面用加权采样让含稀有类别的瓦片被采到的概率更高损失层面用 Dice Loss 或 Focal Loss 替代纯交叉熵。import torch from torch.utils.data import Dataset import albumentations as A class TileDataset(Dataset): def __init__(self, img_paths, mask_paths, size512, trainTrue): self.img_paths img_paths self.mask_paths mask_paths self.train train self.aug A.Compose([ A.RandomRotate90(), A.HorizontalFlip(), A.VerticalFlip(), A.RandomBrightnessContrast(p0.3), ]) if train else None def __len__(self): return len(self.img_paths) def __getitem__(self, i): img read_tif(self.img_paths[i]) # (H, W, C) float32, 归一化到 0~1 mask read_tif(self.mask_paths[i]) # (H, W) int64 if self.aug: out self.aug(imageimg, maskmask) img, mask out[image], out[mask] img torch.from_numpy(img).permute(2, 0, 1).float() mask torch.from_numpy(mask).long() return img, mask逻辑说明增强只做几何变换和轻度色彩扰动不做 Cutout 之类会破坏地物连续性的操作——遥感图像里地物是成片分布的随机遮挡会让模型学到错误的空间关系。permute(2,0,1)把 HWC 转成 CHW这是 PyTorch 卷积层的输入约定。掩膜保持 long 类型因为交叉熵损失要求类别索引是整型。加权采样器我一般单独写一个WeightedRandomSampler权重按每块瓦片里稀有类像素占比算占比越高权重越大这样小目标瓦片不会被淹没。3.3 模型与训练循环U-Net 骨架够用别急着上大模型分割网络选型上U-Net 及其变体ResNet 编码器 解码器在遥感场景依然是性价比最高的选择。卫星图像的分辨率有限地物边界不像医学影像那么精细上太重的 backbone 收益递减还拖慢推理。项目里模型定义大致是编码器逐级下采样提特征、解码器逐级上采样恢复分辨率、跳跃连接把浅层细节补回来。import torch.nn as nn class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.block nn.Sequential( nn.Conv2d(in_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): return self.block(x) class UNet(nn.Module): def __init__(self, in_ch4, num_classes5): super().__init__() # in_ch4 对应 RGB 近红外遥感常用四波段 self.enc1 DoubleConv(in_ch, 64) self.enc2 DoubleConv(64, 128) self.pool nn.MaxPool2d(2) self.up nn.Upsample(scale_factor2, modebilinear, align_cornersFalse) self.dec2 DoubleConv(128 64, 64) self.head nn.Conv2d(64, num_classes, 1) def forward(self, x): e1 self.enc1(x) e2 self.enc2(self.pool(e1)) d2 self.dec2(torch.cat([self.up(e2), e1], dim1)) return self.head(d2)逻辑说明in_ch4是遥感场景的常见设定多一个近红外波段对植被和水体区分帮助很大如果你的数据只有 RGB改成 3 即可。torch.cat([up(e2), e1], dim1)就是跳跃连接把编码器浅层的高分辨率特征和解码器的语义特征拼起来这是 U-Net 能保住边界细节的核心。head用 1×1 卷积把通道数压到类别数输出每个像素的类别 logits。训练循环里损失用CrossEntropyLoss(weightclass_weights)配合 Dice Loss 加权求和优化器 AdamW学习率 1e-4 起步余弦退火。这些超参不是玄学遥感分割任务里这套配置基本能收敛。3.4 滑窗推理与拼接把瓦片预测缝回原图推理阶段最容易翻车的地方在拼接。每块瓦片的预测结果在重叠区域会有不一致直接覆盖会留下明显的接缝。标准做法是加权平均重叠区域按到瓦片中心的距离给权重中心权重高、边缘权重低这样接缝处过渡自然。import numpy as np def stitch_predictions(tiles, positions, full_shape, num_classes): tiles: 每块瓦片的概率图 (C, t, t)positions: 每块的 (row, col) prob np.zeros((num_classes, *full_shape), dtypenp.float32) weight np.zeros(full_shape, dtypenp.float32) t tiles[0].shape[-1] # 高斯权重中心高边缘低 g np.outer(np.hanning(t), np.hanning(t)).astype(np.float32) for tile, (r, c) in zip(tiles, positions): prob[:, r:rt, c:ct] tile * g weight[r:rt, c:ct] g weight[weight 0] 1e-6 return prob / weight # 归一化后取 argmax 即得掩膜逻辑说明np.hanning生成一维汉宁窗外积得到二维权重核中心接近 1、边缘趋近 0。累加时概率图和权重图同步叠加最后逐像素除以权重完成归一化。weight 0的保护是防止原图某些角落没被任何瓦片覆盖理论上不会但边界处理有 bug 时会遇到。拿到prob后argmax(axis0)就是最终掩膜再按原始 profile 写回 GeoTIFF地理坐标就对齐了。4. 避坑与排查那些让我加班到凌晨的问题4.1 显存爆了但 batch_size 已经设成 1现象训练第一个 iter 就 OOMnvidia-smi 显示显存瞬间打满。原因通常不是 batch 太大而是输入尺寸或通道数超预期——比如误把 1024 的瓦片喂给了按 512 设计的模型或者数据里混进了 8 波段影像而模型只声明了 4 通道。解决在 Dataset 里加一句断言assert img.shape (512, 512, 4)让错误在数据层就暴露而不是等到卷积层报一个看不懂的 shape mismatch。另外用torch.cuda.empty_cache()清理缓存只能缓解治标不治本。4.2 训练 loss 正常但验证 IoU 极低现象训练集 loss 一路下降验证集 mIoU 卡在 0.1 上不去。原因八成是掩膜的类别索引和模型输出对不上——比如标注里背景是 255、前景是 1而模型输出 0~4 的连续类别交叉熵算出来全是错的。解决预处理阶段就把掩膜重映射成 0 起始的连续整数并打印每类的像素统计确认。我一般会在训练前跑一遍np.unique(mask)看到非预期值立刻停下来查。4.3 拼接结果出现网格状接缝现象推理输出的大图上有规律的方格纹路。原因是瓦片重叠不足或拼接时直接覆盖而非加权。解决推理 overlap 提到 50%拼接用上面的汉宁窗加权。如果还有轻微接缝检查瓦片 transform 是否写对——window_transform用错会导致坐标偏移半个瓦片接缝位置就会错位。4.4 rasterio 读出来的数组和预期波段顺序不一致现象明明是真彩色影像读出来颜色发蓝或者植被显示异常。原因是部分卫星产品的波段顺序是 BGR 或带全色波段不是标准 RGB。解决读完后先查src.descriptions和src.count按产品文档确认波段顺序再重排。这个坑很隐蔽因为数组 shape 是对的只有肉眼看图才能发现。4.5 推理速度慢到无法接受现象单景影像推理要几十分钟。原因通常是逐瓦片串行推理且没开torch.no_grad()。解决推理包在with torch.no_grad():里batch 化瓦片输入一次喂 8~16 块并考虑半精度model.half()。注意半精度在 CPU 上不生效得确认在 GPU 上跑。5. 二次开发换数据源和换模型时我会怎么改拿到这个项目想改成自己的数据第一步永远是「先跑通再改」。用项目自带的样例数据把 prepare_tiles → train → infer 整条链路走一遍确认环境没问题再动代码。换数据源时改动集中在三处configs/train.yaml里的路径和波段数、dataset.py里的归一化参数不同卫星的辐射量化范围差异很大有的 0~255有的 0~10000、以及类别映射表。我一般会写一个inspect_data.py小脚本先统计所有影像的尺寸、波段数、像素值范围心里有数了再改配置比盲改快得多。换模型时只要保证输入输出契约不变——输入(B, C, H, W)、输出(B, num_classes, H, W)——就能直接替换model.py里的网络定义训练和推理代码一行不用动。想上 SegFormer 或 DeepLabV3把 backbone 换掉即可但要注意预训练权重的输入通道数四波段数据没法直接用三通道的 ImageNet 权重常见做法是把第一层卷积的权重在通道维度上复制或补零再微调。改造目标改动文件关键注意点换卫星数据源configs、dataset.py波段顺序、量化范围、坐标系换分割模型model.py输入输出 shape 契约不变加新类别标注重映射、损失权重类别索引连续、权重重算提升小目标召回采样器、损失函数加权采样 Dice/Focal验证改造是否成功我有个笨但有效的习惯拿一张小尺寸的图比如 1024×1024跑完整推理把预测掩膜和原图叠在一起可视化肉眼确认地物边界对得上、没有整体偏移。指标只是参考可视化才是照妖镜。从那以后我每次改完数据管线都强制先跑这张小图的可视化确认没问题再上全量训练——这个习惯帮我省下了无数次「训练一天才发现坐标错位」的后悔药。希望帮到你。本文还有配套的精品资源点击获取