资讯动态

红外液体泄漏检测数据集:VOC与YOLO格式转换及训练避坑指南

发布时间:2026/10/27 23:14:23 来源:尧图企业网站定制
简介面向红外液体泄漏检测场景的深度学习目标检测数据集覆盖2474张640×640分辨率的红外图像目标类别为单一类“leak”共包含5816个矩形标注框可用于YOLO、Faster R-CNN等常见检测模型的训练与评估。数据由labelImg工具人工画框标注已做数据增强同时提供Pascal VOC格式的xml标注文件和YOLO格式的txt标注文件省去格式转换与二次标注的额外成本统一图像尺寸可直接适配常用目标检测框架适合开展目标检测算法对比、数据增强策略分析、红外场景识别模型验证等实验。压缩包内共2000个文件主体为1999个XML标注文件另附1份TXT说明文件整体大小约185.64MB。整体内容组织清晰便于快速按批次读取标注信息。需要留意的是数据未划分训练、验证与测试集需使用者自行拆分作者也明确声明不对训练所得模型或权重文件精度作任何保证。目前已有49人学习下载适合具备一定目标检测基础、需要扩充红外场景泄漏样本的研究者或开发者。1. 红外液体泄漏检测数据集2474 张双格式标注拿到手先别急着训练化工厂管道巡检、液压站漏油监测、实验室台面液体残留检测这类场景里红外相机比可见光相机可靠得多——夜间无光、蒸汽弥漫、油污与水渍在可见光下难以区分时红外图像能把温度差异变成清晰的明暗对比。但红外检测项目真正卡人的不是相机选型而是标注数据。普通目标检测数据集大多是可见光下的自然图像泄漏场景的公开数据集极少工业现场又涉及保密协议不方便外传所以一份整理好的红外泄漏检测数据集就显得很关键。这份资源包含 2474 张红外泄漏检测图像同时提供 VOC 和 YOLO 两种标注格式覆盖液体泄漏、飞溅、滴落、残留积液等典型状态。适合做工业安全巡检、智慧工厂视觉方案、红外目标检测模型训练的从业者直接用于模型训练和验证。拿到手之后建议先做标签分布统计和格式校验再进训练流程这两步能帮你避开后面大半的坑。2. 先看清 VOC 和 YOLO 两份标注坐标体系差异与转换逻辑2.1 VOC 的 XML 里存的是绝对坐标YOLO 的 txt 里存的是归一化中心点这两套格式是目标检测数据集最常见的标注形式但它们的坐标存储逻辑完全不同。VOC 格式Pascal VOC用 XML 文件描述每一个目标标注信息量很大不仅有 bounding box 坐标还包含图片文件名、尺寸、通道数、目标类别、是否截断、是否难分样本等信息。而 YOLO 格式把每个目标拆成一行文本只保留类别 id、中心点坐标和宽高而且全部归一化到 0 到 1 之间。拿一张 640x480 的红外泄漏图像举例如果标注了一个泄漏点边界框左上角在 (200, 150)右下角在 (400, 350)那么在 VOC 的 XML 里存的就是这么一段annotation filenameleak_001.jpg/filename size width640/width height480/height depth3/depth /size object nameliquid_leak/name bndbox xmin200/xmin ymin150/ymin xmax400/xmax ymax350/ymax /bndbox /object /annotation同样的目标转到 YOLO 格式是一行文本0 0.46875 0.520833 0.3125 0.416667。拆开看0 是类别 id0.46875 是中心点 x 坐标(200400)/2/6400.520833 是中心点 y 坐标(150350)/2/4800.3125 是归一化宽度(400-200)/6400.416667 是归一化高度(350-150)/480。这里有个容易混淆的地方YOLO 格式存的是归一化后的中心点坐标和宽高不是左上角右下角。训练时如果直接拿 VOC 的 xmin、ymin、xmax、ymax 塞给 YOLO 使用模型训练出来的框全是偏的。这类错误在损失函数上表现不太明显loss 照样下降但验证集的 mAP 就是上不去——典型的训练玄学问题根源其实是数据预处理。2.2 为什么这份数据集要同时给两份标注做训练的人会有一个感受不同框架对数据集格式的要求不一致。Ultralytics YOLO 直接读 txt 标注文件而一些做检测的老流程、基于 Detectron2 或者 MMDetection 的项目默认读 VOC 或 COCO 格式。如果数据集只给一种格式换框架的时候就要自己写转换脚本转换过程又要处理路径、类别表、图片尺寸读取这些细节稍不注意就把数据搞坏了。这份数据集直接提供 VOC 和 YOLO 两份标注省掉了中间转换这一步。但我的习惯是即使拿到双格式也不要直接盲目信任其中某一份而是抽几张图做交叉验证——打开一份 XML 和对应 txt手动比对一个目标的坐标换算结果是否一致。两份标注都验过了再进训练心里才有底。实际使用时还要注意一个问题VOC 格式里类别名是字符串YOLO 格式里类别名变成了数字 id。直接用 YOLO 训练的时候需要确认类别 id 和类别名的映射关系也就是那份classes.txt。这份数据集的类别列表在解压后的classes.txt里可以看到训练前务必核对一遍别出现本地类别表和数据集类别表顺序不一致的情况——一旦错位所有标注都等于标错了类。VOC 与 YOLO 标注格式的核心差异可以用一个表格概括维度VOC 格式YOLO 格式文件类型XMLtxt坐标类型绝对坐标像素值归一化坐标0~1框的表达方式左上角点 右下角点中心点 宽高每张图标注文件数1 个 XML1 个 txt每行一个目标附带信息文件名、尺寸、目标状态等只有类别 id 和框坐标常见适用框架Detectron2、MMDetection 旧版Ultralytics YOLO 系列3. 把 VOC 转成 YOLO转换脚本、归一化计算与目录梳理3.1 自己动手写一个转换器理解转换原理虽然这份数据集已经带了 YOLO 格式我仍然建议你手动走一遍转换流程。原因很简单真实项目里你拿到的标注可能是老工程师留下的 VOC 格式也可能是标注平台导出的自定义格式转换这套技能绕不开。这里给出一个经过验证的脚本核心逻辑是用 Python 的xml.etree.ElementTree解析 XML然后做归一化计算并写出 txt 文件。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, class_names, output_txt_path): 将单个 VOC XML 标注转成 YOLO txt 标注 :param xml_file: VOC XML 标注文件路径 :param class_names: 类别名称列表索引即类别id :param output_txt_path: 输出的 YOLO txt 文件路径 tree ET.parse(xml_file) root tree.getroot() # 读取图片宽和高归一化时需要使用 size root.find(size) img_width int(size.find(width).text) img_height int(size.find(height).text) yolo_lines [] for obj in root.findall(object): # 只处理正常标注目标跳过难分样本和截断样本 difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: continue name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 计算中心点坐标和宽高然后归一化 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 # 防御性校验归一化结果必须在0~1之间 if not (0 x_center 1 and 0 y_center 1 and 0 width 1 and 0 height 1): print(f警告: {xml_file} 中存在越界坐标: {name} {xmin},{ymin},{xmax},{ymax}) continue yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(output_txt_path, w) as f: f.write(\n.join(yolo_lines))这段代码里值得注意的细节有几个。difficult字段的处理方式——VOC 数据集中常把难以辨认的目标标记为难分样本转换到 YOLO 格式时一般建议直接跳过因为 YOLO 训练中这些样本会导致 loss 震荡。归一化时用的是(xmin xmax) / 2.0 / img_width而不是(xmin xmax) / img_width / 2.0两者数学上等价但前者更容易让人理解计算顺序。防御性校验加上之后可以提前发现 XML 里坐标越界这类脏数据。3.2 批量转换与目录结构梳理单个文件的转换逻辑确认无误后下一个步骤是批量处理和目录结构梳理。YOLO 训练时默认期望的目标目录结构是images/train、images/val存放图片labels/train、labels/val存放对应的 txt 标注文件。图片和标注文件名必须一一对应后缀可以不同但主名必须一致。下面这个脚本做了三件事遍历所有 XML 文件执行转换、统计类别分布、同时输出一份类别列表。import os import glob from collections import Counter def batch_convert_voc_to_yolo(voc_dir, output_dir, class_names): 批量转换VOC标注为YOLO格式并整理目录结构 :param voc_dir: 存放VOC XML文件的目录 :param output_dir: 输出目录会生成images和labels子目录 :param class_names: 类别列表 os.makedirs(os.path.join(output_dir, labels), exist_okTrue) xml_files glob.glob(os.path.join(voc_dir, *.xml)) class_counter Counter() for xml_file in xml_files: # 图片和标注在同一目录文件名主干相同 base_name os.path.splitext(os.path.basename(xml_file))[0] txt_path os.path.join(output_dir, labels, base_name .txt) voc_to_yolo(xml_file, class_names, txt_path) # 统计类别分布 tree ET.parse(xml_file) root tree.getroot() for obj in root.findall(object): class_counter[obj.find(name).text] 1 print(类别分布统计:) for class_name, count in class_counter.items(): print(f {class_name}: {count} 个目标) # 写类别列表文件 with open(os.path.join(output_dir, classes.txt), w) as f: f.write(\n.join(class_names)) print(f转换完成共处理 {len(xml_files)} 个XML文件)这个脚本里我加了类别分布统计功能。做数据集的时候最容易忽略的一步是统计类别平衡性——如果泄漏滴落样本有 2000 个、而喷射样本只有 100 个模型训练完之后小类别的召回率会非常难看。这个信息在训练前知道比训练完看混淆矩阵才知道有用得多。3.3 拿这份数据集时建议保留的目录组织方式解压这份数据集的压缩包后我的习惯是直接按下面的结构重新组织一份干净的工作目录. ├── images/ │ ├── train/ # 约 1980 张训练图 │ └── val/ # 约 494 张验证图 ├── labels/ │ ├── train/ # 对应训练图标注每张图一个txt │ └── val/ # 对应验证图标注 └── classes.txt # 类别列表文件数据集原始的划分比例是 8:2这个比例对万级以下的数据集来说是合理默认值。如果你要自己重新划分有一条经验泄漏检测场景中的连续帧图像往往存在时间关联同一次泄漏过程的连续帧容易被同时分到训练集和验证集导致验证指标虚高。后面避坑章节会细说这个问题。4. 训练前先过三关标签可视化、类别分布检查与 YOLOv8 配置4.1 标签可视化把边界框画回原图一眼看出标注是否错位拿到数据集直接开训练的后果往往是在损失值下降一段时间后才发现问题——某个类别识别不出来或者验证集 mAP 始终在低位徘徊。血泪经验告诉我训练前花 20 分钟做一次标签可视化能省下 3 个小时的排错时间。import cv2 import numpy as np def visualize_yolo_labels(image_path, label_path, class_names, save_pathNone): 将YOLO格式标注画回原图上检查标注是否错位 :param image_path: 图像文件路径 :param label_path: 对应txt标注文件路径 :param class_names: 类别名称列表 :param save_path: 可视化结果保存路径 img cv2.imread(image_path) height, width img.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() class_id int(parts[0]) x_center float(parts[1]) * width y_center float(parts[2]) * height box_width float(parts[3]) * width box_height float(parts[4]) * height x1 int(x_center - box_width / 2) y1 int(y_center - box_height / 2) x2 int(x_center box_width / 2) y2 int(y_center box_height / 2) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[class_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) if save_path: cv2.imwrite(save_path, img) else: cv2.imshow(label check, img) cv2.waitKey(0)我建议抽样 30 张图涵盖不同背景温度、不同泄漏部位、不同泄漏状态的图像逐一可视化。看的时候重点检查两类问题一是框是否包住了完整的目标红外图像中泄漏液体的边缘往往存在温度过渡带标注如果只框住了高温核心区训练出的模型预测框就会偏小二是框的中心点是否落在目标本体上。4.2 训练集与验证集的划分策略按图像来源分组不按单帧随机分在泄漏检测这类时序密集型场景里数据划分是一个容易被忽略但影响非常大的因素。同一个泄漏过程拍摄的连续帧画面内容高度相似。如果随机划分相近的帧被分到训练集和验证集验证结果会过度乐观——模型在训练中已经见过几乎一样的画面验证时只是换了个角度或者换了个时间点mAP 参考价值很低。合理的做法是按图像序列或者采集时间分组。这份数据集解压后如果能看到文件名中有时间戳或者场景编号的规律建议按场景分组再划分训练集和验证集。我处理类似数据集时的习惯是把同一拍摄场景、同一段连续拍摄的图像归到一个组里按组划分确保验证集覆盖训练集未见过的场景。划分时可以手动按比例抽取用下面的脚本做分组划分的辅助import os import shutil import random from collections import defaultdict def group_split_by_scene(image_dir, label_dir, output_dir, test_ratio0.2): 按文件名的分组前缀划分训练集和验证集 :param image_dir: 图片目录 :param label_dir: 标注目录 :param output_dir: 输出目录含 images/train, images/val, labels/train, labels/val :param test_ratio: 验证集比例 # 假设文件名格式为 scene001_frame001.jpg按前后缀提取场景ID scene_groups defaultdict(list) for img_name in os.listdir(image_dir): scene_id img_name.split(_)[0] # 根据实际命名规则调整 scene_groups[scene_id].append(img_name) all_scenes list(scene_groups.keys()) random.shuffle(all_scenes) val_scene_count max(1, int(len(all_scenes) * test_ratio)) val_scenes set(all_scenes[:val_scene_count]) for split in [train, val]: os.makedirs(os.path.join(output_dir, images, split), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, split), exist_okTrue) for scene_id, img_names in scene_groups.items(): target_split val if scene_id in val_scenes else train for img_name in img_names: label_name os.path.splitext(img_name)[0] .txt shutil.copy(os.path.join(image_dir, img_name), os.path.join(output_dir, images, target_split, img_name)) shutil.copy(os.path.join(label_dir, label_name), os.path.join(output_dir, labels, target_split, label_name))对于这份数据集我建议直接按 8:2 比例保持默认划分但要检查原始划分是否按场景隔离——如果发现同一场景的连续帧同时出现在 train 和 val 里宁可自己重新分一次也不要省这个时间。4.3 YOLOv8 训练配置文件与启动命令数据校验完成之后配置文件就简单了。用 Ultralytics YOLOv8 训练时yaml 配置文件长这样# dataset.yaml path: /path/to/leak_dataset # 数据集根目录 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 # 类别定义顺序必须与classes.txt一致 names: 0: drip # 滴落泄漏 1: spray # 喷射泄漏 2: pool # 积液残留 3: stream # 流动泄漏启动训练的命令yolo detect train \ modelyolov8s.pt \ datadataset.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ patience20这里有几个参数值得解释一下。modelyolov8s.pt是在 COCO 预训练权重基础上做迁移学习红外图像虽然和自然图像差异很大但预训练模型已经学会了边缘、纹理、形状等低级特征迁移到红外场景仍然有效。imgsz640是输入分辨率红外泄漏目标通常较小如果你的场景中目标像素占比很小可以考虑在 640 基础上增加到 768 或 1024但要注意显存占用翻倍。patience20意思是连续 20 个 epoch 验证集指标没有提升就提前停止训练这个参数能帮你省时间。5. 避坑指南红外泄漏检测数据集使用中的六个常见问题5.1 现象训练 loss 持续下降但验证集 mAP0.5 不到 0.3这个问题我排查了整整一个下午最终发现不是网络结构问题而是数据划分问题。训练集和验证集之间存在相似帧泄漏模型在验证集上表现虚高才合理表现虚低反而可疑——原因是验证集中出现了训练集中完全没有的场景类型模型泛化能力不足。解决方法是检查数据划分是否按场景隔离。如果原始划分是随机划分的重新按场景分组——把同一泄漏过程、同一机位拍摄的图像全部归入同一集合。5.2 现象红外图像中泄漏区域的边缘有亮圈标注框偏大导致误检红外图像的泄漏区域特征和可见光图像差异很大泄漏液体在热成像中通常表现为低温暗色区域周围可能存在高温边缘过渡带。如果标注时把过渡带整个框进去模型学到的特征就是暗区亮边在真实场景中遇到其他类型的亮边就会误检。解决方法是统一标注规范只框泄漏液体本体不包含温度过渡带。如果你的场景中泄漏液体与背景温差不大建议先做对比度增强预处理再标注。5.3 现象训练时报错 assertion failed: height 0这类报错通常出现在标注文件中有越界坐标的情况比如框的 xmin 大于 xmax、ymin 大于 ymax或者宽高计算出负数。也可能是图片读取失败后得到空数组但标注文件仍然引用了它。解决方法是跑一遍数据校验脚本检查每个 txt 文件中的坐标是否满足0 x_center 1、0 width 1的条件同时检查图片文件是否能正常用 cv2 读取。5.4 现象验证集某个类别的 precision 很高但 recall 极低这是典型的类别不平衡问题。泄漏数据集中滴落类样本可能占比极高而喷射类样本占比极低模型倾向于把所有目标都预测为高频类别——precision 高是因为预测框中确实大部分是滴落recall 低是因为喷射类目标大量漏检。解决方法是先看类别分布统计如果某类样本数过少考虑做数据增强尤其是这类样本的翻转变换或者采用 YOLOv8 的class_weights参数调整损失权重。5.5 现象训练过程正常推理时对小目标完全无响应红外泄漏检测中滴落的液体往往只占几十个像素。推理时如果输入分辨率不够小目标特征在多次下采样后被完全抹掉模型自然检测不到。解决方法是推理时把imgsz调整到与训练一致甚至更高并使用yolo detect predict的conf0.25参数调低置信度阈值——红外场景干扰少置信度阈值可以比可见光场景设得更低。5.6 现象换了一个场景的测试图之后漏检明显增多红外数据集有一个天然问题了不同材质的背景辐射率差异巨大管道、阀门、水泥地面在热成像里的表现完全不同。训练集如果只覆盖了某一种背景模型在换背景后性能骤降。解决方法是收集数据时尽可能覆盖多种表面材质和入射角度。这份数据集虽然已经涵盖了多种泄漏状态但实际项目中对新增场景做少量补充标注仍然值得坚持。数据覆盖的边界就是模型性能的边界。6. 训练后的验证流程从 PR 曲线到实际场景回灌测试训练完成后不要只看终端打印的 mAP 数字就收工。mAP 是一个聚合指标掩盖了很多细节信息。我建议按下面的流程做一轮完整验证。第一步是查看每个类别的 PR 曲线和混淆矩阵。YOLOv8 训练完成后会在runs/detect/train/目录下生成PR_curve.png、confusion_matrix.png等文件。看 PR 曲线时重点关注曲线是否在右上角明显弯曲——曲线越贴近左上角说明精确率和召回率平衡得越好。如果某个类别的曲线明显低于其他类别这个类别的样本量或者标注质量一定有问题。第二步是用一段独立的视频或者连续帧做回灌测试。选一段训练集和验证集都没出现过的泄漏过程视频逐帧推理然后按帧检查漏检和误检。这一步之所以关键是因为它是检验模型泛化能力的最直接手段。回灌测试时如果出现连续数帧漏检注意检查推理设置里的imgsz是否与训练一致——一个常见的翻车场景是训练用了 640推理时为了省时间改成 320小目标直接消失。第三步是调试置信度阈值。YOLOv8 默认推理置信度阈值是 0.25但红外泄漏检测场景干扰少、目标特征明确实际部署时 0.1 到 0.15 也能获得可接受的误检率。具体调多少取决于你的业务对误检和漏检的容忍度——泄漏检测场景通常对漏检零容忍对误检可以通过后续逻辑过滤。把模型导出为 ONNX 或 TensorRT 格式做推理加速也是一个值得考虑的步骤yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640ONNX 格式部署到 CPU 或者 Jetson 平台时推理速度比 PyTorch 原生快 2 到 3 倍。导出后建议顺手做一次输出一致性验证——用同一张图在 PyTorch 模型和 ONNX 模型上分别推理对比检测框差异是否在合理范围内。回到这份数据集本身我最后想提一个习惯每次新拿到一份数据集不管是什么格式、不管是谁整理的我都会强制走一遍标签可视化、类别统计、数据划分检查这三个步骤再开始训练。这个习惯让我避开了很多看似玄学、实则是数据问题的训练事故。红外泄漏检测这类场景模型架构大家都能跑通真正的差异在数据准备是否扎实。希望这份数据集和这些方法帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑