
简介面向毕业设计场景的航拍图像语义分割项目基于DeepLabv3网络实现采用编解码结构对高分辨率遥感影像进行像素级分类适合计算机视觉方向的学生、研究人员以及相关课题开发者参考。项目提供从模型搭建到训练推理的完整脚本与交互式笔记覆盖基础模型、在线架构分析、离线结果输出等实验流程并附带多种骨干网络实现便于对比不同网络结构对分割精度和速度的影响。压缩包共一百八十四个文件以九十五个脚本、八十四个编译文件以及三个交互式笔记为主另有说明文档整体仅四百七十七千字节轻量易部署。目前已有二百零三人学习下载对于需要快速启动航拍语义分割实验或构建毕业设计演示系统的读者这些内容能提供可运行的代码框架、网络结构参考和实验组织思路帮助节省从零搭建的时间。1. 航拍分割的毕业设计最该先拆的是这套对比链路做航拍语义分割的毕业设计最常见的卡点不在模型跑不起来而是你辛辛苦苦训完之后发现道路、农田这些大目标分得不错车辆、屋顶、独立树木全被下采样吃没了。这个项目的价值不是只给你丢一个 DeepLabv3而是把 baseline 离线输出、SAM 注意力架构在线推理和离线评估放在同一套工程里同时还准备了 resnet、hrnet、swin、twins、beit、bisenetv2 六个主干文件。拿到手能干的事很明确先复现 baseline再换主干做横评最后用 sam_arch 做改进对比。对本科毕设来说复现、消融、可视化、指标评估四个环节都齐了这也是答辩时最容易被追问的部分。2. DeepLabv3 的两种关键结构ASPP 与 Decoder 在航拍上的取舍2.1 空洞卷积不牺牲分辨率地扩大感受野普通分割网络喜欢用池化或大步长卷积把特征图一路压到 1/16、1/32感受野是大了但航拍图里一辆车往往只占十几个像素特征图缩小之后这些目标直接消失。空洞卷积的切入点就是不降分辨率地扩大感受野。一个 3×3 的卷积dilation rate 设为 d 时等效感受野是(3-1)×d 1。rate2 时等效 5×5rate6 时等效 13×13rate12 时等效 25×25rate18 时等效 37×37。航拍图里一个容易被忽略的事实是目标尺度方差极大农田、裸地是几百像素量级道路是连续长条车辆和屋顶只有十几个像素。单一固定感受野只能照顾其中一类所以 DeepLabv3 干脆把几个不同膨胀率的卷积并联起来让每个分支负责一种尺度特征。2.2 ASPP 的四个分支与 rate 选择ASPPAtrous Spatial Pyramid Pooling在编码器输出上并行挂四个分支一个 1×1 卷积、三个 dilation rate 分别取 6/12/18 的 3×3 空洞卷积再加一个全局平均池化分支。前面三个分支负责不同尺度目标全局池化分支兜底整幅图像的上下文这对航拍里大面积裸地、水体这类“局部窗口看不出类别”的区域很关键。项目里deeplabv3plus_baseline_offline_out.ipynb跑的就是这套结构。import torch.nn as nn class ASPP(nn.Module): def __init__(self, in_ch, out_ch256, rates(6, 12, 18)): super().__init__() self.branches nn.ModuleList() self.branches.append(nn.Conv2d(in_ch, out_ch, 1)) for r in rates: self.branches.append( nn.Conv2d(in_ch, out_ch, 3, paddingr, dilationr) ) self.gap nn.AdaptiveAvgPool2d(1) # 这里实际部署时还要接 BN ReLU concat def forward(self, x): feats [b(x) for b in self.branches] # 最后一个分支是全局池化后上采样回原尺寸 feat self.gap(x) feat nn.functional.interpolate(feat, sizex.shape[-2:], modebilinear) feats.append(feat) return torch.cat(feats, dim1)代码里paddingr必须和dilationr保持一致否则输出尺寸会缩水。rates(6, 12, 18)是针对输入 512×512 的常见取值如果你的训练原图是 1024 甚至更大我会把 rate 整体上调到(12, 24, 36)否则感受野覆盖不了大目标。最后把五路输出 concat 起来再接一个 1×1 卷积降维到 256 通道喂给 Decoder。2.3 Decoder 为什么非要“偷”低层特征DeepLabv3 的 Decoder 结构不复杂高层特征先上采样 4 倍和 1×1 卷积降维后的低层特征 concat再用 3×3 卷积融合最后上采样 4 倍得到预测图。这里低层特征取的是 backbone 第一个 stage 的输出论文里把它压到 48 通道。低层特征分辨率高保留着道路边缘、车辆轮廓这些空间细节而高层特征经过 ASPP 之后语义更完整。两者互补。航拍图里道路边界经常和小目标重叠纯靠高层特征上采样会把边缘糊掉。加了低层特征之后边缘像素的类别区分会明显变好。代价是多一份低层特征的显存开销训练时 batch size 要相应调小。2.4 baseline 的推荐实验参数下面这张表是我在类似航拍分割任务里常用的 baseline 配置可以直接抄进训练脚本也可作为调参起点参数取值说明backboneresnet101baseline 默认预训练权重好找output_stride (train)16编码器特征缩小 16 倍省显存output_stride (eval)8推理时切到 8小目标召回率更高crop size512×512航拍原图太大必须随机裁剪optimizerSGDmomentum0.9, weight_decay0.0005lr0.007poly 衰减power0.9max epochs80~120遥感数据量小100 左右足够收敛训练和评估切换 output_stride 是 DeepLabv3 论文里的标准做法实际操作时在model.train()/model.eval()前后改一下 backbone 的 stride 就行。提示航拍数据集类别通常严重不均衡如果发现 mIoU 涨不上去优先检查每类样本数而不是先换模型。3. 六个 backbone 如何封装从 resnet 到 twins 的切换套路3.1 文件布局就是一张 backbone 注册表项目根目录下resnet.py、hrnet.py、swin.py、twins.py、beit.py、bisenetv2.py这六个文件每个文件都只负责一件事给定输入图像返回一个或一组供 ASPP 使用的特征图。也就是说无论 backbone 内部是 CNN 还是 Transformer对外接口是统一的这样 DeepLabv3 的 ASPP 和 Decoder 不用改一行代码。文件结构类型对航拍场景的适配点风险点resnet.pyCNN 残差稳定、预训练权重多跑通最快下采样倍数大小目标信息弱hrnet.py高分辨率并行网络全程保持高分辨率特征小目标友好显存占用明显更高swin.py窗口注意力 Transformer全局建模能力强大区域一致性好边缘细节偏粗twins.py交替注意力 Transformer局部细节与全局上下文兼顾超参敏感需多试beit.py掩码建模预训练 ViT语义特征强适配大目标需要配套预训练权重bisenetv2.py实时双边分割网络推理快适合快速验证精度上限有限适合当 baseline我在实际使用中bisenetv2.py这种独立分割网络一般不会只当 backbone 用更多是作为整网对比项用来证明“DeepLabv3 的 ASPP 设计在航拍数据上确实有用”。3.2 用注册表切换 backbone 的写法毕业设计要横向对比六个主干最忌讳的是每个主干复制一份训练脚本。常见做法是维护一个注册表配置里写什么类型就实例化什么网络。这样跑对比实验只需要改一行配置训练、评估代码完全复用。# models/registry.py BACKBONE_REGISTRY {} def register_backbone(name): def decorator(cls): BACKBONE_REGISTRY[name] cls return cls return decorator def build_backbone(cfg): name cfg[backbone][type] params cfg[backbone].get(params, {}) if name not in BACKBONE_REGISTRY: raise KeyError( fUnknown backbone: {name}, available: {list(BACKBONE_REGISTRY)} ) return BACKBONE_REGISTRY[name](**params)resnet.py、swin.py各自的类定义上方加上register_backbone(resnet)、register_backbone(swin)这样的装饰器注册表里就有条目了。对应的训练配置是 YAML 文件里的一段backbone: type: hrnet params: name: hrnet_w48 pretrained: ./pretrained/hrnet_w48.pth # 想换 swin 时把 type 改成 swin 并填对应参数即可用这种写法对比实验就变成了“改 YAML、看指标”的机械操作后面写论文时也能明确说清楚每个实验变量是什么。params里最关键是pretrained路径换主干时最容易漏的就是权重文件没下载对应版本导致训练从头开始。3.3 公平对比实验的三个坑第一个坑是预训练权重不对齐。resnet 用 ImageNet 分类预训练swin 用 ImageNet-22K 预训练BEiT 用的是掩码图像建模预训练。如果直接拿官方权重跑你对比的是“预训练方式差异 结构差异”不是纯粹的 backbone 差异。我一般会先把所有主干在同样的预训练协议下对齐做不到就至少在论文里说明各自权重的来源。第二个坑是 output_stride 不统一。ResNet 系列默认下采样 32 倍Swin 这类 Transformer 通常下采样 32 倍但 HRNet 全程保持高分辨率。不统一 stride 的话ASPP 输入的 feature map 分辨率都不一样对比不公平。实操上我固定所有 backbone 的输出下采样倍数为 16深层阶段把 stride 改成 1 处理。第三个坑是训练 epoch 不一致。Transformer 类主干收敛比 CNN 慢只跑 50 个 epochswin 和 twins 大概率打不过 resnet。我的做法是统一跑满 120 个 epoch并观察每类 IoU 的收敛曲线不只是看最终 mIoU。4. baseline 离线输出与 SAM 注意力架构在线推理两套 notebook 的分工4.1 online 和 offline 在语义分割里分别指什么项目里有三个 notebook名字很有讲究。sam_arch_online.ipynb是 SAM 注意力架构的在线推理版本一边加载模型一边对单张图像做前向传播结果直接绘制出来方便肉眼检查分割效果和 badcase。deeplabv3plus_baseline_offline_out.ipynb和sam_arch_offline_out.ipynb则是离线批量推理加载训练好的权重对全量验证集跑预测把结果以 numpy 或图片格式保存最后统一算指标。# 在线推理核心逻辑 model.eval() with torch.no_grad(): logit model(image_input) # [1, num_classes, H, W] pred logit.argmax(dim1).squeeze() # [H, W]这里logit是每个像素在类别维上的原始分数argmax(dim1)取分数最大的类别作为预测结果。在线推理适合调参阶段每张图都能立刻可视化离线推理适合最终评估阶段一次批量跑完所有测试图保存结果后再统一做统计分析。提示这里说清楚一点项目里的sam_arch是自注意力模块Self-Attention Module的缩写和 Meta 的 Segment Anything Model 不是一回事别在答辩时混淆两个概念。4.2 为什么航拍分割的指标要看按类 IoU航拍分割里总精度PA参考价值有限。一幅图里道路和裸地可能占 80% 以上就算车辆、屋顶全部分错PA 依然能到 90% 以上。可靠的评估方式是统计混淆矩阵逐类计算 IoU再看平均值。下面的代码直接计算每类 IoU、mIoU 和 PAimport numpy as np def seg_metrics(pred, gt, num_classes): # pred, gt: 都是二维整数标签数组像素值范围 [0, num_classes) ious [] for c in range(num_classes): p (pred c) g (gt c) inter np.logical_and(p, g).sum() union np.logical_or(p, g).sum() ious.append(inter / union if union ! 0 else np.nan) miou np.nanmean(ious) # 忽略不存在的类别 pa (pred gt).sum() / gt.size return miou, pa, ious写这段代码时有个细节某类在真值里完全没出现时union 为 0直接除会得到 inf 或越界值。用np.nanmean而不是np.mean就是让这类缺失类别不参与 mIoU 计算。航拍数据里“车辆”这类小目标占比小但重要逐类 IoU 才能暴露模型对它的真实表现。4.3 用一张结果表快速定位“该保哪个 backbone”离线 notebook 跑完之后最终结果表通常长这样实验组合mIoUPA结论怎么写resnet baseline0.6x0.9x作为参比参照hrnet baseline0.6x0.9x重点看小目标类别 IoU 是否提升swin baseline0.6x0.9x看大类别内部一致性sam_arch best backbone0.7x0.9x说明注意力分支的有效性这张表里数值是示意你跑出来的结果自己填。关键是要把最后一行“sam_arch 对比对应 backbone 基线”的差异幅度写清楚论文里的核心贡献点就落在这里。5. 把 5000×5000 的航拍图喂给 DeepLabv3overlap-tile 推理与后处理航拍原图动辄几千像素见方整图直接塞进显存不现实。一张 512×512 的图像经过 backbone 和 ASPP 后中间特征图就是 512×512×256单张 float 特征图约 268 MB整幅 5000×5000 根本放不下常见做法是分块推理。def predict_tile(model, img, tile512, stride384, num_classes11): h, w img.shape[:2] prob_sum np.zeros((num_classes, h, w), dtypenp.float64) weight np.zeros((h, w), dtypenp.float64) for y in range(0, h - tile 1, stride): for x in range(0, w - tile 1, stride): patch img[y:ytile, x:xtile] with torch.no_grad(): logit model(patch) # [1, C, tile, tile] prob torch.softmax(logit, dim1).squeeze(0).cpu().numpy() prob_sum[:, y:ytile, x:xtile] prob weight[y:ytile, x:xtile] 1 prob_sum / np.maximum(weight, 1) return prob_sum.argmax(axis0)关键参数是tile和stride。tile决定单次推理的输入尺寸受显存限制stride控制滑窗步长当stride tile时相邻 patch 有重叠。重叠区用 softmax 概率累加再除以计数而不是直接用 argmax 投票这样重叠区不会出现硬边界线。我一般把stride设成tile的一半512 的 tile 配 256 的 stride重叠率 50%对小目标边缘尤其友好。后处理方面航拍分割预测图常出现大量孤立噪点尤其是车辆和树木这类小目标。可以用scipy.ndimage.binary_opening做一次形态学开运算去掉小于设定面积的连通域再对道路等类别做连通性约束只保留面积最大的连通域。实际调试时先看sam_arch_online.ipynb里单张可视化是不是边缘断裂再决定stride是改成 256 还是 448多数情况下调整重叠率比换后处理算子更有效。本文还有配套的精品资源点击获取