简介这份资源是面向深度学习入门者与高校学生的遥感影像智能分析工具包适合用作毕业设计、期末大作业或课程设计参考。其核心基于YOLO等卷积神经网络可自动识别与分类遥感影像中的建筑物、植被、道路等地理特征覆盖环境监测、灾害评估、城市规划与农业估算等场景。压缩包共26个文件约94.97MB包含8张jpg与7张png示例图及推理结果图、4份md说明文档、2组模型与参数文件、1份yaml部署配置、1个py推理脚本和1份txt依赖清单结构上涵盖数据预处理、模型训练、评估与部署等模块并配有README与推理脚本便于快速上手。目前已有49人学习下载。读者可借此理解深度学习与遥感技术结合的项目组织方式掌握模型推理、结果可视化与部署配置的完整思路为课程实践与后续研究提供可复用的参考。1. 遥感影像智能分析工具从一张 2 万像素的图说起去年帮一个做农田地块识别的团队看模型他们拿到一幅 20000×20000 的高分遥感影像直接 resize 到 1024×1024 塞进 CNN结果田埂全糊成一团地块边界预测得跟水彩画似的。这不是模型不行是遥感影像和自然图像根本不是一个玩法——幅面大、目标小、通道多、标注贵。所谓「基于深度学习的遥感影像智能分析工具」本质就是把切图、标注、训练、推理、拼接这一整条链路打包成能跑通的工程而不是丢一个模型权重给你就完事。它解决的是从原始 GeoTIFF 到可用矢量结果之间的脏活累活适合做遥感解译、地块提取、变化检测的从业者也适合想拿遥感当深度学习实战项目的新手。下面按我实际搭过的一套流程拆开讲能抄的地方直接抄。2. 遥感影像分析工具到底在分析什么任务拆解与选型逻辑遥感影像智能分析不是一个单一任务它是一组任务的集合。你得先想清楚自己要的是像素级输出还是图斑级输出是单时相还是多时相这决定了后面整个工具链的形态。很多人一上来就问「用哪个模型」其实模型是最后一步前面数据怎么组织才是决定成败的地方。2.1 四类主流任务与对应输出形态按输出粒度从细到粗排常见的有这四类任务类型输出形态典型场景常用骨干语义分割逐像素类别图地表覆盖分类U-Net / DeepLabV3实例分割每个目标独立掩膜建筑物提取Mask R-CNN目标检测带类别矩形框车辆、船舶检测YOLO 系列 / Faster R-CNN变化检测双时相差异图违建监测、地块变更Siamese 网络选型逻辑很简单要面积统计就语义分割要数个数就实例分割或检测要对比两个时间点就变化检测。农田地块识别这种场景地块之间紧挨着、边界就是田埂语义分割比检测更合适因为检测框会互相重叠反而说不清归属。2.2 为什么不能直接 resize遥感影像的三个特殊性第一个特殊性是幅面。卫星或无人机单幅影像动辄上万像素而主流 GPU 显存吃不下这么大输入。直接缩放会丢失小目标田埂、小路、小建筑物首当其冲。第二个特殊性是通道。遥感影像常见 4 通道RGB 近红外甚至更多波段而 ImageNet 预训练权重是 3 通道。你得决定是丢掉近红外还是改第一层卷积这个选择直接影响植被相关任务的精度。第三个特殊性是标注成本。遥感标注需要专业知识一个像素级标注员一天可能只标几平方公里。所以工具必须支持「局部聚焦算法辅助标记」这类半自动标注先用模型出粗结果人工只修错的地方把标注效率拉起来。2.3 工具链的整体架构我一般把工具分成五层从下往上数据层GeoTIFF/JPEG2000 读写、坐标参考系管理、波段组合切图层滑窗切图、重叠度控制、边缘补齐标注层半自动预标注、格式转换GeoJSON ↔ mask训练层数据增强、损失函数、多卡训练推理层分块推理、结果拼接、矢量化输出这五层里最容易翻车的是切图和拼接因为涉及坐标对齐差一个像素整个结果就错位。下面几章按这条链路往下走。3. 把大图切进显存滑窗切图与数据组织的可复现步骤这一章解决「图太大跑不动」的问题。核心思路是滑窗切图但窗口大小、重叠度、边缘处理都有讲究设错了要么丢目标要么拼接有缝。3.1 用 rasterio 读取与波段组合先装环境。我习惯用 miniconda 隔离遥感这套依赖 GDAL用 conda 装比 pip 省心conda create -n rs python3.10 -y conda activate rs conda install -c conda-forge rasterio gdal numpy opencv -y pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118读取影像并做波段组合注意遥感影像的波段顺序和自然图像不同import rasterio import numpy as np def read_image(path, bands(1, 2, 3)): 读取指定波段返回 HWC 的 uint8 数组和地理变换信息 with rasterio.open(path) as src: # 遥感影像波段从 1 开始计数不是 0 data src.read(list(bands)) # (C, H, W) transform src.transform # 地理变换拼接时要用 crs src.crs # 坐标参考系 # 转成 HWC 方便后续处理 img np.transpose(data, (1, 2, 0)) # 16bit 转 8bit按 2% 和 98% 分位数拉伸比固定除以 256 稳 img percentile_stretch(img) return img, transform, crs def percentile_stretch(img, low2, high98): 分位数拉伸避免个别极亮极暗像素拉垮整体对比度 out np.zeros_like(img, dtypenp.uint8) for c in range(img.shape[2]): band img[:, :, c].astype(np.float32) lo, hi np.percentile(band, (low, high)) band np.clip((band - lo) / (hi - lo 1e-6), 0, 1) out[:, :, c] (band * 255).astype(np.uint8) return out逻辑说明src.read返回的是通道优先格式必须转成 HWC 才能喂给 PyTorch 的常规预处理。分位数拉伸是关键遥感影像常有云、水体这类极端值固定除以最大值会让整幅图偏暗。参数low和high我一般设 2 和 98云多的场景可以调到 1 和 99。3.2 滑窗切图的窗口与重叠度参数切图的核心参数就三个窗口大小、重叠度、边缘补齐方式。def slide_crop(img, patch_size512, overlap64): 滑窗切图返回图块列表和每块的左上角坐标 h, w img.shape[:2] stride patch_size - overlap patches, coords [], [] for y in range(0, h, stride): for x in range(0, w, stride): # 边缘不足一块时往回退对齐避免补零引入假边界 y0 min(y, max(h - patch_size, 0)) x0 min(x, max(w - patch_size, 0)) patch img[y0:y0 patch_size, x0:x0 patch_size] # 影像本身小于一块时补零 if patch.shape[0] patch_size or patch.shape[1] patch_size: patch cv2.copyMakeBorder( patch, 0, patch_size - patch.shape[0], 0, patch_size - patch.shape[1], cv2.BORDER_REFLECT) patches.append(patch) coords.append((x0, y0)) return patches, coords逻辑说明stride patch_size - overlap决定相邻块重叠多少。重叠度我一般设 patch_size 的 12.5%512 的块配 64 的重叠。重叠太小拼接处会有明显接缝重叠太大推理时间成倍涨。边缘处理用「往回退」而不是补零是因为补零会在影像边缘造出一条假边界模型会把它当成真实地物。参数怎么调目标越小patch_size 越小但别小于 256否则上下文不够。建筑物提取用 512地块识别用 768 到 1024因为地块本身尺度大。显存不够就减 batch别减 patch_size减了 patch_size 小目标就没了。3.3 标注格式转换GeoJSON 到 mask 的四个边界坑遥感标注常从 GIS 软件导出 GeoJSON要转成训练用的 mask。这一步坑最多from rasterio.features import rasterize import geopandas as gpd def geojson_to_mask(geojson_path, transform, out_shape, class_fieldclass_id): 把矢量标注栅格化成 mask类别从 1 开始0 为背景 gdf gpd.read_file(geojson_path) # 关键矢量必须和影像在同一坐标系否则栅格化全错位 shapes ((geom, int(cid)) for geom, cid in zip(gdf.geometry, gdf[class_field])) mask rasterize( shapes, out_shapeout_shape, transformtransform, fill0, dtypenp.uint8, all_touchedFalse) return mask四个坑一是坐标系不一致GeoJSON 是经纬度而影像投影坐标必须先to_crs二是all_touched设 True 会让边界像素被多类争抢一般设 False三是类别从 0 开始会和背景冲突类别 ID 从 1 起四是多边形自相交栅格化会报错得先buffer(0)修一下。4. 训练与推理从模型选择到分块拼接的完整链路切图和标注搞定进入训练。这一章讲模型怎么选、损失怎么设、推理怎么拼回去。4.1 语义分割模型的骨干选择与预训练权重处理遥感语义分割主流是 U-Net 和 DeepLabV3。U-Net 在小数据集上更稳DeepLabV3 的空洞卷积对大尺度上下文更好。我的经验是数据少于 5000 块就用 U-Net多了再上 DeepLabV3。4 通道输入的处理有两种做法一是丢掉近红外只用 RGB简单但浪费信息二是改第一层卷积把预训练权重的 3 通道权重取平均扩展到 4 通道。后者对植被任务提升明显import torch.nn as nn import torchvision.models as models def build_unet_4ch(num_classes2): 基于 resnet34 的 U-Net第一层改成 4 通道 backbone models.resnet34(weightsmodels.ResNet34_Weights.IMAGENET1K_V1) # 取原第一层权重沿通道维求平均再复制成 4 通道 old_conv backbone.conv1 new_conv nn.Conv2d(4, 64, kernel_size7, stride2, padding3, biasFalse) with torch.no_grad(): w old_conv.weight.mean(dim1, keepdimTrue) # (64,1,7,7) new_conv.weight.copy_(w.repeat(1, 4, 1, 1) / 4) backbone.conv1 new_conv return backbone逻辑说明把 3 通道权重求平均再复制到 4 通道比随机初始化收敛快得多因为 RGB 部分的知识保留了。除以 4 是为了保持输出量级不变。参数上num_classes包含背景二分类地块就是 2。4.2 损失函数与类别不平衡的处理遥感影像里背景往往占 80% 以上纯交叉熵会让模型学会「全预测背景」也能拿高分。常见做法是 Dice Loss 加交叉熵的组合class DiceBCELoss(nn.Module): def __init__(self, weight0.5): super().__init__() self.weight weight self.bce nn.BCEWithLogitsLoss() def forward(self, pred, target): bce self.bce(pred, target) # Dice 部分sigmoid 后算重叠度 prob torch.sigmoid(pred) inter (prob * target).sum(dim(2, 3)) union prob.sum(dim(2, 3)) target.sum(dim(2, 3)) dice 1 - (2 * inter 1e-6) / (union 1e-6) return self.weight * bce (1 - self.weight) * dice.mean()逻辑说明BCE 管像素级分类Dice 管整体重叠度两者互补。weight我一般设 0.5如果小目标漏检严重就降到 0.3让 Dice 主导。加 1e-6 是防除零这个后悔药一定要加。4.3 分块推理与结果拼接推理时按训练同样的窗口切逐块预测再拼回大图。拼接的关键是重叠区怎么融合def merge_patches(patches, coords, full_shape, patch_size512): 重叠区取平均避免拼接缝 accum np.zeros(full_shape[:2], dtypenp.float32) count np.zeros(full_shape[:2], dtypenp.float32) for patch, (x0, y0) in zip(patches, coords): accum[y0:y0 patch_size, x0:x0 patch_size] patch count[y0:y0 patch_size, x0:x0 patch_size] 1 # 加 1e-6 防止边缘 count 为 0 时除零 return accum / (count 1e-6)逻辑说明重叠区取平均是最简单也最稳的融合方式比取最大或投票都平滑。count记录每个像素被预测了几次边缘像素可能只被覆盖一次所以除之前加个极小值。拼完再按阈值二值化最后用rasterio.features.shapes矢量化导出 GeoJSON。5. 避坑与排查遥感深度学习项目里最容易翻车的五件事这一章全是血泪经验每条按现象、原因、解决写。现象一训练 loss 正常下降但验证集精度死活上不去。原因多半是训练集和验证集有空间重叠同一块地既在训练又在验证模型等于背答案。解决是按地理区域划分不是随机划分用影像的经纬度范围切块保证验证区在训练区之外。现象二推理结果整体偏移几个像素。原因是切图时坐标记录和拼接时坐标对不上常见于边缘往回退的逻辑没同步到拼接。解决是切图和拼接共用同一份 coords 列表别各算各的。这个 bug 特别隐蔽因为肉眼看结果「差不多对」但叠加到 GIS 里就露馅。现象三模型把云阴影预测成水体。原因是训练集里云阴影样本太少模型没见过。解决是在数据增强里加亮度扰动和对比度扰动模拟不同光照同时单独收集云阴影样本补进去。遥感里云是永恒的话题别指望模型自己学会。现象四显存溢出batch 只能设 1。原因是 patch_size 太大或用了 fp32。解决是开混合精度训练torch.cuda.amp能省一半显存再配合梯度累积模拟大 batch。别急着换卡先把精度和 patch_size 调明白。现象五标注转 mask 后类别全变成 0。原因是 GeoJSON 的类别字段是字符串int(cid)转换失败被静默跳过。解决是转换前先gdf[class_field].astype(int)并打印类别分布确认。这个坑我踩过两次第二次是因为字段名写错了。6. 进阶技巧用局部聚焦辅助标记把标注效率提上去前面讲的都是「有标注之后」的事但遥感项目真正的瓶颈在标注。一个像素级标注员一天标不了几平方公里而模型要好至少得几千块标注。局部聚焦算法辅助标记是我目前用过最实在的提效手段思路是先用少量标注训一个粗模型让它对未标注区域出预测人工只修错的地方把「从零标」变成「改作业」。具体做法分三步。第一步先手工标 200 到 300 块训一个基础模型精度不用高mIoU 有 0.5 就够用。第二步用这个模型对剩余影像推理把预测结果转成矢量导入标注工具作为预标注图层。第三步标注员在预标注基础上修改只动错的地方。实测下来同样标 1000 块纯手工要两周辅助标记能压到四天左右。这里有个参数要调预标注的置信度阈值。设太高预标注漏得多标注员要补的反而多设太低错的多改起来也累。我的经验是阈值设在模型输出概率 0.5 到 0.6 之间让预标注覆盖 70% 左右的真实目标剩下的靠人工补。这个比例下标注员的心理负担最小因为大部分框已经在了只需要删和改。还有一个技巧是主动学习式的迭代。每标完一批把新标注加进去重新训模型再用新模型出下一批预标注。这样模型越用越准预标注质量越来越高形成正循环。一般迭代三轮预标注的可用率能从 60% 提到 85% 以上。验证辅助标记有没有效果别只看标注速度要看最终模型精度。我一般留一个纯手工标注的测试集对比「辅助标记训出的模型」和「纯手工训出的模型」在同一测试集上的表现。如果精度差距在 2 个点以内说明辅助标记没引入系统性偏差可以放心用。差距大就说明预标注把标注员带偏了得回头查预标注的类别是否均衡。这套流程跑下来最大的体会是遥感深度学习项目的成败七分在数据两分在切图拼接一分在模型。我见过太多人把时间花在换骨干、调学习率上结果数据划分错了、坐标对不齐模型再好也白搭。所以我的习惯是每做一个新项目先花半天把切图和拼接的坐标对齐验证一遍用一张小图跑通全链路再上大数据。这个习惯帮我省了无数次返工。希望帮到你。本文还有配套的精品资源点击获取
