拓冰建站拓冰建站
首页 / 资讯中心 / 正文

地面油污水渍检测数据集:VOC转YOLO与YOLOv8训练实践

简介这是一套面向目标检测研究者和工程师的地面油污水渍检测数据集聚焦油污水渍的自动识别与定位可广泛应用于环境监控、公共安全、工业现场巡检等场景。资源包共2000个文件、大小约70.05MB以VOC格式的XML标注文件为主体并附有说明TXT兼容Pascal VOC与YOLO两种标注格式研究者和开发人员可直接在主流目标检测框架中加载使用。数据集包含2093张jpg图片及对应标注唯一标注类别Spill共标注了2563个目标框由labelImg按统一规则绘制矩形框完成由于已做增强处理能有效提升模型在不同光照、角度和遮挡条件下的泛化能力。资源包内文件组织规整标注与图片对应关系明确便于批量处理和迁移学习对于希望快速搭建检测流程的用户可省去自行收集与标注数据的环节。已有178人学习/下载适合需要训练油污水渍检测模型、进行算法对比验证或从事相关课题研究的技术人员获取既可用于模型训练也可作为评测基准或数据增强参考。1. 地面油污水渍检测数据集2000张增强图背后的三个关键问题地面油污水渍检测是目标检测里典型的“看着简单、做起来上头”的任务。油污和水渍的共同特征是低纹理、边界模糊、受光照反射影响极大和环氧地坪、瓷砖、水泥地面的视觉差异往往只有几个灰阶和一层高光的区别。通用目标检测模型在这个任务上表现不稳定不是因为YOLO网络不够强而是缺乏与现场材质分布匹配的训练数据。以“2000张VOCYOLO已增强”这套数据作为起点可以直接跳过了凑图、清洗、初标这一类最耗时的环节把精力集中在格式转换、标注校验和训练参数调校上。适合它的读者有两类一类是做工业巡检或商用机器人POC需要在几天内跑通一个像样的检测结果另一类是已有现场目标检测经验、想专门补油污水渍专项数据的实施工程师。下面按一条可完整复现的路径展开先理解VOC与YOLO两套格式的内部逻辑再做转换和增强后校验接着接入YOLOv8训练流程最后给出部署阶段能直接用上的阈值校准和形态学后处理技巧。2. 地面油污水渍检测数据集的结构VOC标注与YOLO归一化坐标的对齐逻辑2.1 油污水渍的类别划分与标注边界约定拿到任意一套“VOCYOLO”格式数据第一件要做的事往往被跳过固定类别语义。对地面油污水渍任务来说工程上最稳妥的类别划分方式是二分类oil_stain和water_stain各占一列。VOC格式里对应object.nameYOLO格式里对应txt首列的整数ID。如果数据集中把油污和水渍合并成单一stain类训练看起来简单但到了现场就会发现“需要区分漏油和积水”这个需求完全没法满足。漏油意味着设备泄漏需要走维修工单积水更多与清洁有关。类别语义直接影响后续工单分发逻辑属于方案设计阶段就要定死的东西。标注边界约定是另一个决定数据质量的关卡。油污在环氧地坪上呈现为黄褐色半透明膜水渍则是深浅不一的湿润色斑二者都没有刚性轮廓。常见的工程约定是油污以主油膜的扩散外沿为边界零散油滴要么忽略要么单独成框不能全部包进主框里水渍以潮湿反光边缘为准不要把周边地面上的镜面反射一起框进来。如果发现数据集中大量边界框比油污实际面积大出20%以上说明标注尺度整体偏松。与其在训练后反复调anchor不如先把这些边界框按统一标准整理一遍。2.2 VOC格式XML标注里值得逐项核对的内容VOC格式以xml文件存放标注核心结构是annotation下挂filename、size和若干object节点。每个object里有name、truncated、difficult和bndbox。对油污水渍检测最有区分度的是truncated和difficult两个字段truncated表示目标是否被图像边缘截断difficult表示这个目标是否属于难例。很多发布版数据集为了压缩体积会把这两个字段重置为0但实际场景里摄像头俯拍地面时画面边缘出现半块油污的概率相当高。一个实用的检查方式是先统计这两个字段的分布不要在训练之后才去排查数据问题import xml.etree.ElementTree as ET import glob stats {truncated: 0, difficult: 0, total: 0} for xml_path in glob.glob(annotations/*.xml): root ET.parse(xml_path).getroot() for obj in root.iter(object): stats[total] 1 if obj.findtext(truncated) 1: stats[truncated] 1 if obj.findtext(difficult) 1: stats[difficult] 1 print(stats) print(ftruncated ratio: {stats[truncated] / stats[total]:.2%})这段脚本每跑一次就是在对标注集做一次体检。如果truncated比例低于1%同时图像里又频繁出现大块贴边污渍说明训练集和真实部署视角不一致验证集mAP再高换一个机位就失效。difficult字段同理它告诉模型哪些目标即使人眼都难判断如果全部是0不代表没有难例只代表标注者没有把难易信息记录下来。2.3 YOLO格式txt归一化坐标与VOC的换算关系YOLO的txt每行代表一个目标五个字段依次是类别ID、归一化中心x、归一化中心y、归一化宽度w、归一化高度h。VOC则记录矩形框的左上角和右下角像素坐标。把VOC转换为YOLO格式用下面这组公式转换目标VOC字段YOLO字段计算公式中心点xxmin, xmaxcx(xmin xmax) / 2 / width中心点yymin, ymaxcy(ymin ymax) / 2 / height宽度xmin, xmaxw(xmax - xmin) / width高度ymin, ymaxh(ymax - ymin) / height反向还原成像素坐标也很简单xmin (cx - w / 2) * widthxmax (cx w / 2) * widthymin和ymax同理。这里唯一容易踩的坑是分子分母的单位问题公式中的width和height必须是图片原始像素宽高而不是训练时设置的imgsz640。有人会在读取图片时误用模型输入尺寸做归一化导致所有框的坐标同时放大或缩小mAP直接崩到不可用。2.4 增强操作给标注带来的三类漂移数据集后缀标着“已增强”增强本身也是标注错误的高发来源。水平翻转是最容易处理的cx会变成1 - cx但w保持不变Mosaic拼接是重灾区四张图被重新拼到一张画布上后每个子图的偏移量必须重新加回到归一化坐标里少加一个偏移就会整块偏出图像范围HSV色相扰动虽然不影响坐标但对油污水渍这类颜色区分类别的任务影响很大色相偏移过大时油污的琥珀色特征会滑向水渍的浅灰区域类别可分性被增强器自己揉掉了。一个工作习惯值得固定下来增强后的数据不仅要看图像还要抽样把txt框画回图上逐张过目。人可以一眼看出框和油污边缘是否贴合但机器只能从损失函数里间接感知。这个“画框回看”的步骤比任何后续的网络结构改动都省时间。3. 把VOC转成YOLO格式转换脚本、增强后一致性校验与尺寸分布统计3.1 一份可直接改用的VOC转YOLO脚本在YOLO训练流程里txt是最终喂给数据加载器的格式xml只作为中间存储。从一个典型的数据集压缩包起步把它整理成“images目录存图、labels目录存txt”的标准形态需要一段可直接改用的脚本import os import glob import xml.etree.ElementTree as ET # 类别顺序必须与data.yaml中names保持一致 CLASSES [oil_stain, water_stain] def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() width int(root.findtext(size/width)) height int(root.findtext(size/height)) lines [] for obj in root.iter(object): name obj.findtext(name) if name not in CLASSES: continue bbox obj.find(bndbox) xmin float(bbox.findtext(xmin)) ymin float(bbox.findtext(ymin)) xmax float(bbox.findtext(xmax)) ymax float(bbox.findtext(ymax)) cx (xmin xmax) / 2.0 / width cy (ymin ymax) / 2.0 / height w (xmax - xmin) / width h (ymax - ymin) / height lines.append(f{CLASSES.index(name)} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) base os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, base), w) as f: f.write(\n.join(lines)) if __name__ __main__: os.makedirs(labels, exist_okTrue) for xml_file in glob.glob(annotations/*.xml): voc_to_yolo(xml_file, labels)脚本里有两段逻辑容易被忽略CLASSES的顺序必须与训练时的类别定义完全一致否则类别ID集体错位w (xmax - xmin) / width要求xmax、xmin都是像素坐标如果原始xml里出现负数或超宽坐标这里需要在转换前先过滤一次。转换完成后统计一下labels目录下的txt数量数值应等于xml数量。3.2 增强后标注与图片对齐的四项检查“已增强”三个字意味着jpg和txt已经成对生成过但增强管线里任何一个环节写错图片和txt就会脱钩。最直接的校验方式是逐项检查四个维度文件名一一对应、图片尺寸匹配、归一化坐标是否越界、类别ID是否都在合法范围。下面是一段独立的检验脚本运行成本低到可以每次使用数据集前都跑一遍import os from PIL import Image IMG_DIR, LAB_DIR images, labels bad [] for img_name in sorted(os.listdir(IMG_DIR)): if not img_name.endswith(.jpg): continue img Image.open(os.path.join(IMG_DIR, img_name)) w, h img.size txt_path os.path.join(LAB_DIR, img_name.replace(.jpg, .txt)) if not os.path.exists(txt_path): bad.append((img_name, missing_txt)) continue for line in open(txt_path): parts line.strip().split() if len(parts) ! 5: bad.append((img_name, line_format)) break cid, cx, cy, bw, bh float(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if cid not in (0.0, 1.0): bad.append((img_name, funknown_class_{int(cid)})) break if not (0 cx 1 and 0 cy 1 and 0 bw 1 and 0 bh 1): bad.append((img_name, coord_out_of_range)) break print(total images:, len([x for x in os.listdir(IMG_DIR) if x.endswith(.jpg)])) print(problems:, bad[:20])第二项“图片尺寸匹配”值得单独强调如果txt里的w和h是从1920×1080图片算出来的而同一文件名对应的jpg被增强器缩成了1280×720坐标虽然仍在0到1范围内但框的位置整体偏移。用上面这段脚本无法捕捉这类错位需要额外对比图片实际读出的宽高和标注生成记录里的宽高。批量数据整理时我会把这一步放进pipeline的固定环节防止它变成一次性手工检查。3.3 用长宽比与面积分布理解小目标比例油污水渍数据集里小目标检测是最常见的失效模式。统计每个标注框的宽度和高度分布可以提前知道模型会面对怎样的目标尺度。把这段统计做成文本输出import os import numpy as np areas, ratios [], [] for txt_name in sorted(os.listdir(labels)): for line in open(os.path.join(labels, txt_name)): parts line.strip().split() if len(parts) ! 5: continue w, h float(parts[3]), float(parts[4]) areas.append(w * h) if h 0: ratios.append(w / h) areas np.array(areas) ratios np.array(ratios) print(f样本数: {len(areas)}) print(f面积占比: p50{np.median(areas):.4f}, p75{np.percentile(areas, 75):.4f}) print(f宽高比: p50{np.median(ratios):.2f}, p90{np.percentile(ratios, 90):.2f})如果面积占比的中位数低于0.01意味着在640×640输入下目标边长往往不足64像素属于典型的小目标。油污的宽高比一般大于1.2呈横向铺开水渍则接近1形态更圆整。这个差异可以用在NMS策略和anchor尺寸上但更实用的地方在于验证集评估时单独看小尺寸档位的AP不要被整体mAP掩盖了漏检问题。4. 用YOLOv8训练自己的数据集目录组织、训练参数与损失曲线怎么读4.1 按YOLOv8约定组织VOCYOLO数据把上一步生成的labels目录和原始images目录按YOLOv8的期望结构摆放ground_stain_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── oil_water.yaml目录命名必须严格用train和val图片和与它同名的txt必须放在对应的train或val子目录里。关于划分比例2000张图的规模下我一般按8:2切分也就是1600张训练、400张验证。切分时按图片编号随机抽样并保证两个类别的样本比例在训练集和验证集里都接近原始分布避免某一类全落在验证集里。oil_water.yaml配置如下path: ./ground_stain_dataset train: images/train val: images/val names: 0: oil_stain 1: water_stain这里有个容易踩的路径坑path字段在YOLOv8里是相对或绝对路径的根train和val再相对path去解析。如果把path写成绝对路径换机器或换目录后必须同步更新。做实验的可复现性考虑建议在项目目录里用一个固定的数据路径data.yaml本身也纳入版本管理。4.2 训练命令与需要优先调整的参数YOLOv8训练命令通常长这样yolo detect train \ dataoil_water.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience15 \ seed42 \ projectruns/detect \ namestain_exp01几个参数对结果的影响优先级远高于其他项参数建议值为什么优先调它modelyolov8n.pt起步小数据集先用轻量模型把pipeline跑通imgsz640或1280油污水渍目标偏小加大输入尺寸能显著提升小目标召回batch以显存上限为准batch过小时BN统计不稳定损失曲线震荡幅度变大lr00.005到0.01油污水渍的语义简单学习率偏大容易在早期震荡patience10到15验证集mAP连续多轮不升即停省去无意义的等待轮次在8GB显存级别batch16配合yolov8n能在640输入下稳定运行如果改用1280输入batch要相应降到8或4。训练前先设置seed42固定随机种子同一个数据集的两次训练结果才具备可对比性这在调参数的过程中能省下大量自我怀疑的时间。4.3 从box_loss、cls_loss和dfl_loss曲线判断数据问题训练日志里有三条核心损失曲线box_loss是边界框回归的损失cls_loss是分类损失dfl_loss是分布焦点损失。油污水渍这类边界模糊目标的典型表现是cls_loss降得很快第一轮就从1.5跌到0.5以下但box_loss一直缓慢下行。曲线现象常见数据原因直接对策cls_loss快速下降后平台期类别特征重叠油污与水渍难以区分回查标注边缘是否含大量反光区域box_loss长时间不平滑标注框尺度不统一重新整理外接矩形统一标注口径三条曲线在验证集上反弹增强强度过猛验证集分布被甩开调低hsv_h、hsv_s扰动或暂时关闭Mosaicdfl_loss持续高目标边缘楔形严重加大imgsz让边界细节更好地映射到特征图上更重要的一点是不要只看train loss要同时看验证集loss。如果train loss稳步下降而val loss在某个epoch开始反弹说明模型进入过拟合patience参数会自动触发早停。打断训练后优先怀疑的不是网络复杂度而是训练集里有效样本的多样性不足——2000张图里如果大量是同一地砖场景模型很快就把材质纹理记住了而不是学会区分油污水渍本身。4.4 增强配置对地面场景的特殊处理YOLOv8默认开启Mosaic和HSV增强。Mosaic在这个任务上需要谨慎四张不同材质的地面拼在一起油污边界被切碎的概率很高模型学到的是“半块油污也能算正样本”。一种常见的做法是训练后期把Mosaic关闭让模型适应完整目标另外把hsv_h从默认0.015调整为0.01hsv_v从0.4降到0.3因为油污水渍在光度变化剧烈时特征漂移明显过大的光度扰动会让网络把阴影误判成污渍。还有一个需要明确的边界YOLO系列输出的检测框天然是矩形油污水渍的形状再不规则后处理阶段得到的也是外接矩形。数据集里如果放了不规则多边形的标注要么转成外接矩形要么换用yolov8-seg做实例分割——但这份数据既然以VOCYOLO方形框为标准就按矩形检测的逻辑走不要在图省事的前提下追求自由形状的拟合。5. 部署阶段的调参技巧置信度阈值校准与形态学后处理5.1 用验证集PR曲线定置信度阈值而不是拍脑袋推理时直接套用0.25的默认conf值往往会让地面巡检模型误检频发。建议训练完成后单独跑一遍验证集输出PR曲线数据yolo detect val \ modelruns/detect/stain_exp01/weights/best.pt \ dataoil_water.yaml \ conf0.001 \ plotFalse不需要只依赖YOLO终端打印的mAP也顺便读一下results.csv里的precise和recall对应行找到precision超过0.85时对应的置信度阈值。然后把该值写进部署端的conf参数。工业现场对误检的容忍度通常远低于漏检宁可漏掉少量水渍也不能让系统三天两头把地砖反光当成油污报工单。5.2 形态学闭运算合并被拆成两块的油污框油污面积大且边界模糊时模型常常把它检测成两个相邻框NMS又因为IoU不够高而没合并。在推理输出后加一段形态学处理把二值化的检测掩膜做闭运算再重新生成外框import cv2 import numpy as np boxes [(312, 520, 640, 870), (650, 545, 1140, 900)] # 假设检测输出 mask np.zeros((1080, 1920), dtypenp.uint8) for x1, y1, x2, y2 in boxes: mask[y1:y2, x1:x2] 255 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (40, 40)) merged cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) cnts, _ cv2.findContours(merged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) final_boxes [cv2.boundingRect(c) for c in cnts if cv2.contourArea(c) 2000] print(final_boxes)结构元尺寸不能用固定值。分辨率越高同样大小的油污在像素上占得越多结构元尺寸应与输入图像宽度挂钩1080p下用40×40720p下缩到24×24这样合并行为在不同摄像头之间保持一致。这一步纯粹是后处理不要把它反向加到训练集里训练时该用原始框就用原始框。核心思路只有一个让阈值和合并策略都服从于现场视频流的分辨率与误报率要求而不是依赖默认配置让模型自由发挥。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门