
简介这份资源面向深度学习图像分割方向的学习者与研究者提供大分辨率遥感影像道路提取的完整数据集适合训练与评测分割网络、验证模型在复杂遥感场景下的泛化能力。数据集已预先划分训练集与测试集训练集含4981张图像及4981张对应掩膜测试集含1245张图像及1245张对应掩膜前景像素标注质量较高可直接用于分割网络的测试与对比实验。压缩包共约2000个文件以1999个jpeg图像与掩膜为主另附1个Python可视化脚本整体约336.79MB采用7z格式打包。该脚本可随机抽取一张图片将原始影像、GT图像以及GT在原图上的蒙板叠加结果一并展示并保存至当前目录便于快速核验标注质量与分割效果。目前已有977人学习下载适合需要高质量遥感道路分割数据的中高级读者参考使用。1. 大分辨率遥感道路分割为什么你的模型在 512 切片上训得挺好一上原图就崩遥感影像道路分割这个方向我见过太多人卡在同一个地方拿公开数据集切好的 512×512 小块训练指标刷到 0.85 的 IoU信心满满地拿整幅 8000×8000 的卫星图去推理结果道路断成虚线交叉口糊成一团。问题不在模型在数据集的组织方式。这个标题讲的就是这件事一套面向大分辨率遥感影像的道路图像分割数据集已经划分好训练集和测试集。它解决的核心痛点是——遥感道路分割的难点从来不是「分割算法本身」而是大分辨率图像怎么切、怎么保证切片之间道路的连续性、训练集和测试集怎么划分才不泄漏。适合谁用做遥感解译的算法工程师、想拿道路提取做深度学习实战项目的人、以及需要一套干净数据快速验证 U-Net 或 DeepLab 系列模型的研究生。下面我按「数据集长什么样 → 怎么加载和预处理 → 怎么训 → 坑在哪 → 怎么验证」这条线把整套流程讲透。2. 遥感道路分割数据集的结构与切片策略先搞懂标签怎么对齐2.1 大分辨率遥感道路数据集的典型目录结构遥感道路数据集和自然图像数据集最大的区别在于分辨率。自然图像数据集比如 VOC、COCO单张图几百像素直接 resize 就行遥感影像动辄 5000×5000 以上一张图就上百 MB不可能整图塞进显存。所以这类数据集通常有两种组织方式第一种是原图 原图标签训练时在线切片。目录大概长这样dataset/ ├── train/ │ ├── images/ # 原始大图如 0001.tif (8000x8000) │ └── masks/ # 对应的二值标签同尺寸 ├── test/ │ ├── images/ │ └── masks/ └── splits/ ├── train.txt # 训练集文件名列表 └── test.txt第二种是预切片 切片标签已经切好成 512×512 或 1024×1024 的小块训练时直接读。目录里 images 和 masks 的文件名一一对应靠文件名匹配。我一般优先选第一种原因后面避坑章节会讲——预切片容易在切片边界把道路切断而且训练集和测试集如果来自同一张大图的不同切片会造成数据泄漏测试指标虚高。提示拿到数据集第一件事不是写模型是写个脚本统计图像尺寸分布、标签像素占比、正负样本比例。遥感道路分割里道路像素通常只占 3%~8%这个数字直接决定你后面 loss 怎么设。2.2 大图切片滑窗、重叠与边界处理大分辨率图像进网络必须切片。最朴素的做法是无重叠滑窗但道路是细长连续目标无重叠切片会在边界处把一条路切成两段模型学到的道路永远是「断头路」。常见做法是带重叠的滑窗重叠率一般取切片尺寸的 1/4 到 1/2。下面是我常用的切片脚本支持重叠和边缘补齐import os import numpy as np from PIL import Image def slide_crop(img, mask, crop_size512, stride384): img: HxWx3 numpy array mask: HxW numpy array (0/1) crop_size: 切片边长 stride: 滑动步长stride crop_size 时产生重叠 h, w img.shape[:2] crops [] # 计算需要补齐的边保证最后一块完整 pad_h (stride - (h - crop_size) % stride) % stride pad_w (stride - (w - crop_size) % stride) % stride img np.pad(img, ((0, pad_h), (0, pad_w), (0, 0)), modereflect) mask np.pad(mask, ((0, pad_h), (0, pad_w)), modereflect) H, W img.shape[:2] for y in range(0, H - crop_size 1, stride): for x in range(0, W - crop_size 1, stride): img_crop img[y:ycrop_size, x:xcrop_size] mask_crop mask[y:ycrop_size, x:xcrop_size] crops.append((img_crop, mask_crop, y, x)) return crops逻辑说明stride控制重叠程度取 384 时相邻切片重叠 128 像素能有效缓解边界断裂。np.pad用 reflect 模式补齐避免补黑边影响归一化统计。返回的(y, x)是切片在原图的左上角坐标推理时拼回原图要用。参数怎么调crop_size 受显存限制8G 显存跑 U-Net 一般能上 512跑 DeepLabV3 建议 384。stride 越小重叠越多训练样本翻倍但冗余也大我一般取 crop_size 的 0.75 倍。2.3 训练集与测试集划分别让同一张图同时出现在两边这是遥感数据集最容易翻车的地方。如果数据集是从几张大图切出来的划分时必须按原图划分不能按切片随机划分。否则同一张大图的相邻切片一张进训练集一张进测试集模型在测试集上等于见过「邻居」指标虚高十几个点很正常。正确做法先列出所有原图文件名按 8:2 或 7:3 划分原图再把属于训练原图的所有切片放进训练集。如果数据集已经帮你划分好了 train/test 目录先确认一下两个目录里的切片是不是来自不同的原图——看文件名前缀或者看图像内容有没有重叠区域。注意有些数据集号称划分好了实际是随机切分切片这种要自己重新按原图划分否则你调出来的超参全是过拟合的。3. 用 U-Net 跑通遥感道路分割从数据加载到第一个 baseline3.1 数据加载与增强遥感影像不能照搬自然图像那套遥感影像和自然图像的增强策略差别很大。自然图像常用的随机裁剪、颜色抖动在遥感上要谨慎颜色抖动会破坏地物光谱特征道路的灰度纹理是有物理意义的随机旋转 90 度可以但任意角度旋转会引入黑边且道路方向分布会失真。我一般用这几样增强水平翻转、垂直翻转、90 度整数倍旋转、轻微的亮度对比度扰动±10% 以内。下面是一个 Dataset 类import torch from torch.utils.data import Dataset import numpy as np import cv2 import random class RoadDataset(Dataset): def __init__(self, img_paths, mask_paths, crop_size512, stride384, augmentTrue): self.crop_size crop_size self.stride stride self.augment augment self.samples [] for ip, mp in zip(img_paths, mask_paths): img cv2.imread(ip, cv2.IMREAD_COLOR) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) mask cv2.imread(mp, cv2.IMREAD_GRAYSCALE) mask (mask 127).astype(np.uint8) for ic, mc, y, x in slide_crop(img, mask, crop_size, stride): self.samples.append((ic, mc)) def __len__(self): return len(self.samples) def __getitem__(self, idx): img, mask self.samples[idx] if self.augment: if random.random() 0.5: img np.fliplr(img).copy() mask np.fliplr(mask).copy() if random.random() 0.5: img np.flipud(img).copy() mask np.flipud(mask).copy() k random.randint(0, 3) img np.rot90(img, k).copy() mask np.rot90(mask, k).copy() img img.astype(np.float32) / 255.0 img (img - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] img img.transpose(2, 0, 1) return torch.from_numpy(img).float(), torch.from_numpy(mask).long()逻辑说明切片在__init__里一次性做完避免每个 epoch 重复切片浪费时间。增强只做几何变换和归一化不做颜色抖动。归一化用了 ImageNet 均值方差如果你数据集的影像分布差异大建议换成自己统计的均值方差。参数说明crop_size和stride要和切片脚本一致augment在验证和测试时设 False。3.2 U-Net 模型搭建与损失函数选择遥感道路分割的 baselineU-Net 依然是性价比最高的选择。道路是细长结构编码器-解码器加跳跃连接能保留细节。下面是一个精简版 U-Netimport torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.conv(x) class UNet(nn.Module): def __init__(self, in_ch3, out_ch2): super().__init__() self.d1 DoubleConv(in_ch, 64) self.d2 DoubleConv(64, 128) self.d3 DoubleConv(128, 256) self.d4 DoubleConv(256, 512) self.pool nn.MaxPool2d(2) self.bottleneck DoubleConv(512, 1024) self.u4 nn.ConvTranspose2d(1024, 512, 2, stride2) self.c4 DoubleConv(1024, 512) self.u3 nn.ConvTranspose2d(512, 256, 2, stride2) self.c3 DoubleConv(512, 256) self.u2 nn.ConvTranspose2d(256, 128, 2, stride2) self.c2 DoubleConv(256, 128) self.u1 nn.ConvTranspose2d(128, 64, 2, stride2) self.c1 DoubleConv(128, 64) self.out nn.Conv2d(64, out_ch, 1) def forward(self, x): d1 self.d1(x) d2 self.d2(self.pool(d1)) d3 self.d3(self.pool(d2)) d4 self.d4(self.pool(d3)) b self.bottleneck(self.pool(d4)) u4 self.c4(torch.cat([self.u4(b), d4], dim1)) u3 self.c3(torch.cat([self.u3(u4), d3], dim1)) u2 self.c2(torch.cat([self.u2(u3), d2], dim1)) u1 self.c1(torch.cat([self.u1(u2), d1], dim1)) return self.out(u1)损失函数方面道路像素占比低纯交叉熵会被背景主导。我一般用 Dice Loss BCE 的组合class DiceBCELoss(nn.Module): def __init__(self, weight0.5): super().__init__() self.weight weight self.bce nn.CrossEntropyLoss() def forward(self, pred, target): bce_loss self.bce(pred, target) pred_soft F.softmax(pred, dim1)[:, 1] target_float target.float() inter (pred_soft * target_float).sum() dice_loss 1 - (2 * inter 1e-6) / (pred_soft.sum() target_float.sum() 1e-6) return self.weight * bce_loss (1 - self.weight) * dice_loss逻辑说明BCE 提供稳定的梯度Dice 直接优化重叠度两者加权能缓解正负样本不平衡。weight取 0.5 是经验值如果道路占比低于 3%可以调到 0.3 让 Dice 占主导。3.3 训练循环与关键超参训练循环本身不复杂关键是几个超参学习率、batch size、优化器。遥感分割我一般用 AdamW初始学习率 1e-4配合余弦退火。batch size 受显存限制512 切片下 8G 显存大概能跑 batch 4用梯度累积凑到等效 16。import torch from torch.utils.data import DataLoader from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR device torch.device(cuda if torch.cuda.is_available() else cpu) model UNet(in_ch3, out_ch2).to(device) criterion DiceBCELoss(weight0.5) optimizer AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_max100) train_loader DataLoader(train_dataset, batch_size4, shuffleTrue, num_workers4) accum_steps 4 for epoch in range(100): model.train() optimizer.zero_grad() for i, (imgs, masks) in enumerate(train_loader): imgs, masks imgs.to(device), masks.to(device) preds model(imgs) loss criterion(preds, masks) / accum_steps loss.backward() if (i 1) % accum_steps 0: optimizer.step() optimizer.zero_grad() scheduler.step()逻辑说明梯度累积让显存不够时也能用大等效 batch。weight_decay设 1e-4 防止过拟合。余弦退火让学习率从 1e-4 平滑降到接近 0比阶梯下降更稳。参数怎么改如果训练 loss 震荡大把 lr 降到 5e-5如果收敛太慢检查数据归一化是不是有问题遥感影像的均值和 ImageNet 差很多时换成自己统计的。4. 大分辨率推理与拼接切片边界断裂怎么修4.1 滑窗推理与重叠区域加权融合训练时切片推理时也要切片然后把预测结果拼回原图。直接拼会在切片边界出现明显接缝因为每个切片独立预测边界处上下文不完整。解决办法是重叠推理 加权融合相邻切片重叠区域用高斯权重或线性权重做加权平均。def inference_large_image(model, img, crop_size512, stride384, num_classes2): model.eval() h, w img.shape[:2] pad_h (stride - (h - crop_size) % stride) % stride pad_w (stride - (w - crop_size) % stride) % stride img_pad np.pad(img, ((0, pad_h), (0, pad_w), (0, 0)), modereflect) H, W img_pad.shape[:2] prob_map np.zeros((num_classes, H, W), dtypenp.float32) count_map np.zeros((H, W), dtypenp.float32) # 高斯权重中心高边缘低 weight np.outer(np.hanning(crop_size), np.hanning(crop_size)).astype(np.float32) with torch.no_grad(): for y in range(0, H - crop_size 1, stride): for x in range(0, W - crop_size 1, stride): patch img_pad[y:ycrop_size, x:xcrop_size] patch patch.astype(np.float32) / 255.0 patch (patch - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] patch torch.from_numpy(patch.transpose(2,0,1)).unsqueeze(0).float().to(device) out torch.softmax(model(patch), dim1)[0].cpu().numpy() prob_map[:, y:ycrop_size, x:xcrop_size] out * weight count_map[y:ycrop_size, x:xcrop_size] weight prob_map / np.maximum(count_map, 1e-6) return prob_map[:, :h, :w].argmax(axis0)逻辑说明np.hanning生成一维汉宁窗外积得到二维高斯状权重切片中心权重高、边缘低融合时边缘预测被降权接缝自然消失。count_map记录每个像素被覆盖的权重和做归一化。参数说明stride越小重叠越多融合效果越好但推理越慢。实测 stride 取 crop_size 的 0.5 倍时接缝基本看不出来再小收益递减。4.2 后处理去除小连通域与道路细化模型输出难免有零星误检的小斑块以及道路边缘毛刺。后处理两步先按面积过滤小连通域再做形态学细化。import cv2 import numpy as np def postprocess(mask, min_area50): mask mask.astype(np.uint8) num, labels, stats, _ cv2.connectedComponentsWithStats(mask, connectivity8) clean np.zeros_like(mask) for i in range(1, num): if stats[i, cv2.CC_STAT_AREA] min_area: clean[labels i] 1 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) clean cv2.morphologyEx(clean, cv2.MORPH_CLOSE, kernel) return clean逻辑说明connectedComponentsWithStats统计每个连通域面积小于min_area的丢掉。闭运算填补道路上的小孔洞。min_area根据分辨率调0.5 米分辨率下道路宽度一般 4~8 像素min_area取 50 比较合适。提示后处理不要过度闭运算核太大会把两条平行道路粘在一起交叉口也会糊。3×3 是安全上限。5. 遥感道路分割的避坑清单5 个我踩过的坑5.1 坑一训练集测试集切片来自同一张大图现象测试集 IoU 0.88换一批真实场景图推理IoU 掉到 0.6。原因数据集划分时按切片随机分同一张大图的相邻切片一张在训练集一张在测试集模型见过「邻居」测试指标虚高。解决按原图划分。先提取所有切片文件名里的原图 ID按原图 8:2 分再分配切片。如果数据集已经分好写脚本检查 train 和 test 里有没有相同原图 ID。5.2 坑二无重叠切片导致道路断头现象推理结果里道路在切片边界处整齐地断开像被刀切过。原因训练和推理都用无重叠滑窗模型从没见过跨边界的道路学到的道路永远是断的。解决训练和推理都改成带重叠滑窗重叠率至少 25%。推理时用加权融合训练时重叠切片相当于数据增强让模型见到更多边界情况。5.3 坑三颜色抖动增强破坏光谱特征现象训练 loss 降不下去或者降下去但验证集不降。原因照搬自然图像增强用了 HSV 抖动、随机亮度对比度大幅扰动遥感影像的地物光谱被破坏道路和背景的区分特征被抹掉。解决遥感分割只用几何增强翻转、90 度旋转颜色扰动控制在 ±10% 以内。如果多光谱数据绝对不要做颜色抖动。5.4 坑四正负样本极度不平衡导致全预测背景现象训练几个 epoch 后模型输出全 0IoU 为 0。原因道路像素占比低纯交叉熵下模型发现全预测背景就能拿到很低的 loss陷入局部最优。解决换 Dice BCE 组合损失或者用 Focal Loss。另外在采样时对包含道路的切片过采样让每个 batch 里正样本比例不低于 20%。5.5 坑五推理时归一化参数和训练不一致现象训练指标正常推理结果一塌糊涂或者同一张图训练时预测对、单独推理时预测错。原因训练时用了 ImageNet 均值方差归一化推理时忘了减均值除方差或者用了不同的参数。解决把归一化参数写进配置文件训练和推理读同一份。遥感影像建议统计自己数据集的均值和方差别直接用 ImageNet 的。6. 验证这套数据集值不值得投入三个可量化的判断标准拿到一套遥感道路分割数据集怎么判断它值不值得你花时间我一般跑三个快速验证半天内出结论。第一看标签质量。随机抽 20 张原图把标签叠加到影像上看。道路标签是不是连续、交叉口有没有漏标、窄路有没有断。如果标签本身断断续续模型再强也学不出来。量化指标随机抽 100 个切片统计标签里道路连通域的平均长度如果大量连通域长度小于 20 像素说明标签碎片化严重。第二看划分合理性。写个脚本检查 train 和 test 的图像尺寸分布、道路像素占比分布。如果两个分布差异很大比如训练集都是城市道路、测试集都是乡村道路说明划分有偏。更隐蔽的是按原图泄漏检查方法前面说了看文件名前缀或做图像相似度匹配。第三跑一个 30 分钟 baseline。用 U-Net Dice BCEcrop 512stride 384训 20 个 epoch看验证集 IoU 能不能到 0.6 以上。如果 20 个 epoch 还在 0.3 徘徊要么数据有问题要么标签和影像没对齐。对齐检查很简单把标签和影像叠在一起可视化道路标签应该精确覆盖影像上的道路。下面这个表格是我判断数据集质量的速查表检查项合格线优秀线检查方法道路像素占比2%~15%4%~10%统计标签均值标签连通域平均长度30 像素80 像素连通域分析训练/测试分布差异KL 0.1KL 0.05尺寸占比直方图20 epoch baseline IoU0.550.65U-Net 快速训练切片边界断裂率10%3%重叠推理对比无重叠最后说个我自己的习惯每次拿到新数据集先花两小时做可视化把影像、标签、切片、拼接结果各看一遍。这两小时能省掉后面两天的 debug。遥感道路分割这方向数据质量决定上限模型只决定你离上限有多近。希望帮到你。本文还有配套的精品资源点击获取