ARTICLE DETAIL

资讯详情

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

室内场景实例分割数据集实战:从COCO标注到YOLO训练与mask优化

室内场景实例分割数据集实战:从COCO标注到YOLO训练与mask优化 简介这份室内场景实例分割数据集面向从事计算机视觉算法开发、机器人视觉与智能家居研究的学习者和工程师用于训练和评估能够精确分割室内家具与人物目标的AI模型。数据集共849张图片按训练集594张、验证集170张、测试集85张合理划分覆盖bench、chair、couch、dining table、laptop、person六个常见类别标注采用YOLO格式包含实例分割多边形与类别标签可直接对接YOLO、PyTorch等主流框架。压缩包共1700个文件以849个jpg图片和849个txt标注文件为主另附1个yaml配置文件与1个docx说明文档整体约56.17MB目录结构清晰便于快速开展训练、验证与评估。目前已有72人学习下载。资源类别多样、标注兼容性强适合作为实例分割任务的基准数据也可用于室内监控、智能家居等场景的算法验证与教学实践。1. 室内场景实例分割数据集从压缩包到可训练模型的完整路径拿到一个名为室内场景实例分割数据集_20251116_122654.zip的压缩包第一反应不应该是急着解压看图片而是先想清楚三件事这批数据能不能直接喂给 YOLO 做实例分割训练、标注格式是不是 COCO 那一套、室内场景的类别分布有没有严重长尾。室内场景和自动驾驶、遥感那些室外数据集完全不同——光照变化大、遮挡严重、同类物体堆叠密集沙发靠垫和床单被套在像素级别上经常糊在一起这对实例分割模型的边界回归能力是实打实的考验。这个数据集适合两类人一是想用 YOLOv8-seg 或 YOLO11-seg 跑通室内实例分割全流程的工程师二是需要快速验证某个注意力模块或损失函数在密集遮挡场景下是否有效的算法研究者。下面从数据解压后的目录结构开始一步步走到能跑训练、能看指标、能排查翻车原因。2. 解压后先别急着训练目录结构与标注格式的交叉验证2.1 室内实例分割数据集的典型目录长什么样一个结构规范的室内实例分割数据集解压后通常能看到这样的层级indoor_instance_seg/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── instances_train.json │ ├── instances_val.json │ └── instances_test.json ├── masks/ # 部分数据集会额外提供 PNG 掩码 └── README.md但现实往往没那么整齐。我见过不少数据集把图片全堆在一个images/下标注只有一个annotations.jsontrain/val 划分写在 README 里让你自己切。所以第一步不是写训练脚本而是用几行 Python 把目录结构和标注文件对齐。import json import os from pathlib import Path from collections import Counter root Path(indoor_instance_seg) ann_file root / annotations / instances_train.json with open(ann_file, r, encodingutf-8) as f: coco json.load(f) # 关键字段检查 print(images:, len(coco[images])) print(annotations:, len(coco[annotations])) print(categories:, [(c[id], c[name]) for c in coco[categories]]) # 每类实例数分布 cat_map {c[id]: c[name] for c in coco[categories]} counter Counter(cat_map[a[category_id]] for a in coco[annotations]) for name, cnt in counter.most_common(): print(f{name}: {cnt})这段代码做三件事确认images、annotations、categories三个必备字段存在打印类别 ID 和名称的映射关系统计每个类别的实例数量。参数上唯一需要注意的是ann_file的路径——如果标注文件不在annotations/下改成实际路径即可。跑完之后你会得到一张类别分布表如果发现某个类别的实例数不到总数的 1%那后面训练时就得考虑过采样或类别加权。2.2 COCO 格式的 segmentation 字段到底怎么读实例分割和纯目标检测在标注上的核心区别就是segmentation字段。COCO 格式里这个字段有两种写法多边形点集polygon和 RLERun-Length Encoding。室内场景数据集绝大多数用 polygon因为标注工具LabelMe、CVAT默认导出就是多边形。import numpy as np from pycocotools import mask as mask_utils # 取第一条标注看看 segmentation 类型 ann coco[annotations][0] seg ann[segmentation] if isinstance(seg, list): # polygon 格式list of list每个子列表是 [x1,y1,x2,y2,...] print(polygon, 点数:, len(seg[0]) // 2) poly np.array(seg[0]).reshape(-1, 2) print(bbox:, ann[bbox], area:, ann[area]) print(polygon 坐标范围 x:, poly[:, 0].min(), poly[:, 0].max()) print(polygon 坐标范围 y:, poly[:, 1].min(), poly[:, 1].max()) elif isinstance(seg, dict): # RLE 格式 print(RLE, size:, seg[size]) binary_mask mask_utils.decode(seg) print(mask shape:, binary_mask.shape, 前景像素:, binary_mask.sum())逻辑说明polygon 格式下segmentation是一个列表每个元素是一个扁平化的坐标数组需要 reshape 成(N, 2)才能逐点分析。RLE 格式下需要用pycocotools解码成二值掩码。参数上重点看bbox和area是否与 polygon 坐标范围一致——如果不一致说明标注文件被人工修改过后面训练时 mask 和 bbox 会对不齐。提示如果area字段缺失或全为 0YOLO 训练时计算 mask 损失会出问题需要根据 polygon 重新计算面积。2.3 把 COCO 标注转成 YOLO-seg 格式的脚本与四个边界坑YOLOv8-seg 和 YOLO11-seg 需要的标注格式是每张图一个.txt每行是class_id x1 y1 x2 y2 ... xn yn坐标全部归一化到 0~1。转换脚本本身不复杂但室内场景有几个特有的坑。import json import os from pathlib import Path from PIL import Image def coco_to_yolo_seg(coco_json, images_dir, output_dir): with open(coco_json, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id - (file_name, width, height) 映射 img_info {} for img in coco[images]: img_info[img[id]] (img[file_name], img[width], img[height]) # 建立 category_id - 连续 class_id 映射 cat_ids sorted([c[id] for c in coco[categories]]) cat2cls {cid: idx for idx, cid in enumerate(cat_ids)} # 按 image_id 分组标注 from collections import defaultdict ann_by_img defaultdict(list) for ann in coco[annotations]: ann_by_img[ann[image_id]].append(ann) os.makedirs(output_dir, exist_okTrue) for img_id, anns in ann_by_img.items(): file_name, w, h img_info[img_id] lines [] for ann in anns: seg ann[segmentation] if not isinstance(seg, list): continue # 跳过 RLEYOLO-seg 不直接支持 cls_id cat2cls[ann[category_id]] for poly in seg: if len(poly) 6: # 少于 3 个点无法构成多边形 continue coords [] for i in range(0, len(poly), 2): x min(max(poly[i] / w, 0.0), 1.0) y min(max(poly[i 1] / h, 0.0), 1.0) coords.extend([f{x:.6f}, f{y:.6f}]) lines.append(f{cls_id} .join(coords)) out_path Path(output_dir) / (Path(file_name).stem .txt) with open(out_path, w) as f: f.write(\n.join(lines)) coco_to_yolo_seg( indoor_instance_seg/annotations/instances_train.json, indoor_instance_seg/images/train, indoor_instance_seg/labels/train )四个边界坑第一坐标裁剪。室内场景标注时经常有物体贴着图像边缘polygon 坐标可能超出图像宽高不裁剪的话 YOLO 训练时归一化坐标会大于 1导致 mask 溢出。第二点数过少的多边形。少于 3 个点的 polygon 无法构成有效区域直接跳过。第三RLE 格式。YOLO-seg 不直接支持 RLE遇到 RLE 标注要么用pycocotools转成 polygon要么放弃这些样本。第四类别 ID 不连续。COCO 的category_id可能是 1、3、7、15 这种跳号必须重新映射成 0、1、2、3 的连续整数否则 YOLO 训练时类别索引会越界。3. 用 YOLOv8-seg 跑通室内实例分割训练配置文件与参数调优3.1 数据集 YAML 怎么写才不出错YOLO 训练的第一步是准备数据集配置文件。室内实例分割数据集的 YAML 和检测任务略有不同关键是task字段要设为segment。# indoor_seg.yaml path: /data/indoor_instance_seg train: images/train val: images/val test: images/test task: segment nc: 12 names: 0: chair 1: table 2: sofa 3: bed 4: cabinet 5: lamp 6: tv 7: plant 8: book 9: cup 10: door 11: window参数说明path是数据集根目录train/val/test是相对于path的图片目录。YOLO 会自动在图片同级目录下找labels/文件夹里的同名.txt。nc是类别数必须和转换脚本里cat2cls的映射数量一致。names的顺序就是class_id的顺序写错的话训练出来的模型会把椅子和桌子搞混。注意如果val目录下没有对应的labels/val训练启动时会直接报错退出不会给你后悔药。3.2 训练命令与关键参数从 batch size 到 mask ratioyolo segment train \ dataindoor_seg.yaml \ modelyolov8s-seg.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ momentum0.937 \ weight_decay0.0005 \ warmup_epochs3.0 \ mask_ratio4 \ overlap_maskTrue \ dropout0.0 \ valTrue \ plotsTrue \ device0逐参数说明modelyolov8s-seg.pt是预训练权重室内场景数据量如果不到 5000 张建议从yolov8s-seg或yolov8m-seg起步不要一上来就上yolov8x-seg过拟合风险很高。imgsz640是标准输入尺寸室内场景如果小物体多杯子、书可以提到 800 或 960但显存占用会明显上升。mask_ratio4控制 mask 分支的下采样倍率值越大 mask 分辨率越低室内场景建议保持默认 4调到 2 会显著增加显存但 mask 边界更精细。overlap_maskTrue允许不同实例的 mask 在训练时重叠这对室内场景的遮挡物体很关键设成 False 会让模型学不会区分堆叠的靠垫和沙发。lr00.01配合lrf0.01意味着学习率从 0.01 余弦衰减到 0.0001如果 loss 震荡厉害先把lr0降到 0.005。3.3 训练过程中该盯哪些指标YOLO 训练日志里和实例分割相关的指标主要有四个box_loss、seg_loss、cls_loss、mask mAP50。box_loss下降但seg_loss不降说明模型能定位物体但学不会精确边界室内场景常见于透明物体玻璃杯、窗户和细长物体灯管、窗帘杆。mask mAP50在 50 个 epoch 后还低于 0.3大概率是标注质量问题——要么 polygon 太粗糙要么类别定义有歧义。import pandas as pd # 读取 YOLO 训练结果 df pd.read_csv(runs/segment/train/results.csv) df.columns df.columns.str.strip() # 看最后 10 个 epoch 的 mask mAP print(df[[epoch, metrics/mAP50(M), metrics/mAP50-95(M)]].tail(10)) # 找 seg_loss 和 box_loss 的比值变化 df[loss_ratio] df[train/seg_loss] / df[train/box_loss] print(df[[epoch, train/box_loss, train/seg_loss, loss_ratio]].tail(10))这段代码读取results.csv重点看metrics/mAP50(M)和metrics/mAP50-95(M)这两个 mask 指标。loss_ratio如果持续大于 2.0说明 mask 分支的学习难度远高于 box 分支可以考虑增大mask_ratio或检查 polygon 标注的精细度。4. 室内场景实例分割的避坑与排查五条血泪经验4.1 现象mask mAP 正常但可视化结果全是糊的原因YOLO 训练时默认mask_ratio4输出 mask 是原图 1/4 分辨率再上采样回来的。室内场景的小物体杯子、遥控器在 1/4 分辨率下只剩几个像素上采样后边界完全糊掉。解决把imgsz从 640 提到 960同时把mask_ratio降到 2。显存不够就降batch从 16 降到 8 或 4。如果还不行只能换更大的模型或做切片推理。4.2 现象训练 loss 正常下降但验证集 mask mAP 始终为 0原因验证集的labels/val目录不存在或者标注文件里的class_id超出了nc定义的范围。YOLO 在验证时如果找不到有效标注会直接返回 0 而不是报错。解决手动检查labels/val下是否有.txt文件打开任意一个确认第一列数字小于nc。另外检查indoor_seg.yaml里val路径是否写成了绝对路径但实际是相对路径。4.3 现象某些类别的 mask 完全预测不出来原因类别实例数严重不均衡。室内场景里 chair 可能有 3000 个实例而 cup 只有 50 个。YOLO 默认的损失函数对长尾类别没有额外加权。解决在 YAML 里给稀有类别复制样本或者用copy_paste数据增强。YOLOv8 支持copy_paste0.3参数把稀有类别的实例复制粘贴到其他图上能有效提升长尾类别的 mask mAP。4.4 现象训练到一半 loss 突然变成 NaN原因学习率过高导致梯度爆炸或者标注里有面积为 0 的 polygon。室内场景标注时如果标注员手抖点了两个重合的点polygon 面积就是 0计算 mask 损失时会出现除零。解决先把lr0降到 0.001 重跑。如果还在同一 epoch 附近 NaN用前面的转换脚本加一行面积检查把面积小于 1 像素的 polygon 过滤掉。4.5 现象推理时同一张图上的两个同类物体被合并成一个 mask原因overlap_mask在推理时默认是 False模型会把重叠区域的 mask 合并。室内场景里两个挨着的椅子、叠在一起的盘子经常被合并。解决推理时显式设置overlap_maskTrue并且用retina_masksTrue输出原图分辨率的 mask。命令行加retina_masks参数即可Python API 里是model.predict(..., retina_masksTrue)。5. 从能跑到好用mask 后处理与边界优化技巧训练跑通只是第一步室内场景实例分割真正难的是推理阶段的 mask 后处理。YOLO 输出的 mask 是二值的但室内场景里沙发和靠垫、桌子和桌布在像素级别上经常粘连直接输出会看到两个实例的 mask 连成一片。我一般会在推理后加一步基于颜色梯度的边界细化。import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/segment/train/weights/best.pt) results model.predict(test_indoor.jpg, retina_masksTrue, overlap_maskTrue, conf0.25) img cv2.imread(test_indoor.jpg) for r in results: masks r.masks.data.cpu().numpy() # (N, H, W) boxes r.boxes.xyxy.cpu().numpy() for i, mask in enumerate(masks): mask_uint8 (mask * 255).astype(np.uint8) # 用原图边缘做引导滤波细化 mask 边界 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) guided cv2.ximgproc.guidedFilter(gray, mask_uint8, radius8, eps1e-3) _, refined cv2.threshold(guided, 127, 255, cv2.THRESH_BINARY) # 只保留面积最大的连通域去掉碎片 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(refined) if num_labels 1: largest 1 np.argmax(stats[1:, cv2.CC_STAT_AREA]) refined np.where(labels largest, 255, 0).astype(np.uint8) contours, _ cv2.findContours(refined, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img, contours, -1, (0, 255, 0), 2) cv2.imwrite(refined_result.jpg, img)这段代码做了三件事用guidedFilter以原图灰度图为引导把 YOLO 输出的粗糙 mask 边界对齐到真实物体边缘用连通域分析去掉 mask 里的碎片区域只保留面积最大的连通域最后把细化后的轮廓画到原图上。参数上radius8控制引导滤波的窗口大小室内场景建议 6~12 之间太小边界细化不明显太大又会把相邻物体粘回去。eps1e-3是正则化项值越小边界越贴合原图边缘但噪声也会被放大。还有一个更轻量的技巧如果只是想让 mask 边界看起来不那么锯齿直接在推理时把imgsz设成 1280YOLO 内部会以更高分辨率计算 mask输出再缩回原图边界平滑度提升很明显。代价是推理速度大概下降 40%看你的场景能不能接受。我自己踩过最深的坑是早期用mask_ratio4加imgsz640训了一个室内数据集mask mAP50 到了 0.72 看着还行但可视化出来所有杯子的 mask 都是模糊的椭圆完全不能用。后来把imgsz提到 960、mask_ratio降到 2mAP 只涨了 0.03但可视化质量是质的变化。所以室内实例分割这个方向指标和实际可用性之间经常有 gap别只看 mAP 数字。希望帮到你。本文还有配套的精品资源点击获取
返回列表