首页
/ Ultralytics YOLO 语义分割数据集指南:PNG 掩码、YOLO 多边形标签与 YAML 配置全解析

Ultralytics YOLO 语义分割数据集指南:PNG 掩码、YOLO 多边形标签与 YAML 配置全解析

2026-09-06 22:52:09作者:毕习沙Eudora

本文系统讲解 Ultralytics YOLO 语义分割任务的数据集格式:如何组织 PNG 像素级掩码、如何用现成的 YOLO 多边形标签直接训练语义模型、YAML 中 masks_dirlabel_mapping 等字段的确切含义,以及 Cityscapes / ADE20K 等内置数据集配置的使用方法。读完本文,你将能够独立准备(或复用)一份语义分割数据集并完成 YOLO26 语义模型训练,同时理解数据加载器在底层如何区分两种标签格式、如何完成标签 ID 映射与背景类注入。

什么是语义分割数据集

语义分割(semantic segmentation)为图像中的每一个像素分配一个类别标签。与实例分割不同,语义分割不区分同一类别下的不同个体——训练目标是密集的类别图(dense class map),每个像素存储一个类别 ID。这意味着语义分割数据集的核心资产是与图像逐像素对齐的掩码图像(或可栅格化为掩码的多边形标签),而不是检测任务中的边界框。

支持的两种标签格式

数据加载器支持两种标签格式,且自动选择:当数据集 YAML 定义了 masks_dir 键,或数据集根目录下已存在 masks/ 文件夹时,走 PNG 掩码格式;否则回退到 YOLO 多边形标签格式。这一选择逻辑在 build_yolo_dataset 中实现:

elif cfg.task == "semantic":
    data_path = Path(data.get("path", ""))
    if "masks_dir" in data or (data_path / "masks").exists():
        dataset = SemanticDataset
    else:
        dataset = PolygonSemanticDataset

因此,masks/ 文件夹的存在与否本身就是一条分支条件,这一点对自定义数据集的目录布局影响重大。

PNG 掩码格式

每个样本对应一个图像文件和一个掩码文件。掩码是单通道图像(通常为 PNG),其中每个像素的值即对应图像像素的类别索引。规则如下:

  • 像素值 012、… 对应数据集 names 映射中的类别 ID;
  • 像素值 255 被当作 ignore 标签,在损失计算与指标统计中完全排除;
  • 掩码文件必须与图像文件共享同一 stem(文件名主干),例如 frankfurt_000000_000294.png
  • 掩码默认按 .png 解析,找不到时会尝试其他受支持的图像扩展名(.jpg.tiff 等)。强烈建议使用无损格式.png.tiff),因为 .jpg 等有损压缩会破坏类别 ID 的像素值。

默认目录布局是图像与掩码平行存放,YAML 中 masks_dir 的值会替换 images 路径分量来定位掩码:

dataset/
├── images/
│   ├── train/
│   └── val/
└── masks/
    ├── train/
    └── val/

例如当 masks_dir: masks 时,images/train/aachen_000000_000019.png 会自动配对 masks/train/aachen_000000_000019.png

从源码结构看,掩码路径的解析由 SemanticDataset.get_label_files 完成:它调用 img2label_paths 并传入 self.data.get("masks_dir", "masks") 与后缀 .png,即在 YAML 未写 masks_dir 时默认也是 masks。掩码的实际读取在 load_mask 中:

mask = cv2.imread(mask_file, cv2.IMREAD_GRAYSCALE)   # 灰度读取,保证单通道类别 ID
...
if int(self.data.get("nc", 0)) == 1 and self.labels[index]["is_1bit"]:
    mask[mask == 255] = 1  # cv2 expands 1-bit PNG foreground to 255.

值得注意的是 is_1bit 分支:对于单类别(二分类)数据集,1 位深 PNG 的前景像素会被 OpenCV 扩展为 255,代码会将其重新写为 1,避免被误判为 ignore 标签。

YOLO 多边形标签格式

如果你的数据集已经有 Ultralytics YOLO 多边形标签(每张图一个 .txt,每行 <class-index> <x1> <y1> <x2> <y2> ...),可以直接用它训练语义分割,无需转换为 PNG 掩码。行级格式说明见实例分割数据集文档

该路径的自动触发条件是:YAML 中省略 masks_dir并且数据集根目录(与 images 同级)下不存在 masks/ 文件夹——即使 YAML 没写 masks_dir,只要残留一个 masks/ 目录,加载器就会切到 PNG 掩码模式去那里找掩码。务必删除或重命名它。行为细节:

  • 多边形在加载时被转换为逐图像的语义掩码,且按面积排序,使小物体在重叠区域覆盖大物体;
  • 多类别namesN > 1):会在已声明类别之后自动追加一个 background 类,用于任何多边形未覆盖的像素。模型以 N + 1 个输出通道构建,最后一个通道是背景;
  • 单类别N == 1):仍按 1 类训练,掩码是二值的——你声明的类别为 1,未覆盖像素为 0,不会向 names 追加背景类;
  • 数据增强填充(padding)新增的像素仍用 255 作为 ignore 标签。

这一"自动追加背景类"的机制在 add_polygon_background 中实现:

def add_polygon_background(data: dict) -> dict:
    if data.get("masks_dir") or data.get("_polygon_bg_added"):
        return data
    nc = int(data.get("nc") or len(data.get("names") or {}))
    if nc == 1:  # binary: bg=0, fg=1 (implicit); model uses BCE on a single output channel
        data["bg_class_idx"] = 0
    else:
        names = dict(data.get("names") or {})
        names[nc] = "background"
        data["bg_class_idx"] = nc
        data["nc"] = nc + 1
        data["names"] = names
    data["_polygon_bg_added"] = True
    return data

多边形到掩码的栅格化逻辑位于 PolygonSemanticDataset.load_mask:先将归一化坐标反归一化到当前 (H, W),调用 polygons2masks_overlap 得到按面积排序的实例索引图(0 = 无多边形,1..N = 排序后的实例序号),再把实例序号映射回类别 ID;单类别时直接输出 {0=背景, 1=前景} 的二值掩码。

数据集 YAML 格式

语义分割数据集用 YAML 文件配置,主要字段如下:

说明
path 数据集根目录
train 训练图像路径(相对 path 或绝对路径)
val 验证图像路径(相对 path 或绝对路径)
test 可选的测试图像路径
masks_dir 语义掩码目录名。省略此键(且根目录无 masks/ 文件夹)即切换到 YOLO 多边形标签格式
names 类别 ID 到类别名的映射
label_mapping 可选,将源数据集 ID 映射到训练 ID 或 ignore_label

内置示例:Cityscapes8 配置

仓库内置 ultralytics/cfg/datasets/cityscapes8.yaml,完整展示了 masks_dir、19 类 nameslabel_mapping 三部分:

# Dataset root directory
path: cityscapes8 # dataset root dir
train: images/train # train images (relative to 'path') 4 images
val: images/val # val images (relative to 'path') 4 images

masks_dir: masks # semantic mask directory

# Cityscapes 19-class labels
names:
  0: road
  1: sidewalk
  2: building
  # ... 完整 19 类见原文件
  18: bicycle

# Map source label IDs to train IDs; ignore_label is converted to 255.
label_mapping:
  -1: ignore_label
  0: ignore_label
  # ... 源 ID 0-13、15-16、18、29-30 均映射为 ignore_label
  7: 0    # road
  8: 1    # sidewalk
  11: 2   # building
  # ... 其余 15 个 Cityscapes 源 ID 依次映射到 0-18
  33: 18  # bicycle

何时需要 label_mapping:当源掩码的像素 ID 不是连续的、与训练类别 ID 不一致时。上例中 Cityscapes 官方 labelIds 是 0–33 的非连续编号(如 road 是 7、building 是 11、car 是 24),YAML 通过 label_mapping 把它们重排为 0–18 的连续训练 ID,并把无训练价值的标签(unlabeled、void 等)统一映射为 ignore_label

从源码看,label_mappingSemanticDataset._parse_label_mapping 中被解析为整数到整数的映射(ignore_label 归一化为 255),随后 _build_label_luts 构建 256 项的前向/反向查找表(LUT),读取掩码时通过 convert_labelcv2.LUT 一次性完成全图 ID 重映射——这是逐像素操作,性能上是 O(1) 查表。此外,get_cache_hash 会把 label_mapping 的 JSON 内容纳入缓存哈希,修改映射后旧缓存会自动失效,无需手动清理。

使用方式:训练 YOLO26 语义模型

Python API 或 CLI 均可训练语义分割模型:

# Python
from ultralytics import YOLO

# 加载预训练语义分割模型
model = YOLO("yolo26n-sem.pt")

# 在 Cityscapes8 语义分割数据集上训练
results = model.train(data="cityscapes8.yaml", epochs=100, imgsz=1024)
# CLI
yolo semantic train data=cityscapes8.yaml model=yolo26n-sem.pt epochs=100 imgsz=1024

对应的模型结构配置(n 档)位于 ultralytics/cfg/models/26/yolo26-sem.yaml。完整的预训练模型基准表见语义分割任务页

内置支持的数据集

Ultralytics 为以下语义分割数据集提供了开箱即用的 YAML 配置:

  • Cityscapes:城市街景语义分割数据集,19 个训练类别。对应 cityscapes.yamltrain/val/test 分别含 2975 / 500 / 1525 张图像,download 字段是一个内联 Python 脚本:它遍历 leftImg8bit 下的 *_leftImg8bit.png,将每份图像与 gtFine 中对应的 *_gtFine_labelIds.png 重命名后复制到 images/{split}masks/{split},掩码缺失会抛出 FileNotFoundError。注意官方 Cityscapes 需手动下载解压到 path 下再运行脚本。
  • Cityscapes8:8 张图的 Cityscapes 小子集(train/val 各 4 张),用于快速测试与 CI 检查,download 字段是一个可直接下载的 zip URL,拉取即用。
  • ADE20K:场景解析数据集,150 个语义类别。对应 ade20k.yamltrain: images/trainingval: images/validationmasks_dir: annotations,其 label_mapping 将 ADE20K 的 1–150 源 ID 减一映射为 0–149 训练 ID,源 ID 0 映射为 ignore_label。下载为手动步骤(源文件约 1 GB,解压后目录名 ADEChallengeData2016)。

三个配置的共性:源 ID 与训练 ID 错位时一律用 label_mapping 弥合,忽略类统一收敛到 255

添加你自己的数据集

方案 A — PNG 掩码

  1. 图像存入 images/trainimages/val 等分片目录;
  2. 每张图对应一个单通道掩码,存入镜像的掩码目录 masks/trainmasks/val(stem 一致);
  3. 掩码像素值即类别 ID,需忽略的像素填 255
  4. 创建包含 pathtrainvalmasks_dirnames 的数据集 YAML;
  5. 仅当掩码 ID 需要转换为连续训练 ID 时才加 label_mapping
path: path/to/my-semantic-dataset
train: images/train
val: images/val
masks_dir: masks

names:
    0: background
    1: road
    2: building

方案 B — 多边形标签

  1. 图像与 .txt 多边形文件按实例分割的布局摆放;
  2. 创建含 pathtrainvalnames 的 YAML——不要写 masks_dir
  3. 确保数据集根目录不存在 masks/ 文件夹——它的存在会强制加载器切换到 PNG 掩码模式;
  4. 不要names 中手动添加 "background":多类别时加载器会自动追加;单类别时保持 1 类训练,你声明的类别在掩码中为 1,未覆盖像素为 0
path: path/to/my-polygon-dataset
train: images/train
val: images/val

names:
    0: person
    1: car

此外,Ultralytics Platform 提供面向 semantic 任务的多边形标注工具与 SAM 辅助的 Smart 标注,可直接在浏览器内标注并导出/训练多边形标签数据集,无需手动搭目录结构。

高频问题(FAQ)

语义分割掩码与实例分割标签有什么区别? 语义掩码是密集像素图,每像素存一个类别 ID,每张训练图配一张掩码图;实例分割标签是文本文件,每个物体实例占一行多边形坐标。

训练时哪个像素值会被忽略? 255。这些像素在损失与指标计算中被跳过,适用于 void 区域、未标注像素或不在训练类别集内的类别。

掩码文件名必须与图像文件名一致吗? 是。加载器把 images 目录分量替换为 masks_dir 后按同名查找掩码;.png 找不到时会回退尝试 .jpg.tiff 等其他扩展名,但回退并不校验无损性,因此仍应坚持无损格式。

能否直接使用原数据集的标签 ID? 若它们已与 names 的类别 ID 对齐,可以。若源 ID 非连续或含应忽略的标签,加 label_mapping 将源像素值转换为训练 ID。

能否用实例分割数据集训练语义分割? 可以。同一批 .txt 多边形标签可直接复用——省略 masks_dir 并确保根目录无 masks/ 文件夹,加载器会即时把多边形栅格化为逐图掩码;多类别(N > 1)时自动追加 background 类,模型以 N + 1 个输出通道构建;单类别(N == 1)时保持 1 类,掩码中声明类为 1、未覆盖像素为 0

Ultralytics 内置哪些语义分割数据集? Cityscapes(19 个城市场景类)、轻量 Cityscapes8(管线测试用子集)与 ADE20K(150 个场景解析类),各自的文档页均记录精确的类别列表、下载步骤与已验证的训练示例。

小结

Ultralytics YOLO 的语义分割数据集体系可以归纳为三条主线:

  1. 两种标签格式,一条分支规则——YAML 含 masks_dir 或根目录存在 masks/ 即走 PNG 掩码(SemanticDataset),否则走多边形栅格化(PolygonSemanticDataset),二者共用同一套训练入口;
  2. 255 是贯穿始终的 ignore 标签——无论是手写掩码、label_mapping 归一化的 ignore_label,还是增强填充的新增像素;
  3. label_mapping 是源 ID 与训练 ID 之间的适配器,配合内置的 Cityscapes / ADE20K YAML,官方数据集"下载即用";而多边形路径下加载器还会自动处理背景类的注入,多类别 +1 通道、单类别保持二值掩码。

按上述布局组织好数据、写好 YAML,即可用 yolo semantic trainYOLO(...).train(...) 直接开始训练。

登录后查看全文
热门项目推荐
相关项目推荐