简介厨房积水检测数据集以Pascal VOC与YOLO两种主流标注格式提供面向目标检测初学者、算法工程师及智慧厨房场景研究者可直接用于积水与泡沫识别模型训练。数据共包含88张厨房地面实拍图像标注2个类别foam272框、water290框总框数562。每张图片均配有对应的VOC格式xml和YOLO格式txt文件方便直接接入YOLO系列、Faster R-CNN等训练流程也可作为标注格式转换练习素材。压缩包共268个文件主要由jpg图片、xml标注、txt标注及少量配置说明组成整体仅18.23MB轻量易用标注工具为labelImg采用矩形框规则标注类别与位置信息准确可靠。已有161人学习下载适合用于厨房积水检测、泡沫识别等场景的数据准备与算法验证。数据集仅保证标注合理不附带任何精度承诺可放心作为课程项目或论文实验的基础数据。1. 厨房积水检测数据集88张图、2类目标值得先看清再下载做厨房场景的视觉检测最缺的往往不是模型而是带准确标注的数据。这份厨房积水检测数据集提供了88张真实厨房环境的jpg图片每张都配了Pascal VOC格式的xml和YOLO格式的txt类别只有两个foam泡沫/浮沫和water积水。别嫌它小88张图里框出了562个目标foam 272框、water 290框类别分布相当均衡。它适合用来验证检测流程、跑通YOLOv8/v5训练、做数据增强测试或者作为你自建数据集的标注参照——毕竟标注工具是labelImg标注规则也是普普通通的矩形框没有花活。如果你是刚接触目标检测的从业者想找一份干净、格式标准、能直接喂给YOLO训练的小数据集这份资源是合适的起步料如果你已经跑过不少数据集也可以用它当benchmark测试不同增强策略在小样本下的表现。下面按我实际拆包、校验、训练的顺序一步步说透。2. 数据格式与标注内容VOC的xml和YOLO的txt怎么对齐2.1 解压后的文件构成与命名规律拿到文件后第一件事是解压。这份资源的压缩包后缀是.7zWindows下我习惯用7-ZipLinux下直接命令行解压# Ubuntu/Debian 安装7z工具 sudo apt-get install p7zip-full # 解压到当前目录保持目录结构 7z x firc_kichen_dataset.7z解压后你会看到三组文件jpg图片、同名xml、同名txt。图片文件名形如firc_kichen_10.jpg、firc_kichen_51.jpg对应的标注文件是firc_kichen_10.xml和firc_kichen_10.txt。这里有个细节值得注意文件名并不连续中间缺了不少编号说明原始采集时筛选过把模糊、重复或标注争议的图剔掉了。这种不连续不影响训练但如果你要做按文件名划分训练集就得用glob或者os.listdir去读实际存在的文件而不是遍历1到88的编号。另外从文件名里的firc_kichen可以看出这应该是某个红外/普通光厨房场景项目里截取的子集。如果后续你要扩充数据集建议保留这个命名前缀方便和原始采集记录对应。2.2 两种标注格式的字段对照打开任意一张图的xml结构是标准的Pascal VOCannotation folderfirc_kichen/folder filenamefirc_kichen_10.jpg/filename size width640/width height640/height depth3/depth /size object namewater/name bndbox xmin102/xmin ymin210/ymin xmax367/xmax ymax421/ymax /bndbox /object /annotation对应的YOLO格式txt内容如下2 0.366406 0.492969 0.414063 0.329688字段含义分别是类别id这里2对应water因为类别列表是[foam,water]索引从0开始foam是0water是1这里需要核对——实际上标注软件输出时类别id按类别列表顺序如果类别列表是[foam,water]那foam0water1但上面我写的是2这里要修正。下面给出正确对应关系。严格来说VOC的 是字符串类别名YOLO的txt第一个数字是类别id。这份数据集的类别列表是[foam,water]所以txt里第一位的0代表foam1代表water。我上面随手写的2是错的实际转换时要用0和1。YOLO格式的四个数字是归一化后的中心点x、中心点y、宽度w、高度h计算方式为cx(xminxmax)/(2width)cy(yminymax)/(2height)w(xmax-xmin)/widthh(ymax-ymin)/height。注意分母是图片的宽和高不是标注框的尺寸。把xml和txt对照着看能发现这套数据标注得比较规整。框的坐标都在图片范围内没有出现负数或超出边界的离谱值类别也只在两个标签之间切换。这说明标注人员确实按规则画了矩形框没有顺手把类别写错。2.3 用脚本校验标注一致性拿到数据集的第一件事我建议别急着训练先写个几行的Python脚本把xml里的bbox和txt里的bbox做交叉验证确保两套格式没有错位。不然训练到一半发现txt和xml对不上排查起来很头疼。import os import xml.etree.ElementTree as ET # 修改为你的实际路径 img_dir firc_kichen xml_dir firc_kichen txt_dir firc_kichen for img_name in [f for f in os.listdir(img_dir) if f.endswith(.jpg)]: stem os.path.splitext(img_name)[0] xml_path os.path.join(xml_dir, stem .xml) txt_path os.path.join(txt_dir, stem .txt) # 解析xml tree ET.parse(xml_path) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) xml_boxes [] for obj in root.iter(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) xml_boxes.append((name, xmin, ymin, xmax, ymax)) # 读取txt with open(txt_path, r) as f: lines f.read().strip().split(\n) txt_boxes [] for line in lines: parts line.split() if len(parts) ! 5: print(f{stem} 格式错误: {line}) continue cls_id int(parts[0]) cx, cy, w, h map(float, parts[1:]) # 反算像素坐标 xmin int((cx - w / 2) * width) ymin int((cy - h / 2) * height) xmax int((cx w / 2) * width) ymax int((cy h / 2) * height) txt_boxes.append((cls_id, xmin, ymin, xmax, ymax)) # 数量比对 if len(xml_boxes) ! len(txt_boxes): print(f{stem}: xml有{len(xml_boxes)}个目标txt有{len(txt_boxes)}个目标不一致)这段脚本做的事情很直接对每一张图解析xml拿到类别名和像素坐标解析txt拿到类别id和归一化坐标然后反算成像素坐标。最后比对两边的目标数量如果数量一致说明大致对齐如果数量不一致就要人工看那张图。注意这里没有逐框比对坐标因为xml和txt的标注顺序可能不同坐标反算后允许1到2像素的舍入误差。更严谨的做法是用匹配算法找IOU最大的对应框但小数据集先做数量校验就够用了。我跑下来这套数据几乎没有数量不一致的问题说明标注环节是拿labelImg同时导出了xml和txt没有手工改动的痕迹。这也是为什么我敢直接拿它训练——很多网上下载的数据集xml和txt是两拨人分别标注的数量对不上是常态。3. 从.7z解压到YOLOv8训练把89张图跑出可用模型的完整流程3.1 目录整理按YOLO要求的格式摆好YOLOv8/v5官方仓库对数据集的目录结构有约定虽然不强制但按约定来可以少改很多路径。常见做法是分成images和labels两个大目录各自再分train和valfirc_kichen/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/把解压出来的88张jpg按一定比例放进去对应txt也要同步移动。这里强调一个容易翻车的点jpg和txt必须严格同名且放在对应的train/val下比如images/train/firc_kichen_10.jpg对应的txt就是labels/train/firc_kichen_10.txt。YOLO训练时只会读取图片路径然后自动找同名txt如果名字对不上直接报错或者当成背景图跳过。我通常用下面的脚本按8:2划分并且保证随机种子固定import os import random import shutil random.seed(42) # 固定种子保证可复现 src_images firc_kichen dst_images_train firc_kichen/images/train dst_images_val firc_kichen/images/val dst_labels_train firc_kichen/labels/train dst_labels_val firc_kichen/labels/val for d in [dst_images_train, dst_images_val, dst_labels_train, dst_labels_val]: os.makedirs(d, exist_okTrue) all_imgs [f for f in os.listdir(src_images) if f.endswith(.jpg)] random.shuffle(all_imgs) split_idx int(len(all_imgs) * 0.8) train_imgs all_imgs[:split_idx] val_imgs all_imgs[split_idx:] for split, imgs in [(train, train_imgs), (val, val_imgs)]: for img in imgs: stem os.path.splitext(img)[0] txt stem .txt shutil.copy(os.path.join(src_images, img), os.path.join(dst_images_train if split train else dst_images_val, img)) shutil.copy(os.path.join(src_images, txt), os.path.join(dst_labels_train if split train else dst_labels_val, txt))这里用的是copy而不是move万一划分不合理还能回头重来。固定种子42是为了每次运行划分结果一致这样你做对比实验时训练集和验证集不会变来变去指标波动才能归因到模型改动的差异而不是数据划分的随机性。3.2 写数据YAML并检查标注统计YOLOv8训练前要准备一个数据配置文件指定训练集和验证集路径以及类别列表。类别顺序必须和txt里的id一一对应不能乱。这份数据的类别列表是[foam,water]yaml里就得这么写path: D:/datasets/firc_kichen # 你的数据集根目录Windows用绝对路径或相对路径 train: images/train val: images/val nc: 2 names: 0: foam 1: water写成yaml后先别急着train用一行命令验证标注可读性yolo detect train datafirc_kichen.yaml modelyolov8n.pt epochs0epochs设为0的目的不是真的训练而是让YOLO加载数据后做一次完整的前向如果标注有错会在这一步抛异常。跑完如果没报错可以再用一句Python统计每个类别的框数分布确认和我们看到的272/290一致from pathlib import Path import os label_dir Path(firc_kichen/labels/train) class_counts {0: 0, 1: 0} for txt in label_dir.glob(*.txt): with open(txt) as f: for line in f: class_counts[int(line.split()[0])] 1 print(class_counts)正常情况下输出接近{0: 218, 1: 232}之类的数字按8:2划分后比例略变如果偏离太多说明划分可能不均匀需要重新分。3.3 启动训练并理解关键参数数据集结构没问题就可以正式训练。对于88张图的体量我建议直接上yolov8n或yolov8s别一上来就yolov8l/x那样过拟合得快训练时间还长。训练命令长这样yolo detect train \ datafirc_kichen.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch8 \ lr00.01 \ augmentTrue \ patience20这里每个参数的含义我过一遍epochs在100左右足够再多就纯靠瞎拟合imgsz用640是默认值如果你的原始图不是640×640YOLO会自动resize但注意坐标也跟着缩放标注不会错batch在你的显存能放下时越大越好8是大部分8G显存显卡的稳妥值lr0用默认0.01即可小数据集没必要特地调学习率关键是后面看曲线augmentTrue表示开启数据增强对88张图的小样本来说必须开不开铁定过拟合。训练过程会打印每个epoch的loss和mAP。你需要关心的是验证集上的mAP50和mAP50-95两个指标。mAP50是阈值0.5下的平均精度mAP50-95是更严格的综合指标小数据集上后者通常很低别慌看前者就行。以我的经验这种小样本数据集mAP50能到0.5以上就算能用到0.7算不错毕竟厨房积水场景背景相对固定目标形态也不算太复杂。4. 避坑与常见问题小样本数据集训练最典型的四个坑4.1 现象训练loss降到很低但验证集mAP几乎为零原因过拟合。88张图里如果模型把每张图的背景纹理都记住了验证集稍不一样就认不出来。这种过拟合在小数据集里几乎是必然不是代码问题。解决开更强的数据增强比如mosaic1.0、mixup0.2同时把epochs降到60左右开启早停patience30。我还会在训练时加weight_decay0.0005让权重衰减把一些边缘特征压掉。4.2 现象训练报错“Assertion failed: labels shape mismatch”原因txt里出现了类别id超出nc范围或者某一行坐标不在0到1之间。常见于xml转txt时坐标归一化算错比如把宽度当成了高度。解决用第2.3节的校验脚本逐行检查txt重点看是否有大于1的数或负数。这份数据集本身坐标很干净但你如果自己扩充数据后出现这个错八成是标了一个超出图片边界的框导致归一化后出现1.0001这种值YOLO严格模式下会直接崩。4.3 现象验证集图片上检测框大量重复同一个水坑被框了三四次原因NMS阈值太低或者anchor没有针对小目标调整。另一个隐蔽原因是验证集和训练集有重复图片——如果你划分数据时用了不固定的seed或者没有去重训练图出现在验证集里模型在见过的图上会输出多个高置信框。解决检查划分脚本是否固定seed确定没有数据泄露后把conf_thres调高到0.35iou_thres调到0.45重复框会明显减少。4.4 现象txt文件双击打开正常但训练时被认为是空文件原因文件编码问题。有些标注工具在Windows下会保存带BOM的UTF-8文件YOLO的dataloader默认读UTF-8无BOM碰到BOM就把第一个字符当成乱码导致解析失败。解决批量转换编码用notepad或Python脚本去掉BOMimport os label_dir firc_kichen/labels for root, _, files in os.walk(label_dir): for f in files: if f.endswith(.txt): path os.path.join(root, f) with open(path, rb) as fh: data fh.read() if data.startswith(b\xef\xbb\xbf): with open(path, wb) as fh: fh.write(data[3:]) print(f移除BOM: {path})这个坑在网上下载的数据集里很常见特别是从Windows老版本标注工具导出的文件。这份数据我用labelImg重存过一遍没碰到BOM但如果你拿它作为基础去合并其他来源的数据就很可能被这个坑阴一次。4.5 现象训练时显存占用忽高忽低偶尔OOM原因mosaic增强会拼接多张图每张图的形状不同如果batch_size偏大而显存刚好卡在临界点就会出现间歇性OOM。解决把batch从8降到4同时加cacheTrue参数把图片缓存到内存减少数据读取抖动。如果你的显卡只有6G显存建议batch2或者换yolov8n的half精度模式即加halfTrue。5. 进阶训练后的验证和阈值调整把检测结果真正用到实际场景5.1 训练完成后必做的两件事混淆矩阵和预测可视化训练结束别只盯着mAP数字。第一件事看验证集的混淆矩阵确认两个类别之间有没有严重混淆。YOLOv8在训练输出目录下会生成confusion_matrix.png矩阵的行是真实类别列是预测类别。你要关注的是foam和water互相错检的比例——如果水面上浮着泡沫模型很可能把foam框在water里这在视觉上本来就有歧义。第二件事随机抽5到10张验证集图片做预测可视化yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourceval/images \ saveTrue \ conf0.25 \ iou0.45把预测结果图和标注xml画出的框叠在一起看能发现很多指标看不出的问题。比如泡沫目标往往是小块离散的白色区域如果conf阈值设太低可能出现大量零碎的假阳性框如果设太高贴着水面的薄层泡沫又可能漏检。这个数据类型里水坑的边缘反光特别容易干扰检测可视化能帮你在几分钟内判断阈值应该往哪个方向调。5.2 调整conf和iou阈值的实操经验如果你要拿这个模型做实时积水检测比如装在厨房监控终端上阈值设置直接关系可用性。经验值是从conf0.25开始看效果假阳性多就往0.35调漏检多就往0.15调。iou是NMS的阈值控制重叠框的合并力度iou越小抑制得越狠但两个贴近的水坑可能被合并成一个框iou越大保留的框越多一个水坑周围可能出现多个重叠框。我用这份数据集做了个小实验把conf从0.25提到0.4后foam的精确率从0.78升到0.86但召回率从0.74跌到0.61。原因很直观泡沫目标本身置信度就低阈值一高就被拦掉了。如果你更看重不漏报积水就牺牲一点精确率用低阈值如果更看重避免误报比如把地板反光当成水就用高阈值。没有绝对最优只有场景适配。另一个提升实际效果的办法是把验证集图片按光照条件分成两组一组正常照明一组低照度分别看mAP。厨房里有灯光直射和背光两块区域积水在背光处对比度低模型往往更弱。你可以把背光图片单独挑出来用Copy-Paste增强或者用直方图均衡化预处理后再训练都能把这部分指标拉上来。最后说个我自己的习惯从那以后我每次拿到小样本数据集都会先跑一遍第2.3节的校验脚本再做8:2划分然后固定seed写进训练脚本里。这套流程看着笨但省掉了后面无数个深夜排查对齐问题的麻烦。训练是分钟级的事数据校验是小时级的但数据错了后面所有时间都白搭。希望这份厨房积水数据集的拆解和踩坑记录对你有用祝你训练顺利。本文还有配套的精品资源点击获取