☰
U-Net与Attention U-Net遥感水体提取毕设源码解析
2026/9/28 15:53:19 网站建设 项目流程

简介:这是一份基于Python深度学习的高分辨率城市遥感图像水体提取系统源码项目,源自评审分98.5分的大四毕业设计,适合计算机、人工智能、数据科学、通信等相关专业学生用于毕业设计、课程设计或期末大作业。项目针对城市高分影像中水体区域的自动化识别与分割问题,完整覆盖数据预处理、U-Net与Attention U-Net网络构建、训练求解与测试评估等流程;代码模块划分清晰,初学者可按脚本逐步掌握语义分割项目的常见套路,有基础者也可直接基于现有结构做二次开发。整个资源共56个文件,压缩包仅1.42MB。核心为22个py脚本,涵盖主流程、网络结构定义、数据集加载、模型训练与评估、单图测试等功能模块;另有2个pth训练权重文件、2个csv评估结果、26张png网络结构图与分割效果图,以及md/txt说明文档,目录结构紧凑,便于快速定位所需代码。源码上传前已经本地运行成功且功能测试无误,目前已有196人学习下载,作为毕设参考或深度学习实战练习具有较高学习价值。

1. 城市遥感水体提取:为什么这个毕设源码值得你跑一遍

遥感图像水体提取在环保监测、城市规划和灾害评估里是非常基础的需求,但高分辨率城市影像的挑战在于建筑物阴影、道路反光、植被遮挡这些干扰因素,传统阈值分割和光谱指数方法很难把这些和水体干净地分开。基于python深度学习的方案可以直接学习水体的空间分布特征,泛化能力比传统方法强很多。这个项目是一个完整可运行的毕业设计源码,网络部分包含U-Net和Attention U-Net,数据预处理、训练、验证和测试流程都封装好了,适合做毕设和课程设计的计算机相关专业学生,也适合想了解遥感语义分割完整流程的从业者。我拿到源码后实际跑通了一遍,整个流程对新手比较友好,把数据准备好就能开始训练和测试。

2. 网络选型与代码结构:U-Net和Attention U-Net的选择逻辑

2.1 两个网络模型的核心差异

network.py文件里实现了两个经典语义分割网络,一个是标准U-Net,另一个是带注意力机制的Attention U-Net。U-Net是编码-解码结构,编码器逐步缩小特征图尺寸同时增加通道数,解码器逐步恢复分辨率,跳跃连接把浅层细节特征传给深层,让输出结果保留边缘信息。在高分辨率遥感图像里,建筑物阴影和水体边界常常连在一起,U-Net能通过跳跃连接保住局部细节,这是它能胜任城市遥感水体提取的基础。

Attention U-Net在跳跃连接上加了注意力门控,能学习哪些特征更重要。默认的注意力模块会计算一个门控系数,对编码器特征进行加权,保留水体相关区域的特征,抑制阴影和道路的反响特征。在内存受限情况下,Attention U-Net比普通U-Net稍微重一点,但分割精度通常会更好,水体边缘会更加连续。

network.py中的U-Net代码核心部分如下:

class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels): super().__init__() self.double_conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), ) def forward(self, x): return self.double_conv(x)

这里使用两次3x3卷积加BatchNorm加ReLU组合,是U-Net的基本卷积块。padding设为1保持特征图尺寸不变,BatchNorm能加快收敛并减少不同批次数据的分布差异。读代码时如果发现BatchNorm和ReLU的位置有不同版本,输出的性能会有细微差异,我一般习惯保持源码默认设置,不做无谓改动。

2.2 从dataset.py到data_loader.py:数据管线的组装方式

dataset.py和data_loader.py是数据管线的关键部分。dataset.py主要是定义了Dataset类,用于读取原始遥感影像和对应的标注掩码。遥感图像一般是RGB三波段的高分辨率tif或jpg图片,标注图是单通道的png二值图,水体像素为255,背景为0。data_loader.py则通过DataLoader把这些样本包成多个批次,并支持随机打乱、多线程预加载和归一化。

dataset.py中一个可以学习的核心预处理逻辑:

# 数据增广策略:只做几何变换,不做颜色扭曲 if self.transform: img = Image.fromarray(img) mask = Image.fromarray(mask) if random.random() > 0.5: img = img.transpose(Image.FLIP_LEFT_RIGHT) mask = mask.transpose(Image.FLIP_LEFT_RIGHT) # 其他随机裁剪、旋转操作类似

这里的关键是没有对图像做颜色抖动,因为遥感水体提取任务中颜色和光谱特征极其重要,颜色增强会误导模型学错误特征。训练时可用随机翻转、随机旋转、随机裁剪,让模型适应不同方向的纹理特征,但不要动颜色通道。显存不足时,常见做法是在Dataset里直接输出原始尺寸,在batch层面做小尺寸随机裁剪,这比缩放到固定尺寸的效果要好。

3. 数据准备:标注格式与预处理脚本的实际用法

3.1 数据集目录组织与训练样本格式

压缩包里的data_set目录划分了train、valid、test三个子集,每个子集下面有image和annotation两个目录。train下面放的是用于训练的高分辨率城市遥感图像和对应的分割掩码,valid用于训练中验证模型泛化能力,test用于最终效果测试。Urben_pre目录存放经过预处理的增强图片和掩码,可以直接作为补充训练数据。

要跑通这个项目,数据目录必须和代码里data_loader.py预设的相对路径保持一致,否则第一次启动大概率会报文件不存在。我处理这类项目的一般习惯是:先把一个训练样本的图像和标注图可视化看一下,确认前景背景分配合理,再开始训练。

3.2 enhance_image.py和data_preprocess.py的主要功能

enhance_image.py实现了简单的图像增强,例如对比度拉伸和锐化,这能改善薄雾覆盖的城市影像质量。data_preprocess.py实现了把原始大图裁剪成训练用小块的过程,因为高分辨率遥感图通常很大,直接输入显卡容易显存溢出。这个脚本常见做法是在原图上滑动窗口裁剪,同时同步裁剪对应的标注掩码,并检查裁剪区域是否包含足够比例的水体像素,如果目标过少则丢弃,避免过多无效训练样本。

data_preprocess.py中的关键逻辑如下:

# 滑动窗口裁剪原图和标注图,保持两者对齐 for y in range(0, height - crop_size + 1, stride): for x in range(0, width - crop_size + 1, stride): img_crop = img[y:y+crop_size, x:x+crop_size] lbl_crop = label[y:y+crop_size, x:x+crop_size] # 筛选:如果该块水体像素占比低于阈值,跳过 if np.sum(lbl_crop > 0) / (crop_size * crop_size) < ratio_th: continue np.save(...)

这个脚本的可改参数包括crop_size(裁剪尺寸)、stride(滑动步长)和ratio_th(最低正样本占比)。crop_size一般取256或512,stride小于crop_size时会产生重叠区域,增加样本量但也会引入重复数据,所以要控制重叠比例。ratio_th设太低会塞进大量背景块,设太高容易丢弃许多有用的复杂场景样本,我实测0.05到0.1是比较合理的区间。

附带的说明.txt会告诉你虚拟环境需要的依赖版本,例如pytorch、torchvision、numpy、pillow等。如果本地显卡显存有限,建议在训练配置中关闭数据增强里的大尺寸随机裁剪,或者把输入尺寸改成256,待验证通过后再换回512来提升精度。

4. 训练与验证:从main.py到evaluation.py的完整流程

4.1 main.py和solver.py的工作流

main.py是程序入口,负责读取配置、创建数据集和实例化solver。打开main.py可以看到它加载了train和valid路径,然后创建solver对象并调用training方法。整个训练过程的细节都封装在solver.py中,优化器一般选择Adam,初始学习率通常在1e-4左右,并配合StepLR或CosineAnnealingLR学习率衰减。以下是一个比较典型的Adam配合StepLR的实现逻辑:

# solver.py 中的优化器构造部分 optimizer = torch.optim.Adam(model.parameters(), lr=init_lr, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=epochs // 3, gamma=0.5)

weight_decay设为1e-4有助于抑制过拟合,StepLR每过epochs/3轮迭代把学习率减半,让训练后期更稳。第一次跑时可以把epochs设置小一些,比如20,验证整体流程能跑通再加量。

4.2 模型评估:从测试脚本到精度指标

test目录中的test_one_data.py用于对单张图片进行推理,会加载训练好的train.pth权重文件,输出预测的掩码。evaluation.py负责计算训练结果在验证集或测试集上的指标,核心指标一般是IoU(交并比)和F1-Score,而像素精度反而是次要的。因为在水体提取任务里,类别不平衡非常严重,背景像素可能占90%以上,即使全预测成背景,像素精度也可能很高,所以主要关注IoU。

evaluation.py中典型的指标计算代码:

# 计算IoU:intersection / union intersection = np.logical_and(pred_mask, true_mask).sum() union = np.logical_or(pred_mask, true_mask).sum() iou = intersection / (union + 1e-6)

输出结果会写入result目录下的res.csv,记录每个epoch或每个测试样本的IoU、F1、像素精度等数值,方便训练中比较模型变化。我实际操作时会每个epoch末尾记录一次验证集的IoU,并把表现最好的模型另存为best_model.pth,这样即使训练后半段出现过拟合,也依然有可用权重。

如果自己测试数据,最好先用test_one_data.py跑两三张图,肉眼对比预测遮罩和标注图的差别。如果预测结果中有大量细小碎块,通常是后处理去噪不够,可以通过形态学开闭运算清理小于特定像素数的连通域。

5. 避坑指南:复现这个项目时最容易踩的5个坑

5.1 坑一:路径配置不对,DataLoader直接报文件不存在

现象:运行main.py后立即提示FileNotFoundError,路径拼写错误或者把数据文件夹放在别的目录下。

原因:data_loader.py里默认按项目相对路径找图像和标注,比如data_set/train/image这种结构。如果你把数据集单独放到桌面,或者自己重命名了文件夹,代码就找不到了。

解决:先把压缩包解压后保持一级目录结构不变,再打开data_loader.py,检查里面的绝对路径变量,改成自己机器上真实数据所在的位置。以后每次换机器运行项目,第一件事就是检查这堆路径变量。

5.2 坑二:显存不足导致训练OOM中断

现象:一张NVIDIA 1060 6G显卡,batch_size设8,训练刚开始就报CUDA out of memory,训练中断。

原因:默认batch_size可能是按更高配置显卡设定的,而且训练尺寸为512或更大,显存不够用。

解决:把batch_size调成2或4,同时把输入图像尺寸改到256。一般做法是在solver.py里找到batch_size变量,再找到transform中的尺寸设置。这样训练能跑通,模型的精度也会略有下降,但至少流程完整,换到更好显卡或调大batch_size后再提升精度。

5.3 坑三:train.pth与其他代码版本不匹配

现象:直接拿train.pth去predict.py或其他测试脚本跑,结果网络层名称对应不上,加载权重时报错。

原因:压缩包里的train.pth是配合项目内main.py和network.py训练出来的,权重名称与配置文件是严格绑定的。你自己改了模型结构或换了版本后,再加载这个权重就不匹配。

解决:第一次运行时不要乱改network.py的结构。如果只是测试功能,直接用原始代码加载train.pth即可。需要二次开发时,先基于原模型预训练权重进行微调训练,或用pytorch的load_state_dict(..., strict=False)来忽略不匹配层。

5.4 坑四:训练集和验证集划分不当,导致准确率虚高或偏低

现象:验证集和训练集来自同一张遥感大图的相邻裁剪区域,验证时看起来高达95%以上,但一放到新城市图上效果一般,水体边缘失真。

原因:高分辨率遥感影像中相邻裁剪块的相关性很强,如果预处理脚本滑窗裁完随机分到训练集和验证集,等同于“背答案”,模型并没有真正泛化到新场景。

解决:正确做法是先按地理位置或图像文件划分,而不是按裁剪块划分。把一张完整大图的一部分区域用于训练,另一部分完全不参与训练的区域放在验证集中,这样才能检验模型的学习能力。

5.5 坑五:结果图水体边缘锯齿感强烈,碎块多

现象:模型提取出的水体分布整体正确,但边缘凹凸不平,大量零散小色块被识别成水体。

原因:模型对每个像素独立预测,没有考虑上下文一致性,尤其是高分影像中道路表面的反光也容易造成小块误检。

解决:在预测环节加一个后处理步骤,用opencv做中值滤波或形态学开运算去掉小块噪点,再用闭运算填补空隙。这类后处理一般能让IoU提升0.5到1个百分点,效果立竿见影。

6. 进阶用法:修改训练配置、批量推理与结果可视化

验证完整个流程后,可以把这套基础版本往自己的实际需求上改。最值得改的三个方向是调整模型输入大小、修改类别数量和增加模型集成。项目本身是二分类任务,如果你想把任务改成水体、道路、植被、建筑四类地物提取,需要把标注图改成多通道one-hot编码,对应修改dataset.py和evaluation.py里的类别数量,同时把网络输出通道数从2改成4,再重新训练。

批量推理时,常见做法是写一个循环脚本扫描整个测试集文件夹,逐张图读取、预处理、推理、保存结果,同时把所有预测值汇总到一张大图上进行可视化对比。我可以给出一个典型批量推理的伪代码结构:

# 批量测试脚本:循环遍历test图片 for img_path in test_img_list: img = read_and_normalize(img_path) with torch.no_grad(): pred = model(img.unsqueeze(0).cuda()) pred_mask = pred.argmax(dim=1).squeeze().cpu().numpy() cv2.imwrite(os.path.join(save_dir, img_path.name), pred_mask * 255)

其中argmax取通道维度上最大值对应的类别,得到二值掩码,再映射到0和255的像素值保存。注意保存前一定要确认原始影像和掩码的投影坐标系、地理位置是严格对齐的,否则后面叠加分析会错位。

另一个可以提升精度的手段是TTA(测试时增强),把测试图做水平翻转、垂直翻转,分别预测后取平均。TTA在遥感分割上效果稳定,耗时会增加3到4倍,但能明显改善边界连续性,我一般在验收前跑一次。结果可视化时,推荐把原图、预测掩码、真实标注三者拼接成对比图,便于检查细节差异。如果发现某类场景反复出现误检,就针对这类场景补充样本或调整loss权重。

从那以后我每次部署模型前,都会强制走一遍“检查路径 → 观察数据 → 小批量训练 → 验证集评估 → 测试集后处理”这个流程,实际能减少大量低级错误。希望这份拆解能帮你在自己的毕设或实际项目中少走弯路。

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

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

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

立即咨询