ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

遥感影像语义分割7类数据集:从标签体系到训练实战

遥感影像语义分割7类数据集:从标签体系到训练实战 简介面向遥感图像解译的语义分割数据集适合计算机视觉、GIS与地学研究者用于建筑、土地、农田、植被、水体等地物识别与分割模型训练。图像统一为1024×1024分辨率jpg原图与png掩码标签严格配对能够支持细粒度分割与多尺度地物提取。数据已划分训练集149张图片149个掩码与验证集37张37个掩码并附带类别说明txt与Python脚本可快速完成数据加载、标签映射与预处理。整个资源压缩包共375个文件以186张jpg、187张png为主总体积仅47.03MB便于下载部署与分享目前已有166人浏览学习适合直接用于U-Net、DeepLabV3及YOLOv5分割等模型的训练与验证。提供明确的类别ID背景、土地、建筑、农田、植被、水体等配备类别文本与示例脚本可降低数据准备门槛也非常便于扩展到遥感地物分类、变化检测等研究方向。1. 遥感影像语义分割7类标签背后的真实工作量遥感背景下的语义分割和日常见到的街景分割完全是两个世界。街景分割的标签画起来直观汽车就是汽车、行人就是行人到了遥感影像里一栋建筑的顶部轮廓、一片山地的阴影边界、一块植被和农田的交界从高空俯瞰下去全是锯齿状、带状和模糊过渡。很多团队在公开数据集上跑Unet、SegFormer效果不错一换到自己采集的遥感影像上mIoU直接拦腰斩问题十有八九不在模型而在数据集的标签质量上。这个标题提供的是一份7类遥感语义分割数据集建筑、山地、植被加上水体、道路、裸地、农田等类别配套标签文件。它的价值不只是省掉你下载和清洗原始影像的时间而是让你直接把精力放到语义分割算法的调试、迭代和部署上。适合三类人刚入门遥感图像分割的研究生需要标准数据集验证模型效果做高精度遥感业务的工程师需要真实地形类别来测试算法边界以及准备投遥感方向论文、需要一份可复现实验数据的从业者。接下来我按拿到这份数据集之后最常走的路径来讲标签体系、训练接入、踩坑清单和进阶玩法。2. 七类标签体系与文件组织先搞懂标签文件里存的是什么2.1 七类地物的类别定义与标注逻辑遥感影像的语义分割标签核心问题是“这个像素属于哪一类”。7类标签看起来简单但每一类的标注边界都有隐含规则。建筑通常指屋顶轮廓不包括建筑阴影山地按等高线和山脊线划分连片的裸岩归入山地还是裸地不同数据集标准不一植被覆盖的是林地、灌木和草地而农田单独成一类时和植被的区别取决于有没有明显的田块边界。类别定义直接影响模型训练的上限。我见过有人拿到标签文件后直接拿去训练跑了200轮发现建筑和阴影总是混淆最后发现标签里阴影根本没单独成类而是被归到了裸地里。另一个常见的定义问题是道路水泥路、土路、乡村小路宽度差异极大标注时如果只标主干道模型学到的就是“宽的路才是道路”。这份数据集标的7类可以覆盖多数业务场景但你动手训练前必须先建立一个类别与RGB颜色的对照表确认每个像素值对应哪个类别。import numpy as np from PIL import Image # 读取标签文件查看类别ID分布 label_path labels/train_0001.png label np.array(Image.open(label_path)) # 统计所有类别ID及像素占比 unique, counts np.unique(label, return_countsTrue) total label.size for cls_id, cnt in zip(unique, counts): print(f类别ID: {cls_id}, 像素数: {cnt}, 占比: {cnt / total * 100:.2f}%)这段代码做的是最基本的事打开标签图统计每个类别占了多少像素。不要小看这一步它能直接暴露类别不均衡的程度——如果山地和植被占了80%以上像素建筑和水体加起来不到5%那么模型的mIoU一开始就会被大类拉高小类怎么学都学不好。参数上需要注意的是标签文件通常是单通道PNG或带调色板的PNG用Image.open读出来可能是P模式需要先.convert(L)转成灰度再转numpy数组否则你统计出来的是调色板索引而不是类别ID。2.2 数据集目录结构与标签文件的三种常见形态拿到数据集后第一件事不是打开深度学习框架而是先摸清目录组织方式。常见做法是 images 和 labels 两个主目录下面按 train、val、test 划分也可能所有影像放在一起另附一个 train.txt、val.txt 的划分文件。这份数据集如果带了划分文件建议直接采用不要自己重新划分——自己划分容易造成同一区域的不同影像块被拆到训练集和验证集导致验证指标虚高。标签文件的形态有三个层次。第一层是单通道灰度图像素值0到6分别对应7类这种最省内存也是PyTorch默认支持的形式。第二层是RGB彩色标签每个类别一种颜色视觉上直观但训练时需要把三个通道的RGB映射回类别ID映射表错了全部白费。第三层是JSON或GeoJSON格式的矢量边界这种多见于带地理坐标的专业遥感数据集需要先栅格化。三种形态里单通道灰度最容易出错的是“0到底是背景还是第一类”如果数据集的7类里没有背景类0就代表第一类如果训练时加了ignore_index还得单独处理。tree /dataset如果是在Linux环境用上面这条命令就能看到完整的目录结构。正常的遥感语义分割数据集应该是这个样子原始影像和标签图一一对应文件名前缀相同后缀分别是卫星影像和标签的约定后缀如果还有一份 class_dict.csv 或 label_info.json里面通常存了类别名和RGB颜色的对应关系这就是训练时写数据加载器的依据。我拿到任何数据集都会先核对三件事文件一一对应、类别数和预期一致、有没有损坏的标签图。2.3 标签文件的预处理裁剪、重投影与格式转换遥感影像的特殊之处在于原始尺寸通常很大512x512甚至1024x1024的图块直接扔进Unet训练是常态。数据集的标签文件如果已经是切好的图块那省了很大麻烦如果是一整张大影像加一个完整标签图你需要先做滑窗裁剪。裁剪时要注意重叠率——相邻图块之间留一定重叠可以避免目标恰好被切在边缘导致标签断裂。另一个隐蔽的坑是重投影影像来自不同传感器时原始影像和标签可能不在同一坐标系直接叠加会错位几个像素。这份数据集如果来源单一坐标大概率是齐的但你做多源数据融合时一定得查。格式转换是第二个高频需求。很多人拿到的是PNG标签训练框架要求的是灰度图或者反过来标签是RGB彩色图但训练代码默认读单通道。转换脚本的核心只有一条务必验证转换前后类别ID没有变化。from PIL import Image import os # RGB彩色标签转单通道灰度标签 def rgb_label_to_gray(rgb_path, gray_path, color_map): rgb Image.open(rgb_path).convert(RGB) pixels rgb.load() w, h rgb.size gray Image.new(L, (w, h)) gp gray.load() for y in range(h): for x in range(w): r, g, b pixels[x, y] gp[x, y] color_map.get((r, g, b), 0) gray.save(gray_path)这段转换脚本是像素级映射逻辑上没有任何优化空间就是逐像素查表。关键参数是 color_map——这是一个从RGB三元组到类别ID的字典必须和数据集的类别定义完全一致。举例说如果建筑的颜色是(255,0,0)山地的颜色是(0,255,0)表里写错一个通道建筑就会被映射成山地。我建议转换完成后立刻做一次反向校验统计灰度标签的类别比例和转换前的RGB标签逐类对比数值对不上就是映射表写错了。3. 把7类遥感语义分割数据集接入训练从数据划分到模型选型3.1 数据划分策略别让验证集变成“开卷考试”遥感语义分割数据集的划分原则是空间隔离。你可以按文件名随机划分也可以按区域划分。如果数据集的影像块来自同一张大图的不同位置随机划分就会出问题相邻图块内容高度相似验证集里出现的建筑和训练集里的建筑是同一栋楼的不同部分验证指标会虚高模型真正遇到新区块时就翻车。我处理遥感数据集的习惯是先看文件名是否带坐标信息或区域编号带的话按区域划分不带的话按影像块的中心点做网格划分。简单做法是拿文件名前缀做划分依据前缀相同的数据放入同一集合。训练、验证、测试的比例按7:1.5:1.5或8:1:1都行但要保证每个类别在三个集合里都有足够样本。一个容易被忽略的细节是验证集必须包含边缘案例——只有山地和植被的图块以及建筑密集的城市场景否则你验证的是模型在平均值上的表现而不是在困难样本上的表现。数据增强的选择也要跟着场景走。遥感影像的语义不变性包括旋转和翻转90度旋转、水平垂直翻转不会改变地物类别但颜色抖动要慎用不同时相、不同传感器的影像本来就存在辐射差异过度颜色增强会让模型学到错误的色彩特征。最常见的组合是随机水平翻转加随机旋转再加少量尺度缩放。除非你的业务场景明确需要多时相泛化否则不需要加复杂的色彩扰动。3.2 数据加载器实现归一化、均值方差与类别权重接入训练之前数据加载器里需要处理三个问题归一化方式、类别不均衡、标签的ignore_index。遥感影像的归一化常见做法是用ImageNet的均值和方差但遥感影像的波段分布和自然图像差异很大更好的做法是统计自己数据集的均值和方差。import numpy as np from PIL import Image import glob image_paths sorted(glob.glob(images/train/*.png)) pixel_sum np.zeros(3) pixel_sq_sum np.zeros(3) count 0 for path in image_paths: img np.array(Image.open(path).convert(RGB)).astype(np.float32) / 255.0 pixel_sum img.sum(axis(0, 1)) pixel_sq_sum (img ** 2).sum(axis(0, 1)) count img.shape[0] * img.shape[1] mean pixel_sum / count std np.sqrt(pixel_sq_sum / count - mean ** 2) print(mean:, mean) print(std:, std)这段代码逐张统计每个通道的像素均值和方差。逻辑不复杂但效果直接你拿遥感影像跑训练时用ImageNet的mean和std第一层卷积的输入分布就会偏移模型收敛速度明显变慢。我一般把统计结果硬编码到训练脚本里避免每次启动训练都重新统计一遍。注意这里除以255是常见做法你完全可以不除直接把原始像素值范围0-255进去算只要训练时保持一致即可。类别不均衡处理是遥感分割的必修课。7类里建筑和水体像素占比可能只有几个百分点模型会天然偏向占比大的类别。两种常见解决思路一是损失函数层面给每个类别加权权重和像素占比成反比二是在采样时对小类别所在图块做oversample。我建议先做损失加权改动最小、效果最直接。计算权重的代码通常在数据加载器里完成用torch.bincount统计标签频次然后取倒数再归一化注意要对标签值先过滤掉ignore_index对应的像素。3.3 模型选型Unet起步SegFormer做精度上限针对这份7类遥感分割数据集模型选型有一条清晰的路先用Unet系列把流程跑通再用SegFormer这类Transformer结构提升精度上限。Unet的优点是结构简单、显存占用可控、训练稳定适合做基线和排错SegFormer的优势是全局上下文建模能力强对大面积连片地物山地、植被的边界处理更好但训练时间更长、显存需求更高。如果你的目标是快速验证数据质量Unet在512x512输入下通常50-100轮就能收敛到可用的精度。一个常被忽略的选型维度是backbone的预训练权重。遥感影像和ImageNet分布差异大但backbone的前几层学到的边缘、纹理特征仍然有效。建议backbone用ImageNet预训练权重初始化解码器从头训练。我自己遇到的情况是完全从零训练Unet在遥感数据上需要多花一倍时间才能追上预训练权重的效果而且精度还不一定够。选择SegFormer时要注意版本选择B0到B5的参数量和精度差异很大7类分割任务用B2或B3性价比最高B4以上在小数据集上容易过拟合。评估指标以mIoU和类别IoU为主。mIoU是所有类别IoU的平均值但7类不均衡时mIoU会被大类主导。我一般还会额外看两个指标建筑类的IoU和边界区域的F1分数。建筑是多数遥感业务的核心关注对象建筑类IoU单独拉出来才有业务参考价值边界F1分数可以用分割结果和标签的边缘做匹配计算。训练时每5轮保存一次checkpoint同时记录验证集的逐类IoU这样能准确判断模型在哪个类别上开始过拟合、哪个类别的学习速度最慢。4. 遥感语义分割数据集的避坑清单7类标签训练时的典型踩坑记录4.1 标签颜色≠标签数值RGB映射表错位导致训练崩盘现象训练时loss正常下降但验证集mIoU始终在低位徘徊可视化预测结果发现所有类别整体偏移了建筑被预测成道路道路被预测成裸地。原因数据加载器里写死了RGB到类别ID的映射表但数据集的类别定义顺序和映射表不一致。比如数据集里山地的RGB是(128,128,128)映射表里却把(128,128,128)写成了裸地所有像素的标签整体错一位。解决写一个独立脚本加载一张训练标签图和一张验证标签图逐像素打印RGB值和对应类别ID手动核对映射表。更稳妥的做法是在训练前用程序自动校验读取标签图的unique值和类别定义文件逐一比对不一致直接报错。不要相信你记忆里的颜色顺序一定以实际文件为准。4.2 类别不均衡让mIoU虚高大类涨分、小类报废现象训练30轮后mIoU已经超过70%看着不错但检查逐类IoU发现建筑只有30%出头水体几乎没学会山地和植被的IoU高达90%以上。原因像素占比决定梯度贡献模型把所有像素都预测成山地和植被就能拿到高准确率mIoU被大类拉高掩盖了小类的失败。解决训练日志里同时输出逐类IoU不要只看mIoU。损失函数里对每个类别乘上权重权重取1/sqrt(class_frequency)做平滑数据加载时对小类样本做oversample把包含建筑或水体的图块以更高概率送入训练。如果你看到mIoU在涨但建筑类IoU不涨说明类别权重还没有生效。4.3 裁剪窗口切掉关键目标目标被劈成两半导致标签断裂现象训练出来模型对小尺寸建筑预测完整对大面积连片建筑反而出现中间断裂。排查发现验证集里很多建筑恰好被裁剪窗口边界切开模型学到的建筑特征是“带完整边界的块”。原因遥感大影像切成图块时没有重叠率或者重叠率过低目标物体被切到边缘后只有一个残片。训练集里这些残片被标注为建筑但模型学到的模式不完整。解决滑窗裁剪时相邻窗口保留20%-30%的重叠率损失一部分数据量换取完整目标。更稳妥的方式是用目标检测的方式先找出大目标再按目标边界中心裁剪。对于建筑这类有明确矩形的目标还可以做按目标中心点裁剪而不是按固定网格裁剪。4.4 阴影区域全部预测成山地遥感特有阴影干扰现象高层建筑旁边的阴影区域、山体背光面都被模型标成山地或裸地预测图上一片一片的深色区域都归了山地。原因阴影区域的像素值整体偏低纹理信息被弱化模型学到了“低亮度山地”的捷径。遥感影像的阴影是真实存在的但业务上往往需要把阴影归到对应的地物至少不能全部归到山地。解决两个思路。一是数据增强时对影像做随机亮度扰动破坏“低亮度对应阴影”的固定关联二是把阴影当作独立类别标注一次或者从标签里识别出阴影区域后归入对应的真实类别。实际项目里我倾向于在标签文件里补充阴影区域的类别修正代价是多一轮标注但对山地和建筑的分割精度提升明显。5. 从训练到产出批量推理脚本与分割结果的业务化输出5.1 推理阶段的滑窗拼接别让图块边界漏出马脚训练时切块推理时就得拼回完整大图。遥感业务里最终交付的通常是一整张区域的分割结果不是512x512的小图。推理阶段的最常见错误是每个小块独立推理拼回大图时边界出现明显的拼接缝或标签错位。解决方法是推理时也做重叠滑窗权重合并重叠区域。常见做法是把重叠区的预测概率做加权平均或者只取中心区域。实现上先做概率输出保存每个图块的softmax结果拼回大图时按权重叠加。import numpy as np import torch import torch.nn.functional as F def slide_inference(model, image, window_size512, stride384, batch_size4): _, h, w image.shape prob_map torch.zeros((7, h, w), dtypetorch.float32) count_map torch.zeros((1, h, w), dtypetorch.float32) windows [] coords [] for y in range(0, h - window_size 1, stride): for x in range(0, w - window_size 1, stride): patch image[:, y:ywindow_size, x:xwindow_size] windows.append(patch) coords.append((y, x)) with torch.no_grad(): for i in range(0, len(windows), batch_size): batch torch.stack(windows[i:ibatch_size]) batch batch.to(cuda) output F.softmax(model(batch), dim1).cpu() for j, (y, x) in enumerate(coords[i:ibatch_size]): prob_map[:, y:ywindow_size, x:xwindow_size] output[j] count_map[:, y:ywindow_size, x:xwindow_size] 1 prob_map / count_map.clamp(min1) pred prob_map.argmax(dim0).numpy().astype(np.uint8) return pred这段代码把一张大图按步长滑窗推理通过概率叠加和计数归一化实现重叠区域融合。关键参数是 window_size 和 stride 的比例stride 越小重叠率越高拼接边界越平滑但推理时间也成倍增加。我一般用512窗口配384步长即25%重叠在平滑度和耗时之间取一个平衡。注意 prob_map 的 num_classes 参数写死了7如果你的数据集类别数不同这里要跟着改。5.2 分割结果的矢量化与业务指标提取遥感分割业务的交付物往往不是像素级图片而是需要转换成矢量边界才能落到GIS系统里。像素分割结果转矢量有成熟的工具链二值化每个类别提取轮廓做边缘平滑和多边形简化。如果数据集的影像带地理坐标信息转换后的矢量还应该追加投影信息这样才能和底图对齐。import geopandas as gpd from rasterio.features import shapes # pred_mask 是单通道预测结果meta 是影像的元数据 results list(shapes(pred_mask.astype(int16), transformmeta[transform])) geometries [geom for geom, value in results if value ! 0] values [value for geom, value in results if value ! 0] gdf gpd.GeoDataFrame( {class_id: values, geometry: geometries}, crsmeta[crs] ) gdf.to_file(output/result.geojson, driverGeoJSON)这段代码用了rasterio的shapes函数做栅格转矢量输出的GeoJSON带有类别ID和地理坐标。参数上要注意 transform 和 crs 必须来自原始影像的元数据否则矢量会丢失地理位置。类别的业务化指标也在这一步体现建筑覆盖率是多少、水体面积有多大、植被占比如何这些数值在业务汇报和交付中远比一张预测图有说服力。5.3 模型精调的最后一公里类别权重微调与难例挖掘训练结束后如果建筑类IoU还差几个点我的习惯是微调类别权重或做难例挖掘。类别权重微调很简单把建筑类的损失权重再调高重跑20-30轮用之前的checkpoint做初始化。难例挖掘的做法是把验证集里建筑类IoU最低的几十个图块挑出来混入训练集再训一轮。这两种手段都改变模型结构只动数据和损失效果立竿见影。多波段遥感影像也是一个潜在提效方向。如果这份数据集的原始影像只有RGB三个波段那没什么可扩展的但如果原始数据源包含近红外或更多波段而数据集只提供了RGB你可以考虑拿原始多波段数据做输入扩展植被和山地的区分通常能靠红边波段提升精度。这类优化依赖数据源本身不是所有数据集都能做但值得在拿到数据时确认一下。我在遥感分割项目里养成的习惯是每次训练前先花半小时检查类别的样本量和边界质量宁可砍掉一批边界画得乱七八糟的数据也绝不让脏标签在训练集里捣乱。希望这份7类遥感数据集的落地路径能让你少走几趟弯路。本文还有配套的精品资源点击获取
返回列表