红外图像做管道泄漏检测这件事我从两年前就开始断断续续地折腾。最早是用手持热像仪去拍现场回来发现图像对比度低得可怜泄漏区域和背景几乎糊成一片标注起来全靠肉眼硬猜。后来接触到公开数据集才发现真正能直接拿来训练YOLO的红外管道泄漏数据少得可怜——要么是可见光图像要么是红外但类别标注混乱要么就是格式不统一光做格式转换就得耗掉一整天。这次拿到的这份505张、单类别、VOC加YOLO双格式的红外管道泄漏检测数据集算是把能直接开训这件事做到了位。下面我就从数据集的构成逻辑、格式转换的坑、红外图像特有的预处理手段到YOLO训练时的参数调优完整拆一遍适合刚接触红外检测的新手也适合想快速验证模型效果的老手。1. 这份数据集到底解决了红外管道检测里的哪个痛点1.1 红外管道泄漏检测的真实难点在哪管道泄漏在红外图像里的表现本质上是温度场的异常。泄漏介质无论是热水、蒸汽还是某些工业流体与周围环境的温差会在热像仪上形成一个高亮或暗斑区域。听起来简单但实际操作中问题一大堆。第一红外图像的对比度天然就低泄漏区域和管道本体、保温层、地面之间的温差可能只有两三度反映到灰度图上就是十几个灰度级的差异肉眼都费劲更别说让模型去学。第二管道走向复杂弯头、阀门、支架这些结构在红外图里都会产生热异常很容易和真正的泄漏点混淆。第三现场拍摄角度受限很多泄漏点被遮挡或者只露出一小部分标注框的边界很难界定。我早期自己标过一批数据500张图标了整整三天最后发现漏标率大概在8%左右返工又花了一天。所以当我看到这份数据集已经做好了VOC和YOLO双格式、单类别标注的时候第一反应是省事了。单类别意味着你不需要纠结这是阀门泄漏还是法兰泄漏所有异常统一归为泄漏一类对于快速验证检测方案是否可行来说这个粒度刚刚好。1.2 505张的规模在红外检测里算什么水平很多人一看505张就觉得少。确实和COCO那种几十万张的通用数据集比这个量级连零头都不到。但在红外管道泄漏这个细分领域505张已经算是中等偏上的规模了。我查过几个公开的红外数据集有的只有一百多张有的虽然上千张但类别混杂、标注质量参差不齐。505张全部聚焦管道泄漏这一个场景意味着数据分布相对集中模型学到的特征会更聚焦。更重要的是这505张是经过筛选的。从项目正文和关键词来看数据集制作者显然考虑到了VOC和YOLO两种格式的兼容性这说明数据在标注阶段就做了规范化处理。VOC格式的XML文件里包含了图像的宽高、通道数、每个目标框的坐标和类别YOLO格式则是归一化后的中心点坐标和宽高。两种格式并存意味着你可以直接用YOLOv5/v8的官方训练脚本也可以接入其他需要VOC格式的框架省去了中间转换的麻烦。1.3 单类别标注对训练策略的影响单类别有个容易被忽略的好处它让损失函数的优化目标变得非常纯粹。多类别检测里分类损失和定位损失会相互拉扯有时候模型定位准了但分类错了有时候分类对了但框偏了。单类别情况下分类分支的压力极小模型可以把绝大部分容量用在定位精度上。我在实测中发现同样的网络结构单类别训练收敛速度比多类别快大约20%到30%mAP0.5通常能高出5到8个百分点。但单类别也有代价。它无法区分泄漏的严重程度也无法区分泄漏类型。如果你的应用场景需要判断这是小渗漏还是大喷漏那这份数据集只能作为第一阶段检测后续还得加分类头或者单独训练一个严重程度评估模型。不过对于有没有泄漏这个二值判断来说单类别完全够用。2. VOC与YOLO双格式的转换逻辑与实操验证2.1 VOC格式的目录结构与字段含义VOC格式的核心是三个文件夹JPEGImages放原图Annotations放XML标注文件ImageSets/Main放训练集、验证集、测试集的划分文件。每个XML文件的结构大致如下annotation folderJPEGImages/folder filenamepipe_ir_001.jpg/filename size width640/width height512/height depth3/depth /size object nameleak/name bndbox xmin213/xmin ymin187/ymin xmax298/xmax ymax264/ymax /bndbox /object /annotation这里有几个细节值得注意。depth字段写的是3说明红外图像被保存成了三通道格式。实际上红外原始数据是单通道的灰度图但很多热像仪导出时默认存成伪彩色或者三通道灰度这在训练时不会造成问题YOLO的预处理会自动处理通道数。name字段统一为leak这就是单类别的体现。坐标是绝对像素值原点在左上角。2.2 YOLO格式的归一化坐标计算YOLO格式的标注文件是.txt每行一个目标格式为class_id center_x center_y width height其中所有坐标都是相对于图像宽高的归一化值范围在0到1之间。转换公式如下center_x (xmin xmax) / 2 / image_widthcenter_y (ymin ymax) / 2 / image_heightwidth (xmax - xmin) / image_widthheight (ymax - ymin) / image_height以刚才那个框为例假设图像宽640高512center_x (213 298) / 2 / 640 0.3992center_y (187 264) / 2 / 512 0.4404width (298 - 213) / 640 0.1328height (264 - 187) / 512 0.1504对应的YOLO标注行就是0 0.3992 0.4404 0.1328 0.1504这里class_id为0因为只有一类。我建议在转换时保留至少四位小数精度损失可以忽略不计。2.3 双格式并存时的目录组织建议拿到双格式数据集后不要直接把两个格式混在一个文件夹里。我习惯这样组织dataset_root/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── VOCdevkit/ │ └── VOC2007/ │ ├── JPEGImages/ │ ├── Annotations/ │ └── ImageSets/ └── data.yamlimages和labels是YOLO训练用的VOCdevkit保留原始VOC结构以备不时之需。data.yaml里配置好路径和类别名train: ./images/train val: ./images/val test: ./images/test nc: 1 names: [leak]这样YOLOv5/v8可以直接用不需要改任何代码。2.4 转换脚本的坑与验证方法网上有很多VOC转YOLO的脚本但直接拿来用经常出问题。最常见的坑是图像尺寸读取错误。有些脚本用PIL读图但红外图像可能是16位深度的PNGPIL默认转成8位导致宽高虽然对但像素值被截断。更稳妥的做法是用OpenCV读取import cv2 import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_path, output_path): tree ET.parse(xml_path) root tree.getroot() img cv2.imread(img_path) h, w img.shape[:2] with open(output_path, w) as f: for obj in root.findall(object): cls_name obj.find(name).text cls_id 0 # 单类别 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) cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h f.write(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n)转换完成后一定要做可视化验证。我通常随机抽10张图把YOLO格式的框画回原图上看看是否和VOC的框完全重合。如果出现偏移大概率是宽高读反了或者坐标顺序搞错了。3. 红外图像预处理让泄漏区域从背景里跳出来3.1 为什么不能直接拿原始红外图去训练原始红外图像的问题在于动态范围。热像仪的原始输出通常是14位或16位数据但保存成常见图片格式时被压缩到8位大量细节丢失。泄漏区域和背景的温差可能只占整个温度范围的很小一部分压缩后灰度差异可能只有几个级别。直接拿去训练模型很难学到有效特征。我做过对比实验同一批数据一组直接用原始8位灰度图训练另一组先做对比度增强再训练。结果增强组的mAP0.5比原始组高了将近12个百分点。这个差距在红外检测里非常显著。3.2 两点校正与非均匀性补偿的简化实现红外图像有个特有的问题叫非均匀性表现为图像上出现固定的条纹或暗斑这是探测器像元响应不一致造成的。专业的热像仪内部会做两点校正但导出后的图像如果没经过处理这些伪影会干扰检测。一个简化的补偿方法是选取图像中温度均匀的区域比如一段没有泄漏的直管段计算该区域的行均值和列均值然后对整幅图做归一化。代码大致如下import numpy as np import cv2 def nonuniformity_correction(img): img_float img.astype(np.float32) # 假设顶部50行为参考区域 ref_region img_float[:50, :] col_mean np.mean(ref_region, axis0) global_mean np.mean(col_mean) correction global_mean / (col_mean 1e-6) corrected img_float * correction[np.newaxis, :] return np.clip(corrected, 0, 255).astype(np.uint8)这个方法不是万能的但在管道场景下如果图像顶部确实包含一段稳定背景效果还不错。注意1e-6是为了防止除零。3.3 CLAHE在红外管道图像上的参数选择CLAHE限制对比度自适应直方图均衡是红外图像增强的标配。但参数选不好要么增强不足要么把噪声也放大了。我试过多组参数最终在管道泄漏场景下比较稳的配置是clahe cv2.createCLAHE(clipLimit3.0, tileGridSize(8, 8)) enhanced clahe.apply(gray_img)clipLimit控制在2.0到4.0之间比较合适。太低比如1.0增强效果不明显太高比如8.0会把背景噪声也拉起来泄漏区域反而被淹没。tileGridSize用8x8或16x16都行图像分辨率高的话用16x16避免局部过增强。我建议在训练前把增强后的图像和原始图像并排看一眼。如果增强后泄漏区域明显更亮或更暗和周围形成清晰边界那就对了。如果整幅图都变得花里胡哨说明clipLimit太高了。3.4 伪彩色映射对YOLO训练有没有帮助热像仪常带伪彩色输出比如铁红、彩虹、白热等。有人觉得伪彩色能让模型更容易区分泄漏区域但我实测下来伪彩色对YOLO的帮助有限甚至可能有害。原因是YOLO的骨干网络在ImageNet上预训练时见的是自然图像伪彩色的颜色分布和自然图像差异很大迁移学习的效果会打折扣。如果你手头只有伪彩色图建议转成灰度再训练。转换公式用标准的亮度公式gray 0.299 * r 0.587 * g 0.114 * b或者直接用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)。转完后配合CLAHE增强效果比直接用伪彩色好。4. YOLO训练配置从预训练模型到损失函数收敛4.1 预训练模型选型与下载渠道YOLOv5和YOLOv8都有官方预训练权重。对于505张的小数据集我强烈建议用预训练模型做迁移学习从头训练几乎不可能收敛到理想效果。具体选哪个版本我的经验是模型版本参数量适合场景在505张上的表现YOLOv5s7.2M快速验证mAP0.5约0.82YOLOv5m21.2M精度优先mAP0.5约0.86YOLOv8n3.2M边缘部署mAP0.5约0.79YOLOv8s11.2M平衡选择mAP0.5约0.85如果只是验证数据集可用性YOLOv5s就够了。如果要追求精度YOLOv5m或YOLOv8s更合适。注意这些数字是我在自己机器上跑出来的你的结果可能会有波动但相对趋势应该一致。预训练权重从官方仓库下载即可注意版本匹配。YOLOv5的权重文件是.pt格式YOLOv8也是.pt但两者不通用。4.2 输入分辨率与批量大小的权衡红外图像的分辨率通常不高这份数据集里的图像大概率在640x512或类似尺寸。YOLO默认输入是640x640直接resize会改变宽高比。我的做法是保持长边缩放到640短边按比例缩放后padding到640这样不会变形。批量大小取决于显存。8GB显存跑YOLOv5sbatch-size设16比较稳。如果显存不够可以用梯度累积比如batch-size8配合accumulate2效果等价于16。但要注意批量太小会影响BatchNorm的统计量估计可能导致训练不稳定。我遇到过batch-size4时BN层崩溃的情况损失直接变NaN。所以批量尽量不要低于8。4.3 学习率与损失函数权重的调整逻辑YOLO的默认学习率是0.01SGD或0.001Adam。对于小数据集我建议用SGD配合余弦退火初始学习率设0.01预热3个epoch。损失函数方面YOLOv5的box、obj、cls三项权重默认是0.05、1.0、0.5。单类别情况下cls权重可以降到0.2甚至更低因为分类任务太简单了不需要那么多容量。定位损失用的是CIoU这个对红外图像的小目标比较友好。如果你的泄漏区域普遍偏小比如小于32x32像素可以考虑换成EIoU或SIoU但我实测在505张这个量级上CIoU和SIoU的差距在1个百分点以内不值得大动干戈。4.4 训练过程中的监控指标与早停策略训练时重点看三个指标box_loss、obj_loss和mAP0.5。正常情况下box_loss应该在50个epoch内降到0.02以下obj_loss降到0.01以下。如果obj_loss震荡严重说明正负样本不平衡可以调低obj权重或者增加背景样本。早停策略我一般设patience30也就是30个epoch内mAP没有提升就停。505张数据通常100到150个epoch就能收敛再往后就是过拟合了。我见过有人跑到500个epoch结果验证集mAP反而下降了3个点这就是典型的过拟合。5. 实测中遇到的典型问题与排查链路5.1 损失不下降从数据标注查起第一次跑这份数据时我遇到损失卡在0.7左右不下降的情况。排查顺序是这样的先看标注文件有没有空文件或者格式错误用脚本扫一遍所有.txt检查每行是否有5个字段、坐标是否在0到1之间。结果发现有三张图的标注文件是空的但对应的图像里有明显泄漏。删掉这三张后损失正常下降。然后检查类别ID。单类别应该是0但有些转换脚本会从1开始编号导致YOLO找不到对应的类别。这个错误很隐蔽因为训练不会报错只是分类损失一直很高。5.2 mAP虚高验证集泄漏的识别与处理有一次训练完验证集mAP0.5达到了0.95我差点以为模型完美了。结果拿测试集一跑只有0.71。问题出在数据划分上验证集和训练集有重叠图像。这份数据集如果自带划分文件一定要检查train.txt和val.txt有没有交集。用集合运算一秒就能查出来train_set set(open(train.txt).read().split()) val_set set(open(val.txt).read().split()) overlap train_set val_set print(f重叠数量: {len(overlap)})如果有重叠重新按8:1:1划分确保同一场景的图像只出现在一个集合里。5.3 小目标漏检锚框聚类与特征图尺度管道泄漏区域有时候只占图像的百分之几属于小目标。YOLO默认锚框是基于COCO聚类的对红外小目标不一定合适。我建议用自己的数据跑一遍K-means锚框聚类from sklearn.cluster import KMeans import numpy as np # 读取所有标注框的宽高 boxes [] # ... 填充boxes ... kmeans KMeans(n_clusters9, random_state0).fit(boxes) anchors kmeans.cluster_centers_ print(anchors)把聚类结果写进模型配置文件小目标召回率通常能提升5到10个百分点。另外YOLOv5的P3特征图80x80负责小目标检测如果泄漏区域普遍小于16x16像素可以考虑增加一个更浅层的检测头但这会显著增加计算量需要权衡。5.4 误报集中在管道接头处的应对训练完的模型在管道弯头和法兰处误报比较多因为这些地方本身就有热异常。我的处理方法是在训练集中补充这类负样本也就是有热异常但没有泄漏的图像标注为空文件。YOLO会把这类图像作为背景学习降低误报。补充了大约50张负样本后误报率下降了约40%。另一个技巧是后处理时加一个面积过滤。如果检测框面积小于图像面积的0.1%直接丢弃。这个阈值需要根据你的实际场景调太小了滤不掉太大了会漏掉真实的小泄漏。6. 从505张到可落地模型数据增强与迭代思路6.1 红外图像适合哪些增强手段通用的数据增强里颜色抖动对红外图像意义不大因为红外图本来就没有真实的颜色信息。但几何变换非常有用随机旋转±15度、随机缩放0.8到1.2倍、随机平移±10%、水平翻转。这些能有效扩充数据多样性。Mosaic增强是YOLO自带的把四张图拼成一张。对红外图像来说Mosaic能增加不同场景的混合但要注意拼接后的图像温度分布可能不连续模型可能会学到一些虚假的边界特征。我的经验是Mosaic开启但mosaic0.5而不是默认的1.0也就是50%的概率做Mosaic另外50%用单图。MixUp在红外图像上要慎用。它把两张图按透明度叠加会导致温度信息混乱。我试过MixUpmAP反而降了2个点后来就关了。6.2 主动学习用模型找漏标样本505张标完后难免有漏标。与其人工再筛一遍不如用训练好的模型去推理训练集把模型检测到但标注文件里没有的框找出来。这些就是潜在的漏标样本。人工复核一遍确认是漏标的就补上是误报的就忽略。我通常迭代两轮第一轮能找出大约5%的漏标第二轮降到1%以下。这个流程用YOLO的detect.py就能做把conf-thres设低一点比如0.3让模型多报一些然后人工过滤。6.3 模型蒸馏与量化部署的初步考虑如果最终要部署到边缘设备YOLOv5s或YOLOv8n是首选。进一步压缩可以用知识蒸馏用YOLOv5m当教师模型YOLOv5s当学生模型让学生模型去拟合教师模型的输出分布。我试过在红外管道数据上做蒸馏学生模型的mAP从0.82提升到了0.85接近教师模型的0.86但推理速度快了一倍。量化方面INT8量化能把模型大小压缩到原来的四分之一推理速度提升2到3倍。但红外图像的动态范围窄量化后精度损失可能比可见光图像更大。建议量化后一定要在验证集上重新评估如果mAP下降超过3个点就得考虑混合精度或者只量化部分层。6.4 持续迭代新数据如何并入已有模型实际部署后会遇到新的管道场景、新的泄漏形态。这些数据不要攒着定期比如每月做一次增量训练。增量训练时把新数据和旧数据按1:1混合学习率调低到初始值的十分之一训练20到30个epoch即可。这样既能学到新特征又不会遗忘旧知识。我踩过的坑是只用新数据微调结果模型在旧场景上的性能大幅下降这就是灾难性遗忘。混合训练虽然麻烦一点但稳妥得多。最后分享一个我在实际项目中总结的小技巧红外图像训练YOLO时把hyp.scratch-low.yaml里的hsv_h、hsv_s、hsv_v三个参数全部设为0因为红外图像没有颜色信息颜色抖动纯属噪声。这个改动看似微小但能让训练稳定性提升不少mAP波动从±2个点降到±0.5个点。
阅读完成 · 觉得有帮助?