简介:一套面向红外液体泄漏检测场景的目标检测数据集,适合算法工程师与科研人员用于训练和评估泄漏目标识别模型。数据包含2474张640x640红外图像,采用Pascal VOC与YOLO双格式标注,类别仅leak,共5816个矩形标注框;数据已做增强处理,但未划分训练/验证/测试集,需使用者自行划分。压缩包共2000个文件,以VOC格式xml标注文件为主,另附一个说明txt,整体约185.64MB。已有44人学习下载。该数据集可直接导入YOLO、Faster R-CNN等主流检测框架,也可配合图像增强与标签转换工具验证不同模型在红外场景下的泛化性能,适合作为工业安全巡检、管道泄漏监测、化工园区智能监控等方向的实验基准与算法比较。
1. 红外液体泄漏检测数据集:2474张图能撑起一个什么样的检测任务
做工业视觉的人都知道,液体泄漏检测最难的不是算法,而是数据。可见光相机在管道、阀门、法兰这些场景里受反光、阴影、背景纹理干扰严重,而红外热像仪能直接捕捉泄漏液体与周围环境的温差,让"漏点"在画面上变成一块边界清晰的亮斑或暗斑。这套名为"红外液体泄漏检测数据集2474张VOC+YOLO格式.zip"的数据集,就是把红外场景下的泄漏目标整理成了可以直接喂给 YOLO 系列模型的标准格式。2474张图不算大,但配合 VOC 和 YOLO 双格式的标注,你不需要再写繁琐的格式转换脚本,从数据准备到跑通训练的时间能压缩到几个小时以内。适合正在做工业巡检、管道监测或安全环保类项目的算法工程师和学生,也适合刚入手 YOLO 训练、想找一个"标注干净、场景聚焦"的数据集来练手的人。下面我会把这个数据集的目录结构、格式转换逻辑、训练配置和踩坑点完整拆开讲。
2. 读懂数据集的组成:VOC 与 YOLO 双格式的目录结构、标注字段与文件对应关系
拿到压缩包后,第一件事不是急着训练,而是把目录结构看清。VOC 和 YOLO 两种格式的标注思路完全不同,混着用会出大问题。这一章先把数据集的"骨架"讲清楚。
2.1 目录结构:JPEGImages、Annotations、labels 三件套到底怎么对应
常见的做法是,VOC 格式数据集包含 JPEGImages、Annotations、ImageSets/Main 三个目录,而 YOLO 格式只需要 images 和 labels 两个目录。这个压缩包既然同时提供两种格式,目录基本是脱胎于 VOC 标准再扩展出 YOLO 标注目录。
我一般拿到手后会先跑一个tree命令看看实际结构:
unzip 红外液体泄漏检测数据集2474张VOC+YOLO格式.zip -d leak_dataset cd leak_dataset find . -maxdepth 2 -type d | sort正常应该能看到类似这样的布局:
leak_dataset/ ├── VOC2007/ │ ├── JPEGImages/ # 2474张红外原图 │ ├── Annotations/ # 2474个XML标注文件 │ └── ImageSets/Main/ # train.txt / val.txt 划分文件 ├── images/ # YOLO格式使用的原图(可能与JPEGImages内容相同) ├── labels/ # YOLO格式使用的txt标注 └── classes.txt # 类别列表这套结构的对应关系是:JPEGImages里的每一张图,在Annotations里有一个同名 XML 文件。如果 YOLO 目录是独立复制的,那么images和labels之间也是同名对应,只是后缀从.jpg和.xml变成了.jpg和.txt。
几个容易忽略的点:
- 检查是否有图片没有对应标注文件。用一条命令就能查:
cd VOC2007/JPEGImages for f in *.jpg; do base="${f%.jpg}" [ -f "../Annotations/${base}.xml" ] || echo "缺少标注: $f" done- 检查 YOLO 的 labels 目录里有没有空 txt 文件。空文件代表这张图没有任何目标,训练时 YOLO 会跳过或产生警告,但不能直接删——因为训练集会根据
images目录里的文件名找标注,删了 txt 会导致"标签文件不存在"报错。
2.2 VOC 标注的字段结构:从<object>到<bndbox>,哪些字段训练时会用到
VOC 格式的 XML 标注是理解整个数据集的关键。打开一个文件,典型的字段结构如下:
<annotation> <folder>JPEGImages</folder> <filename>IR_01024.jpg</filename> <size> <width>640</width> <height>512</height> <depth>3</depth> </size> <object> <name>leak</name> <bndbox> <xmin>214</xmin> <ymin>156</ymin> <xmax>389</xmax> <ymax>302</ymax> </bndbox> </object> </annotation>训练时真正用到的字段其实只有三个:filename、name、bndbox。size字段在 VOC 转 YOLO 换算归一化坐标时必须用到,因为 YOLO 的框坐标是以图片宽高为基准的比值。folder、segmented、pose、truncated、difficult这些字段在标准 YOLO 训练流程里不会被读取,但如果后续要接一些检测框架的 VOC 解析器,可能需要保留。
需要特别注意"一张图多个目标"的情况。工业泄漏场景里,同一帧可能出现多处泄漏点,XML 里就会有多个<object>块。你在检查标注质量时,要数一下每个 XML 里<object>的数量分布,如果大多数图片只有 1 个目标,说明这个数据集偏向"单目标定位"任务,模型训练出来对"多泄漏点同时出现"的场景鲁棒性可能不足。
2.3 YOLO 格式的 txt 标注:归一化坐标与类别编号的换算逻辑
YOLO 格式的标注极其简单,每行代表一个目标,格式是:
class_id x_center y_center width height五个值全部是归一化浮点数。这里特别容易栽跟头:x_center,y_center是边界框中心点的相对坐标,width,height是边界框的相对宽高,分母都是图片的原始宽高。
举例,如果 VOC 文件里的bndbox是xmin=214, ymin=156, xmax=389, ymax=302,图片宽度是 640,高度是 512,那么换算逻辑是:
x_center = ((214 + 389) / 2) / 640 = 0.4711 y_center = ((156 + 302) / 2) / 512 = 0.4473 width = (389 - 214) / 640 = 0.2734 height = (302 - 156) / 512 = 0.2852YOLO 系列训练时对标注文件的校验很严格。如果出现x_center大于 1、width为 0 或负数,训练会直接报错。另一个高频问题是有目标跨出图片边界——泄漏液体在红外图里如果流到画面边缘,标注框的xmax可能等于图片宽度,换算后x_center + width/2会大于 1,这种样本训练时会触发 warning,loss 计算也会异常。我建议数据清洗阶段就把这类"边界越框"的样本单独筛出来,要么裁剪,要么删除,不要让模型去学这种错误标注。
3. 把数据集喂进 YOLO 训练:从目录规整到训练参数的完整流程
目录结构搞清楚之后,下一步就是让它真正跑起来。不管你是用 YOLOv5 还是 YOLOv8,数据准备流程都差不多。这一章给出一套完整可复现的操作路径。
3.1 用脚本把 VOC 转成 YOLO 格式:转换脚本、边界框换算与易错点
虽然压缩包已经自带了 YOLO 格式的 labels,但你不一定信任它的完全正确性。血泪经验是:自己动手写一遍转换脚本,既能验证数据质量,也能在出问题时快速定位。
import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_list): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_list: continue class_id = class_list.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) # 归一化并裁切到 [0, 1] 范围内 x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") if lines: img_name = root.find('filename').text txt_name = os.path.splitext(img_name)[0] + '.txt' with open(os.path.join(out_dir, txt_name), 'w') as f: f.write('\n'.join(lines) + '\n') # 使用示例 class_list = ['leak'] os.makedirs('yolo_labels', exist_ok=True) for xml_file in os.listdir('Annotations'): if xml_file.endswith('.xml'): voc_to_yolo(os.path.join('Annotations', xml_file), 'yolo_labels', class_list)这个脚本的逻辑核心是:读取size拿到图片宽高,遍历每个<object>,把 VOC 的xmin/ymin/xmax/ymax转成中心点加宽高的归一化表示。注意保留 6 位小数,不要四舍五入到 4 位,否则小目标框会偏差几个像素。
易错点有三个:第一,img_w和img_h必须使用 XML 里<size>的原始值。如果图片被 resize 过但 XML 没改,生成的标注会整体错位;第二,类别编号必须和data.yaml里names的顺序严格一致,否则训练出来的预测类别是错的;第三,空标注文件不要生成——如果一张图没有任何有效目标,建议直接跳过而不是写出空 txt,避免后续数据加载时的歧义。
3.2 配置 data.yaml:类名、路径与验证集划分
YOLO 系列训练时的数据配置是一个 YAML 文件,里面定义了训练集、验证集路径和类别列表。以 YOLOv8 为例:
# leak_data.yaml path: /data/leak_dataset # 数据集根目录 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 names: 0: leakpath建议写绝对路径,避免相对路径在不同机器上失效。train和val指向的是图片目录,YOLO 会按照同名规则自动找到对应 labels 目录下的 txt 文件——images/train对应labels/train。
验证集划分是另一个关键点。2474张图不多,常见做法是 8:1:1 划分成训练、验证、测试。划分时要保证同一条管道的不同帧尽量落在同一个集合里,否则相邻帧之间背景高度相似,验证集会有"泄漏样本已被模型偷看"的虚高表现。
python -c " import os, random random.seed(42) imgs = os.listdir('images') random.shuffle(imgs) n = len(imgs) train = imgs[:int(n*0.8)] val = imgs[int(n*0.8):int(n*0.9)] test = imgs[int(n*0.9):] for split, imgs_list in [('train', train), ('val', val), ('test', test)]: os.makedirs(f'images/{split}', exist_ok=True) os.makedirs(f'labels/{split}', exist_ok=True) for img in imgs_list: os.rename(f'images/{img}', f'images/{split}/{img}') base = img.rsplit('.', 1)[0] # 注意 labels 目录也要同步移动 os.rename(f'labels/{base}.txt', f'labels/{split}/{base}.txt') "这里用固定随机种子确保每次划分结果一致,方便复现。移动时 labels 和 images 必须同步,一旦错位,训练时 YOLO 会报Label file not found错误。
3.3 训练参数:imgsz、batch、epochs、anchor 相关选择的依据
对于 2474 张红外图训练 YOLO,参数选择直接决定最终效果。以 YOLOv8n 或 YOLOv8s 为例,我的常用起始配置:
yolo train data=leak_data.yaml model=yolov8n.pt epochs=200 imgsz=640 batch=16 lr0=0.01 patience=50imgsz=640:多数红外图像是 640x512 或类似分辨率,保持 640 输入可以避免过度缩放导致的小目标信息丢失。如果泄漏点在原图中只有二三十像素,可以考虑imgsz=768,代价是显存占用和训练时间上升。batch:按显存来定。8GB 显存跑 YOLOv8n 建议 16,跑 YOLOv8s 建议 8。batch 太小会导致 BN 统计不稳定,红外图像又普遍是单通道灰度转三通道的,均值和方差分布和自然图像差别大,更需要足够大的 batch 来稳定训练。epochs=200:200 轮对中小数据集比较合适,配合patience=50做早停。虽然 YOLO 在 COCO 上通常 100 轮就能收敛,但红外场景是域迁移,模型需要更多轮次适应灰度热像图的纹理特征。pretrained:用yolov8n.pt做初始化是标准做法。红外图和自然图像差异大,但底层边缘、纹理特征可以迁移。如果你怀疑预训练权重在灰度图上效果差,也可以试from scratch训练,只是收敛更慢,小数据集上更易过拟合。
训练过程中要盯两个曲线:train/box_loss是否持续下降,以及val/box_loss是否在某个 epoch 后开始反弹。如果 val loss 一路上升而 train loss 还在降,就是过拟合信号,需要早停或加大数据增强。
4. 红外泄漏检测的避坑清单:4 个容易让模型翻车的实际问题
这一章写的是我实际做过类似项目后总结的踩坑记录。这些问题在红外泄漏检测这个任务里几乎必然出现,提前知道能省掉大量排查时间。
4.1 漏标问题:红外图像里"泄漏"的边界为什么难画
现象:训练出来的模型在验证集上 mAP 不错,但拿到现场新图上频繁漏检,回查训练集发现很多图片里泄漏区域只有一半被标注,另一半被漏掉。
原因:红外热像图里泄漏液体和周围环境的温差会形成渐变过渡带,不像自然图像里物体边缘那么清晰。标注员在画框时,通常会框住"高亮核心区",忽略温度过渡区。模型学到的是"完整亮斑"模式,遇到实际拍摄中温度梯度更大的场景就漏检。
解决:在数据清洗阶段,把所有标注框和图像叠加可视化,逐张检查框是否完整包住目标。我用的是 OpenCV 画框叠加:
import cv2 img = cv2.imread('IR_01024.jpg') h, w = img.shape[:2] label_path = 'labels/IR_01024.txt' with open(label_path) as f: for line in f: parts = line.strip().split() cls, xc, yc, bw, bh = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) x1, y1 = int((xc - bw/2) * w), int((yc - bh/2) * h) x2, y2 = int((xc + bw/2) * w), int((yc + bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite('check_overlay.jpg', img)如果发现框与目标边缘偏差大,宁可放宽标注框也不要收紧。检测任务的目标是"找到疑似泄漏区域",框稍大一点对 mAP 影响小,漏标导致模型学不到该样本才是致命的。
4.2 类别不平衡与正负样本失衡:数据增强参数怎么调
现象:2474 张图里真正带泄漏目标的白一千多张,其余是干净背景。训练后模型在背景帧上频繁误报,在泄漏帧上又漏报。
原因:数据集里"有泄漏"和"无泄漏"的帧数量不均衡是真实工业场景的常态。巡检视频里 90% 时间都是正常状态,只有少数帧出现异常。如果直接用原始比例训练,模型会偏向学习"大部分时候没有目标"的先验。
解决:训练时用 YOLO 的增强参数制造更多正样本。我的做法是开大hsv_h、hsv_s、fliplr,同时对泄漏帧做在线复制增强。
yolo train data=leak_data.yaml model=yolov8n.pt epochs=200 imgsz=640 batch=16 \ hsv_h=0.02 hsv_s=0.8 hsv_v=0.6 fliplr=0.7 mosaic=1.0mosaic=1.0会把四张图拼在一起训练,相当于每轮都在生成新组合样本,显著缓解正样本不足。hsv_h对红外灰度图意义不大——灰度图没有色调信息,但hsv_v的明暗扰动对红外图很有用,能模拟不同环境温度下的热像差异。如果数据里正样本占比低于 20%,建议再做一次过采样,让每个 epoch 里泄漏帧重复出现至少 1.5 次。
4.3 灰度分布问题:红外热像是否要做预处理或伪彩色转换
现象:直接把红外单通道灰度图按三通道重复输入训练,模型收敛很慢,loss 在 epoch 50 后还在大幅震荡。
原因:很多 YOLO 预训练权重是在彩色自然图像上训练的,模型第一层卷积核偏向 RGB 三通道的梯度组合特征。灰度图三通道内容相同,相当于输入信息冗余,预训练特征提取器无法发挥应有的作用。这不是模型问题,而是输入表示与预训练分布不匹配。
解决:两个方向。一是做红外图像的对比度拉伸,把 14-bit 原始数据映射到 8-bit 时用直方图均衡化,而不是简单线性缩放。二是在线做伪彩色变换——把单通道灰度映射到三个不同的色彩通道,常用COLORMAP_JET:
import cv2 gray = cv2.imread('IR_01024.jpg', cv2.IMREAD_GRAYSCALE) # 自适应对比度增强 clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) # 伪彩色映射 pseudo = cv2.applyColorMap(enhanced, cv2.COLORMAP_JET) cv2.imwrite('IR_01024_pseudo.jpg', pseudo)这个预处理脚本可以在训练前批量跑一遍,也可以写在一个自定义 Dataset 类里做在线增强。优先推荐离线转换后训练,因为在线转换会增加数据加载开销,而且增强参数的随机性会引入额外变量。用伪彩色图训练后,模型的收敛速度明显改善,因为预训练权重在 RGB 彩色特征上的统计特性被充分利用了。
4.4 过拟合风险:2474 张图对小目标检测来说够不够
现象:训练集 mAP 到了 0.95 以上,验证集 mAP 只有 0.7,且 val loss 从 60 epoch 开始持续上升。
原因:2474 张图的规模属于中小型数据集,红外场景本身背景模式比较单一,如果模型参数量偏大、训练轮数过长,很容易把训练集里的固定背景"背下来",而不是学到泄漏目标的本质特征。
解决:三个手段配合使用。第一,用轻量模型,选择yolov8n而不是yolov8m,参数量差 5 倍以上,在小数据上yolov8n泛化更好。第二,开大 dropout 类正则,YOLOv8 里可以设置dropout=0.1,尽管它对卷积层影响有限,聊胜于无。第三,尽早用早停,patience设小一点,比如 30,不要给它机会在训练集上过拟合。
最重要的判断指标不是训练集 mAP,而是验证集在"不同光照或不同角度"下的表现。红外相机位置固定、背景固定的场景,验证集 mAP 0.8 以上已经可用;如果相机要移动巡检,建议收集更多角度、更远距离的红外帧来扩充数据集,这个方向值得投入。
5. 模型效果的验证与调优:从 mAP 到实际泄漏检测的最后一公里
训练跑完不是终点。检测模型在测试集上 mAP 高,不代表现场能用。这一章说几个直接影响实际部署效果的验证和调优技巧。
5.1 用混淆矩阵看漏报还是误报:两种失败模式要区别对待
YOLOv8 训练完成后,在runs/detect/train目录下会生成confusion_matrix.png。这个图能直接告诉你模型犯错的方向。工业泄漏检测场景里,漏报(假阴性)比误报(假阳性)严重得多——漏报意味着真实泄漏没被发现,后果是安全事故;误报最多是让巡检人员多跑一趟,确认是误报即可。
如果混淆矩阵显示 False Negative 偏高,也就是真实目标没被检出的样本多,那问题主要出在正样本不足或目标过小。这时优先做的是补充数据,或者提高imgsz到 768 / 896,让小目标在特征图上有更多像素。如果 False Positive 偏高,说明模型把背景误判成了泄漏,优先调整置信度阈值和 NMS 参数,也可以检查是否数据集中混入了温度异常但非泄漏的干扰物(比如人手、发热设备、阳光直射区域)。
5.2 置信度阈值与 NMS 参数调整:一个被低估的调参环节
训练完成后,推理阶段的两个参数直接决定输出效果。第一个是conf_thres,置信度阈值。YOLO 默认是 0.25,但在泄漏检测场景,我一般建议调低到 0.1 到 0.15,宁可多出几个误检框,也不要漏掉真实泄漏点。第二个是iou_thres,NMS 的 IoU 阈值,默认 0.45,在红外泄漏场景通常调到 0.5 到 0.6 更合适——因为同一个泄漏点的热像梯度会导致模型输出多个相互重叠的候选框,IoU 阈值太紧会让 NMS 把应该合并的框拆开。
测试命令:
yolo predict model=runs/detect/train/weights/best.pt source=test_images/ \ conf=0.1 iou=0.55 imgsz=640 save=True调参时要记录不同参数组合下的测试结果,不要只凭肉眼感觉。我一般用脚本批量跑几组阈值,把 Precision 和 Recall 算出来,选 Recall 最高的那一组作为部署底线。
5.3 部署验证:拿视频连续帧测试,单张图片不靠谱
训练时用单张图片测试觉得很不错,但一到视频连续帧就问题频出,这种现象常见的原因有两个:一是单帧测试挑了光照和角度最好的图,二是视频帧之间存在运动模糊和温度扩散,泄漏区域是动态变化的。
我的习惯是,用一段 5 到 10 秒的红外视频做部署前测试,统计连续 300 帧的检测结果。重点看两个指标:目标在帧间是否连续(漏检是否造成目标"闪烁"消失),以及检测框位置是否抖动(是否严重影响后续报警判断)。如果抖动严重,可以对多帧检测结果做平滑处理,比如用指数移动平均更新框坐标,或者用 ByteTrack 之类的跟踪器把目标 ID 关联起来。检测模型只负责"看见目标",跟踪器负责"目标不丢",这两者配合才能达到实际可用的程度。
这套方案里最值得投入的方向,是把模型从固定场景推进到多角度、多距离的通用检测。红外泄漏检测的核心难点不是算法本身,而是数据对场景分布的覆盖程度。拿到这套数据集后,能用它跑通流程、建立评估基准,后续再根据你自己的现场数据持续积累和校准,模型的可靠性就会逐步起来。希望这些从目录解析到部署验证的实操细节对你有所帮助。
本文还有配套的精品资源,点击获取