
简介面向遥感影像分析与深度学习入门者的智能分析工具包基于YOLO目标检测算法集成了模型推理、部署配置与说明文档可用于地表覆盖分类、目标检测、变化监测等场景也适合毕业设计、课程设计或机器学习实战训练。压缩包共26个文件以jpg/png影像及标注结果、md说明文档、模型权重model__与__params为主另含infer.py推理脚本、deploy.yaml部署配置及requirements.txt依赖清单总大小94.97MB。已有51人学习下载。工具内含README.md详细说明、freeze_model模型冻结脚本、docs文档目录以及PaddleSeg推理基准测试与TensorRT编译指南既能快速上手推理流程也能根据需求进行二次开发。对初学者而言借助该工具可完整走通遥感影像从输入到智能分析的流程理解YOLO在目标检测任务中的回归思想同时为模型部署和性能优化提供参考。1. 遥感影像智能分析难点不在模型而在管线遥感影像智能分析这几年几乎被深度学习重写了一遍从建筑物提取到道路分割再到农田地块识别公开模型在基准数据集上一个赛一个漂亮。真正让工程团队卡住的是管线一个基于深度学习的遥感影像智能分析工具面对的往往是一幅几百 MB 甚至几个 GB 的 GeoTIFF多波段、16 bit、带着地理参考直接扔进用三通道 JPEG 训练的模型里第一行代码就会报错。这个工具包要解决的就是这条从原始栅格到预测矢量图的完整链路而不是重新发明一个网络结构。适合刚接触遥感加深度学习组合的算法工程师、GIS 开发者和研究生把数据读取、预处理、推理、后处理四段重复劳动串成一条可复制的流水线。下面按这条流水线逐层拆开讲再落到最小可运行命令和参数调优上最后给一条省标注的进阶路径。2. 拆开工具包从原始 TIF 到预测矢量图的四个功能层拿到名为“基于深度学习的遥感影像智能分析工具.zip”这类压缩包解压后第一件事不是找可执行文件而是判断它的功能边界。常见做法是把整条链路拆成四层数据读取、预处理、模型推理、后处理导出。每一层职责单一层间只传 numpy 数组和配置字典这样你换传感器、换模型、换输出格式都不必把流程推倒重来。先确认压缩包本身完整。命令行解压时遇到invalid zip archive: could not find eocd这类报错多数是下载中断导致压缩包尾部缺失重新下载一次通常比折腾修复工具更快。解压后看目录结构如果包含data_loader、preprocess、model、postprocess之类的分包目录基本就是上面的分层设计若没有按这个思路重新组织一遍也行后面所有参数调整才有落点。2.1 数据读取层栅格不是一张普通照片遥感影像读取和普通图片读取完全不同。普通图片解码后是 HWC 顺序的 uint8 数组遥感影像则是一个带地理坐标的多波段数组。波段顺序可能是 RGB也可能是 BGR、RGBNIR多光谱甚至十几个波段dtype 常见 uint16宽高动辄几万乘几万像素。拿 PIL 的open去打开 GeoTIFF多半读不全波段或者直接解码失败。import rasterio with rasterio.open(scene.tif) as src: img src.read() # (C, H, W)第 0 维是波段 profile src.profile # transform, crs, height, width, count, dtype bounds src.bounds print(波段数:, img.shape[0], 尺寸:, img.shape[2], x, img.shape[1]) print(CRS:, profile[crs], 仿射变换:, profile[transform])这段代码的逻辑很直接read()一次读入全部波段profile保存写出结果时必需的地理参考。注意read()返回的顺序是 (C, H, W)和 PyTorch 输入一致但与 OpenCV 习惯的 HWC 顺序不同转换时别把维数顺序写反。uint16 影像转 float32 后内存直接翻倍处理超大影像前先估算一下16 bit 的单波段两万乘两万影像转成 float32 就要占约 1.6 GB。拿到影像后第一件事不是写模型而是确认波段顺序和坐标系。很多坑都出在这一步有的影像文件头标着 RGB实际波段顺序是 BGR有的 transform 是旧版本记录问题投影到 WGS84 会有整像素偏移。把profile和bounds打印存档是这条流水线成本最低的第一个好习惯。2.2 预处理层切片、归一化和内存规划预处理层解决三件事把不同来源的影像统一到相近的数值范围把超大影像切成模型能吃下的 patch给每个 patch 带上在原图的位置方便推理后拼回整幅。这里最容易忽视的是内存规划滑窗看起来简单实际一次性把几千个 patch 都放进列表内存会先被吃光。def sliding_window(img, tile_size512, overlap64): h, w img.shape[1], img.shape[2] step tile_size - overlap patches [] for y in range(0, h - tile_size 1, step): for x in range(0, w - tile_size 1, step): patches.append((y, x, img[:, y:ytile_size, x:xtile_size])) return patches tiles sliding_window(img) print(切片数量:, len(tiles), 每块:, tiles[0][2].shape)滑窗函数是全流程里最值得反复调的一块。tile_size由模型输入尺寸和显存共同决定常见值 256、512、1024遥感分割模型大多在 512 附近表现最稳。overlap用于抵消边缘预测误差一般取 tile_size 的四分之一到八分之一64 或 128 是安全区间。overlap 太小拼接缝明显太大推理时间几乎翻倍。归一化要单独强调。遥感影像的直方图差异极大同一片地在不同季节、不同传感器下 DN 值可以差好几倍。常见做法是用训练时统计的均值方差做标准化而不是简单除以 255。如果训练数据是 8 bit、推理影像是 16 bit要先做分位数截断再缩放否则模型看到的数值分布和训练时完全不同预测结果就会一片黑或者一片灰。2.3 推理层框架选型和批量大小推理层的职责是拿切好的 patch 和训练好的权重输出逐像素类别概率。选型上有讲究直接用 PyTorch 原生权重推理最省事但上生产环境更多人会先导出 ONNX 再切换到推理引擎因为 ONNX 对动态尺寸和 CPU 指令集的适配更好部署时不依赖完整训练框架。工具包里如果同时给了.pth和.onnx优先用 ONNX 做批量推理常见情况下能快两到三成。import onnxruntime as ort import numpy as np tile_size 512 session ort.InferenceSession(model.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name pred_mask np.zeros((len(tiles), tile_size, tile_size), dtypenp.uint8) for i, (y, x, patch) in enumerate(tiles): tensor patch[None, ...].astype(np.float32) # (1, C, H, W) probs session.run(None, {input_name: tensor})[0] # (1, num_classes, H, W) pred_mask[i] probs[0].argmax(axis0)这里的关键参数是providers。有 N 卡时把CPUExecutionProvider换成CUDAExecutionProvider并在正式预测前跑一次 warmup否则第一次推理会卡几秒。显存足够时一次喂 4 到 8 个 patch比外层循环逐张推理效率高不少CPU 上也可以靠 numpy 批量计算稍微压榨吞吐。逐 patch 单次session.run是最慢的写法能避免就避免。还要注意类别数量。二分类模型输出 1 通道、走 sigmoid多分类模型输出num_classes通道。如果影像里明明有水、植、建筑、道路四类但模型只输出三类通常不是推理代码的问题而是权重文件的类别数和训练标签不一致。这种不一致不会报错只会静静地少一类。2.4 输出层把掩膜写回带坐标的 TIF推理得到的是大量 patch 的类别索引下一步要拼回整幅影像并带上原图的地理参考写出去。没有坐标的预测结果对 GIS 同事来说等于白做。拼回时要处理 overlap 区域直接一块盖一块会产生接缝常见做法是累计求和后除以重叠权重。full_mask np.zeros((img.shape[1], img.shape[2]), dtypenp.float32) weight np.zeros((img.shape[1], img.shape[2]), dtypenp.float32) for i, (y, x, patch) in enumerate(tiles): full_mask[y:ypatch.shape[1], x:xpatch.shape[2]] pred_mask[i] weight[y:ypatch.shape[1], x:xpatch.shape[2]] 1 full_mask full_mask / np.maximum(weight, 1) out_meta profile.copy() out_meta.update(dtypeuint8, count1, compresslzw) with rasterio.open(pred_mask.tif, w, **out_meta) as dst: dst.write(full_mask.astype(np.uint8), 1)写回有两个高频错点。一是out_meta必须在打开原图时保存写入时只改 dtype 和 counttransform 与 crs 保持原样否则在 QGIS 里叠不到原图上。二是压缩算法类别索引图重复值多用 LZW 往往能把几百 MB 压到几十 MB。如果还需要矢量输出先用 GDAL 的 polygonize 转矢量再做边缘简化这一步在避坑章节里单说。3. 跑通最小用例用公开数据做一次建筑物提取目标定为一小时跑出第一张预测图。环境用常见 CPU 方案数据用一张公开高分影像模型用现成的语义分割权重。以建筑物提取为例因为公开数据多、验证直观流程推通后换成农田地块识别只是换权重和类别数的事。下面分环境、推理、验证三步。3.1 环境准备深度学习环境配置的几个注意点建议配置Python 3.9 PyTorch CPU 版 rasterio onnxruntime。GPU 不是必需有 N 卡就把 torch 换成 CUDA 版推理时间能缩小一个数量级。严格做环境管理用 conda别给系统 Python 添乱。想补理论基础的翻翻“动手学深度学习”里环境配置和图像分类那几章对照着改成遥感场景完全够用。conda create -n rs_ai python3.9 -y conda activate rs_ai pip install torch --index-url https://download.pytorch.org/whl/cpu pip install rasterio onnxruntime opencv-python-headless numpy--index-url指定 CPU 版 torch文件小、不依赖 CUDA 驱动opencv-python-headless是无 GUI 依赖的服务器版遥感批量推理不需要弹窗预览用它比opencv-python干净。内网离线环境就用pip download拉到本地后再内网安装比在目标机上源码编译省太多时间。有云 GPU 平台的话把 conda 环境打包传上去省掉重复装依赖的半个钟。3.2 最小推理脚本切片、逐块预测、拼接写盘把上一章的四段逻辑串成完整脚本。数据用公开高分影像权重换成你手里在建筑物数据上训练好的 ONNX。暂时没有权重就先用一个建筑/非建筑二分类权重把流程跑通再谈精度。import rasterio import numpy as np import onnxruntime as ort TILE_SIZE 512 OVERLAP 64 MEAN np.array([0.485, 0.456, 0.406], dtypenp.float32) STD np.array([0.229, 0.224, 0.225], dtypenp.float32) with rasterio.open(scene.tif) as src: img src.read()[:3].astype(np.float32) # 只取 RGB 三个波段 profile src.profile img (img - MEAN[:, None, None]) / STD[:, None, None] sess ort.InferenceSession(building_seg.onnx, providers[CPUExecutionProvider]) step TILE_SIZE - OVERLAP h, w img.shape[1], img.shape[2] mask np.zeros((h, w), dtypenp.uint8) for y in range(0, h - TILE_SIZE 1, step): for x in range(0, w - TILE_SIZE 1, step): tile img[:, y:yTILE_SIZE, x:xTILE_SIZE][None, ...] pred sess.run(None, {sess.get_inputs()[0].name: tile})[0] mask[y:yTILE_SIZE, x:xTILE_SIZE] pred[0].argmax(axis0).astype(np.uint8) out_meta profile.copy() out_meta.update(dtypeuint8, count1, compresslzw) with rasterio.open(pred.tif, w, **out_meta) as dst: dst.write(mask, 1) print(输出 pred.tif类别数:, mask.max() 1)逻辑不复杂读 RGB 三波段用训练集均值方差做标准化再滑窗逐块预测每块取 argmax 写回对应位置。两个必须注意的参数是MEAN和STD要用所属权重训练时统计的值不能随手填 ImageNet 默认值否则预测结果整体偏移。TILE_SIZE应该与模型输入保持一致乱改会遭遇未经验证的尺寸变化边缘预测明显变糊。脚本里 overlap 只有 64重叠区写回时按最后一个 patch 覆盖所以拼接处会有轻度接缝这很正常下一章讲怎么消。3.3 验证结果别只看一眼要和人工标注对齐跑出pred.tif后先在 QGIS 里叠加到原图上看边界再抽一小块区域和人工标注算 IOU。肉眼判断在建筑物这种轮廓清晰的类别上会骗人模型边缘略圆润时只觉得“差不多”定量一算可能才 0.6。gdal_translate -srcwin 1000 1000 1000 1000 pred.tif pred_sample.tifgdal_translate的-srcwin按像素取子集适合快速抽样参数依次是左上角行列号、窗口宽高。验证时注意预训练权重在它自己的数据集上 IOU 常超 0.8换到新影像掉到 0.5 以下很常见。先检查影像的 GSD 是否与训练数据匹配再检查归一化参数有没有传对最后才怀疑模型本身。4. 参数调整影响结果精度的五个关键旋钮模型结构定了之后真正决定落地效果的是下面几个参数。它们不改变网络层数却经常能让误检率掉一半。按影响从大到小排逐个讲清楚为什么有效、怎么调。4.1 切片尺寸与重叠度显存和拼接缝的平衡tile_size决定模型感受野大小。512 是大多数遥感分割模型的甜点值256 对小目标敏感但上下文不足细长道路容易断1024 需要至少 8 GB 显存CPU 推理慢得让人失去耐心。选型时先看权重训练时用的 patch 尺寸训练 512 推理也 512别为了保边缘改成 1024。overlap至少要覆盖模型感受野的一半64 到 128 像素是安全区。切换数据集时这套参数要和训练设置保持一致否则模型看到的上下文分布变了边界类会乱跳。4.2 归一化方式线性拉伸还是均值方差标准化遥感影像直方图差异大同一区域夏季和冬季的亮度能差数倍。线性拉伸适合目视解译把 2% 到 98% 分位数映射到 0 到 255而基于均值方差的标准化才是给神经网络用的标准做法因为训练数据正是在同一分布上统计的。16 bit 影像有个很常见的坑直接除以 65535 通常会整体偏暗因为像素都堆在低值端。正确做法是先用 1% 到 99% 分位数截断再做标准化等于先抑掉椒盐噪声和高光饱和。4.3 推理阈值二分类和多分类的调法不一样二分类模型的概率阈值默认 0.5但在建筑物提取、道路提取场景0.45 和 0.55 的结果差异可能很大。阈值调低找回细碎目标但虚警变多调高边缘更干净但漏检更多。别拍脑袋定阈值在验证集上从 0.3 到 0.6 扫一遍画 IOU 曲线选最高点。多分类模型用 argmax 取类别没有阈值问题但可以加最小置信度过滤例如概率低于 0.7 的像素置为背景。代价是阴影遮挡的真实目标可能被滤掉适合背景不重要的场景。4.4 形态学后处理先补空洞再按连通域过滤预测掩膜里最典型的噪声是小碎块和内部空洞。闭运算能填掉建筑内部漏检的小洞按连通域面积过滤能删掉道路旁零星噪声。顺序必须先把空洞补上再做面积过滤否则面积统计不稳定。import cv2 import numpy as np mask (mask 0).astype(np.uint8) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) num, labels, stats, _ cv2.connectedComponentsWithStats(mask) area_thresh 20 keep np.where(stats[:, 4] area_thresh)[0] mask np.isin(labels, keep).astype(np.uint8)MORPH_CLOSE是用 5×5 结构元先膨胀后腐蚀把细小孔洞填平connectedComponentsWithStats统计每个连通域面积小于area_thresh的置 0。核心参数是area_thresh它由影像地面分辨率决定0.5 米分辨率影像上一栋一百平米的房子约 400 像素2 米分辨率只有 25 像素按实际 GSD 换算别固定给 20。4.5 类别不均衡先看占比再决定改损失还是改采样遥感影像类别天然不均衡水体、植被占地多房子、道路占地少。用交叉熵训练时小类别的梯度被大类淹没结果就是小目标类别输出近乎全黑。判断方法是数标签文件里各类别像素占比相差两个数量级基本就是这个原因。解决办法是过采样加损失函数调整两者一起用比只调一样有效。比如滑窗时强制让小类别像素出现在一定比例的窗口里训练损失换成带 focal loss 的变体难样本贡献更大梯度。这个参数藏在训练脚本里不属推理配置但它决定了你推理阈值怎么调。5. 遥感影像深度学习避坑实录5 个必须排查的翻车场景把前面几章放到真实项目里下面的坑几乎人人都踩过。按现象、原因、解决三段写照顺序排查比盲目调参快。5.1 现象预测结果是一块块马赛克每块中央准、边缘错原因滑窗推理时 overlap 太小边缘预测被直接丢弃最后拼接又没有做融合。模型在 patch 边缘的表现天生比中央差这是卷积填充造成的边界效应不是过拟合。现象最典型的特点就是每块中央轮廓清晰靠近块边界的线状目标连续断开。解决overlap 提高到 tile_size 的四分之一并在拼接时做加权平均让每块中央的预测权重高于边缘。简单实现是保存一张距离权重图中央为 1边缘线性衰减到 0.2重叠区按权重混合。边缘不整除时常见做法是补零预测再按真实长度裁回不要整块丢弃否则窄条目标直接消失。5.2 现象水域被当成建筑物阴影里全是道路原因训练数据没覆盖这类光谱特征。水体在近红外波段的表现和建筑物阴影在某些波段下很接近模型的决策边界在训练数据分布内是合理的一出分布就翻车。这是最典型的训练域偏移和玄学无关想清楚光谱分布就能定位。解决影像含近红外波段时把它加入输入通道模型可以用 NDVI 这类指数区分水体与植被减少误分。把阴影单独当一个类别也是常见做法后处理阶段再按几何特征过滤掉细长虚警。核心是给模型增加光谱维度的区分信息而不是简单堆训练数据。5.3 现象几 GB 影像在 CPU 推理时内存爆炸原因代码里src.read()一次读掉整幅影像加上 float32 转换和几千个 patch 的列表内存峰值能到原始文件的十倍。很多演示脚本默认你有 32 GB 内存实际跑的时候才发现不够。解决改成窗口按需读取用 rasterio 的Window一次只读一个 patch 需要的矩形patch 用完即丢。大图也可以写进np.memmap让操作系统管换页滑窗与读取同步进行内存能压到 1 GB 以下。with rasterio.open(scene.tif) as src: for y in range(0, h - TILE_SIZE 1, step): for x in range(0, w - TILE_SIZE 1, step): window rasterio.windows.Window(x, y, TILE_SIZE, TILE_SIZE) tile src.read(windowwindow)[:3]注意Window参数顺序是 x 在前、y 在后也就是先列后行容易和 numpy 里[y, x]的习惯搞混写反了照样能读出数据只是位置完全错位且不报错。5.4 现象矢量化输出的多边形边缘锯齿明显面积统计偏大原因预测掩膜逐像素边界不平滑矢量化后自然锯齿再掺进零碎噪声点面积会被放大。GIS 里直接用这种矢量统计面积误差能超过 10%。解决矢量化前先做一次中值滤波或形态学开闭运算再用 GDAL 的 Simplify 做顶点抽稀tolerance 取 1 到 3 个像素肉眼几乎看不出损失文件体积小很多。统计面积时一定要转投影坐标系再计算直接拿经纬度算面积结果没有参考价值。5.5 现象训练损失一直在降但验证集的小目标类别全黑原因类别分布失衡损失被多数类主导模型学到了“全预测成背景损失也不高”这条捷径。这是遥感标注数据里最常见的结构性问题目标越细小越容易踩中。解决损失换成 Focal Loss 或 Lovász-Softmax让模型更关注难样本同时抽 patch 时提升包含小类别样本的窗口比例相当于过采样。两件事一起做效果最好把道路类像素覆盖的 patch 比例从自然分布的 10% 提到 30%小类别 IOU 往往能翻一倍。验证时除了看整体 IOU还要分别看每个类别的 IOU整体分数会掩盖小类别的灾难。6. 进阶局部聚焦辅助标注把人工标注成本降下来推理流程稳定后下一步通常是对新区域做标注和增量训练。这时人工逐像素画边界成本会迅速超过工具本身的价值。常见做法是局部聚焦辅助标注用已有模型先预测再让标注员只修正低置信度区域而不是从头画起。流程如下。先用现有模型对新影像预测并生成掩膜再把掩膜转成半透明叠加层叠加在原图上。然后把影像和掩膜同时切成小窗口按模型置信度排序标注任务置信度高的窗口由软件自动确认置信度低的碎块、边缘和误检区域才推给人工修正。每个标注任务只放大显示一个目标及小范围上下文人工框选或局部重画即可。关键在设计低置信度窗口的筛选规则常见实现是统计每块 patch 中低于概率阈值的像素占比超过 5% 就进入人工队列。这套流程省下的时间相当可观。一名熟练标注员一天精标 5 到 8 平方公里的建筑物用辅助标注在建成区这类模型表现好的场景能做到 20 到 30 平方公里高密度城中村这类模型常翻车的场景也能省下一半工作量。省下来的人力要投到困难样本收集上也就是模型低置信度或预测错误的 patch这才是持续提升精度的增量来源。我的习惯是每轮辅助标注后统计人工修正比例。低于 10%说明模型在这个区域基本够用可以扩大覆盖范围高于 40%先回头看训练数据分布和归一化参数把模型底子修好再继续。这个比例就是判断值不值得继续投入的标尺。把带坐标的辅助标注样本喂回训练集重训一轮再用新模型跑新一轮预测循环往复。我自己在遥感影像智能分析这个方向上的经验是真正卡住项目进度的很少是网络结构而是数据、归一化参数和后处理流程这些细节。希望这条基于深度学习的遥感影像智能分析落地路径能帮到你。本文还有配套的精品资源点击获取