ARTICLE DETAIL

资讯详情

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

室内积水检测357张VOC+YOLO双格式数据集,目标检测训练全流程实践

室内积水检测357张VOC+YOLO双格式数据集,目标检测训练全流程实践 简介该数据集面向室内积水监测与目标检测模型训练而整理适合算法工程师、科研人员以及具备一定深度学习基础的入门学习者。资源包含三百五十七张室内场景图像全部使用LabelImg工具完成矩形框标注标注类别为“积水”共三百七十八个目标框。图像覆盖不同房间布局、光照条件和积水形态可用于训练和验证YOLO系列、Faster R-CNN等目标检测模型也可用于迁移学习与模型调优。压缩包内共有一千零七十三个文件包括jpg原图、Pascal VOC格式的xml标注和YOLO格式的txt标注两种主流格式可让使用者直接接入常见训练框架省去自行转换标注的步骤整包大小约84.65MB数据规模适中特别适合算法快速迭代与教学实验。该数据集标注规范、类别单一且目标明确目前已有两百一十六人学习浏览可作为室内积水识别落地项目的前期数据基础也可作为目标检测入门实战的练习数据。1. 目标检测数据集里的“积水”长什么样357 张 VOCYOLO 双格式能做什么看到“室内房间内积水检测数据集 357 张”这个资源时我第一反应是这是一个小而完整的单类别目标检测数据集。它不像 COCO 那样动辄几十万张也不像某些论文复现包那样附一堆权重文件它就是老老实实地给了 357 张 JPG 图片每张图片配一个 VOC 格式的 xml 和一个 YOLO 格式的 txt类别只有一个“jishui”总共 378 个标注框。拿到手能直接干的事很清楚拿来验证你的训练环境、跑通一版 YOLO 全流程、或者作为换肤改类别的模板数据。适合刚入门目标检测、想“从 0 开始跑通训练”的纯小白也适合需要快速做原型验证、又不想从零标注的从业者。别指望这份数据做出高精度落地模型——357 张单类别数据量摆在那但它足够让你把环境、脚本、标注格式、训练参数整条链路摸透。2. 数据集文件结构拆解jpg/xml/txt 三者如何对齐2.1 VOC 格式的 xml 到底存了什么拿一张图打开对应的 xml你会看到标准 PASCAL VOC 结构。第一眼可能觉得字段多但真正起作用的只有几个节点filename、size、object。其中object下的name是类别名bndbox是矩形框的左上角和右下角坐标单位是像素。annotation folderimages/folder filename197.jpg/filename size width640/width height480/height depth3/depth /size object namejishui/name bndbox xmin120/xmin ymin80/ymin xmax350/xmax ymax260/ymax /bndbox /object /annotation逻辑说明xml 记录的是绝对像素坐标xmin/ymin是框的左上角xmax/ymax是右下角。很多新手会把xmax/ymax误当成框的宽高这是个非常容易埋雷的点。如果之后你想在 OpenCV 里直接画框cv2.rectangle接收的就是(xmin, ymin)和(xmax, ymax)这对对角点。参数说明size里的 width 和 height 必须和实际图片分辨率一致否则训练时 YOLO 会算出错误的归一化坐标。我拿到数据集后会先跑一个脚本批量比对后面避坑章节会给出具体检查方法。另外这份数据的标注工具是 labelImg它的习惯就是画矩形框时自动把这两个对角点写进 xml。2.2 YOLO 格式的 txt 归一化坐标原来这么好读与 xml 的像素坐标不同每个 jpg 对应的同名 txt 里存的是 YOLO 格式的归一化坐标一行一个目标格式是类别编号 中心点x 中心点y 框宽 框高。四个数值全部除以图片宽高范围在 0 到 1 之间。0 0.367188 0.354167 0.359375 0.375000逻辑说明这个 txt 和上面的 xml 是同一张 197.jpg 的标注。图片宽 640、高 480xml 里的框是(120, 80)到(350, 260)计算时用中心点公式cx (120 350) / 2 / 640 ≈ 0.367cy (80 260) / 2 / 480 ≈ 0.354宽 (350 - 120) / 640 ≈ 0.359高 (260 - 80) / 480 ≈ 0.375。你会发现 txt 里的数值就是 xml 坐标经过一次除法得到的。参数说明YOLO 训练读取 txt 时只认类别编号不认类别名称。所以 txt 里的0对应的是 data.yaml 中names列表的第一个类。如果你的数据集只有一个类别那么 txt 里所有行都是0开头这是正常的别怀疑自己下错了文件。2.3 目录结构怎么摆才能直接训练这个数据集下载解压后图片、xml、txt 大概率是平铺在同一个文件夹里的。YOLO 的训练代码不直接吃这种平铺结构它要求图片和标签按一定目录规则组织。所以第一步不是急着训练而是先把目录整理成 Ultralytics YOLO 能识别的结构。project/ ├── images/ │ ├── train/ │ │ ├── 197.jpg │ │ └── ... │ └── val/ │ ├── 146.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 197.txt │ │ └── ... │ └── val/ │ ├── 146.txt │ └── ... └── data.yaml逻辑说明images 和 labels 这两个一级目录的名称必须固定Ultralytics YOLO 默认找的就是这两个名字。labels 目录下存放的是 txt 标签不是 xml。xml 在这个阶段可以暂时放在另一个文件夹里留作备份训练时用不上。参数说明如果你将来用 YOLOv5 的老版本它有时候能容忍images和labels并列的结构但 YOLOv8 及之后版本强烈建议使用上面这种 train / val 二次划分方式。不要自己发明dataset/water/images这种嵌套目录越深越容易在写 data.yaml 时搞乱路径。3. 把数据集跑进 YOLOv8从 0 开始的一条训练命令3.1 环境准备装一个能跑的训练环境如果你以前没跑过 YOLO最省事的方案是直接用 Ultralytics 的 pip 包。我建议用 Python 3.9 以上的环境先建一个虚拟环境再装别直接往系统 Python 里塞依赖。pip install ultralytics逻辑说明Ultralytics 这个包会顺带拉入 torch 和 torchvision 两个深度学习框架依赖。如果你的机器没有 N 卡CPU 也能训练只是速度会慢不少。357 张图片、640 分辨率下CPU 训练 100 个 epoch 大概要几个小时GPU 十几分钟就能跑完。参数说明安装时如果遇到网速问题可以用国内镜像源。另外不建议装最新的 nightly 版 torch用 pip 自动解析出的稳定版即可。装完之后跑一句yolo detect predict能正常输出说明环境已经通了。3.2 划分 train/val切分脚本与随机种子拿到数据后你会发现它没有提供类似train.txt、val.txt这种划分文件。摘要里说“不包含分割路径的 txt 文件”意思是这份数据只给了每张图片的标签 txt没有划分训练集和验证集。所以需要自己写脚本做切分。我这人习惯按 8:2 比例切357 张就是大约 285 张训练、72 张验证。import os import random import shutil random.seed(42) src_images D:/jishui_dataset/images_flat src_labels D:/jishui_dataset/labels_flat train_img D:/jishui_dataset/images/train val_img D:/jishui_dataset/images/val train_lab D:/jishui_dataset/labels/train val_lab D:/jishui_dataset/labels/val for d in [train_img, val_img, train_lab, val_lab]: os.makedirs(d, exist_okTrue) files [f for f in os.listdir(src_images) if f.lower().endswith(.jpg)] random.shuffle(files) split_idx int(len(files) * 0.8) for f in files[:split_idx]: shutil.copy(os.path.join(src_images, f), os.path.join(train_img, f)) shutil.copy(os.path.join(src_labels, f.replace(.jpg, .txt)), os.path.join(train_lab, f.replace(.jpg, .txt))) for f in files[split_idx:]: shutil.copy(os.path.join(src_images, f), os.path.join(val_img, f)) shutil.copy(os.path.join(src_labels, f.replace(.jpg, .txt)), os.path.join(val_lab, f.replace(.jpg, .txt))) print(ftrain: {split_idx}, val: {len(files) - split_idx})逻辑说明脚本先固定随机种子保证每次运行得到的划分结果一致。复制图片的同时把同名的 jpg 后缀换成 txt 一起复制过去。这里用的是copy而不是move避免切分出错时原始文件被搞丢算是给自己留一张后悔药。参数说明如果你的图片扩展名不全是.jpg比如有.JPG或者.png需要把后缀判断那行改一下。另外别忘了标签文件后缀这个数据集是 txt 就按 txt 处理不要想当然地认为还有别的格式。3.3 写 data.yaml让模型知道去哪里找水和标签Ultralytics YOLO 用 data.yaml 做数据配置里面只写了路径和类别名。路径尽量用绝对路径或者相对于 yaml 文件位置写避免在不同工作目录下启动训练时报找不到图片。path: D:/jishui_dataset # 数据集的根目录 train: images/train # 训练图片目录相对 path 的路径 val: images/val # 验证图片目录 names: 0: jishui # 类别编号 0类别名称 jishui逻辑说明path指向数据集的根目录train和val是相对根目录的图片路径。YOLO 会根据图片路径自动把images换成labels去找对应的 txt 标签所以 labels 目录不需要写进 yaml。names是一个字典键从 0 开始txt里的类别编号就是按这个字典解释的。参数说明类别名用英文小写最保险。如果你的类别名是中文yaml 文件本身可以存 UTF-8但训练日志和可视化时会出现乱码后面避坑章节会专门说这个。3.4 开始训练命令、参数与日志怎么读目录整理好、yaml 写好之后直接一条命令启动训练。这里我建议用detect任务里的yolov8n.pt预训练权重作为起点。虽然数据只有 357 张但预训练权重能帮模型更快收敛。yolo detect train dataD:/jishui_dataset/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16 patience20逻辑说明这行命令的意思是用 yolov8n 这个最轻量的模型在积水数据集上训练 100 轮输入图片缩放到 640×640每批 16 张如果验证集指标连续 20 轮不提升就提前停。patience参数在这里很关键单类别小数据集很容易出现过拟合早停能帮你省下大量时间。参数说明如果你显存有限比如只有 4G 的显卡把batch降到 8 甚至 4。如果你用的是 CPUbatch4都算大的。日志里除了 loss 数值重点盯mAP50和mAP50-95这两个指标。小数据集上 mAP50 能到 0.8 以上已经算不错了别拿它跟 COCO 上的 0.5 比。yolo detect train data/path/to/data.yaml modelyolov8s.pt epochs50 imgsz640 batch8 device0逻辑说明这是第二个可选训练方案把模型从 n 换成 s规模和参数量大一些精度通常会略高但小数据集上优势不明显。device0指定用第一块 GPU如果你是多卡机器想省事就写device0,1。参数说明这两个方案里model参数决定的是初始权重。如果你不想下载预训练权重可以写成yolov8n.yaml那就会从头训练一个随机初始化的模型。我不建议小数据集上从头训收敛慢且效果差。3.5 训练结束后看什么权重文件训练完成后Ultralytics 会在runs/detect/train目录下生成一堆文件包括best.pt和last.pt。best.pt是验证集指标最好的那一轮权重last.pt是最后一轮保存的权重。平时测试、导出都优先用best.pt。yolo detect predict modelruns/detect/train/weights/best.pt sourceD:/jishui_dataset/images/val saveTrue逻辑说明这条命令把训练好的 best 权重拉到验证集图片上做一次推理输出的图片会保存到runs/detect/predict目录下。打开这些图片能直观看到哪些水坑被识别出来了哪些没被识别出来比只看数字指标更有体感。参数说明source可以指向单张图片、一个文件夹或视频文件。如果你想看置信度分数可以把参数save_confTrue加上想控制预测框不过度重叠调整conf0.25。这个阈值在积水检测这种漏检比误检更可怕的场景里建议调到 0.15 左右。4. 注意标签数量、中文路径和单类别小样本的几个常见训练陷阱4.1 现象训练刚开始没报错但 loss 是 nan原因最常见的是图片本身有问题比如下载过程中某些 jpg 文件损坏、分辨率异常或者图片是灰度三通道格式不统一。nan 的另一个来源是学习率设置过大但用 Ultralytics 默认学习率一般不会出现这个。解决先跑一遍数据自检脚本遍历所有图片和标签文件确认每张图能被 OpenCV 正常读取、所有坐标值都是合法数字。做完这一步nan 问题能解决一大半。另外把batch降小再试试也能排除显存溢出导致的异常。4.2 现象txt 标签里出现了小数位数不一致或坐标越界原因labelImg 在手动标注时偶尔会手抖画出框超出图片边缘或保存时把坐标写成了浮点。YOLO 训练时会把越界坐标强行裁剪但如果坐标是负数或超过图片尺寸太多训练时可能出现警告提示。解决写一个标签清洗脚本读取每个 txt 的五个值检查类别编号是否在合法范围内、中心点和宽高是否大于 0、框是否超出图片边界如果超出就裁剪到合法范围。这个数据集 357 张的标注质量整体不错你不一定遇到越界但养成清洗习惯对以后自己标注的数据大有好处。4.3 现象类别名 jishui 是拼音训练出来的模型在可视化时看不出是什么原因这个数据集的类别名就是jishui没有中文形式的 name。如果你希望模型输出的标签是中文需要在训练前把 yaml 里的 names 改成中文。但要注意Ultralytics 输出中文字符经常出现字体缺失导致乱码。解决我的习惯是模型内部类别名保持英文在外部可视化或者业务系统里做一层映射比如检测结果返回jishui时展示为“积水”。这样既避免了训练阶段字体问题又方便后续对接业务系统。4.4 现象验证集 mAP 很高但测试时误检全是地砖纹理或阴影原因357 张单类别数据集室内积水场景中背景高度相似模型学到的不一定是“积水”本身的特征而是学习到了“暗色区域”这个规律。地砖缝隙、倒影、阴影都会被误判成积水。解决这是小样本单类别数据集的天花板靠调参很难根治。如果你只是做流程验证这不影响你跑通整个链路如果你真要落地建议后续补充不同光照、不同地面材质的图片并且用mosaic0.5这种数据增强参数来增加多样性。4.5 现象目录结构明明没错但训练报unable to find label file原因图片和 txt 标签不在对应的 labels 目录里或者文件名大小写不一致。比如图片叫197.JPG标签叫197.txt但你的代码是按.jpg去查找替换的。解决先把所有图片扩展名统一改成小写再执行一次文件复制脚本确保 labels 目录下每个 txt 都能在 images 目录下找到同名 jpg。准备一个小工具函数做交叉检查缺失的文件直接打印出来。5. 把这份积水数据集迁移到自己的场景一张图都不会浪费的改造方案5.1 一键把 jishui 改成你的业务类别名很多从业者下载这份数据不是真的要做积水检测而是看重它“结构干净、单类别简单”的特点想拿它当模板跑通流程再把数据换成自己的业务数据。这个过程核心是批量改类别标签和 yaml 文件。import os label_dir D:/jishui_dataset/labels/train new_name_mapping {0: 0} # 假设你只有一个类别 for file in os.listdir(label_dir): if not file.endswith(.txt): continue path os.path.join(label_dir, file) with open(path, r, encodingutf-8) as f: lines f.readlines() new_lines [] for line in lines: parts line.strip().split() if parts and parts[0] in new_name_mapping: parts[0] new_name_mapping[parts[0]] new_lines.append( .join(parts) \n) with open(path, w, encodingutf-8) as f: f.writelines(new_lines) print(label class mapping done)逻辑说明这段脚本遍历 labels 目录下所有 txt把每行的第一个字段也就是类别编号替换成你想要的编号。由于这个数据集只有一个类别类别编号始终是 0所以实际改的不是编号而是后续 yaml 中的 names 文本。你需要在 data.yaml 里把jishui改成你的业务名比如water_leak、puddle、flood_area等。参数说明如果你的业务是多类别分类这个脚本的new_name_mapping需要写成完整映射表比如{0: 1, 1: 2}同时还要同步修改 yaml 里的 names 列表新增的类别索引必须从 0 开始连续递增中间不能跳号。5.2 从自己的视频抽帧开始造数据如果你想自己采集数据别一上来就拍照先从视频里抽帧是效率最高的方式。拿手机固定角度拍摄积水场景然后用 OpenCV 每 N 帧抽一张这样一分钟的视频能抽出几百张原始图片。import cv2 video_path D:/data/water_video.mp4 output_dir D:/data/frames os.makedirs(output_dir, exist_okTrue) cap cv2.VideoCapture(video_path) frame_count 0 save_count 0 while True: ret, frame cap.read() if not ret: break if frame_count % 10 0: cv2.imwrite(f{output_dir}/{save_count:05d}.jpg, frame) save_count 1 frame_count 1 cap.release() print(ftotal frames: {frame_count}, saved: {save_count})逻辑说明frame_count % 10 0意味着每 10 帧保存一帧如果视频是 30fps抽取出来的图片相当于每秒钟 3 张。这样抽出来的图片之间相似度较高要注意在划分训练集和验证集时按时间段切分避免同一场景的连续帧同时出现在训练和验证里。参数说明抽帧间隔不是固定的画面变化越快间隔要越短。如果场景几乎不变间隔 50 帧都可以。建议在不同时间段、不同角度各采集一段视频保证样本多样性。5.3 单类别 vs 多类别先别急着加类别357 张单类别数据集的训练流程已经很成熟但不少人在迁移时犯的错是一上来就把业务类别扩展到十几个比如积水、漏水、管道破裂、地面积水等等。每个类别至少需要几百张样本如果素材不够训练出来的模型就是个大号随机猜测器。我的建议是先按单类别跑通一版确认整个流程没有坑再逐步增加类别。每增加一个类别至少要补充 300 张以上标注图片且新增类别的图片要尽量覆盖该类别在真实场景中的各种形态。移动小目标检测的实践也反复证明类别越多、目标越小数据需求量是呈指数级上涨的。6. 落一个验证方法看一眼 mAP 和坏图再决定怎么用训练结束后不要急着把权重部署到业务上先做一次完整的验证。Ultralytics 默认输出的 mAP 只在训练日志里你可以跑一遍带验证模式的命令单独评估最佳权重在验证集上的表现。yolo detect val modelruns/detect/train/weights/best.pt dataD:/jishui_dataset/data.yaml逻辑说明这条命令会把验证集所有图片跑一遍输出mAP50、mAP50-95、各类别 AP 及 Precision/Recall 曲线。对于单类别数据集你重点看三样东西mAP50 数值、Precision 和 Recall 的平衡点、以及 val 目录下生成的带预测框的图片。参数说明如果 mAP50 在 0.8 以上说明模型对这类室内积水场景有较好的识别能力可以继续用。如果 Precision 高而 Recall 低说明很多真实积水漏掉了把预测时的conf阈值降下来。如果 Recall 高而 Precision 低说明误检太多把conf阈值往上调。这个阈值就是在第 3 章推理命令里提到的conf参数。还有一个小技巧用脚本直接检查验证集图片中有没有标注框数量异常的文件。这个数据集总框数 378框数 357 张图片平均每张 1.06 个框这意味着大多数图片只有一个目标也可能存在少量图片完全没有框。而这些无目标图片如果被分到验证集会直接影响 mAP 的绝对值。from pathlib import Path label_dir Path(D:/jishui_dataset/labels/val) empty_files [] for txt in label_dir.glob(*.txt): with open(txt, r, encodingutf-8) as f: count sum(1 for line in f if line.strip()) if count 0: empty_files.append(txt.name) print(fempty label files in val: {len(empty_files)}) for name in empty_files: print(name)逻辑说明脚本统计验证集目录下每个 txt 文件的有效行数行数为 0 说明这张图没有任何目标。这类图片放在验证集里会降低 Recall放在训练集里会影响训练稳定性因为模型找不到能匹配的正样本。参数说明对于单类别小数据集我一般会把无目标图片单独挑出来放到一个文件夹不作为训练或验证使用或者把它们归入背景类单独处理。这个数据集的标注比较完整大概率不会出现空标签但你后续自己标注时一定会遇到这个脚本留着备用。从那以后我拿到任何数据集的第一件事就是先跑一遍标签完整性和路径一致性检查确认 jpg、xml、txt 三者能一一对上再整理目录结构最后才谈训练。这套流程听起来慢但能省掉后续排错的好几个小时。这次拆这份积水数据集也是一样357 张图片不多但整个链路从头走到尾该踩的坑基本都踩了一遍写出来希望帮到你。本文还有配套的精品资源点击获取
返回列表