
简介面向家庭场景家具语义分割与全景分割任务的数据集资源图像统一为640×640分辨率标注覆盖背景、床、椅子、橱柜、门、灯、地毯、桌子、窗户等19类目标可用于细粒度分割模型的训练与效果验证。资源包共727个文件包括362张jpg原图、363张png掩膜图、1份txt类别标签说明和1个Python可视化脚本整体压缩后仅14.74MB轻量易用。数据集已划分为训练集309张图像及对应掩膜和验证集53张图像及对应掩膜并附带show脚本运行后可直接在原始图像上叠加查看掩膜结果帮助快速检查标注质量与模型预测效果。目前已有147人学习下载适合计算机视觉入门者、算法工程师及需要家庭场景标注数据的研究人员使用。1. 家庭场景全景分割数据集19 类家具掩膜到底能拿来做什么做图像分割的人应该都有这种体会公开数据集不少但大多偏向自动驾驶或街景像 ADE20K、Cityscapes 这类场景里的人和车占了半边天。真要做室内场景理解、家具识别、扫地机器人避障或者智能家居视觉方案反而找不到一个干净的、专门针对家庭场景家具的数据集。这个数据集补的正是这个空档。它是一套家庭场景下 19 类家具的全景分割数据训练集 309 张图片加对应掩膜验证集 53 张分辨率统一为 640×640图片是 jpg 格式掩膜是 png 格式标签文件用 txt 文本维护。拿到手直接就能用不用费劲去做格式转换。适合做家具级细粒度分割的算法验证、YOLOv8 分割任务的微调或者作为室内视觉项目的预训练数据。更关键的一点它附带了可视化脚本跑一下就能把掩膜叠到原图上对新手理解分割任务很有帮助对熟手来说也能快速确认标注质量。2. 先搞清楚数据格式图片、掩膜、标签文本三者怎么对应2.1 数据集的目录结构长什么样解压之后建议先用 tree 命令把目录结构过一遍。常见做法是先确认一下 images 和 masks 是不是一一对应的。这个数据集的做法是训练集和验证集各自独立目录images 下放原图masks 下放掩膜。掩膜是 png 格式因为 png 无损压缩分割任务的标准掩膜几乎都是 png这一点没有悬念。jpg 是有损压缩直接拿来做掩膜会在物体边缘出现伪影pixel 级别的标签一旦有损就会影响训练效果这个后面讲避开坑的时候还会再提。掩膜的命名和原图严格对应比如原图叫 95725a287808df0df79b3a7679ea550e_jpg.rf.xxx.jpg掩膜就叫 95725a287808df0df79b3a7679ea550e_jpg.rf.xxx.png。这种命名风格是 Roboflow 导出的典型方式从.rf.这个标记大致能推断这是从 Roboflow 平台导出的数据集。好处是所有文件名都带原图 id批量处理的时候不会乱。建议先写一个 Python 脚本核对一下是否有漏图或者多图的情况特别是如果后续要用 YOLOv8 进行训练这种一一对应关系直接决定了你的数据集能不能正常加载。import os train_img_dir train/images train_mask_dir train/masks train_imgs sorted(os.listdir(train_img_dir)) train_masks sorted(os.listdir(train_mask_dir)) img_stems {os.path.splitext(f)[0] for f in train_imgs} mask_stems {os.path.splitext(f)[0] for f in train_masks} missing_masks img_stems - mask_stems extra_masks mask_stems - img_stems print(f图片数量: {len(img_stems)}) print(f掩膜数量: {len(mask_stems)}) print(f缺少掩膜的图片: {missing_masks if missing_masks else 无}) print(f多余掩膜: {extra_masks if extra_masks else 无})这段脚本首先把 images 目录和 masks 目录下所有文件名取出来用os.path.splitext去掉扩展名拿到 stem再求差集来检查缺失和多余。如果结果显示无缺失说明图片和掩膜是对齐的。如果有多余掩膜一般是把同一场景的多个标注版本导出来了这种情况下建议手工挑出自己的目标文件不要让多余的掩膜混进训练集——它们是重复样本会引入偏差。2.2 classes.txt 标签文本的读取逻辑标签信息集中在 classes.txt 里这是一行一类的格式从 0 开始编号。摘要里提到 0 是背景1 是床2 是椅子3 是橱柜5 是门10 是灯14 是地毯15 是桌子18 是窗户。中间缺了编号这是 Roboflow 导出的常见现象——原始标注的时候有些类别没有出现在这个数据集的任何图片中就没有导出。所以你在代码里不要写死类别编号应该动态从 classes.txt 读取这样万一你后续往数据集里增加了新类别代码不用跟着改。def load_class_names(txt_path): with open(txt_path, r, encodingutf-8) as f: lines [line.strip() for line in f.readlines() if line.strip()] # 跳过注释行 lines [line for line in lines if not line.startswith(#)] class_names {} for i, name in enumerate(lines): class_names[i] name return class_names class_names load_class_names(classes.txt) print(class_names)encodingutf-8是必须的。Roboflow 导出的标签文件虽然基本都是 UTF-8但如果你在 Windows 上用记事本打开再另存过可能变成 GBK训练脚本就会在读取的时候直接报 UnicodeDecodeError这一条踩坑经验后面还会专门列出来。动态读取标签的意义在于任何训练框架无论 YOLO 还是 MMDetection最终训练时都要先加载一份类别映射表只有数据集的类别索引和你的模型输出头完全对齐才能跑通。2.3 可视化脚本 show.py一行命令看到掩膜效果这个数据集自带一个可视化脚本摘要里说得很直接——“运行 show 脚本即可查看 gt 在 images 上的掩膜结果”。这是一个非常实用的功能也是模型训练前必做的工作。原理很简单把 png 掩膜加载进来用 PyPlot 或 OpenCV 把掩膜作为半透明图层叠到原图上。常见做法是用cv2.addWeighted或者plt.imshow加上alpha参数。脚本主要逻辑是遍历指定目录下所有图片对每张图片加载同名掩膜并叠加显示。import cv2 import matplotlib.pyplot as plt import os def visualize_masks(img_dir, mask_dir, sample_name, alpha0.5): img_path os.path.join(img_dir, sample_name .jpg) mask_path os.path.join(mask_dir, sample_name .png) img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) plt.figure(figsize(12, 5)) plt.subplot(1, 3, 1) plt.imshow(img) plt.title(Original Image) plt.axis(off) plt.subplot(1, 3, 2) plt.imshow(mask, cmaptab20) plt.title(Mask (19 classes)) plt.axis(off) # 半透明叠加原图作为背景掩膜上色后叠加 overlay img.copy() mask_viz cv2.applyColorMap(mask.astype(uint8), cv2.COLORMAP_TURBO) mask_viz cv2.cvtColor(mask_viz, cv2.COLOR_BGR2RGB) blended cv2.addWeighted(img, 1 - alpha, mask_viz, alpha, 0) plt.subplot(1, 3, 3) plt.imshow(blended) plt.title(Overlay alpha0.5) plt.axis(off) plt.tight_layout() plt.show()这里参数alpha0.5表示掩膜的透明度。alpha 取值在 0 到 1 之间0 表示完全透明看不出来1 表示完全覆盖原图。建议可视化检查时取 0.4 到 0.6能同时看到物体边界和原图的纹理细节。掩膜用IMREAD_GRAYSCALE读是因为这种数据集的 mask 是单通道标注图每个像素的灰度值就是类别索引不是三通道 RGB 彩色图。如果你用三通道读法后面做np.unique(mask)统计类别时会出现三维数组处理起来会绕。可视化时cmaptab20适合 19 类这种量级的类别颜色区分度足够了。跑可视化脚本的意义是快速确认掩膜是否对齐、是否存在类别缺失、是否有大片漏标区域。建议每张图都过一遍309 张训练图虽说多了点但抽看 30 到 50 张基本能把明显的问题暴露出来。3. 全景分割是什么和语义分割有什么不一样3.1 从语义分割到全景分割的差异很多人在接触这个数据集时会有一个模糊点这个数据集到底是语义分割还是全景分割摘要里明确写了“全景分割图像”。全景分割和语义分割的区别在于语义分割只区分像素的类别不区分实例全景分割则要同时区分语义类别和实例编号。比如客厅里有两张椅子语义分割把所有椅子的像素统一标成类别 2全景分割则会标成椅子 1 和椅子 2 两个独立的物体。但这个数据集有一个隐蔽的细节它的 mask 是 png 单通道图像单通道图像本质上只能存一个整数索引不能直接表示实例。我看到数据集标题和摘要中用的是“全景分割”但从数据格式上来讲它更接近语义分割的 mask 标注。这里要展开说清楚杨 XX 项目也就是这个数据集的做法是每个类别对应一个灰度值0 是背景1 是床2 是椅子以此类推。严格意义上的全景分割要区分同一类别的多个个体那么 mask 中应该出现两个不同实例的两个独立编号。这个数据集的标注方式如果一个场景下有两张椅子那么两张椅子的像素其实都属于类别 2这种情况下它更适合用于训练语义分割模型。不过这不影响它的价值语义分割模型同样可以用。我的理解是标题里“全景分割”是从广义上说的对一个完整的家庭场景做所有家具的分割识别而不只是对某一个物体做分割。实际使用时你需要根据自己要解决的问题来决定是把它当语义分割来训练还是需要额外处理得到实例分割。以 YOLOv8-seg 为例它本身就是实例分割模型训练时需要的是多边形标注或 mask 标注YOLOv8 会从 mask 中自动完成实例挖掘——每个连通域自动算一个实例。所以这个数据集的 mask 如果直接拿来喂 YOLOv8-seg同一个类别多个实例的问题会让它把所有连通域当成独立的个体。3.2 这个数据集的 19 类标签体系有什么特点标签体系是家具分割任务里比较完整的床、椅子、橱柜、门、灯、地毯、桌子、窗户等 19 类加上背景就是 20 个类别。从类别构成看这个数据集关注的不是精细物体分类比如不同类型的椅子而是场景覆盖度——把家庭场景里常见的硬家具和软装全部覆盖。这个思路对训练一个通用室内场景理解模型非常合适。它的另一个特点是分辨率统一为 640×640这是一个在分割精度和计算资源之间较为均衡的选择。如果分辨率是 512×512细小物体如灯、窗户的轮廓就容易被丢失如果分辨率到 1024 或以上训练时间会翻倍。640×640 恰好是很多 YOLO 系列模型默认的输入尺寸这意味着你可以直接在 YOLOv8 里设置imgsz640不需要额外做缩放对齐。3.3 从 mask 重建实例处理多实例场景的常见方案如果你确实需要把这个数据集升级成真正的实例分割常见做法是按连通域切分。用 OpenCV 的connectedComponents或者cv2.findContours把每个类别内部分离成多个实例再为每个实例生成独立的 RLE mask 或多边形坐标。这一步在代码层面不复杂但要注意同一类别的相邻物体如果像素粘连连通域算法会把它们算成一个实例。这个问题的缓解方案是在二值化之后做一个腐蚀操作断开细小的粘连然后再做连通域分析。import cv2 import numpy as np def mask_to_instances(mask, class_id): # 提取当前类别的二值掩膜 class_mask (mask class_id).astype(uint8) * 255 # 轻度腐蚀断开相邻物体的粘连内核大小按像素调 kernel np.ones((3, 3), np.uint8) class_mask cv2.erode(class_mask, kernel, iterations1) # 连通域标记 num_labels, labels cv2.connectedComponents(class_mask, connectivity8) instances [] for label_id in range(1, num_labels): instance_mask (labels label_id).astype(uint8) if instance_mask.sum() 50: # 去掉太小的噪点 continue instances.append(instance_mask) return instances这个函数的输入是整体 mask 和你要拆分的类别编号输出是该类别下所有实例的二值掩膜列表。connectivity8是常见的邻居判据即像素上下左右和四个对角都算相邻这样连接性更强不容易把同一个物体拆成两半。instance_mask.sum() 50是过滤噪点的阈值因为 640×640 的图像中单个家具的面积应该远大于 50 像素这个阈值可以根据实际数据分布调整。处理完之后你可以把每个实例分别存成独立的 png 文件也可以转成 COCO 格式的多边形坐标用于训练 Mask R-CNN 或者 YOLOv8-seg。腐蚀操作的 kernel 大小是影响分裂效果的关键超参数对于家具这种边缘相对规整的物体3×3 通常就够用如果物体粘连特别严重可以加大到 5×5但注意腐蚀会丢掉物体边缘的细节信息分割精度会有毫米级别的损失这一点需要在效果和精度之间做取舍。4. 把数据集接到 YOLOv8-seg 实战链路格式转换、数据组织与训练4.1 从 Roboflow 的目录结构到 YOLOv8 需要的目录结构YOLOv8 训练分割任务时官方推荐的目录结构是 images 和 labels 平级labels 下放 txt 格式的标签文件每个对象一行格式为class_id x1 y1 x2 y2 ... xn yn坐标是归一化的多边形顶点。很多开源数据集给的掩膜是 png 格式不能直接喂 YOLOv8必须先转成归一化多边形坐标 txt。import cv2 import numpy as np import os from glob import glob def mask_to_yolo_polygon(mask, class_id, img_w, img_h, min_area50): contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) boxes [] for cnt in contours: area cv2.contourArea(cnt) if area min_area: continue # 多边形顶点归一化保留浮点坐标 cnt cnt.squeeze().astype(float) if cnt.ndim ! 2 or cnt.shape[0] 3: continue cnt[:, 0] / img_w cnt[:, 1] / img_h poly [class_id] for x, y in cnt.flatten(): poly.append(round(float(x), 6)) poly.append(round(float(y), 6)) boxes.append(poly) return boxes def convert_mask_directory(mask_path, output_txt_path, class_id, img_w640, img_h640): mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # 默认把所有前景当作同一类或者按需求循环处理 polygons mask_to_yolo_polygon((mask 0).astype(uint8), class_id, img_w, img_h) with open(output_txt_path, w) as f: for poly in polygons: f.write( .join(map(str, poly)) \n)RETR_EXTERNAL表示只取外轮廓这样同一个物体内部如果有镂空比如椅子的缝隙不会影响包围轮廓。CHAIN_APPROX_SIMPLE用来压缩轮廓点减少输出多边形顶点数量对训练速度有帮助。min_area50是面积阈值低于这个值的轮廓视为噪点因为 640×640 图像中一个真实的家具轮廓面积通常在上千像素级别。坐标归一化是 YOLO 格式的硬性要求模型在训练时会自动把归一化坐标映射回特征图尺度如果这一步漏了或写错训练时会直接报 shape 错误或 loss 数值异常。转换完成后txt 文件应该放在 labels 目录文件名和原图一致后缀改为 txt。4.2 你的 dataset.yaml 应该怎么写YOLOv8 的分割训练是通过 dataset.yaml 文件来指定路径和类别名称的。这里有一个常见的翻车场景很多人直接从官方示例里复制 dataset.yaml里面的类别名称是 coco 的 80 类复用到自己的数据集上结果类别完全不匹配。这个数据集应该按照 classes.txt 来写路径用相对路径或者绝对路径都要保持一致。path: /home/user/furniture_dataset train: train/images val: val/images names: 0: background 1: bed 2: chair 3: cabinet 5: door 10: lamp 14: carpet 15: table 18: window注意写入 names 的时候key 的编号必须与 mask 灰度值一致。train 和 val 的路径是相对于 path 的指向的是 images 目录YOLOv8 会利用同样的前缀去自动寻找 labels 目录。如果 labels 目录名不是默认的 labels或者存放位置不对就会报AssertionError: Label file ... not found。建议在训练之前先手动把目录结构调整成上面这个样子——train 下放 images 和 labelsval 下放 images 和 labels所有训练验证图片的标签都在对应目录下。如果你下载的数据集解压后是 train/images train/masks 的结构需要写一个脚本把 masks 目录下的 png 全部转成 txt 并移动到正确位置。4.3 用 YOLOv8 训练自己的数据集参数怎么设YOLOv8 的分割训练入口是yolo segment train。以 yolov8s-seg 为例命令如下yolo segment train \ modelyolov8s-seg.pt \ datafurniture.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ device0modelyolov8s-seg.pt是使用预训练权重做迁移学习的做法s 版本是速度和精度的折中。imgsz640与数据集分辨率一致不需要额外 resize。patience20表示连续 20 个 epoch 验证集 loss 不下降就提前停止防止过拟合。lr00.01是初始学习率YOLOv8 的调度器会在训练过程中自动调整。如果你的显存只有 8G建议把 batch 降到 8 或者使用yolov8n-seg.pt的 nano 版本。训练完成后在runs/segment/train*/weights/best.pt目录下能看到最佳权重。这个目录是 YOLOv8 默认的输出目录里面同时保存了验证集上的掩膜可视化结果和 mAP 指标曲线可以用来快速评估训练是否收敛。5. 避坑指南这几个坑我帮你踩过了5.1 掩膜是 png 但被当成三通道彩色图读取现象用 OpenCV 读取掩膜后打印 shape 发现是(640, 640, 3)十六进制颜色值错乱训练时类别数量对不上。 原因cv2.imread()默认以三通道 BGR 模式读取所有图片png 格式的标注图也会被读成三通道。 解决读取掩膜时强制指定灰度模式cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)。同时可以用np.unique(mask)检查像素类别确认类别数量是否预期的 19 类。如果看到类别编号跨度太多说明这张掩膜可能有标注异常需要抽出来检查。5.2 类别编号不连续标注文件里没有类别 4、6、7、8、9、11、12、13、16、17现象加载类别名称后模型训练的类别数量总是对不上有的是 19 类有的写 20 类。 原因Roboflow 导出时只导出数据中实际出现的类别但 classes.txt 保留了原始类别编号。中间缺号的类别没有在任何一张图片里出现所以没有标注。 解决不要修改类别编号按原编号作为 label id 训练。YOLO 格式的 class_id 必须与 mask 灰度值严格一致如果你强行重编号到 0-18那么原本类别 5门的 mask 像素值会被错标成 4模型学到的就是错的。训练时 YOLOv8 会自动根据 dataset.yaml 中的 names 键数量决定输出头维度如果你的 yaml 只写了 10 个条目而 mask 中有类别编号 15 的像素训练时会报class index out of range的错。所以要保证 yaml 中列出所有 19 个类别即使有些类别没有样本。5.3 数据划分导致某一类别在验证集中没有样本现象验证集 loss 抖动剧烈某些类别的 mAP 是 0。 原因这个数据集的验证集只有 53 张对于 19 个类别来说样本分布可能不平均。有的类别在训练集中有 50 个实例在验证集中可能只有 1 个甚至没有。这对小类别比如灯、窗户影响特别大。 解决如果你模型的最终应用场景是特定类别建议在训练前自己重新划分数据集用sklearn.model_selection.train_test_split按类别分层抽样。常见做法是保证每个类别在训练集和验证集中的实例比例保持大致相同比例设置为 8:2 或 9:1 均可。划分后要对验证集单独做一次类别统计确认每个类别至少有几个样本。如果缺少样本就把该样本全部划入验证集并减少其他类别的验证样本数或者直接做 K 折交叉验证来评估模型稳定性。这个方法虽然会增加训练耗时但对小样本集来说是值得的。5.4 可视化脚本中掩膜的染色与类别不对应现象用 matplotlib 显示掩膜时物体颜色跟想象中的类别颜色完全不同。 原因matplotlib 的imshow默认用 viridis 或 jet 色带把灰度值映射到色彩空间相邻类别的颜色差异不可控人眼看不出到底是哪个类别。 解决如果是要给标注人员看掩膜建议用cmaptab20——这是 20 种颜色的离散色带与这个数据集的 19 类加背景刚好匹配每个类别的颜色是固定且可区分的。如果是要给客户或项目汇报做效果图建议用cv2.applyColorMap把掩膜上色后与半透明方式叠加到原图这样既能看到物体边界也不会遮挡原图纹理。另外提示一下COLORMAP_TURBO这种彩色映射比较适合展示连续值比如深度图用于离散类别时相邻类别的颜色区分度不高不建议。5.5 txt 标签文件是 UTF-8 还是 GBK直接影响加载是否成功现象Windows 上直接双击打开 classes.txt 再另存Linux 下训练时加载类别名报编码错误。 原因Windows 记事本默认编码是 GBK另存会覆盖原 UTF-8 格式。 解决所有 txt 文件用 UTF-8 编码处理。写脚本时加encodingutf-8读取。如果你在 Windows 上编辑过这个文件先file classes.txt查看编码格式如果是ISO-8859或者Non-ISO extended-ASCII用iconv -f GBK -t UTF-8 classes.txt classes_utf8.txt转换后替换。6. 进阶技巧用连通域统计和 confusion matrix 验证分割质量数据集训练完之后很多人只看 loss 曲线和 mAP 就判断模型好坏这在多类别家具场景中是不够的。家具之间存在大量相似外观比如桌子和橱柜都是矩形色块mAP 高不代表每个类别都均衡。建议做一个像素级别的混淆矩阵把每个类别的分类准确度单独拉出来分析。常见做法是加载验证集的预测结果和真实掩膜用一个 Python 脚本统计每个类别像素的 IoU。如果你的预测结果是 YOLOv8 输出的多边形先要把多边形栅格化为二值掩膜再与真实掩膜做逐像素比对。这里有一个实用技巧对网络输出的置信度做一个过滤处理。YOLOv8 在分割模式下默认输出多个候选掩膜你需要按置信度阈值过滤掉低质量的预测然后再进行像素统计。阈值一般取 0.25 到 0.5 之间Over 0.7 时预测数量会明显减少适合评估模型的精确率Under 0.25 时会有大量噪声适合评估模型的召回率边界。import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/segment/train/weights/best.pt) def compute_ious(gt_mask, pred_mask, num_classes20): ious {} for cls_id in range(num_classes): gt (gt_mask cls_id) pred (pred_mask cls_id) intersection np.logical_and(gt, pred).sum() union np.logical_or(gt, pred).sum() if union 0: ious[cls_id] None # 该类别未出现 else: ious[cls_id] intersection / union return ious results model.predict(sourceval/images, conf0.3, save_txtTrue, save_confTrue) for result in results: # result.masks.data 是归一化的掩膜数组 pred_mask result.masks.data.cpu().numpy().sum(axis0) pred_mask (pred_mask 0).astype(uint8) # gt_mask 从同名 png 读取 # 计算 IoU 并打印iouNone的类别说明该类别在验证集中没有出现不代表模型学到不好。但如果某类别的 IoU 明显低于其他类别比如灯这类小物体IoU 通常低于大件家具 10 个百分点以上建议针对性增加该类别的样本或做数据增强比如随机旋转、亮度扰动增强小物体的对比度。做这类分析时要时刻注意当前数据集的类别分布——家庭场景数据集中灯、窗户的面积占比本来就小模型在像素级别上的分数容易被大类别主导。一个更合理的评估指标是 frequency-weighted IoU即按每个类别在验证集中出现的像素比例对 IoU 进行加权这样不会被背景类占据大头而掩盖小物体的问题。从那以后我训练分割模型不管用什么数据集都会在训练前先检查 label id 和 classes.txt 的对应关系训练结束后强制走一遍像素级 IoU 统计。这个数据集虽然小但胜在干净和场景专一用来跑通从数据整理到训练的完整链路非常合适。希望这组从数据格式到训练配置再到质量评估的操作路径能帮你省下重复踩坑的时间。本文还有配套的精品资源点击获取