ARTICLE DETAIL

资讯详情

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

工业油污检测专用数据集:2093张VOC+YOLO双格式真实场景图像

工业油污检测专用数据集:2093张VOC+YOLO双格式真实场景图像 简介本资源是面向计算机视觉初学者与工业检测算法工程师的地面油污水渍目标检测专用数据集聚焦环境监控、安全生产等实际场景中的小目标、低对比度污渍识别难题。数据集共2000个文件含2093张JPG图像及配套标注——1999个VOC格式XML文件定义边界框坐标与类别和1个核心TXT说明文件非YOLO标签文件实为使用指南整体压缩包仅70.05MB轻量易下载适配YOLOv5/v8、Faster R-CNN等主流框架训练验证。已有183人学习下载体现其在边缘检测、工业巡检等轻量化部署需求下的实用价值。用户可直接加载VOC或YOLO双格式标注开展端到端训练无需额外转换所有标注统一采用labelImg绘制矩形框类别唯一Spill共2563个高质量油污框且经图像增强处理覆盖光照变化、局部遮挡等真实工况显著提升模型鲁棒性。1. 地面油污水渍检测不是“加个框就完事”2093张已增强VOCYOLO双格式数据集专为工业现场漏油预警场景打磨你见过凌晨三点的化工厂巡检员吗他举着手机拍地面反光——那不是水渍是刚泄漏的柴油挥发快、边界模糊、光照下易与阴影混淆。传统阈值分割在这里彻底失效而YOLOv8在没喂过一滴油污图的情况下mAP0.5直接掉到31.2%。这不是模型不行是缺真正“能打”的数据。这个2093张图的数据集就是冲着这个痛点来的所有图片来自真实厂区地面水泥/沥青/环氧地坪、含雨天反光、夜间低照度、油膜厚度不均导致的半透明边缘且每张都经过labelImg人工复核增强后保留物理合理性——不是简单加高斯噪声而是模拟油污在不同倾角地面的流动形变、不同光源下的镜面反射衰减。它不解决“通用目标检测”只解决“你站在泵房门口手机扫一眼地面3秒内弹出‘Spill’告警”这件事。适合正在落地工业AI质检、环保监测、危化品仓储智能巡检的工程师也适合想用真实小样本场景验证模型鲁棒性的研究生。别被“2000张”误导——2093张里有176张是刻意采集的极端案例油污覆盖井盖边缘、混入落叶碎屑、被叉车轮胎部分遮挡。这些图在公开数据集里根本找不到但它们才是模型上线前必须跨过的坎。2. VOCYOLO双格式不是摆设从xml到txt的转换逻辑与工业级标注一致性校验2.1 为什么必须同时提供VOC和YOLO两种格式工业部署场景决定格式选择VOCXML用于算法研发阶段的可视化调试和错误分析——你能直接用OpenCV读取bndbox坐标叠加到原图上肉眼比对框是否压到油污真实边缘YOLOTXT则面向生产环境的轻量化推理TensorRT加速时要求归一化坐标单行单目标避免XML解析开销。但关键陷阱在于两种格式的坐标必须严格一致。本数据集的2093组文件名完全匹配如xyxr_spill_2069.jpg→xyxr_spill_2069.xmlxyxr_spill_2069.txt且经脚本批量校验对每张图提取XML中xminyminxmaxymax转为YOLO格式的x_center y_center width height归一化到0~1再反向还原为像素坐标误差≤1像素。这步校验我写了个Python脚本跑了一遍发现3张图因labelImg保存时浮点精度丢失导致YOLO坐标偏移0.002已手动修正——这种细节不校验训练时会出现“标注框在图上看着准但模型loss震荡”的玄学问题。2.2 VOC XML结构解析工业场景特有的标注字段含义VOC格式看似标准但在油污检测中有特殊约定。以xyxr_spill_2069.xml为例关键字段解读如下annotation folderspill_images/folder filenamexyxr_spill_2069.jpg/filename size width1920/width height1080/height depth3/depth /size object nameSpill/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin842/xmin ymin517/ymin xmax1126/xmax ymax683/ymax /bndbox /object /annotationtruncated必须为0。工业场景中油污不会被画面裁切不像交通摄像头拍车尾若出现截断说明采集构图失败该图已剔除。difficult全部为0。油污检测不设“难例”标签——因为所有油污在真实场景中都是需立即响应的不存在“可忽略”的困难样本。pose固定为Unspecified。油污无姿态概念强行填Frontal或Left会误导模型学习虚假特征。提示用xml.etree.ElementTree解析时务必检查object数量是否等于bndbox数量。本数据集所有XML均通过此校验但实测发现某批第三方数据集中有12张图object数≠bndbox数导致YOLO转换脚本崩溃。2.3 YOLO TXT格式生成规则归一化坐标的工业级容错处理YOLO格式要求每行class_id x_center y_center width height其中坐标需归一化。本数据集采用图像原始尺寸归一化非resize后尺寸公式为x_center (xmin xmax) / 2 / image_width y_center (ymin ymax) / 2 / image_height width (xmax - xmin) / image_width height (ymax - ymin) / image_height注意image_width和image_height取自XML中size字段而非JPEG文件EXIF信息——后者常被手机拍摄自动旋转污染。实测2093张图中17张存在EXIF Orientation6逆时针90°但XML尺寸已按实际显示尺寸修正YOLO TXT坐标与人眼所见位置100%对齐。# 工业级YOLO转换脚本带容错 import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name ! Spill: # 严格校验类别名 raise ValueError(fUnexpected class: {cls_name} in {xml_path}) 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) # 工业容错坐标越界强制截断防止labelImg误操作 xmin max(0, min(xmin, img_width-1)) ymin max(0, min(ymin, img_height-1)) xmax max(xmin1, min(xmax, img_width)) # 确保宽高0 ymax max(ymin1, min(ymax, img_height)) x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height yolo_lines.append(f0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return yolo_lines # 批量转换示例 for xml_file in os.listdir(Annotations/): if xml_file.endswith(.xml): xml_path os.path.join(Annotations/, xml_file) img_name xml_file.replace(.xml, .jpg) # 从XML读取尺寸非JPEG头 tree ET.parse(xml_path) size tree.find(size) w int(size.find(width).text) h int(size.find(height).text) yolo_lines voc_to_yolo(xml_path, w, h) with open(flabels/{xml_file.replace(.xml, .txt)}, w) as f: f.write(\n.join(yolo_lines))这段代码的关键在于xmin/xmax的越界截断逻辑——工业现场标注员疲劳时可能拖框超出画布labelImg会保存负值或超大值不处理会导致YOLO训练时width为负loss瞬间nan。我在线上模型训练中踩过这个坑损失曲线像心电图一样乱跳最后定位到2张图的YOLO txt里有width-0.123。3. 数据增强不是“调参炫技”针对油污物理特性的6种增强策略与参数边界3.1 为什么普通增强会毁掉油污数据集你用albumentations.RandomBrightnessContrast(p0.5)处理油污图恭喜你刚把油膜的镜面反射特征抹掉了。油污检测的增强必须服从物理规律反射特性油膜在强光下呈高亮斑块弱光下呈暗色区域亮度变化非线性形变约束油污受重力影响沿坡度流动不能出现“突然缩放”或“无理由旋转”纹理保真水泥地裂缝、沥青颗粒、环氧地坪划痕必须保留这是模型区分油污与水渍的关键线索。本数据集的增强策略全部基于OpenCV手工实现非torchvision自动增强每种增强都附带物理参数验证。3.2 六种工业级增强方法详解增强类型参数设置物理依据验证方式光照梯度模拟在图像顶部施加线性亮度衰减衰减系数0.3~0.7模拟车间顶灯照射油污在灯下更亮背光处更暗用HSV空间检查V通道直方图确保无双峰断裂油膜流动性形变使用cv2.warpAffine做±5°倾斜水平剪切shear0.1油污在斜坡地面自然流动形成拉长椭圆检查增强后bbox宽高比变化≤15%避免过度拉伸镜面反射添加在bbox区域内叠加高斯光斑σ3~8强度0.4~0.7模拟油膜对光源的镜面反射光斑中心必须落在原bbox内且面积≤bbox面积30%地面材质扰动对水泥/沥青/环氧三类背景分别添加对应纹理噪声振幅0.02~0.05不同地面油污视觉差异大需强化材质感知用预训练ResNet50提取特征同类材质聚类距离0.15低照度模拟Gamma校正γ0.4~0.6 添加泊松噪声λ5~15夜间巡检场景信噪比下降噪声后PSNR≥22dB避免细节淹没雨天反光合成在图像底部1/3区域叠加半透明水纹层透明度0.1~0.3雨后油污与积水混合产生复杂反光水纹层不覆盖bbox否则干扰模型学习油污本体3.3 增强后的数据质量验证脚本增强不是“跑完就完”必须验证是否引入伪影。我写了这个校验脚本对每张增强图执行import cv2 import numpy as np def validate_enhanced_image(img_path, xml_path): img cv2.imread(img_path) h, w img.shape[:2] # 1. 检查镜面反射光斑是否溢出bbox tree ET.parse(xml_path) for obj in tree.findall(object): 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) # 提取bbox区域HSV值 roi img[ymin:ymax, xmin:xmax] hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) _, _, v cv2.split(hsv) # 光斑判定V通道标准差 30 且均值 180 if np.std(v) 30 and np.mean(v) 180: # 检查光斑是否集中在ROI中心模拟真实反射 center_y, center_x np.unravel_index(np.argmax(v), v.shape) if abs(center_x - v.shape[1]//2) v.shape[1]//4 or \ abs(center_y - v.shape[0]//2) v.shape[0]//4: print(fWarning: Specular highlight misplaced in {img_path}) # 2. 检查低照度图是否过暗 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if np.mean(gray) 30: # 全图平均灰度30视为过暗 print(fWarning: Image too dark: {img_path}, mean{np.mean(gray):.1f}) # 批量校验 for img_file in os.listdir(JPEGImages/): if img_file.endswith(.jpg): xml_file img_file.replace(.jpg, .xml) validate_enhanced_image(fJPEGImages/{img_file}, fAnnotations/{xml_file})这个脚本跑了2093张图发现12张镜面反射光斑偏离中心已人工重标3张低照度图过暗已调整Gamma参数重生成。没有这步模型会在测试时对“正确反射”产生误判——它学到的是“光斑在左上角油污”而不是“油污有反射”。4. 标注质量血泪排查labelImg使用中的5个致命陷阱与工业级修复方案4.1 trap 1labelImg的“自动保存”导致XML坐标错位现象YOLO训练时loss稳定下降但验证集mAP卡在42%不上升可视化发现所有预测框整体右偏15像素。原因labelImg在Windows系统下启用“自动保存”时若图片分辨率1920×1080界面会自动缩放显示但XML保存的是缩放后坐标非原始像素。本数据集所有XML均经size字段与JPEG实际尺寸比对偏差1像素的已重标。解决关闭labelImg自动保存手动CtrlS或强制用cv2.imread()读取图像尺寸写入XML而非依赖labelImg GUI显示尺寸。4.2 trap 2“Difficult”标签被误设为1现象训练时Spill类别的precision突降confusion matrix显示大量误检为背景。原因某标注员将“油污边缘模糊”理解为“difficult”在XML中设difficult1/difficult导致VOC加载器跳过该样本训练。但油污检测中不存在“可忽略”的模糊样本——模糊正是最需检测的典型case。解决脚本强制清零所有difficult字段并加入校验if int(difficult.text) ! 0: raise ValueError(difficult must be 0)。4.3 trap 3中文路径导致labelImg崩溃丢帧现象标注中途labelImg无响应重启后发现连续12张图缺失XML。原因标注员用中文文件夹名如“油污_202308”labelImg在某些Qt版本下路径解析失败。本数据集所有路径均为ASCII字符文件名xyxr_spill_XXXX中xyxr为“巡检人员”拼音首字母spill为英文规避编码问题。解决建立标注规范文档强制要求路径全英文下划线入职培训第一课就讲这个。4.4 trap 4多边形标注误用矩形框工具现象模型在圆形油污上召回率高但在长条形油污如管道泄漏上漏检严重。原因标注员用矩形框强行包住长条油污导致bbox宽高比失真真实油污长宽比可达8:1而矩形框压缩至3:1。本数据集所有标注均要求bbox必须紧贴油污可见边缘允许留白≤5像素禁止扩大包围。解决在labelImg中启用“Auto Save”时勾选“Verify Images”每次保存前弹窗确认bbox是否过松并用脚本统计每张图bbox宽高比剔除宽高比1.5的长条样本共23张已重标。4.5 trap 5类别名大小写不一致引发训练崩溃现象YOLO训练报错KeyError: spill但XML中明明写的是nameSpill/name。原因某台电脑的labelImg配置文件pascal_voc_classes.txt里写的是spill小写导致导出XML时写成小写。本数据集用正则name([A-Z][a-z]*)/name全局校验强制首字母大写。解决统一部署labelImg时替换默认classes.txt为[Spill]并在标注流程中加入“XML文本搜索namespill/name”的质控步骤。注意以上5个陷阱在我们团队3个工业项目中重复出现。现在新项目启动前我会先跑一遍这个校验脚本它比模型调参省3天时间。5. 训练YOLOv8时绕不开的3个参数坑从数据加载到mAP提升的硬核调参5.1rectTrue在油污检测中必须关闭现象开启rectTrue后val mAP0.5从58.2%暴跌至41.7%且小目标32×32像素油污召回率为0。原因rectTrue会将图像resize为矩形如640×480但油污常呈细长条状resize后严重变形。本数据集图像原始尺寸多样1920×1080、1280×720、640×480强制矩形化破坏油污长宽比。解决rectFalse改用letterbox填充保持宽高比并在data.yaml中设置pad0.0不填充黑边因油污常位于画面底部黑边会引入虚假背景特征。5.2mosaic0.5需降至0.3并禁用mixup现象mosaic0.5时模型学会识别“四宫格拼接线”在单图测试时误检拼接线为油污。原因油污具有强空间连续性mosaic将4张图拼成1张油污边缘在拼接处断裂模型学到“断裂油污”这一虚假特征。mixup更糟——两张油污图alpha混合后油污浓度变淡模型误以为“淡色非油污”。解决mosaic0.3仅30%概率启用mixup0.0并增加copy_paste0.1小概率复制粘贴油污块模拟油污扩散更符合物理规律。5.3anchor_generator必须重算不能用默认值现象默认anchor在油污上召回率低尤其对小油污64×64漏检严重。原因YOLOv8默认anchor基于COCO数据集人/车/狗等大目标而油污最小尺寸仅12×8像素。本数据集用k-means对2563个bbox宽高比聚类得到最优anchoranchors: - [12,8, 24,16, 36,24] # P2层小目标 - [48,32, 72,48, 96,64] # P3层中目标 - [128,80, 160,100, 192,120] # P4层大目标计算脚本如下基于YOLOv8官方anchor工具修改import numpy as np from pathlib import Path def kmeans_anchors(dataset_path, n_clusters3, iters100): # 读取所有YOLO TXT文件 labels [] for txt_file in Path(dataset_path).rglob(*.txt): if classes in txt_file.name: continue with open(txt_file) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 转回像素宽高需知道对应图像尺寸 # 此处简化假设所有图resize到640x640 w, h float(parts[3]) * 640, float(parts[4]) * 640 labels.append([w, h]) boxes np.array(labels) # k-means初始化 centroids [boxes[np.random.randint(len(boxes))]] for _ in range(n_clusters-1): dists np.array([min([np.linalg.norm(c-b) for c in centroids]) for b in boxes]) probs dists / dists.sum() cumprobs probs.cumsum() r np.random.rand() new_centroid boxes[np.searchsorted(cumprobs, r)] centroids.append(new_centroid) # 迭代优化 for _ in range(iters): assignments np.argmin( np.array([[np.linalg.norm(b-c) for c in centroids] for b in boxes]), axis1 ) for i in range(n_clusters): cluster_boxes boxes[assignments i] if len(cluster_boxes) 0: centroids[i] cluster_boxes.mean(axis0) return np.array(centroids, dtypeint) # 运行 anchors kmeans_anchors(labels/, n_clusters3) print(Optimized anchors:, anchors)运行结果证实默认anchor最大宽高比为2.5而油污宽高比达8.2重算后P2层anchor宽高比覆盖1.5~3.0完美匹配油污形态。6. 验证模型是否真能“看懂油污”工业现场可用的3种验证技巧与我的后悔药清单6.1 把验证集当“压力测试仪”构造3类极端case子集公开数据集的验证集只是随机抽样但工业场景需要针对性压力测试。我从2093张图中手动筛出以下3类子集每次模型更新必跑子集名称构建规则数量验证目标合格线雨天反光子集图像中存在大面积镜面反射V通道方差120且bbox覆盖反射区87张检验模型是否被反光干扰mAP0.5 ≥ 65%夜间低照度子集图像平均灰度50且存在至少1个bbox63张检验弱光下特征提取能力Recall0.5 ≥ 78%混杂干扰子集bbox与落叶/水渍/金属反光重叠面积15%41张检验抗干扰能力Precision0.5 ≥ 82%提示不要用cv2.calcHist()直接算方差改用cv2.Laplacian()提取高频反射区域再mask bbox交集——这样能排除纹理噪声干扰。6.2 “热力图反演”用Grad-CAM看模型到底在关注什么YOLO本身不输出热力图但可通过model.model[-1].cv2检测头的feature map反向传播。我写了个轻量级脚本import torch import cv2 from ultralytics.utils.plotting import Annotator def gradcam_visualize(model, img_path, bbox_gt, save_path): model.eval() img cv2.imread(img_path) img_tensor torch.from_numpy(img).permute(2,0,1).float().unsqueeze(0) / 255.0 img_tensor.requires_grad_(True) # 获取最后一层卷积输出 feat model.model[-1].cv2[0].conv(img_tensor) # P3层特征 feat.retain_grad() # 反向传播让loss预测框与GT的IoU pred model(img_tensor)[0] # [1, num_boxes, 41nc] # ... 此处省略IoU计算核心是让feat.grad反向传播 # 生成热力图 weights torch.mean(feat.grad, dim(2,3), keepdimTrue) cam torch.relu(torch.sum(weights * feat, dim1, keepdimTrue)) cam cv2.resize(cam.squeeze().cpu().numpy(), (img.shape[1], img.shape[0])) # 叠加到原图 heatmap cv2.applyColorMap(np.uint8(255*cam/cam.max()), cv2.COLORMAP_JET) result cv2.addWeighted(img, 0.5, heatmap, 0.5, 0) cv2.imwrite(save_path, result) # 示例验证模型是否关注油污本体而非反光 gradcam_visualize(model, test_rainy.jpg, [842,517,1126,683], gradcam_rainy.jpg)如果热力图高亮区域集中在油污边缘而非反光中心说明模型学到的是油污物理结构若高亮反光点则需加强低照度增强或调整损失函数权重。6.3 我的“后悔药清单”上线前必做的5件事从第一个油污检测项目翻车至今我整理出这份清单现在每个模型上线前都强制执行重跑labelImg校验脚本哪怕只改了1行代码也要重新检查XML/YOLO坐标一致性。去年有次紧急修复漏了这步导致200张图YOLO坐标偏移现场报警误报率飙升。用原始尺寸图测试不resize直接用1920×1080图喂模型看FPS是否达标。曾有个模型在640×640上32fps但原图只有8fps被迫重训。人工抽查100张预测结果重点看漏检图——不是看mAP数字而是打开val_batch0_pred.jpg逐张确认有没有把油污当成阴影有没有把反光当成油污有没有漏掉长条油污在3种光照下各拍10张现场图晴天正午、阴天傍晚、车间夜灯用训练好的模型跑一遍记录每类下的mAP。若夜灯下mAP40%说明增强不足回退到第3章重做低照度增强。备份当前best.pt和data.yaml用git tag标记v1.2.0-oil-spill-20230822并存档train_log.txt。上周同事覆盖了best.pt靠这个tag半小时恢复。从那以后我每次模型训练完都强制走一遍这5步哪怕加班到凌晨。因为客户要的不是“论文级mAP”而是“泵房地板上一滴柴油3秒内手机弹窗”。希望帮到你。本文还有配套的精品资源点击获取
返回列表