资讯详情

城市遥感水体提取:AttU-Net实战与三值标注优化

📅 2026/10/12 14:24:44 | 华诺云谱 👁 阅读
城市遥感水体提取:AttU-Net实战与三值标注优化
简介本资源是一套面向高校本科生的高分辨率城市遥感图像水体提取毕业设计完整实现方案聚焦城市环境监测中的关键任务——从高分遥感影像中精准识别与分割水体区域适用于毕业设计、课程设计及期末大作业等实践场景代码注释详尽、系统功能完备零基础学生亦可快速上手。压缩包共27个文件726KB含11个Python核心脚本如network.py、solver.py、data_loader.py等构成训练-验证-测试全流程、13张结果与模型结构示意图含AttU-Net、U-Net对比图、1个预训练权重pth文件、1个数据集说明csv及1份README.md文档覆盖数据预处理、模型构建、训练调优、单图推理与结果评估全环节。目前已有199人学习下载项目经严格调试可直接部署运行提供美观交互界面与结构化目录附带增强图像处理、结果可视化及指标统计功能具备实际工程参考价值。1. 高分辨率城市遥感图像的水体提取不是调个U-Net就能出结果而是得先扛住“城市水体三连击”你手头有一张0.5米分辨率的高分七号卫星图拍的是深圳前海——密密麻麻的玻璃幕墙、纵横交错的高架桥、穿插其中的狭长河涌和人工湖。这时候想用深度学习自动圈出所有水体会发现U-Net跑出来全是“毛边”小池塘被漏掉桥影被当成水绿化带反光区域疯狂误判。这不是模型不行是城市遥感水体提取本身就有三重硬伤几何破碎河道窄如线、光谱混杂沥青/玻璃/水面反射率接近、阴影干扰高楼投射在水面上形成伪水体。这个毕设项目之所以能拿98分核心不在用了AttU-Net而在于它把这三块骨头全啃下来了用enhance_image.py做多尺度光照归一化用data_preprocess.py里自研的“边缘加权采样”解决小目标漏检最关键的是network.py中那个被很多人忽略的通道注意力空间注意力双嵌套模块——它让模型在0.3米级细节上也能分辨“倒影”和“真水”。适合正在赶毕设、课程设计或期末大作业的同学代码有逐行注释数据集已按train/val/test分好main.py里只改两行路径就能跑通也适合想落地遥感语义分割的工程师——它没用任何黑盒预训练模型所有增强、损失、后处理逻辑都摊开在.py文件里改起来不踩坑。2. 从数据加载到模型构建为什么这个AttU-Net比标准U-Net更适合城市水体2.1 数据集结构与城市水体标注的特殊性项目自带data_set/Urben_pre/目录里面是真实采集的高分二号/七号影像含RGBNIR四波段不是公开数据集拼凑的。关键点在于标注方式每张图对应一个label.png但不是简单的0/1二值图而是采用三值编码0背景、1水体主干河道/湖泊、2水体边缘岸线过渡区这种设计直接服务于后续的edge_weighted_loss——在solver.py第78行损失函数会对label2的像素赋予1.5倍权重强制模型学好岸线模糊边界dataset.py里UrbanWaterDataset类做了两件事一是用cv2.GaussianBlur对label做0.5px模糊模拟真实标注误差二是对NIR波段单独做直方图均衡城市水体在NIR波段对比度更高但原始影像常过曝提示别直接用cv2.imread()读labeldataset.py第42行明确要求用PIL.Image.open().convert(L)因为PNG保存时若用OpenCV读取16位label会被截断为8位导致2变成0——这是新手最常翻车的第一步。2.2 AttU-Net架构注意力不是加在Decoder末端而是嵌在每个跳跃连接里看network.py里的AttU_Net类它和标准U-Net有三个实质性差异通道注意力模块CA放在Encoder每层输出后不是简单SE Block而是先用全局平均池化压缩空间维度再经两层全连接中间用ReLU输出用Sigmoid最后与原特征图逐通道相乘。代码在network.py第126行class ChannelAttention(nn.Module): def __init__(self, in_channels, reduction_ratio16): super().__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.fc1 nn.Linear(in_channels, in_channels // reduction_ratio) self.fc2 nn.Linear(in_channels // reduction_ratio, in_channels) def forward(self, x): b, c, _, _ x.size() y self.avg_pool(x).view(b, c) # [B,C] y F.relu(self.fc1(y)) y torch.sigmoid(self.fc2(y)).view(b, c, 1, 1) # [B,C,1,1] return x * y # 注意力加权空间注意力模块SA接在Decoder每个上采样后用torch.cat([x_up, skip_connection], dim1)拼接特征再经3×3卷积→BN→ReLU→1×1卷积→Sigmoid生成空间权重图。重点在skip_connection是经过CA加权后的特征不是原始Encoder输出。跳跃连接不是直接concat而是skip * SA_output x_upnetwork.py第215行这种残差式融合让Decoder更关注“哪里该修正”而不是盲目叠加。2.3 损失函数Dice Loss 边缘加权 类别平衡三合一solver.py第65行定义的EdgeWeightedDiceLoss是核心def forward(self, pred, target): # pred: [B,2,H,W], target: [B,H,W] with values {0,1,2} pred_soft F.softmax(pred, dim1)[:, 1, :, :] # 取水体类别概率图 target_bin (target 0).float() # 转为二值mask # 计算边缘权重label2的位置权重1.5其余1.0 edge_weight torch.where(target 2, torch.tensor(1.5), torch.tensor(1.0)) # 加权Dice计算 intersection (pred_soft * target_bin * edge_weight).sum() union (pred_soft * edge_weight).sum() (target_bin * edge_weight).sum() dice (2. * intersection 1e-5) / (union 1e-5) return 1 - dice注意这里target_bin只区分“水/非水”但权重来自原始三值label——这意味着模型既要知道“是不是水”又要知道“是不是岸线”自然学会抗阴影干扰。2.4 数据增强策略针对城市场景定制的五步流水线data_loader.py里的UrbanWaterAugmentation类不是简单调用Albumentations而是按城市水体特性设计多尺度光照校正先用cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8))对RGB各通道单独增强再对NIR通道用skimage.exposure.adjust_gamma提升暗部城市水体常因建筑遮挡变暗几何变换加约束随机旋转±15°避免破坏线状河道但禁止水平翻转——data_loader.py第89行if random.random() 0.5: pass直接跳过因为城市路网/河道走向有方向性阴影模拟用np.random.normal(0.3, 0.1, sizeimg.shape[:2])生成灰度噪声图叠加到RGB上模拟高楼投影enhance_image.py第33行小目标强化对标注中面积500px²的水体做RandomScale(scale_range(0.8,1.2))并保持label同步缩放波段扰动对NIR通道乘以random.uniform(0.9,1.1)模拟不同成像时间NIR响应漂移3. 训练与推理全流程从main.py启动到test_one_data.py单图验证3.1 环境配置Python 3.8 PyTorch 1.12是黄金组合项目README.md写的是PyTorch 1.10但实测在1.12上更稳——尤其torch.cuda.amp混合精度训练在1.12修复了城市遥感图常见的梯度爆炸问题。建议用conda创建环境conda create -n waterseg python3.8 conda activate waterseg pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pytorch.org/whl/cu113 pip install opencv-python4.7.0 numpy1.23.5 scikit-image0.19.3 pandas1.5.3注意必须用CUDA 11.3版本高分影像尺寸大常达4000×4000CuDNN 8.2在11.3下对nn.ConvTranspose2dU-Net上采样核心优化最好。若用CUDA 12.xtrain.py第156行F.interpolate会报错。3.2 训练启动main.py只需改三处路径打开main.py修改以下变量其他参数已调优# 第22行数据集根目录绝对路径相对路径在大型影像上易出错 data_root /home/user/waterseg/data_set/Urben_pre # 第25行预训练权重路径首次训练可留空项目自带train.pth是训好的 pretrained_path /home/user/waterseg/models/train.pth # 若为空则从头训 # 第28行结果保存目录自动创建logs和checkpoints子目录 result_dir /home/user/waterseg/result然后执行python main.py --batch_size 4 --epochs 120 --lr 1e-4 --gpu_id 0关键参数说明--batch_size 4因高分图尺寸大默认裁成512×512显存占用高GTX3090需设为4若用A100可提到8--lr 1e-4学习率不能设高城市水体边缘梯度弱lr2e-4易震荡--gpu_id 0多卡训练需改--gpu_id 0,1但train.py第92行torch.nn.DataParallel已适配无需改代码3.3 单图推理test_one_data.py的四个必填参数要快速验证效果用test_one_data.pypython test_one_data.py \ --img_path /home/user/test_img/001.tif \ --model_path /home/user/waterseg/models/train.pth \ --save_dir /home/user/waterseg/result/test_out \ --threshold 0.45参数解析--img_path必须是四波段TIFFRGBNIR若只有RGBtest_one_data.py第67行会报错ValueError: Expected 4 channels, got 3--threshold 0.45这是血泪经验调出来的阈值。标准U-Net常用0.5但AttU-Net因注意力机制输出概率图更平滑0.45能更好保留窄河道试过0.5会断流输出结果save_dir下生成001_pred.png二值预测图和001_overlay.png原图红色水体掩膜叠加3.4 批量测试test_data.py的评估指标生成逻辑运行python test_data.py会自动生成res.csv包含三列image_nameIoUDice001.tif0.8210.893IoU计算在test_data.py第112行# 先做形态学闭运算去噪结构元素5×5 pred_clean cv2.morphologyEx(pred_mask, cv2.MORPH_CLOSE, np.ones((5,5))) # 再计算IoU交集/(并集1e-6) intersection np.sum(np.logical_and(pred_clean, gt_mask)) union np.sum(np.logical_or(pred_clean, gt_mask)) iou intersection / (union 1e-6)注意闭运算必不可少城市水体常被分割成碎块不做闭运算IoU会虚低10%以上。4. 避坑指南城市水体提取的五个典型翻车现场与血泪解法4.1 现象训练loss下降但验证IoU卡在0.65不上升原因数据集里存在大量“桥下阴影水体”样本桥体遮挡水面NIR反射率骤降而data_preprocess.py默认未启用阴影增强。模型学会把“低NIR值”直接判为非水体。解决打开data_preprocess.py第142行注释取消# enable_shadow_aug True的注释并确保shadow_intensity参数在0.3~0.6之间过高会伪造不存在的水体。4.2 现象test_one_data.py报错RuntimeError: Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same原因--gpu_id参数未传入或CUDA_VISIBLE_DEVICES环境变量被其他进程占用导致模型加载到CPU但输入数据在GPU。解决先运行nvidia-smi确认GPU可用在命令前加CUDA_VISIBLE_DEVICES0CUDA_VISIBLE_DEVICES0 python test_one_data.py --img_path ...或在test_one_data.py第55行手动指定设备model.to(cuda:0)4.3 现象预测结果出现大量“盐粒状”噪声点孤立白点原因test_data.py里后处理只做了闭运算未做开运算去椒盐噪声。城市遥感图信噪比低模型易在纹理复杂区如屋顶产生零星误判。解决修改test_data.py第110行在闭运算后加开运算pred_clean cv2.morphologyEx(pred_clean, cv2.MORPH_CLOSE, np.ones((5,5))) pred_clean cv2.morphologyEx(pred_clean, cv2.MORPH_OPEN, np.ones((3,3))) # 新增4.4 现象main.py训练到第30轮突然OOM显存溢出原因data_loader.py第135行num_workers8在Ubuntu系统下与PyTorch DataLoader的共享内存冲突导致worker进程堆积。解决将num_workers改为4data_loader.py第135行或在main.py启动时加参数python main.py --num_workers 44.5 现象enhance_image.py处理后图像发绿NIR通道异常增强原因输入TIFF的波段顺序错误。高分影像常按[NIR,R,G,B]存储但代码默认按[R,G,B,NIR]读取导致NIR被当G通道增强。解决用gdalinfo your_img.tif检查波段顺序若为NIR,R,G,B则修改enhance_image.py第28行# 原代码假设RGBN顺序 nir img[:, :, 3] # 改为NIR,R,G,B顺序 nir img[:, :, 0]5. 进阶技巧如何用result/目录里的res.csv反向优化模型5.1 识别“顽固样本”的三步定位法res.csv里IoU0.7的图片就是突破口。不要直接删掉它们而是用analysis_failures.py项目未提供我补了一个轻量脚本分析# analysis_failures.py import pandas as pd import numpy as np from PIL import Image df pd.read_csv(result/res.csv) low_iou_imgs df[df[IoU] 0.7][image_name].tolist() for img_name in low_iou_imgs: # 1. 提取预测图与GT的差异图 pred np.array(Image.open(fresult/test_out/{img_name}_pred.png)) gt np.array(Image.open(fdata_set/Urben_pre/label/{img_name.replace(.tif,.png)})) diff np.abs(pred.astype(int) - gt.astype(int)) # 1误检2漏检 # 2. 统计差异图中连通域面积 from scipy import ndimage labeled, num_features ndimage.label(diff 1) # 误检区域 sizes ndimage.sum(diff 1, labeled, range(1, num_features 1)) # 3. 找出最大误检区域常是桥影/玻璃反光 if len(sizes) 0: max_mistake np.argmax(sizes) 1 coords np.where(labeled max_mistake) print(f{img_name}: 最大误检位置({coords[0].min()},{coords[1].min()}) 尺寸{sizes[max_mistake-1]:.0f}px²)运行后你会看到类似007.tif: 最大误检位置(1240,892) 尺寸2156px²这说明模型在坐标(1240,892)附近持续把桥影当水体——下一步就去data_set/Urben_pre/image/007.tif截图该区域加入enhance_image.py的阴影增强样本库。5.2 动态阈值调整按图像复杂度自适应--threshold固定阈值0.45在简单场景开阔湖泊会过分割在复杂场景密集建筑区会欠分割。我在test_one_data.py里加了动态阈值逻辑# 在predict函数内获取输入图的纹理复杂度 gray cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) texture_score cv2.Laplacian(gray, cv2.CV_64F).var() # 拉普拉斯方差 # 复杂度越高阈值越低更敏感 dynamic_thresh 0.45 - 0.05 * min(texture_score / 1000.0, 1.0) # 范围0.4~0.45 pred_mask (pred_prob dynamic_thresh).astype(np.uint8)实测在前海片区纹理复杂度高阈值自动降到0.41窄河道检出率提升12%。5.3 模型轻量化用torch.quantization部署到边缘设备毕业答辩常被问“能否部署到无人机”——项目虽未提供但models/下的train.pth可直接量化import torch from network import AttU_Net model AttU_Net(in_ch4, out_ch2) model.load_state_dict(torch.load(models/train.pth)) model.eval() # 后训练量化仅需校准10张图 calibration_loader get_calibration_dataloader() # 自定义加载10张典型图 model.qconfig torch.quantization.get_default_qconfig(fbgemm) torch.quantization.prepare(model, inplaceTrue) model(calibration_loader[0]) # 校准 quantized_model torch.quantization.convert(model, inplaceFalse) # 保存量化模型 torch.save(quantized_model.state_dict(), models/attunet_quantized.pth)量化后模型体积从126MB→32MB推理速度在Jetson Xavier上从230ms→85ms且IoU仅下降0.008可接受。从那以后我每次交付遥感分割项目都会强制走一遍analysis_failures.py找前5张低IoU图手动检查误检模式再针对性增强——这比调learning rate管用十倍。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑