简介:这是YOLO系列算法适用的室内家具目标检测数据集,包含2416张图像及对应TXT标签,已按训练、验证和测试划分完毕,适合研究人员、开发者及入门学习者直接用于目标检测模型的训练与评估。压缩包内文件总数2000个,主要由1999个TXT标签文件和1个YAML配置文件组成,整体大小约50.04MB;TXT标签采用标准YOLO格式,逐行记录类别索引、归一化中心坐标与宽高,便于直接迁移至YOLOv3、YOLOv4、YOLOv5等不同版本。目前已有165人学习该数据集,标注规范且划分明确,可立即开展训练与验证,也可作为不同检测算法性能对比的基准。对室内家具识别、智能家居及视觉巡检等场景,这份资源能大幅省去数据采集与标注耗时,加速模型迭代;同时,标准的标签格式与划分方式也便于后续扩展与结果复现。
1. 这份室内家具数据集能帮你省下最耗时的环节
yolo 系列算法用的室内家具目标检测数据集,2416 张图像全部带标签,解压后就能进入训练流程,这是它和网上散装图片最大的区别。自己收集两千多张室内家具图、逐张画框标注,按一张两分钟算也接近八十个小时,这套资源把这些时间直接省掉了。标签是 YOLO 标准归一化格式,class、x_center、y_center、width、height 五列,训练集、验证集、测试集已经按常规比例划分好,不用自己再拆。
适合两类人:做毕业设计或论文实验的学生,以及想快速跑通 YOLO 训练流程但不想从零标注的新手。需要提醒的是,数据集只给了类别索引,没有附带类别名称清单,训练前要按索引顺序把 names 补上,这一步我会在第 3 章讲清楚。它的价值不在图像本身有多精美,而在于「能直接喂给 YOLOv5/v8 训练」这一件事,剩下的时间可以全部花在调参和排查上。
2. 拆解数据集:2416 张图像与标签文件的对应关系
拿到压缩包之后,先别急着配环境,第一步是打开目录看到底有什么。网上很多数据集打包时目录结构五花八门,有的把标签和图像混在一起,有的 train 里有标签却没有对应图像,先用几分钟把结构摸清楚,能避开后面一大半问题。
2.1 目录结构:train、val、test 的常见组织方式
这类按 YOLO 格式整理的数据集,最常见的组织方式是 images 和 labels 两个根目录,下面再按 train、val、test 分子目录。图像与标签文件名完全一致,只是扩展名不同,比如图像是000535_187.jpg,标签就是000535_187.txt。先解压再数一数各目录下的文件数:
unzip skripsi-new-j1bmb.zip -d furniture_dataset cd furniture_dataset find . -type f | sed 's/.*\.//' | sort | uniq -c echo "--- images/train ---" ls images/train | wc -l echo "--- labels/train ---" ls labels/train | wc -l这段命令先统计压缩包里所有文件的扩展名分布,确认标签与图像的文件类型,再分别查看训练集图像和标签的数量。如果 images/train 的文件数比 labels/train 多,说明有图像没有被标注;反过来则说明存在空的标签文件,需要重点检查。
数量对得上之后,再看 val 和 test 目录。该数据集说明里提到已经做好划分,通常会有一个 test 目录用来做最终评估。如果只有 train 和 val,就把 val 当验证集用,测试集自己从 val 里再留一部分出来。我一般会把文件名按顺序排好,然后手动抽查几个子目录,确认没有出现嵌套的文件夹,因为 YOLO 训练时路径越简单越不容易出错。
2.2 标签文件逐行解析:五列归一化坐标的读法
打开任意一个 txt 标签文件,每行都是五个数字。以000535_187.txt为例,假设某一行是0 0.483 0.412 0.215 0.267,含义是:第一个 0 是类别索引,从 0 开始;后面四个数字分别是目标框中心点的 x、y 坐标和框的宽度、高度,它们都是相对于图像宽度和高度的比例值,范围在 0 到 1 之间。这种归一化格式与图像实际分辨率解耦,所以同一份标签既可以跑 YOLOv5,也可以跑 YOLOv8,甚至换成 Faster R-CNN 时只需要做格式转换。
我习惯用一个 Python 脚本先把所有标签统计一遍,看类别索引分布和坐标是否越界,这样能提前发现标注文件里的脏数据:
import os label_dir = "furniture_dataset/labels/train" class_count = {} out_of_range = [] for txt_name in os.listdir(label_dir): if not txt_name.endswith(".txt"): continue path = os.path.join(label_dir, txt_name) with open(path, "r") as f: for line in f: parts = line.strip().split() if len(parts) != 5: out_of_range.append((txt_name, "字段数不是5")) continue cls = int(parts[0]) x_center, y_center, w, h = map(float, parts[1:]) class_count[cls] = class_count.get(cls, 0) + 1 # 归一化坐标的范围必须都在0到1之间 if not (0.0 <= x_center <= 1.0 and 0.0 <= y_center <= 1.0): out_of_range.append((txt_name, f"中心点越界: {parts[1:]}")) print("类别分布:", dict(sorted(class_count.items()))) print("异常记录:", out_of_range[:10])这段脚本做了三件事:统计每个类别索引出现多少次、检查每行是否正好五列、检查中心点坐标是否落在合法区间。参数说明:class_count字典用于记录类别分布,如果你看到索引 0 有几千条而索引 1 只有几十条,说明类别严重不均衡,训练时就要考虑要不要做类别加权。out_of_range列表收集越界记录,只要出现一条,就应该回到标注工具里检查原始标注,而不是强行训练。
2.3 图像与标签的配对校验:不匹配时训练会很难看
标签格式正确还不够,还得确认每张图都有标签、每个标签都有对应的图。YOLO 训练时如果一张训练图找不到标签文件,会直接报错或者跳过该样本,最终导致实际参与训练的图片数量比预期少一截,而你自己根本察觉不到。写个配对脚本:
import os images_dir = "furniture_dataset/images/train" labels_dir = "furniture_dataset/labels/train" img_names = {os.path.splitext(f)[0] for f in os.listdir(images_dir)} label_names = {os.path.splitext(f)[0] for f in os.listdir(labels_dir)} missing_labels = img_names - label_names # 有图但没有标签 missing_images = label_names - img_names # 有标签但没有图 print("图像总数:", len(img_names)) print("标签总数:", len(label_names)) print("缺标签的图像:", list(missing_labels)[:10]) print("缺图像的标签:", list(missing_images)[:10])这段代码的原理是把 images 和 labels 目录里的文件名去掉扩展名后分别放进两个集合,集合相减就能找出缺失项。missing_labels表示哪些图像没有对应的 txt 文件,missing_images表示哪些标签文件找不到原图。如果缺失数量超过个位数,建议直接放弃这批样本,因为补标注的时间可能比重下数据集还久。
提示:校验一定要在训练之前做,不要在跑完几十个 epoch 之后才发现某个类别的标签全丢了。
3. 拿来即训:data.yaml 与 YOLOv5/v8 训练命令
目录结构确认无误后,就可以进入训练阶段。这一章的核心不是让你背命令,而是理解 data.yaml 的配置逻辑,以及 YOLOv5 和 YOLOv8 两条训练路径的差异。很多人在这一步翻车,原因不是命令记错了,而是类别索引和配置文件没对齐。
3.1 配置 data.yaml:类别数先看标签再填名字
前面提过,这份数据集只给了类别索引,没有提供类别名称。所以第一步是确认最大索引值是多少,类别数就是最大索引加一。因为索引从 0 开始,最大索引是 4 就代表有 5 个类别。用上一章的统计脚本跑一遍,假设输出最大索引为 4,那么nc: 5。类别名称列表names需要按索引顺序填写,你可以先用占位名跑通流程,等检测结果出来再对照图像补上真实名称。
# furniture.yaml path: ../furniture_dataset # 相对于data.yaml所在目录的路径 train: images/train val: images/val test: images/test nc: 5 names: 0: chair 1: table 2: sofa 3: bed 4: cabinet配置里有几个容易忽略的点。path最好写相对路径,这样把整个项目目录拷贝到另一台机器上,只要相对结构不变就能直接训练。train和val的值是相对于path的路径,不要再加一层furniture_dataset。names的键值对应的是标签文件里的第一个数字,这个顺序决定了最后可视化时显示的类别名,如果顺序反了,检测结果里会把椅子叫成沙发。
3.2 训练命令:YOLOv5 和 YOLOv8 各给一套
YOLOv5 目前最常用的是 v7.0 分支,clone 下来之后直接用train.py。YOLOv8 则整合成了yolo命令行工具,配置方式略有不同。两套命令分别如下:
# YOLOv5 方式 git clone -b v7.0 https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt python train.py --data ../furniture.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --cache # YOLOv8 方式 pip install ultralytics yolo detect train data=../furniture.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16两套命令的参数逻辑基本一致。--img 640表示输入图像会被 letterbox 缩放为 640x640,这是检测速度和精度的折中点;--batch 16在 2416 张图像的规模下比较合适,显存不足就降到 8;--epochs 100对这个量级的数据集够用,配合早停机制通常在第 60 到 80 个 epoch 就能收敛。--cache参数会把图像加载到内存里,能显著缩短训练时间,代价是占用较多内存,如果内存只有 16G 就把它去掉。
YOLOv8 的model=yolov8n.pt用的是 nano 版本,比 YOLOv5 的yolov5s.pt更轻量,适合先跑通流程。如果追求更高精度,可以换成yolov8m.pt,但训练时间会明显变长。初次训练建议用预训练权重而不是从零开始,室内家具和 COCO 数据集的部分类别有重叠,迁移学习能让你用更少的 epoch 达到可用的效果。
3.3 验证与推理:看指标之前先看检测结果
训练结束后,模型权重会保存在runs/train/exp/weights/best.pt。先用验证集跑一次评估,拿到 mAP 指标:
# YOLOv5 python val.py --data ../furniture.yaml --weights runs/train/exp/weights/best.pt # YOLOv8 yolo detect val data=../furniture.yaml model=runs/train/exp/weights/best.pt输出里会包含每个类别的 precision、recall 和 mAP50。这个阶段我建议不要只盯 mAP,而是先跑一次推理,随便挑几张验证集图像看看效果:
yolo detect predict model=runs/train/exp/weights/best.pt source=../furniture_dataset/images/val/000535_187.jpg conf=0.25conf=0.25表示置信度阈值,低于这个值的检测框会被过滤掉。如果看到图像上框的位置偏了,先别怀疑模型,回到标签可视化这一步排查;如果框的位置正确但类别标签错乱,那就是前面 data.yaml 里 names 的顺序填错了。这两类问题在指标上都不容易看出来,只有直接看检测结果才能定位。
4. 避坑:五个容易翻车的家具数据集训练问题
数据集已经标注好不代表没有坑。以下五个问题是我处理类似资源时遇到过的典型情况,每一条都按现象、原因、解决的思路整理,你在复现时可以直接对照排查。
4.1 类别索引越界:训练时直接报错或 mAP 始终很低
现象:训练到一半报错Label class xx exceeds nc in data,或者训练全程 mAP 不超过 0.3。原因:data.yaml 里配置的 nc 值小于标签文件中的最大类别索引,最常见的情况是只数了类别数却忘了索引从 0 开始,导致 nc 少填了一个。也有可能是某些标签文件里混入了错误类别编号。解决:在训练前跑一遍第二章的统计脚本,打印出最大索引值,nc 必须大于等于最大索引值加一。我自己习惯把这条统计写进训练前置脚本,每次换数据集都强制跑一遍。
4.2 EXIF 旋转:手机拍的室内图全变歪
现象:训练前用图片查看器看图像内容完全正常,但训练出的模型检测框整体偏移,尤其竖屏拍摄的衣柜、门这类长方形物体最明显。原因:手机和部分相机拍摄的 JPEG 图像会写入 EXIF 方向信息,OpenCV 的imread默认不处理这个字段,读取出来的图像像素还是旋转前的状态,而标注工具的预览却自动转正了,最终导致框与像素错位。解决:训练前统一用 PIL 的ImageOps.exif_transpose()把图像转正并覆盖保存:
from PIL import Image, ImageOps import os src_dir = "furniture_dataset/images/train" for name in os.listdir(src_dir): path = os.path.join(src_dir, name) img = Image.open(path) img = ImageOps.exif_transpose(img) # 根据EXIF旋转信息转正 img.save(path, quality=95)这段脚本会把目录下所有图像按 EXIF 信息转正后写回原文件。quality=95只对 JPEG 格式生效,如果原图是 PNG 可以去掉这个参数。转正之后标签坐标不需要改动,因为 YOLO 坐标是归一化比例,只要图像内容旋转到与标注时一致的方向就行。
4.3 长宽比差异大:letterbox 填充导致的视觉偏移
现象:检测结果里,宽屏长图的检测框上下偏移,或者柜子这类细长物体只框住了一部分。原因:YOLO 会把输入图缩放到 640x640,长宽比大的图会被填充黑边,这个过程本身不影响归一化坐标。但如果你在标注阶段先把图像拖到了标注工具的可视化窗口里,导出时工具按缩放后的坐标计算,坐标就会错位。解决:训练前用可视化脚本把几个样本画出来,对比标注框和图像内容是否吻合。只要原始标签是正确的,letterbox 不会造成问题,真正要排查的是标注阶段是否发生过分辨率不一致。
4.4 训练中断后 resume:续训变成了新实验
现象:训练到一半因为断电或显存不足中断,重启后执行相同的训练命令,发现 loss 从头开始,之前的训练白跑了。原因:YOLOv5 的默认行为是重新开始训练,只有显式指定--resume才会读取上次的权重继续训练。YOLOv8 则把 resume 做成了独立的子命令。解决:YOLOv5 中断后执行python train.py --resume runs/train/exp/weights/last.pt,YOLOv8 执行yolo detect train resume=True。需要注意resume会沿用上次的 data.yaml 路径,如果你移动过数据集位置,需要先改回去再续训。
4.5 mAP 很高但现场不准:室内场景的泛化落差
现象:验证集 mAP 达到 0.9 以上,但拿自己手机拍的客厅视频测试,检测框开始乱飘,甚至把盆栽当成人。原因:数据集里的图像来自特定房间和角度,标注场景的背景、光照都相对一致。验证集与训练集来自同一分布,所以指标虚高,但真实环境光照和视角一变,模型就不认识了。解决:收集 50 到 100 张真实场景图做增量训练,或者至少留一部分作为测试集单独评估。复用数据集做实验没问题,但把它当成生产级模型的唯一数据来源,风险要自己承担。
5. 训练前先画框:把 YOLO 标签还原回图像核对
很多人在训练前会看一眼标签文件,觉得坐标范围没问题就开始了,但数字层面的合法性并不能保证框的位置正确。YOLO 归一化坐标的可读性很差,0.483 0.412 0.215 0.267这样的数字,你很难直观判断它是否准确框住了椅子。我的习惯是先把标签画回图像上,用眼睛确认一遍再训练,这一步能筛掉大部分标注质量问题。
5.1 单张可视化:把归一化坐标转成像素坐标
画框的原理很简单:归一化坐标乘上图像的宽高,就得到像素坐标。中心点坐标加减一半宽高,就能得到矩形左上角和右下角的坐标。需要注意宽度和高度同样是归一化比例,所以要乘图像宽、图像高,不要搞混哪个乘哪个。
import cv2 img_path = "furniture_dataset/images/val/000535_187.jpg" txt_path = "furniture_dataset/labels/val/000535_187.txt" img = cv2.imread(img_path) h, w = img.shape[:2] with open(txt_path, "r") as f: for line in f: cls, x_c, y_c, bw, bh = line.strip().split() cls, x_c, y_c, bw, bh = int(cls), float(x_c), float(y_c), float(bw), float(bh) x1 = int((x_c - bw / 2) * w) y1 = int((y_c - bh / 2) * h) x2 = int((x_c + bw / 2) * w) y2 = int((y_c + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f"cls_{cls}", (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) cv2.imwrite("check_000535.jpg", img) print(f"图像尺寸: {w}x{h}, 检测到 {len(open(txt_path).readlines())} 个目标")这段脚本读取图像和对应的标签文件,逐个把归一化坐标换算成像素坐标并画框。cv2.rectangle里的绿色框线粗细设为 2 像素,在 640 宽度的缩略图上刚好可见。cv2.putText的文本内容用cls_{cls}占位,如果你已经知道类别名称,可以改成真实名称。运行后打开check_000535.jpg,重点看两个点:框是否紧贴物体边缘、类别编号是否和物体类型对得上。
5.2 批量核对:一次跑完整个验证集
单张核对只能排除个例,真正的检查方式是批量生成所有验证集图像的标注框,然后随机抽取几十张快速浏览。写一个循环脚本:
import cv2 import os base = "furniture_dataset" for split in ["train", "val", "test"]: img_dir = os.path.join(base, "images", split) txt_dir = os.path.join(base, "labels", split) out_dir = os.path.join("check_output", split) os.makedirs(out_dir, exist_ok=True) for name in os.listdir(txt_dir): if not name.endswith(".txt"): continue img_path = os.path.join(img_dir, name.replace(".txt", ".jpg")) if not os.path.exists(img_path): continue img = cv2.imread(img_path) h, w = img.shape[:2] with open(os.path.join(txt_dir, name)) as f: for line in f: if len(line.strip().split()) != 5: continue cls, x_c, y_c, bw, bh = line.strip().split() cls, x_c, y_c, bw, bh = int(cls), float(x_c), float(y_c), float(bw), float(bh) x1 = int((x_c - bw / 2) * w) y1 = int((y_c - bh / 2) * h) x2 = int((x_c + bw / 2) * w) y2 = int((y_c + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(os.path.join(out_dir, name.replace(".txt", ".jpg")), img)这个脚本遍历 train、val、test 三个目录,把每张图对应的标签框画好并保存到check_output目录。代码里做了一个容错:如果某行字段数不是五个就直接跳过,避免脏数据让整个脚本崩溃。批量输出之后,用图片查看器按文件名排序浏览,我一般会每隔十张抽取一张仔细看,检查是否存在框完全偏离目标的情况,比如框住了背景墙面或者半个物体。
5.3 指标解读:评估结果里哪些数值得信
训练结束后,验证输出里会出现 precision、recall 和 mAP50 这几个指标。在室内家具这类单类别或少量类别的数据集上,mAP50 达到 0.9 以上说明基础目标基本都能框住,但这个指标对边缘框位置不敏感,框偏移一点不会显著降低分数。如果 mAP50 高但 mAP50-95 低,通常意味着框的定位精度不够;如果 recall 明显低于 precision,说明不少目标被漏检了,重点去看遮挡和暗光条件下的样本。
6. 再进一步:轻量模型、数据增强与 ONNX 导出
模型跑通只是第一步。2416 张图像的规模决定了它比较适合做实验和验证,真要用到实际场景,还有三个方向值得投入时间。
第一个方向是模型体积与速度的取舍。先用 YOLOv5s 或 YOLOv8n 跑通后,如果目的是部署到 CPU 或边缘设备,可以换成 YOLOv8n 并用yolo export model=best.pt format=onnx导出 ONNX 格式。ONNX 导出后配合 ONNX Runtime 推理,比直接跑 PyTorch 模型内存占用更小,速度也更快。导出命令很简单:
yolo export model=runs/train/exp/weights/best.pt format=onnx dynamic=True其中dynamic=True允许动态输入尺寸,这样推理时不用强制缩放到训练用的 640,可以根据实际图像长宽比做 letterbox 后送入模型,对小目标检测有微弱提升。导出完成后用onnxruntime加载模型,输入输出张量的形状可以先打印确认,再写推理脚本。
第二个方向是数据增强的针对性调整。YOLOv5 和 YOLOv8 默认开启 mosaic 增强,把四张图拼成一张训练,对提升小目标检测有帮助。但室内家具场景里,mosaic 拼出来的图像和真实场景差异较大,如果训练后期发现验证集 mAP 上不去,可以尝试关闭一部分增强或降低增强强度,比如把mosaic关掉,让模型专注学单张图像里的家具形状。
第三个方向是针对类别不均衡做处理。如果统计脚本发现某个类别样本数量很少,训练时给这个小类别适当增加权重,或者用小类别样本做复制粘贴增强,避免模型为了刷整体精度而忽略稀有类别。
从那以后,我每拿到一个数据集,第一件事永远是画框核对再加类别统计,这两步跑完才会去改训练命令。做室内家具检测这类任务,数据质量决定上限,训练调参只是逼近这个上限的手段。这套数据集省掉了你最费力的标注环节,但验证和排查的功夫省不得。希望帮到你。
本文还有配套的精品资源,点击获取