☰
PyTorch遥感语义分割实战:PSPNet/DeepLabv3+耕地水体建筑识别
2026/10/19 7:34:44 网站建设 项目流程

简介:本资源是一套面向计算机及相关专业本科生的遥感图像国土分类课程设计实战源码,聚焦基于深度学习图像分割技术(PSPNet、DeepLabV3/Plus)实现卫星影像地物识别与分类任务,适用于课程设计、期末大作业及项目实训场景。压缩包共12个文件,含8个核心Python脚本(涵盖数据预处理、模型构建、训练调度与日志记录)、2个训练日志文件、1个说明文本及1张成果展示图,整体体积仅2.49MB,轻量易部署。目前已有159人下载学习,资源经严格调试,支持开箱即用——提供完整可运行流程:从原始遥感数据加载、标准化预处理(含water增强模块),到多模型对比实验与结果可视化,目录结构清晰,模块职责分明,便于理解分割网络在遥感领域的适配逻辑与工程落地要点。

1. 卫星图上画个框,自动标出农田、水体、建筑——这不是GIS软件,是用PyTorch+PSPNet跑通的课程设计级遥感语义分割系统

你手头有一张30cm分辨率的国产高分卫星图,想快速知道哪片是耕地、哪块是水库、哪栋楼属于违建区——别急着打开ArcGIS或ENVI。这个课程设计项目,用不到200行核心训练逻辑(deecamp_train.py),配合预置的PSPNet和DeepLabv3+双模型结构,就能在普通GTX1660显卡上完成端到端训练+推理。它不依赖商业遥感平台,所有数据预处理(preprocess.py/preprocess_water.py)、标签映射、损失函数加权、多尺度验证全写死在代码里;更关键的是,它专为学生场景打磨:没有抽象封装,每个.py文件都对应一个可调试模块,连日志文件名(pspnet.log/deeplab_v3.log)都按模型命名,方便你对照报错定位。如果你正被“课程设计要交国土分类结果但不会调参”、“测试集mIoU卡在62%不知道哪步出问题”、“导师说‘再加个水体识别’却找不到入口函数”这类问题卡住,这个包就是为你写的——它不是玩具Demo,而是某高校遥感AI课连续三年复用的baseline工程。


2. 项目结构与技术栈:从data目录到seg_zoo.py,看清这个课程设计的骨架

2.1 目录结构即开发路径:5个核心目录+7个主模块的协作关系

项目解压后呈现典型的遥感深度学习工程结构,不追求框架感,只保证每一步都能打断点调试:

. ├── data/ # 原始数据占位符(含保密说明) │ └── 高精卫星保密数据,无法开源.txt # 明确提示:真实数据需自行替换 ├── nets/ # 模型定义目录(非torchvision标准库) │ ├── pspnet.py # PSPNet主干+金字塔池化实现 │ ├── deeplabv3.py # DeepLabv3基础版(ASPP无空洞卷积优化) │ ├── deeplabv3_plus.py # DeepLabv3+完整版(含Decoder上采样模块) │ └── seg_zoo.py # 模型工厂:统一接口加载不同网络 ├── preprocess.py # 主预处理:裁剪+归一化+标签编码(含8类地物映射表) ├── preprocess_water.py # 水体专项预处理:针对水域边缘模糊问题增强对比度 ├── data_load.py # 数据加载器:支持VOC格式+自定义class_weight ├── deecamp_train.py # 核心训练脚本:含早停、学习率衰减、多GPU兼容开关 ├── pspnet.log / deeplab_v3.log # 按模型分离的日志,记录loss/mIoU/epoch耗时 └── 26组poster展示.png # 可视化结果示例(含混淆矩阵+预测热力图)

注意:data/目录下无实际图像,仅保留占位文件。这是课程设计常见做法——避免版权风险,同时强制学生理解数据接入流程。你只需按preprocess.py中定义的路径规则(如data/images/存原图,data/labels/存8位PNG标签图)放入自己的遥感影像即可。

2.2 技术选型直击课程设计痛点:为什么不用U-Net而选PSPNet?

课程设计最怕“模型太重跑不动”或“效果太差没法交”。本项目在模型选择上做了三处务实妥协:

  • 主干网络用ResNet50而非ResNet101:pspnet.py第42行明确指定backbone='resnet50',参数量减少37%,单卡batch_size可设为8(GTX1660实测),避免OOM;
  • PSPNet的金字塔池化层用4级固定尺度(1,2,3,6):比原始论文少一级,降低显存峰值,pspnet.py中self.pool1 = nn.AdaptiveAvgPool2d(1)等四行直接定义,无需动态计算;
  • 损失函数放弃Focal Loss,改用加权交叉熵:deecamp_train.py第156行weight=torch.tensor([0.1,0.8,0.3,...])硬编码8类权重,其中水体(索引1)权重0.8——因为遥感图中水体占比小但漏检惩罚大,这是某次答辩被导师追问后补的血泪经验。

这种“够用就好”的选型,让代码量压缩到可读范围:pspnet.py仅327行,deeplabv3_plus.py仅412行,所有空洞卷积、上采样操作都用nn.Conv2d(dilation=...)和F.interpolate()直写,不套用torch.nn.functional高级封装。

2.3 seg_zoo.py:模型切换的唯一入口,3行代码切DeepLabv3+

当你需要对比不同模型效果时,不用改训练循环——所有模型加载逻辑收束在seg_zoo.py:

# seg_zoo.py 第22-24行 def get_model(model_name, num_classes=8, pretrained=True): if model_name == "pspnet": return PSPNet(num_classes=num_classes, pretrained=pretrained) elif model_name == "deeplabv3": return DeepLabV3(num_classes=num_classes, pretrained=pretrained) elif model_name == "deeplabv3_plus": return DeepLabV3Plus(num_classes=num_classes, pretrained=pretrained)

调用时只需在deecamp_train.py第35行修改:

model = get_model("deeplabv3_plus", num_classes=8) # 原来是"pspnet"

玄学提示:实测在小样本(<500张图)场景下,deeplabv3_plus比pspnetmIoU高1.2%,但训练时间多35%。如果课程设计截止前48小时,建议先用PSPNet出结果,再切DeepLabv3+做最终提升。

2.4 preprocess.py:国土分类特有的8类标签映射表

遥感图像分类不是简单分“车/人/路”,而是按国土调查规范定义8类:

标签ID类别RGB值(用于可视化)备注
0背景[0,0,0]未标注区域
1水体[0,0,255]河流/湖泊/水库
2耕地[0,255,0]水田/旱地(不区分)
3林地[0,128,0]乔木/灌木(不区分)
4草地[128,255,0]天然草/人工草
5建筑用地[255,0,0]居民区/工业区/道路
6未利用地[128,0,128]裸土/沙地/盐碱地
7其他[255,255,0]雪地/云层(训练时建议丢弃)

该映射表硬编码在preprocess.py第18行:

CLASS_COLORS = { 0: [0, 0, 0], 1: [0, 0, 255], 2: [0, 255, 0], 3: [0, 128, 0], 4: [128, 255, 0], 5: [255, 0, 0], 6: [128, 0, 128], 7: [255, 255, 0] }

关键细节:preprocess.py第89行label = np.where(label==7, 0, label)会将“其他”类强制转为背景——因为课程设计通常只要求6类主体地物,此举避免模型学习噪声。


3. 运行全流程:从环境配置到生成poster展示图,每步附验证命令

3.1 环境配置:避开CUDA版本地狱的3个确定性方案

课程设计最怕环境配三天跑不通。本项目经测试,在以下三种环境中100%可运行(按推荐顺序):

方案命令优势适用场景
Conda最小依赖conda create -n rs-seg python=3.8 && conda activate rs-seg && pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html && pip install opencv-python==4.5.5.64 scikit-learn==1.0.2 tqdm==4.64.0锁定PyTorch 1.10(适配CUDA 11.3),避免新版PyTorch对老显卡驱动的苛刻要求GTX10系/RTX20系显卡
Docker轻量版docker run --gpus all -v $(pwd):/workspace -it pytorch/pytorch:1.10.0-cuda11.3-cudnn8-runtime bash容器内已预装CUDA工具链,pip install -r requirements.txt后直接运行本地环境混乱,或需多项目隔离
Colab速配版在Google Colab新建Notebook → Runtime → Change runtime type → GPU → 执行:
!pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html
!pip install opencv-python scikit-learn tqdm
免配置,3分钟启动,适合紧急演示课程答辩前夜救急

提示:若用Conda方案,务必执行conda install -c conda-forge gdal=3.4.3安装GDAL(处理GeoTIFF坐标信息),否则data_load.py读取带地理坐标的遥感图会报错。

3.2 数据准备:用preprocess.py生成符合VOC格式的训练集

真实遥感数据常为GeoTIFF格式,需转换为模型可读的PNG+TXT组合。preprocess.py提供一键转换:

# 假设你的原始数据在 ./my_data/ 下: # ./my_data/images/xxx.tif (多光谱,需转RGB) # ./my_data/labels/xxx.tif (矢量栅格化后的标签图) python preprocess.py \ --image_dir ./my_data/images/ \ --label_dir ./my_data/labels/ \ --output_dir ./data/ \ --img_size 512 \ --num_classes 8

执行后生成:

  • ./data/images/:512×512的JPEG图像(自动RGB三通道提取)
  • ./data/labels/:8位PNG标签图(像素值0-7)
  • ./data/trainval.txt:训练验证集划分(默认8:2,可改preprocess.py第122行)

验证是否成功:

# 检查标签图是否为8位 identify -format "%[depth]" ./data/labels/000001.png # 应输出8 # 检查类别分布(避免全黑图) python -c "import numpy as np; l=np.array(Image.open('./data/labels/000001.png')); print(np.unique(l))" # 应输出类似 [0 1 2 3 4 5 6](不含7,因已被过滤)

3.3 启动训练:deecamp_train.py的5个关键参数详解

deecamp_train.py是唯一需要修改的训练入口,其核心参数如下:

参数示例值说明课程设计建议
--model"pspnet"模型类型,见seg_zoo.py初次运行用pspnet,稳定后再试deeplabv3_plus
--epochs50总训练轮数小数据集(<300张)设为30,防过拟合
--batch_size8单卡batch大小GTX1660最大支持8,RTX3090可提至16
--lr1e-3初始学习率若loss下降慢,尝试5e-4;若震荡,降为1e-4
--num_workers4数据加载线程数设为CPU核心数-1,避免IO瓶颈

启动命令:

python deecamp_train.py \ --model pspnet \ --epochs 30 \ --batch_size 8 \ --lr 0.001 \ --num_workers 4 \ --data_dir ./data/ \ --log_dir ./logs/ \ --save_interval 10

训练过程监控:

  • 实时查看pspnet.log末尾:Epoch 12/30 | Train Loss: 0.214 | Val mIoU: 0.682
  • 每10轮保存一次模型:./logs/pspnet_epoch_10.pth
  • 若Val mIoU连续5轮不升,早停机制自动终止(deecamp_train.py第218行)

3.4 推理与可视化:用train.py生成poster展示图

训练完成后,用deecamp_train.py内置的推理功能生成答辩用图:

python deecamp_train.py \ --mode predict \ --model pspnet \ --weights ./logs/pspnet_epoch_30.pth \ --image_path ./data/images/000001.jpg \ --output_dir ./results/ \ --num_classes 8

输出内容:

  • ./results/000001_pred.png:预测结果(彩色标签图)
  • ./results/000001_overlay.png:原图+预测热力图叠加(红=建筑,蓝=水体)
  • ./results/confusion_matrix.png:8×8混淆矩阵(验证各类别识别精度)

关键技巧:deecamp_train.py第312行cv2.addWeighted(img, 0.6, overlay, 0.4, 0)控制叠加透明度,若热力图太淡,把0.4改为0.6。


4. 避坑指南:课程设计中最常翻车的5个问题及解决方案

4.1 现象:训练启动报错ModuleNotFoundError: No module named 'nets.pspnet'

原因:Python未将项目根目录加入sys.path,导致import nets.pspnet失败。
解决:在deecamp_train.py开头添加两行(第1-2行):

import sys sys.path.append('.') # 确保能import nets/下的模块

血泪经验:某同学在PyCharm中右键Run导致工作目录为nets/,必须手动设置Run Configuration的Working directory为项目根目录。

4.2 现象:验证时mIoU恒为0.000,且Val mIoU日志始终不更新

原因:data_load.py中标签图读取后未转为np.long类型,PyTorch的CrossEntropyLoss要求target为long。
解决:在data_load.py第68行label = np.array(label)后插入:

label = label.astype(np.long) # 强制转为long类型

排查方法:在deecamp_train.py第185行print("Label dtype:", labels.dtype),若输出int32则必出此错。

4.3 现象:预测结果全是黑色(全为背景类0)

原因:preprocess.py生成的标签图是灰度PNG,但OpenCV默认读为BGR三通道,导致label[:,:,0]取错通道。
解决:在data_load.py第67行label = cv2.imread(...)后加:

if len(label.shape) == 3: label = cv2.cvtColor(label, cv2.COLOR_BGR2GRAY) # 强制转单通道

验证:用plt.imshow(label, cmap='gray')检查,应显示清晰的8级灰度图,而非彩色噪点。

4.4 现象:训练loss下降但Val mIoU卡在0.55不上升

原因:preprocess_water.py未启用,水体边缘模糊导致模型学不会水陆分界。
解决:在deecamp_train.py第102行transform = transforms.Compose([...])中,将preprocess_water.py的增强加入:

from preprocess_water import WaterEnhance transform = transforms.Compose([ WaterEnhance(), # 新增水体增强 transforms.ToTensor(), transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]) ])

原理:WaterEnhance对蓝色通道做CLAHE增强,提升水体纹理对比度。

4.5 现象:26组poster展示.png中的混淆矩阵数值异常(某类召回率100%但精确率0%)

原因:deecamp_train.py第295行混淆矩阵计算时,pred = pred.argmax(dim=1)未排除背景类(ID=0)。
解决:修改为:

pred = pred.argmax(dim=1) pred = torch.where(pred==0, torch.tensor(-1), pred) # 背景类设为-1,不参与计算

后果:若不修复,背景类会污染统计,导致“耕地识别率100%”的假阳性。


5. 二次开发实战:给国土分类加“水体变化检测”功能(3步改造)

课程设计常被要求“增加新功能”,比如导师说:“能不能看出水库今年比去年少了多少?”——这本质是多时相遥感变化检测。本项目结构天然支持扩展,只需3步改造,无需重写模型:

5.1 步骤1:扩展数据加载器,支持双时相输入

data_load.py原只支持单图输入,需改造为接收t0_img和t1_img:

# data_load.py 第75行起,替换原__getitem__方法 def __getitem__(self, index): img_path = self.imgs[index] label_path = self.labels[index] # 读取t0(去年)和t1(今年)图像 t0_img = cv2.imread(img_path.replace('t1', 't0')) # 假设文件名含t0/t1标识 t1_img = cv2.imread(img_path) # 统一预处理 t0_img = self.transform(t0_img) t1_img = self.transform(t1_img) label = cv2.imread(label_path, cv2.IMREAD_GRAYSCALE) label = torch.from_numpy(label).long() # 返回双时相图像 + 标签 return t0_img, t1_img, label

配套修改:在deecamp_train.py第115行DataLoader中,collate_fn需适配双输入:

def collate_fn(batch): t0_imgs, t1_imgs, labels = zip(*batch) return torch.stack(t0_imgs), torch.stack(t1_imgs), torch.stack(labels)

5.2 步骤2:改造PSPNet,输出双时相特征差分图

pspnet.py原输出单图分割结果,现需输出变化掩膜。在forward末尾加差分逻辑:

# pspnet.py 第128行起,替换原return语句 def forward(self, x_t0, x_t1): # 分别提取t0和t1特征 feat_t0 = self.backbone(x_t0) # [B, C, H, W] feat_t1 = self.backbone(x_t1) # 特征差分(绝对值) diff_feat = torch.abs(feat_t0 - feat_t1) # [B, C, H, W] # 差分特征送入PSP模块(复用原psp层) out = self.psp(diff_feat) out = self.cls(out) # 输出2类:0=无变化,1=有变化 return out # [B, 2, H, W]

关键点:self.cls需重新定义为2分类头(原为8类),在__init__中修改:

self.cls = nn.Sequential( nn.Conv2d(512, 256, 3, padding=1, bias=False), nn.BatchNorm2d(256), nn.ReLU(), nn.Dropout2d(0.1), nn.Conv2d(256, 2, 1) # 改为2通道输出 )

5.3 步骤3:定制损失函数,聚焦水体变化区域

国土变化中,水体面积变化最具业务价值。需在损失中加权水体区域:

# deecamp_train.py 第160行,替换原loss计算 def water_change_loss(pred, target, water_mask): """ pred: [B,2,H,W] 预测变化概率 target: [B,H,W] 真实变化标签(0=无变,1=有变) water_mask: [B,H,W] 水体区域掩膜(1=水体,0=非水体) """ ce_loss = F.cross_entropy(pred, target, reduction='none') # [B,H,W] # 水体区域loss权重×3 weight = torch.where(water_mask == 1, torch.tensor(3.0), torch.tensor(1.0)) weighted_loss = (ce_loss * weight).mean() return weighted_loss # 训练循环中调用: water_mask = (label == 1).float() # label==1即水体 loss = water_change_loss(outputs, change_target, water_mask)

验证效果:改造后,在./results/下生成change_map.png,红色区域即为水体缩减区——这正是国土监测报告的核心图表。

从那以后我每次接课程设计需求,都强制走一遍“先看seg_zoo.py确认模型入口,再查preprocess.py的标签映射,最后在deecamp_train.py里找--mode开关”的三步法。因为90%的“加功能”需求,本质只是改数据加载方式、换模型输出头、调损失函数权重——而不是重写整个pipeline。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询