石油泄漏检测数据集制作与YOLOv8训练实践:6633张图VOC/YOLO双格式

发布时间:2026/8/26 9:19:02
石油泄漏检测数据集制作与YOLOv8训练实践:6633张图VOC/YOLO双格式 简介目标检测模型的训练效果高度依赖标注数据的质量与格式。在工业视觉场景中数据集稀缺且格式繁杂常常是落地的主要瓶颈。VOC与YOLO是两种主流的标注格式前者适用于经典检测框架后者因归一化坐标设计而兼容多尺度输入更受现代模型青睐。一套结构规范、包含负样本的数据集能显著减少模型误检配合离线增强与按场景划分的训练集可有效提升泛化能力。对于石油化工等垂直领域泄漏检测需专门的数据支撑。本文整理了一套包含6633张图像的石油泄漏检测数据集提供VOC与YOLO双格式标注并详细演示了基于YOLOv8的配置、训练、评估与推理全流程可为同类工业视觉任务提供可复用的工程参考。 做目标检测的同行应该都深有体会找数据集有时候比训练模型本身还让人头疼。通用物体检测有COCO、VOC这些大牌数据集可以随便下但一落到石油、化工这类垂直领域公开资源几乎是一片空白。石油泄漏检测就是个典型需求——管道法兰渗漏、罐区地面油污、海面溢油膜这些场景都需要专门的视觉模型来识别可数据从哪来我整理了一套石油泄漏检测数据集总共6633张图片单一类别VOC和YOLO两种标注格式都做好了整个打包成一个7z压缩包解压就能开训。这篇文章把数据集的格式细节、制作思路、以及我用它跑YOLOv8的完整过程都记录下来想省去标注功夫、直接上手训练的同学可以参考。话不多说我们从这套数据集本身聊起。1. 项目概述为什么石油泄漏检测需要专门的数据集1.1 石油泄漏检测的行业痛点与视觉方案的可行性石油泄漏在石化、管道运输、海上采油平台这些场景里是安全管理的头号问题。泄漏点如果发现不及时轻则造成物料损失和环境污染重则引发火灾爆炸。传统做法主要靠人工巡检但人的精力有限尤其是大面积的储罐区、绵延几百公里的输油管道单靠肉眼去盯漏检是大概率事件。所以近些年工业视觉在这个方向非常火很多团队想用目标检测模型来自动识别监控画面里的油污、油膜、渗漏痕迹。但这里有个尴尬的现实模型训练要数据而这类数据恰恰是最难获取的。泄漏是偶发事件正常运营的厂区不可能天天漏油给你拍真要出了大事故现场也没人顾得上给你做标注。网上公开的石油泄漏图像零散分布在各种新闻报道、学术论文里质量和格式五花八门直接用根本不行。这也是我做这套数据集的初衷——把散落的、自己采集的、合成的图像统一整理成规范格式让后面做这个方向的人少走弯路。1.2 数据集核心参数解读先把这个数据集的关键参数摆出来大家心里有个底图片数量6633张类别数1个类别类别名定位oil_leak实际使用可自行改名标注格式VOC格式XML和YOLO格式TXT双份打包格式7z压缩包6633张对单类别检测任务来说是一个比较实在的量级。检测模型训练最怕样本量太少导致过拟合单类别任务相对简单正常训练的话6000多张数据配合数据增强足够训练出一个能用的模型。当然和COCO那种几十万张的量级没法比但在工业垂直场景里这个体量已经算是可以落地的水平了。VOC和YOLO双格式是这套数据集的另一个实用点。VOC格式是很多经典检测框架如Faster R-CNN、SSD的标准输入YOLO格式则是YOLO系列模型直接使用的格式。很多公开数据集只给一种格式你用的时候还得自己写脚本转换这套直接把两种都备齐省了一步麻烦事。至于为什么用7z而不是ZIP或RAR核心原因就一个字省。7z在相同压缩级别下压缩率通常比ZIP高10%到20%对图片这种体积比较大的文件来说能节省不少空间和下载流量。而且7-Zip是免费开源的主流系统都有对应版本解压没有任何门槛。2. VOC与YOLO标注格式从目录结构到坐标换算想用好这套数据集先得把两种标注格式的底层逻辑搞清楚。很多新手在这上面栽过跟头不是XML字段看错就是TXT坐标算错最后训练出来的模型乱成一团。所以这一节我把两种格式掰开揉碎了讲。2.1 VOC格式的目录组织与XML标注解析Pascal VOC是深度学习目标检测领域最经典的标注格式之一它的目录结构长这样VOC2007/ ├── Annotations/ # 存放所有XML标注文件 ├── JPEGImages/ # 存放所有原始图片 └── ImageSets/ └── Main/ # 存放训练/验证/测试的图片名列表这里有个容易忽略的细节ImageSets/Main下面放的并不是图片本身而是纯文本的图片名列表。每个文件名一行不带扩展名比如train.txt里写的是000001、000002这样模型训练时会根据这些名字去JPEGImages里找对应图片。每个XML标注文件的结构是这样的annotation folderJPEGImages/folder filename000001.jpg/filename size width1280/width height720/height depth3/depth /size object nameoil_leak/name bndbox xmin245/xmin ymin180/ymin xmax620/xmax ymax430/ymax /bndbox /object /annotation重点看两个地方。一个是size里面记录的宽高是绝对像素值转换YOLO格式时靠的就是它另一个是object每个object块代表图中的一个目标name是类别名bndbox里的四个值是目标框的左上角和右下角坐标单位是像素。一张图里有多个泄漏点就会有多个object块。这套数据集因为只做了单类别所以每个XML里的name基本都是一样的oil_leak。如果你想扩展成多类别在后面加新的object块就行结构完全通用。2.2 YOLO格式的TXT标注与归一化坐标YOLO系列用的标注格式和VOC有本质区别。YOLO不存XML文件而是每张图片对应一个同名的TXT文件放在labels目录下。目录结构长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/images和labels目录严格一一对应图片叫000001.jpg对应的标注文件就叫000001.txt。TXT文件里每一行代表一个目标格式是五个值class_id x_center y_center width height注意这里的四个坐标值全部是归一化后的比例不是像素值。归一化就是除以图片的实际宽高把所有值压缩到0到1之间。比如中间的框坐标转换逻辑是x_center (xmin xmax) / 2 / 图片宽 y_center (ymin ymax) / 2 / 图片高 box_width (xmax - xmin) / 图片宽 box_height (ymax - ymin) / 图片高还是用上面XML的例子算一下。图片宽1280、高720框的坐标是xmin245、ymin180、xmax620、ymax430x_center (245 620) / 2 / 1280 0.33789 y_center (180 430) / 2 / 720 0.42361 box_width (620 - 245) / 1280 0.29297 box_height (430 - 180) / 720 0.34722所以TXT里存的这一行就是0 0.33789 0.42361 0.29297 0.34722由于所有值都是0到1之间的比例图片无论怎么缩放、训练时无论怎么改变输入分辨率标注坐标都不会失真。这也是YOLO格式在现代检测框架里更受欢迎的原因之一——它天然和resize操作兼容。2.3 两种格式互转的脚本化处理这套数据集虽然两种格式都给了但实际使用时你经常需要自己转换。比如你拿到一个只有VOC格式的数据集想用YOLO训练就要转换。这里分享一个我常用的转换脚本核心逻辑import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, output_dir, class_names): tree ET.parse(xml_path) root tree.getroot() # 读取图片宽高 size root.find(size) img_width int(size.find(width).text) img_height int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text class_id class_names.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) x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines))写脚本的时候有几点要特别注意。首先class_names列表的顺序就是类别ID的顺序oil_leak排在第0位标签里就写0这个顺序必须和后续训练配置里的names列表保持一致。其次输出的小数建议保留6位精度够用且不会让文件过大。最后也是很多人容易犯的错——(xmin xmax) / 2这种算式在Python 3里要用浮点数除法确认变量已经是float类型否则整数除法会截断坐标就错了。3. 数据集制作全流程从原始帧到可用训练集拿到了现成的数据集可能很多人不会去想它到底怎么来的。但了解制作流程其实非常有用——当你需要扩充数据、或者自己做类似数据集时这套方法论可以直接复用。3.1 数据采集与清洗石油泄漏图片的来源大体有几个渠道炼油厂和化工厂的固定监控摄像头、无人机巡检拍摄的管道和罐区画面、海上平台周边的溢油监测影像、以及少量公开的新闻和论文配图。监控视频一般要按一定帧率抽帧因为相邻两帧高度相似全留下没有任何意义还会占大量存储空间抽帧间隔通常设在1秒到5秒之间具体看镜头下场景变化速度。采集完的原始图片不能直接标注先要经过一轮清洗。我主要过滤三类图第一是严重模糊的画面糊成一团人眼都看不清标注了也是噪音第二是完全遮挡的比如油污区域被设备或者人员挡住了大面积第三是强重复的监控视频抽帧很容易产生几乎一样的帧连续十几张都差不多只保留一两张就够了。除了清洗还有一类数据是负样本也就是完全没有泄漏的正常场景图片。这是很多人会忽略的。模型训练如果清一色都是带油污的正样本推理的时候很容易把正常的地面反光、阴影误检成油污。加入一批没有目标的负样本标注文件为空模型才能学会区分没有泄漏的状态。我个人建议负样本比例控制在10%到20%效果比较理想。3.2 标注规范与质量管控标注是数据集制作中最耗时、也最影响模型上限的环节。石油泄漏的标注有个天然难点——油污边界不清晰。油膜在水面或地面上是渐变的不可能像框一辆车那样有明确的四条边。我在标注时定的规范是只框油污/油膜的核心可见区域边缘那种浅浅的、似有似无的过渡地带不算。这样做的好处是模型学到的是油污最典型的视觉特征而不是把大片背景颜色也学进去。如果框得太大会包含大量背景干扰框得太小又会丢失油污边缘的纹理。实际操作下来框到可见油污的保守范围是准确率最高的策略。质量管理上我采用双人交叉标注加抽检复核。两个标注员分别标同一批图片然后逐张对比差异不一致的区域讨论协商。抽检比例一般不低于10%如果某批次的标注错误率超过2%整批退回重标。这套流程很土但确实有效它能最大程度减少因为标注者主观判断不同带来的标签噪声。3.3 数据增强与划分策略6633张原始图片直接丢给模型训练当然可以但如果配合数据增强能让模型泛化能力上一个台阶。我这里说的增强不只是训练时的在线增强YOLO自带Mosaic、HSV扰动这些还有训练前的离线增强用来扩充数据集的多样性。石油泄漏场景最有效的离线增强方式我体验下来是这么几种水平翻转油污、泄漏没有方向性翻转后语义完全不变亮度对比度随机调整模拟一天中不同光照条件这对户外巡检场景特别重要小幅度的旋转比如正负10度适量的随机裁剪但要小心别把主要油污区域切掉一半。数据划分是另一个容易踩坑的地方。很多做监控视频抽帧的数据集在划分train/val/test时要特别小心。如果同一个场景的连续帧一部分进了训练集、一部分进了验证集那验证集其实是开卷考试因为和训练集里的帧高度相似评价指标会虚高。正确做法是按场景划分把不同时间段、不同地点的镜头分开保证同一场景的帧全部在一个集合里。我用的比例是训练集占70%、验证集占20%、测试集占10%划分前先按场景分组再随机分配。4. 基于数据集的YOLOv8训练实践数据准备好接下来就是用这套数据集实际训练模型。目前YOLOv8是社区用得最多的版本train、val、predict命令非常简洁配合这套数据集的YOLO格式标签开箱即用。4.1 环境准备与数据配置环境这块不复杂因为YOLOv8的安装已经做得很简洁pip install ultralytics它会自动把PyTorch、Torchvision这些核心依赖装好前提是你的机器上有Python 3.8以上的环境并且CUDA版本和PyTorch匹配。显卡能上NVIDIA最好没有驱动和CUDA环境的话先装好显卡驱动再装对应版本的PyTorch。内存建议16GB以上显存最低4GB但8GB以上会更从容。数据配置用data.yaml文件搞定内容如下# data.yaml path: /path/to/oil_leak_dataset train: images/train val: images/val test: images/test nc: 1 names: - oil_leakpath是数据集根目录的绝对路径train、val这些是相对路径YOLO会拼成完整的图片目录地址。这里要特别留意names里的类别顺序。如果我们数据集的TXT标签里写的是0那names列表的第一个元素就必须是oil_leak错位的话模型会把油污识别成别的类别指标一塌糊涂还在那调参。4.2 训练参数选择与命令详解训练命令长这样yolo detect train datadata.yaml modelyolov8s.pt epochs100 imgsz640 batch16 patience20 lr00.01逐项解释一下我的选型逻辑。modelyolov8s.pt用的是YOLOv8的small版本。为什么不选nanonano速度快但精度上限低对石油泄漏这种工业落地场景检测准确率是硬指标。为什么不选large或xlarge受限于单卡显存和训练时间s版本在精度和效率之间最平衡先用s跑通基线后续再根据测试集表现决定要不要上大模型。imgsz640是YOLOv8的默认输入尺寸适合大多数情况。但如果你的泄漏目标在画面里占的面积很小比如无人机高空中拍到的油膜试试调大到768甚至896小目标的检测效果会有肉眼可见的提升代价是训练和推理变慢。batch16取决于显存。我用的是一张16GB显存的卡16很稳定。显存不够时batch降到8够用的话开到32训练收敛速度会快一些。这里有个规律batch越大训练越稳定但太大反而可能影响收敛效果实用主义做法是先用默认值跑一个epoch看显存占用再决定调整方向。patience20是早停参数意思是验证集的mAP如果连续20个epoch都没有提升训练自动停止。这个参数能帮你省时间防止浪费算力在那徒劳地跑几百个epoch。lr00.01是初始学习率YOLOv8的默认值。一般不需要动但如果你观察到loss曲线剧烈震荡可以把学习率降到0.001再看看这是调参时最常用的降噪手段。4.3 结果评估与推理验证训练完成后runs/detect/train/目录下会生成weights/best.pt和weights/last.pt两个权重文件。best.pt是验证集表现最好的权重日常使用就选它。评估用的是这行命令yolo detect val modelruns/detect/train/weights/best.pt datadata.yaml输出里重点看三个指标mAP0.5、mAP0.5:0.95和Precision。mAP0.5是IoU阈值取0.5时的平均精度工业场景最常用mAP0.5:0.95是把IoU阈值从0.5到0.95每隔0.05算一个然后取平均更严格但也更全面。我用这套数据集训练的情况下正常跑完100个epochmAP0.5普遍在0.85以上mAP0.5:0.95在0.6到0.7之间。如果你的结果和这个差距很大优先检查数据划分有没有泄漏再检查标注文件有没有问题。推理验证就用best.ptyolo detect predict modelruns/detect/train/weights/best.pt source/path/to/test.jpg conf0.25conf0.25是置信度阈值低于这个值的检测框会被过滤掉。在实际工业场景里如果漏检比误检更可怕可以把阈值降到0.15或者更低——多出几个假框可以人工再筛但漏掉一个真正的泄漏点可能就是安全事故。5. 实际使用中的常见问题与排查技巧这部分记录我在制作和使用这套数据集过程中实际踩过、以及身边同行经常问到的坑按两个维度整理方便随时查。5.1 压缩包与文件层面的问题7z压缩包解压时最常遇到的是CRC校验错误提示文件头损坏或者数据错误。九成情况是下载不完整导致的因为7z压缩率高压缩包通常有一两个GB网络波动很容易断流或丢包。处理办法先用7-Zip的测试功能验证压缩包完整性测试通过再解压测试不通过就直接重新下载别在损坏的压缩包上浪费时间。解压后先别急着训练花两分钟做一次完整性检查。看一下labels/train下的TXT文件数量和images/train下的JPG数量是否一致如果有缺漏说明某个环节的文件丢失了。检查脚本很简单ls images/train | wc -l ls labels/train | wc -l另外留意一下TXT文件的大小正常情况都应该有几十到几百字节。如果发现大量TXT文件是0字节说明这些图片没有标注目标。这未必是坏事——它们可能是负样本但要提前确认自己是否需要在训练时排除或保留它们。5.2 训练过程中的典型故障训练时最常见的问题是日志里出现大量这样的警告WARNING: Ignoring corrupted image and/or label: xxx.jpg这通常意味着图片和标签文件名对不上。YOLO寻找标签文件的逻辑是找到图片名后去labels目录下找同名TXT。如果TXT文件名和JPG文件名不一致模型就会跳过这张图。排查思路并不复杂写个脚本比对两个目录的文件名把不匹配的找出来重新命名或重标。另一个高频故障是mAP一直在0附近徘徊loss也不下降。这种情况我一般按顺序排查第一看标签TXT里的类别ID是否在data.yaml的nc范围内如果标签里写了5但nc: 1模型肯定学不会第二看names列表的顺序是不是和标签对应我前面强调过这是最常见的人为失误第三检查标注坐标是否有异常值比如width或height等于0或者归一化坐标大于1这些非法标注会让训练过程产生大量NaN梯度。还有一个体验层面的问题——单类别模型容易草木皆兵把一切暗色反光区域都当成油污。这个不完全是数据集的问题更可能是负样本不足。解决思路是增加不带油污的正常场景图片让模型见过足够多没有泄漏的样子它才不会被背景表面反光骗到。我实测下来负样本从5%提升到15%之后误检率大概能下降30%到40%效果非常明显。最后再说几句实在话。这套数据集我前前后后花了将近一个月的时间整理、清洗、标注、复核过程远比想象中琐碎。但整理完这一遍最大的收益反而是对数据格式、标注规范、模型训练之间的关联理解得更透彻了。如果你拿到这6633张图我的建议是先用默认参数完整跑一遍流程拿到一个基线结果再针对自己的业务场景管道巡检、罐区监控还是水域溢油做定向的数据筛选和增量标注。6600多张对单类别检测来说已经够用了但真要部署到生产环境负样本扩充和特殊光照场景的补充几乎是必然要做的事。祝大家都能一次性训练出靠谱的模型少踩几个我踩过的坑。本文还有配套的精品资源点击获取