ARTICLE DETAIL

资讯详情

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

室内积水检测数据集实战:357张VOC+YOLO双格式YOLOv8训练全流程

室内积水检测数据集实战:357张VOC+YOLO双格式YOLOv8训练全流程 简介本资源为室内房间积水检测目标检测数据集面向从事计算机视觉、安防巡检或智能家居场景的算法开发者与学习者可用于训练和验证积水区域识别模型。压缩包共1073个文件包含357张jpg图片、357个VOC格式xml标注文件及359个txt文件含YOLO格式标注整体约84.65MB图片与标注一一对应方便直接接入Pascal VOC或YOLO训练流程。数据集仅设一个类别jishui共标注378个矩形框采用labelImg完成画框标注标注准确合理。目前已有216人学习下载适合作为小样本目标检测练手或积水识别任务的起步数据。读者可获取完整图片与双格式标注快速搭建训练与评估环境省去自行采集和标注的成本并在此基础上做数据增强、模型微调与效果对比。1. 室内积水检测数据集357 张 VOCYOLO 双格式到底能训出什么室内房间积水检测这个方向听起来冷门但做过机房、仓库、地下车库巡检的人都知道它有多刚需。摄像头架在角落地面反光、水渍、阴影混在一起人眼都要盯两秒才能确认更别说让模型自动报警。这个标题给的是一个 357 张、VOC 和 YOLO 双格式打包的目标检测数据集专门标注室内地面的积水区域。357 张不算大但足够跑通一条从数据校验到 YOLO 训练再到推理验证的完整链路适合做原型验证、课程设计、或者给现有巡检系统加一个积水检测分支。它解决的不是“通用目标检测”问题而是“小样本、单类别、强反光干扰”这个具体场景。如果你手头正好有室内巡检需求或者想找一个真实的小数据集练 YOLO 全流程这个方向值得花一个下午跑一遍。2. 拿到压缩包先别急着训VOC 与 YOLO 双格式的校验与转换2.1 为什么这个数据集同时给了 VOC 和 YOLO 两套标注VOC 格式用 XML 存标注每个文件对应一张图里面记录了图片尺寸、目标类别和边界框的左上角、右下角坐标。YOLO 格式则是每张图配一个 txt每行是类别索引 中心x 中心y 宽 高坐标全部归一化到 0 到 1 之间。两套格式同时存在通常意味着标注工作是在 VOC 体系下完成的然后通过脚本转了一份 YOLO 出来。对使用者来说VOC 适合做数据审查和可视化因为 XML 可读性强能直接看到原始像素坐标YOLO 格式则直接喂给 YOLOv5、YOLOv8 这类框架省去训练前的转换步骤。但这里有个常见坑转换脚本如果没处理好边界框越界或者图片尺寸读取错误YOLO 的 txt 里会出现负数或者大于 1 的值。所以拿到数据后第一件事不是配置训练而是做一轮完整性校验。357 张的规模校验脚本跑起来也就几秒钟但能帮你省掉训练到一半 loss 变 NaN 的后悔药。2.2 用 Python 做一轮 VOC 标注完整性检查下面这段脚本遍历 VOC 的 XML 文件检查图片是否存在、尺寸是否匹配、边界框是否越界。你只需要把voc_dir和img_dir换成实际路径。import os import xml.etree.ElementTree as ET from PIL import Image voc_dir ./VOC/Annotations img_dir ./VOC/JPEGImages problems [] for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() filename root.find(filename).text img_path os.path.join(img_dir, filename) if not os.path.exists(img_path): problems.append(f图片缺失: {filename}) continue img Image.open(img_path) w, h img.size for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) if xmin 0 or ymin 0 or xmax w or ymax h: problems.append(f越界: {filename} {name} ({xmin},{ymin},{xmax},{ymax}) 图片尺寸 {w}x{h}) if xmax xmin or ymax ymin: problems.append(f无效框: {filename} {name}) print(f共发现 {len(problems)} 个问题) for p in problems[:20]: print(p)这段代码的逻辑很直接先确认 XML 里记录的图片文件真实存在再读取图片实际宽高然后逐个目标检查边界框是否超出图片范围以及右下角坐标是否大于左上角。参数方面voc_dir指向 Annotations 文件夹img_dir指向 JPEGImages 文件夹。如果你拿到的压缩包目录结构不同比如图片和 XML 混在一起改一下路径拼接就行。跑完如果输出“共发现 0 个问题”说明 VOC 侧是干净的可以进入下一步。2.3 VOC 转 YOLO 的脚本与四个边界坑如果压缩包里已经带了 YOLO 格式的 txt你可以跳过转换但建议还是用脚本重新生成一遍确保坐标归一化正确。下面这个转换脚本把 VOC 的 XML 转成 YOLO 需要的 txt类别名统一映射为water。import os import xml.etree.ElementTree as ET from PIL import Image voc_dir ./VOC/Annotations img_dir ./VOC/JPEGImages out_dir ./YOLO/labels os.makedirs(out_dir, exist_okTrue) classes [water] # 单类别索引为 0 for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() filename root.find(filename).text img_path os.path.join(img_dir, filename) img Image.open(img_path) w, h img.size lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图片范围内防止越界 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_name os.path.splitext(filename)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))四个边界坑需要特别注意。第一xmin和xmax可能因为标注工具的问题出现浮点数直接取整会丢精度这里用 float 处理。第二如果标注框超出图片边界归一化后会出现大于 1 或小于 0 的值YOLO 训练时虽然不会立刻报错但会影响回归损失所以脚本里做了裁剪。第三图片文件名和 XML 里的filename字段可能不一致比如大小写或者扩展名不同脚本用 XML 里的字段去拼路径如果找不到图片会直接抛异常建议先跑一遍 2.2 的校验。第四类别名如果有空格或者中文映射到索引时容易出错这里统一用英文water如果你的数据集里类别名不同改classes列表即可。2.4 生成训练集与验证集的划分文件YOLO 训练需要两个 txt 文件分别列出训练图和验证图的路径。357 张按 8:2 划分训练集 285 张验证集 72 张。下面这段脚本随机打乱后生成train.txt和val.txt。import os import random img_dir ./VOC/JPEGImages out_dir ./YOLO os.makedirs(out_dir, exist_okTrue) imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.seed(42) random.shuffle(imgs) split int(len(imgs) * 0.8) train_imgs imgs[:split] val_imgs imgs[split:] with open(os.path.join(out_dir, train.txt), w) as f: for name in train_imgs: f.write(os.path.abspath(os.path.join(img_dir, name)) \n) with open(os.path.join(out_dir, val.txt), w) as f: for name in val_imgs: f.write(os.path.abspath(os.path.join(img_dir, name)) \n) print(f训练集 {len(train_imgs)} 张验证集 {len(val_imgs)} 张)random.seed(42)保证每次划分结果一致方便复现。路径用绝对路径避免训练时因为工作目录不同找不到图片。如果你的图片分散在多个子文件夹把os.listdir换成os.walk递归收集即可。3. 用 YOLOv8 在 357 张图上跑通训练配置文件与关键参数3.1 数据集 YAML 怎么写才不出错YOLOv8 需要一个 YAML 文件告诉它去哪里找图片和标签。在项目根目录新建water.yaml内容如下path: /absolute/path/to/dataset train: YOLO/train.txt val: YOLO/val.txt names: 0: waterpath是数据集根目录的绝对路径train和val是相对于path的 txt 文件路径。names里类别索引从 0 开始和转换脚本里的classes列表顺序一致。常见错误是path写了相对路径训练时从其他目录启动就找不到文件或者names里写了中文YOLOv8 读取时编码报错。单类别场景下nc不需要单独写YOLOv8 会根据names的长度自动推断。3.2 训练命令与 batch size 的选择357 张图属于小数据集显存压力不大。如果你用的是 8GB 显存的卡batch16比较稳4GB 显存降到batch8。下面这条命令从预训练权重开始微调yolo detect train \ datawater.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/water \ nameexp1modelyolov8n.pt是最小的 nano 版本357 张图用 nano 足够参数量小、训练快过拟合风险也低。epochs100配合patience20如果 20 轮验证指标没提升就提前停止避免无效计算。imgsz640是 YOLOv8 的默认输入尺寸如果你的图片分辨率远大于 640比如 1920x1080可以保持 640让框架自动缩放如果积水区域在图中占比很小比如远距离摄像头拍的地面可以尝试imgsz1280但显存占用会翻倍。lr00.01是初始学习率小数据集上如果 loss 震荡厉害降到 0.001 再试。3.3 训练过程中该盯哪些指标YOLOv8 训练时会在终端输出每一轮的 box_loss、cls_loss、dfl_loss 和 mAP50。357 张图、单类别正常情况下 mAP50 在前 30 轮就能爬到 0.7 以上。如果 50 轮后还在 0.3 附近晃大概率是标注有问题或者学习率太大。box_loss 反映边界框回归误差cls_loss 反映分类误差dfl_loss 是分布焦点损失三个一起看box_loss 下降但 cls_loss 不降说明框的位置学得还行但类别分不清单类别场景下 cls_loss 理论上应该很快接近 0如果 cls_loss 一直很高检查 YAML 里的names和 txt 里的类别索引是否对得上。验证集 mAP50 是最终参考指标但小数据集上波动大不要因为某一轮掉了 0.05 就停。看趋势连续 10 轮没有新高再考虑调参。训练结束后runs/water/exp1/weights/best.pt就是验证集上表现最好的权重。4. 室内积水检测的避坑与排查反光、小目标、过拟合4.1 地面反光被误检成积水现象模型在验证集上 mAP 不错但实际推理时把光滑地面的反光、瓷砖高光、甚至白色地板都框成积水。原因357 张图里如果反光样本和积水样本外观接近模型学到的特征可能是“亮色区域”而不是“水的纹理”。解决在标注阶段就把明显反光的非积水区域标成背景或者收集一批纯反光负样本加入训练集。推理时把置信度阈值从默认 0.25 提到 0.5能过滤掉一部分低置信误检。4.2 小面积积水在 640 输入下丢失现象图片里积水区域只占几十个像素训练后模型完全检测不到。原因YOLOv8 在 640 输入下特征图最小 stride 是 32对应原图 32x32 像素的区域如果积水小于这个尺寸特征图上就没了。解决把imgsz提到 1280或者用 YOLOv8 的 P2 变体如果框架支持增加浅层特征图。另一个办法是在数据增强里关掉mosaic因为 mosaic 会把四张图拼成一张小目标进一步缩小。4.3 训练 loss 正常但验证 mAP 为 0现象训练日志里 box_loss 在降但验证集 mAP50 一直是 0。原因最常见的是val.txt里的图片路径和train.txt不一致或者验证集的标签文件没有放到 YOLO 期望的目录结构里。YOLOv8 默认从图片路径推导标签路径把images替换成labels如果你的目录不是这个结构需要在 YAML 里显式指定val的标签路径。解决检查val.txt里的路径是否存在标签 txt 是否和图片同名同目录层级。4.4 过拟合训练 mAP 0.95验证 mAP 0.6现象训练集上指标很高验证集差一大截。原因357 张图对 YOLO 来说偏少模型容易记住训练样本。解决开启数据增强YOLOv8 默认已经带了 mosaic、HSV 抖动、随机翻转可以额外加degrees10做小角度旋转translate0.1做平移。如果还不行换更小的模型比如 yolov8n 换成 yolov8n 的剪枝版本或者直接减少训练轮数在验证 mAP 最高点停。4.5 推理时图片尺寸和训练不一致导致漏检现象训练时用 640推理时直接喂原图 1920x1080结果什么都检测不到。原因YOLO 推理时会自动缩放但缩放后的长边如果远大于 640小目标会被压缩到消失。解决推理时显式指定imgsz640让框架按训练时的尺寸处理。如果原图分辨率很高先把图缩到 1280 再推理或者用滑动窗口切图检测。5. 小数据集的进阶玩法从 357 张里榨出更多信息357 张图训一个单类别检测器上限就在那里。想让模型真正可用得在数据层面做文章。我一般会做两件事一是用训练好的模型去跑一批未标注的室内地面图把高置信度的检测结果导出来人工修正后加入训练集这叫自训练或者伪标签能把有效样本量翻两三倍二是把检测框裁出来单独训一个分类器判断“真积水”还是“反光”检测器负责召回分类器负责精度两级级联在误检率上比单模型好很多。验证模型是否真的学到了积水特征而不是记住了背景有个简单办法把验证集图片的地面区域裁出来贴到一张纯色背景上再推理。如果模型还能框出来说明它关注的是水本身的纹理如果框不出来了说明它依赖的是地面上下文换个房间就可能翻车。这个测试我每次做完小数据集都会跑一遍翻车次数不少但每次都能找到该补什么样的数据。最后一个习惯不管数据集多小训练前一定把train.txt和val.txt里的图片路径打印出来随机抽几张用画框脚本可视化一遍。我见过太多次因为路径写错、标签错位、类别索引对不上导致的白跑一晚上。357 张图的可视化也就几分钟但这几分钟能帮你确认标注框是不是真的落在积水区域上。希望帮到你。本文还有配套的精品资源点击获取
返回列表