简介遥感城市图像语义分割数据集约1000张已标注图像与对应掩码面向遥感影像分析与语义分割学习者可直接用于8类别图像分割模型的训练与验证尤其适合海陆区域、建筑背景等场景的精细化分割。资源包共2000个文件包含1156个PNG、842个JPG、1个txt及1个pyPNG与JPG共同构成原始影像与对应掩码txt记录类别标签py为可视化脚本整体压缩后仅42.05MB。目前已有130人学习下载数据集已划分训练集约800张、验证集约300张原始图片与GT掩码一一配套无需额外预处理即可送入网络。同时附带可视化脚本可随机抽取一张样本将原图、GT图像及GT蒙版叠加图展示并保存到当前目录便于直观检查标注质量与模型输入。该数据集非常适合U-Net、Swin-UNet等语义分割网络的快速实验与改进。1. 遥感城市图像语义分割数据集1000张图能训出什么先说清楚手里攒了一批城市遥感影像想直接训语义分割模型却卡在标注上——这是很多做城市规划、生态监测和遥感方向的同学都遇到过的尴尬。这个遥感城市图像语义分割数据集约1000张图和对应的标签8类别关键信息是“已处理完可以直接训练”意味着拿到手不必再折腾标注格式转换、类别映射这些脏活。它适合谁刚接触遥感语义分割、想用现成数据把模型训练流程跑通的新手以及想快速验证某个分割算法比如SegFormer、U-Net变体在遥感场景下效果的老手。这篇文章讲清楚数据集内部结构、训练配置和踩坑点照着做就能把这个数据集变成你的模型性能基线。2. 数据集里到底有什么8类标签、目录结构与标注格式2.1 8个类别都是什么从背景到建筑先建好类别清单拿到数据集第一步不是写代码而是打开类别定义文件搞清楚“每个像素的标签值”对应“地面上什么东西”。遥感城市影像的语义分割和自然图像分割有个显著差异类别里几乎一定包含“阴影”或者“背景”这类干扰项而且建筑物、道路、植被这些类别的像素占比极不均衡。这个数据集共8个类别常见做法是前景类别从1开始编号0留给背景或未标注区域。一个典型的遥感城市8类别设定是像素值类别名英文标签配置里用典型占比区间估0背景/未标注background10%~30%1建筑物building15%~35%2道路road10%~20%3植被vegetation15%~35%4水体water5%~15%5裸地barren3%~10%6车辆vehicle1%~5%7阴影shadow2%~8%“背景”在某些数据里是真实类别比如未被分类的空地在另一些数据里是标注时留下的边界过渡区。训练前务必确认这一点因为mIoU计算时背景类会显著拉高或拉低分数。我遇到过一种情况某份数据的0类实际是人工标注遗漏占比接近40%直接训练会让模型学会“偷懒”输出全背景指标还虚高。2.2 目录结构与文件命名拿到手先看这棵树不管从哪个渠道获取数据先花五分钟把目录结构看一遍。一个“已处理完、可直接训练”的数据集目录组织通常遵循某种约定比如VOC风格或ADE风格。常见做法是这样的data/ ├── images/ # 原始RGB影像 │ ├── img_0001.png │ ├── img_0002.png │ └── ... ├── labels/ # 标签掩膜 │ ├── img_0001.png │ ├── img_0002.png │ └── ... ├── train.txt # 训练集文件清单每行一个文件名无扩展名 ├── val.txt # 验证集文件清单 └── test.txt # 测试集文件清单可能没有注意文件名是否一一对应、图片和标签是否同名同前缀。最容易翻车的点是扩展名不一致图片是.jpg标签是.png但train.txt里只写文件名不带后缀那么加载代码一旦默认补全.png或.jpg就全部错位。另一个常见坑是train.txt里混入了Windows换行符\r\n在Linux上读取时会报“文件不存在”用cat -A train.txt | head可以确认。如果数据没有划分train/val/test你需要按比例自己划分。遥感语义分割有个和自然图像不同的细节同一张影像被切块后相邻图块高度相关如果随机划分会引入数据泄漏验证集分数虚高。正确做法是按“原始大图”分组划分保证同一原始影像的所有切块都在同一个集合里。2.3 标注格式为什么“已处理完”不等于“直接用”“已处理完”这三个字最容易被忽略它通常意味着标注已经做了类别合并把细粒度类别合并成8类、颜色映射从PNG的RGB颜色转成像素索引、尺寸统一。也就是说你现在看到的labels目录大概率是单通道的索引图黑色背景每个像素值直接对应类别ID。验证标注是否到位用一段脚本扫一遍就行了import numpy as np from PIL import Image import glob label_paths sorted(glob.glob(data/labels/*.png)) print(f共发现 {len(label_paths)} 张标签) for p in label_paths[:5]: la np.array(Image.open(p)) print(f{p.split(/)[-1]}: shape{la.shape}, dtype{la.dtype}, f唯一值{np.unique(la)}, 像素范围[{la.min()}, {la.max()}])这段脚本的作用是核对三件事第一标签是否是单通道shape是H×W而不是H×W×3如果是三通道说明还是RGB可视化图需要做颜色到索引的映射第二dtype是否uint8是的话说明像素值在0~255之间第三唯一值是否包含“类别列表之外的数字”比如出现了255或10就说明标注里混入了其他类别训练时类别数配置不对会直接报shape错误。提示遥感影像输入尺寸通常是1024×1024或512×512比自然图像大。如果标签图是RGB伪彩色转换时别直接用np.argmax要用颜色查找表colormap匹配一个像素颜色可能对应多个类查表时注意“颜色冲突”。3. 用SegFormer跑通第一次训练加载配置、改数据路径、启动命令3.1 为什么选mmsegmentation而不是自己写训练循环别自己写训练循环。遥感语义分割虽然任务标的标准但实现细节极多类别不平衡的损失权重、验证时的mIoU计算、学习率调度、多尺度测试、结果可视化。这些自己从零写光调通就要好几天。常见做法是直接用mmsegmentation这个框架它把数据集加载、模型、训练、评估都封装好了。数据侧你只需要把数据集转成mmseg支持的格式并写一个简短的dataset类配置。选模型时优先看SegFormer。这个模型在遥感分割场景里口碑不错它兼顾了全局上下文和细节对道路这种长条形结构的分割效果好而且没有类似U-Net那种层层下采样导致小目标丢失的问题。遥感图像里建筑物边缘、车辆这些小目标恰恰最需要细节。如果你显卡显存宽余想冲更高精度Mask2Former也可以接在同一个框架里先拿SegFormer跑通再替换模型配置即可。3.2 改数据配置文件的三个关键位置mmseg的配置采用“继承”机制你不需要从头写一个完整的config文件。这里以SegFormer的mit-b0为例给一个能直接改用的最小配置# configs/_base_/datasets/rs_city_8class.py dataset_type CustomDataset data_root data/rs_city/ img_norm_cfg dict( mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375], to_rgbTrue) crop_size (512, 512) train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations), dict(typeResize, img_scale(1024, 1024), ratio_range(0.5, 2.0)), dict(typeRandomCrop, crop_sizecrop_size, cat_max_ratio0.75), dict(typeRandomFlip, prob0.5), dict(typePhotoMetricDistortion), dict(typeNormalize, **img_norm_cfg), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_semantic_seg]), ] test_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations), dict(typeResize, img_scale(1024, 1024)), dict(typeNormalize, **img_norm_cfg), dict(typeDefaultFormatBundle), dict(typeCollect, keys[img, gt_semantic_seg]), ] data dict( traindict(typedataset_type, data_rootdata_root, img_dirimages, ann_dirlabels, splittrain.txt, pipelinetrain_pipeline, classes(background, building, road, vegetation, water, barren, vehicle, shadow)), valdict(typedataset_type, data_rootdata_root, img_dirimages, ann_dirlabels, splitval.txt, pipelinetest_pipeline, classes(background, building, road, vegetation, water, barren, vehicle, shadow)))三个关键位置分别是classes元组必须和你的8类别顺序完全一致顺序不一致会导致标签错乱ann_dir指向标签目录且必须是索引图split文件路径写正确并且split里的文件名不带扩展名。cat_max_ratio0.75表示随机裁剪时限制某个类别占比不超过75%这是遥感分割最重要的一条参数——它防止裁剪块里全是背景或单一类别导致模型学不到边界。3.3 训练命令与输出日志怎么看配置改好后一条命令启动训练。mmseg官方脚本里单卡训练的命令是python tools/train.py configs/segformer/segformer_mit-b0_512x512_160k_rs_city.py --work-dir work_dirs/segformer_b0_rs8c --seed 42没做预训练的话收敛速度会明显更慢。常见做法是加载在ImageNet-1k上预训练的主干权重这是新训练更稳的开局方式。显存不够时调低crop_size到(512, 512)或(384, 384)batch size按显存调8G显存建议batch size为4到8。日志里重点看这几个数loss曲线是否下降、mIoU是否逐epoch上升、每张图的耗时训练速度有没有异常波动。如果loss下降但mIoU不动问题多半在类别不平衡或标签错误。注意第一次训练建议先跑50个iteration而不是完整epoch先确认数据加载没报错、loss在下降再去睡觉挂长训。50个iteration出错成本最低直接CtrlC改配置再重来比天亮发现路径写错空跑一夜舒服得多。4. 遥感语义分割的5个高频踩坑点标签错位到类别不平衡4.1 现象训练刚开始就报shape不匹配或label越界训练脚本启动后几秒内崩溃错误信息类似IndexError: index 12 is out of bounds for axis 0 with size 8。原因是标签图里出现了类别数之外的像素值可能是把RGB颜色图直接当索引图喂进去了也可能是某个类别漏了合并。原因标注原始图中存在不属于8类的像素例如255、透明通道0或者标签是三通道彩色图直接按索引读导致越界。解决先运行2.3里的检查脚本确认唯一值如果发现非法值用掩膜替换——把所有不在[0,7]范围的值统一改成0背景。这段修正代码排查时很常用import numpy as np from PIL import Image import glob for p in glob.glob(data/labels/*.png): la np.array(Image.open(p)) if la.ndim 3: # RGB伪彩色图 - 灰度索引图这里假设颜色已在0~7范围内 la la[:, :, 0] la[(la 0) | (la 7)] 0 Image.fromarray(la.astype(np.uint8)).save(p)4.2 现象loss在下降但mIoU卡在30%上下不动数据集8个类别训练几十个epoch后训练loss还在降验证mIoU却纹丝不动。这几乎都是类别不平衡在作祟。遥感城市影像里车辆和阴影占比很小模型很快学会忽略这两个类别把像素分给背景和道路整体loss依然好看。解决给损失函数加权重。mmseg的CrossEntropyLoss支持class_weight参数权重按“类别像素占比的倒数”算。比如统计结果显示车辆类只占全图2%就把它的loss权重设为50背景占20%就设5。计算方式常见做法是用中位数频率平衡法代码import numpy as np from collections import Counter # 统计所有标签的像素频次 counter Counter() for path in label_paths: la np.array(Image.open(path)).flatten() counter.update(np.unique(la).tolist()) # 注意补上像素值为0的语法 freq np.array([counter.get(i, 0) for i in range(8)], dtypenp.float64) freq / freq.sum() class_weight np.median(freq) / (freq 1e-8) print(class_weight)权重算出来直接填进config里的lossdict(typeCrossEntropyLoss, class_weightclass_weight.tolist())。4.3 现象训练集mIoU到了90%验证集只有50%这是过拟合。1000张遥感图像在这个任务里属于“小数据”量级SegFormer的transformer结构参数多硬train几十个epoch模型会把训练集背下来。缓解手段按优先级排先开数据增强再考虑换小模型。解决把RandomFlip改成RandomFlip加Rotate遥感影像对旋转不敏感90度旋转在语义上依然正确、加RandomBrightnessContrast。同时把学习率调低或者把crop_size从512降到384。如果还压不住把backbone换成mit-b2或更小的mit-b0。遥感分割里“数据增强抗过拟合”比“换模型”优先级更高因为遥感影像的拍摄角度多样增强带来的泛化收益比自然图像明显。4.4 现象验证分数和paper说的差一大截很多遥感语义分割论文报告的mIoU都是基于某种特定验证协议多尺度推理、翻转融合测试时把图片水平翻转再推理一次两次结果取平均、滑窗重叠推理。这些技巧在测试时可以稳定提升1~3个点mIoU但如果你只做单尺度单方向推理自然对不上。解决测试时开启--aug-test或者在config的test_cfg里设置modeslide、crop_size(512, 512)、stride(384, 384)。滑窗推理对遥感这种大图特别重要。注意滑窗推理时间会翻好几倍一张1024×1024的图可能要跑20秒这是时间换精度的问题按照你要交付的场景取舍。4.5 现象保存的预测图只有黑白灰画不了彩色的效果图训练结束生成预测图发现每一张都是灰蒙蒙的类别信息要在但没法拿去汇报展示。原因是保存的预测图是索引图单通道像素值是类别ID 0~7不是RGB可视化图。解决推理后做一次颜色映射。写个小工具把索引转成RGB并叠加半透明图层import numpy as np from PIL import Image COLORMAP np.array([ [0, 0, 0], # background 黑 [255, 0, 0], # building 红 [255, 255, 0], # road 黄 [0, 255, 0], # vegetation 绿 [0, 0, 255], # water 蓝 [128, 128, 128], # barren 灰 [255, 128, 0], # vehicle 橙 [128, 0, 128] # shadow 紫 ], dtypenp.uint8) def index_to_rgb(index_map): rgb COLORMAP[index_map] return Image.fromarray(rgb) # 使用pred是单通道索引图 pred_rgb index_to_rgb(pred) pred_rgb.save(visualize/pred_rgb.png)这类问题不是模型问题是工作流缝合问题。很多人训出好模型却栽在“预测图怎么导出”这种细节上务必提前做一次端到端验证别等到交付那天才开始调颜色。5. 把模型用到自己的影像上推理脚本、滑窗策略与验证指标5.1 单张影像推理的最小脚本模型训练完最终要做的是对新遥感影像推理。拿到一张大图常见做法是先切成小块再逐块预测最后拼回全景。推理脚本不依赖mmseg也能跑因为推理本质只是“图像预处理 forward argmax”可以直接从训练好的pth权重加载backbone和decode head。一个可用的推理流程如下import torch import numpy as np from PIL import Image from mmseg.apis import init_segmentor, inference_segmentor config_file configs/segformer/segformer_mit-b0_512x512_160k_rs_city.py checkpoint_file work_dirs/segformer_b0_rs8c/best_mIoU_iter_8000.pth model init_segmentor(config_file, checkpoint_file, devicecuda:0) img Image.open(data/rs_city/images/your_img.png).convert(RGB) # 滑窗推理重叠128像素 result inference_segmentor(model, np.array(img)) pred result[0].astype(np.uint8) Image.fromarray(pred).save(output/your_img_pred.png)这里inference_segmentor已经内置了test时间的数据增强和归一化逻辑。参数上注意device要和你训练时一致否则权重载入时出现key不匹配报错。预测结果result[0]是单通道索引图要确认像素值范围在0~7内再去做VIS颜色映射。5.2 验证指标mIoU还是Kappa差在哪训练过程中mmseg默认记录mIoU但遥感领域中很多审稿人和交付方还要求Kappa系数它衡量的是“排除随机一致性的分类一致度”对极度不平衡的类别比mIoU更敏感。计算Kappa不复杂from sklearn.metrics import cohen_kappa_score import numpy as np # 假设gt_val是展平的真实标签pred_val是展平的预测标签 kappa cohen_kappa_score(gt_val, pred_val) print(fKappa: {kappa:.4f})这两类指标的区别要想清楚mIoU对每一类平等看待小类拉低分数的程度有限Kappa受大类别占比影响更大。在车辆、阴影这类小类上模型几乎全分错但Kappa可能依然高达0.85。所以汇报结果时mIoU和Kappa都要算单独报一个都会让结果看起来比实际乐观或悲观。评测代码建议固定一套offline脚本不要每次在线算保证前后可比。5.3 进阶用法用训练好的模型做伪标注扩展数据集如果你手里还有几千张没有标签的遥感影像一个最实用的进阶技巧是“伪标注pseudo-label迭代”。用这个数据集训好的模型对未标注影像做推理筛选置信度高的区域作为新标注像素级softmax最大概率大于0.9的才保留混合真实标签一起再训一轮。这个技巧对遥感语义分割特别有效因为遥感影像的类别分布相对稳定模型迁移到同城区不同时相的影像时置信度高的预测通常很可靠。注意筛选阈值不要设得太低否则噪声标注会拉低模型精度一般从0.9起步每轮提高0.02。这算是一种“自举”训练成本低收益在精度和泛化能力上都很直接。到了这一步这个数据集的用法你已经完整走通了从目录检查、格式验证到训练配置、坑点排查再到推理和指标评估。折腾完这一整套流程我自己的体会是遥感分割翻车基本都翻在数据细节上而不是模型结构上类别顺序错了、索引和RGB混了、验证集泄漏一个都别小看。希望帮到你少熬几个夜。本文还有配套的精品资源点击获取