ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

开关柜接头红外过热检测图像数据集:VOC转YOLO训练避坑指南

开关柜接头红外过热检测图像数据集:VOC转YOLO训练避坑指南 简介这套开关柜接头红外过热检测图像数据集面向电力设备运维人员、算法工程师及相关专业研究生用于红外热像中开关柜接头过热点的自动检测与定位。数据整体为红外热像图每张图像均配有VOC格式的XML标注文件精确标示出每个过热点的边界框图像中还内嵌温度信息可结合温度阈值对隐患程度进行研判。压缩包约217.68MB共含5527个XML标注文件与5527张JPG红外图像合计11054个文件目录按采集时间和现场站点组织便于按批次或场景划分训练集与验证集。样本覆盖不同时段、不同负载条件下的红外影像包含正常与过热两种状态有助于训练模型区分干扰与真实发热点。已有834人浏览学习适合用于YOLO、Faster R-CNN等目标检测模型训练以及温度特征分析、电力设备智能巡检等研究。借助该数据集可快速构建并验证红外过热检测流程缩短算法落地周期。1. 开关柜接头红外过热检测图像数据集能做什么先解决你没标注可用的痛点凌晨的变电站运维人员拎着红外热像仪一台柜子一台柜子扫拍到某面开关柜上口接头温度已经逼近80℃。但他一时判断不了这是持续发热还是短时波动只能把这段红外图像导出来慢慢比对。这个痛点背后是算法本来可以帮忙、却长期被数据卡住的事开关柜接头红外过热检测。标题里的这份数据集恰好补上这一环——3000多张红外图像不是随手拍的可见光照片而是带VOC标签的目标框标注并且每张图还带了温度信息。把它拿来训练一个能自动定位过热接头的检测模型完全可行。这篇文章就沿着“数据集是什么、怎么处理、怎么训练、坑在哪”往下拆适合做电力设备状态检修的算法工程师也适合想从可见光目标检测转红外域的人。2. 开关柜接头红外数据集里到底装了什么灰度图、VOC标签与温度信息怎么对齐2.1 红外看开关柜接头温差比绝对温度更值得检测红外热像仪接收被测物体表面发射的长波红外辐射输出的是一个温度阵列再映射成灰度图或伪彩图。正常工作电流流过导体本身就会发热但接头发热的根因通常是接触电阻变大——常年震动、导体氧化、螺栓松动电流经过接触面时被耗散成额外热量。因此检测的目标不是“有热”而是“接头比同环境下导体更热”。这个物理特征决定了训练集里不能只放“严重过热”的图也要有载流正常但温度略高的样本。模型学的是相对温升而不是死记一个80℃阈值。这也是“有温度信息”这类红外数据集比普通红外图像更值钱的地方。普通红外图像数据集只给类别和框模型只能学会“这个形状像接头”容易把正常接头也框出来。而带温度信息之后数据集的用途立刻被拓宽既能做目标检测又能做温升趋势回归还能在推理阶段做温差二次校验。拿到数据集以后第一件该做的事不是开训练脚本而是把图像、VOC标签、温度信息三者完整解析一遍确认它们对得上。2.2 数据集常见结构JPEG/PNG、XML标注和温度文件怎么对齐这类红外过热检测数据集最常见的目录布局是JPEGImages存放图像Annotations存放VOC格式的XML标注另外会有单独的温度文件目录或者把温度字段内嵌在XML里。下面这个结构是我处理电力红外数据时常用的摆放方式拿到新数据集后先按这个思路核对switchgear-infrared-sets/ ├── JPEGImages/ │ ├── IR_0001.jpg │ ├── IR_0002.jpg │ └── ... ├── Annotations/ │ ├── IR_0001.xml │ ├── IR_0002.xml │ └── ... └── temperature/ ├── IR_0001.npy ├── IR_0002.npy └── ...你拿到手的目录可能不叫这些名字也可能温度信息直接写在XML的temperature节点里。不管哪种形式第一步都是核对三个列表是否完全一一对应图像文件数、标注文件数、温度文件数。这一步做不好后面训练时经常会出现“某一张图没有label直接跳过”的幺蛾子。常见做法是用一行命令看数量ls JPEGImages | wc -l ls Annotations | wc -l数量一致还不够文件名前缀也要对齐。我一般会写一个小脚本把三者的basename抽出来做集合差凡是文件名对不上的样本直接移出训练列表而不是在训练时让框架报错。2.3 用Python解析VOC标签并统计温度分布不管数据集里的温度信息是独立文件还是XML内嵌字段先用Python把VOC标签完整解析一遍并把每个标注框的温度值提取出来做分布统计。下面这段代码能同时完成两件事读取VOC框坐标、读取温度节点并统计框面积分布import glob import xml.etree.ElementTree as ET import numpy as np def parse_voc(xml_file): tree ET.parse(xml_file) root tree.getroot() objs [] # VOC里每个object表示一个标注框这里统一用iter找所有object for obj in root.iter(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) objs.append({name: name, box: [xmin, ymin, xmax, ymax]}) # 温度信息可能挂在根节点下也可能在object里做容错处理 temp_node root.find(.//temperature) temperature float(temp_node.text) if temp_node is not None else None return objs, temperature xml_files glob.glob(Annotations/*.xml) all_areas [] temp_list [] for xml_file in xml_files: objs, temperature parse_voc(xml_file) for obj in objs: box obj[box] area (box[2] - box[0]) * (box[3] - box[1]) all_areas.append(area) temp_list.append(temperature) print(f标注框总数: {len(all_areas)}) print(f温度字段非空比例: {np.mean([t is not None for t in temp_list]):.2f}) print(f框面积分布: min{np.min(all_areas):.0f}, fmedian{np.median(all_areas):.0f}, max{np.max(all_areas):.0f})逻辑上这段代码先遍历Annotations目录下所有XML用root.iter(object)把每个目标框拉出来这是VOC格式最通用的做法比root.find(object)更稳因为有些标注工具会把object嵌在group或part里。温度字段不一定是temperature有的写Temp有的写tempC我会把几种字段名做成一个列表逐个尝试提高容错率。参数上要注意glob.glob(Annotations/*.xml)只匹配一层目录如果标注文件按子目录分块存放这个写法会漏掉。换成glob.glob(Annotations/**/*.xml, recursiveTrue)就能覆盖多层目录。统计框面积的价值在于后续训练策略如果发现中位数面积很小比如只有几百像素就说明图像里接头目标普遍偏小得按小目标检测的思路处理。3. 把VOC标签转成YOLO能直接吃的txt坐标归一化与温区样本划分3.1 为什么YOLO训练器偏爱txt而不是XMLVOC格式的坐标是绝对像素值xmin/ymin/xmax/ymax而YOLO家族的label格式是每行一个目标class_id x_center y_center width height全部归一化到0到1之间。差异不只是格式问题归一化坐标天然和输入图像尺寸解耦模型在缩放输入时不需要改动标签训练脚本也省去逐张解析XML的开销。另外红外图像经常出现不同相机分辨率不一致的情况同一个数据集里可能混有640×480和1280×1024的图。如果用VOC绝对坐标不同分辨率下同样一个接头的框尺寸差异很大模型收敛会慢转成YOLO的归一化坐标后尺寸差异被抹平了。但要注意归一化只是让模型兼容不同分辨率并不会自动解决“接头在整张图里占比太小”的问题这个坑放到后面单独说。下表把两种格式的关键字段列在一起方便对照写转换脚本。格式坐标表示文件组织适用场景VOC XML绝对像素xmin/ymin/xmax/ymax每张图一个XML文件通用检测数据集跨框架兼容性好YOLO txt归一化cx/cy/w/h每张图一个txt文件YOLOv5/v8等训练直接读取速度快3.2 转换脚本坐标归一化的三个细节下面这个脚本负责把VOC转成YOLO格式还做了越界保护。它依赖cv2读取图像尺寸而不是从XML里读size因为有些标注工具写歪了XML里的宽高和实际图像不一致以实际图像为准更安全。import os import cv2 import xml.etree.ElementTree as ET CLASS_DICT {connector: 0, overheat_joint: 1} def voc_to_yolo(xml_path, image_path, label_path): tree ET.parse(xml_path) root tree.getroot() # 实际图像尺寸优先XML里的size只在图像缺失时做兜底 img cv2.imread(image_path) if img is None: size_node root.find(size) img_w int(size_node.find(width).text) img_h int(size_node.find(height).text) else: img_h, img_w img.shape[:2] lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASS_DICT: continue cls_id CLASS_DICT[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_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 越界保护标注框超出图像边缘会导致loss突变 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) # 归一化值保留6位小数训练时浮点精度足够 lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(label_path, w) as f: f.write(\n.join(lines))这个脚本里有三个细节值得单独说。第一图像尺寸一定以cv2.imread读取为准如果XML的size和实际图片尺寸不一致转换出来所有框都会偏移。第二越界保护不能省红外数据集标注时经常有标注员把框拉出图像边缘不处理的话YOLO训练时nan loss随时出现。第三类别名要提前定好有些数据集合了connector和joint表示同一个东西转换前先做一次类别名统计把重复语义合并掉。调用方式也很简单os.makedirs(labels, exist_okTrue) for xml_file in os.listdir(Annotations): if not xml_file.endswith(.xml): continue name xml_file[:-4] voc_to_yolo( fAnnotations/{xml_file}, fJPEGImages/{name}.jpg, flabels/{name}.txt )转换完成后抽查一个txt文件如果出现负坐标或者大于1的值说明原始标注里有脏数据要回头清洗。这一步值得花时间因为它直接影响后面的训练稳定性。3.3 按温度信息划分训练集避免“冷接头”和“热接头”混在一起很多红外观测数据集有一个隐蔽问题严重过热样本集中在几个连续拍摄时段正常温度样本又集中在另一批直接随机划分训练集和验证集会导致验证集里全是高温样本或者全是常温样本。带温度信息的好处就是可以用温度分档来做分层采样。我习惯把每个标注框对应的温度信息提取出来分成低温、中温、高温三档然后按档位分别随机划分import numpy as np # 假设temp_array是每个标注样本的温度值列表 temp_array np.array(all_temperatures) # 按温差绝对值分档40℃以下为常温40-70℃为温升70℃以上为过热 bins [-np.inf, 40, 70, np.inf] labels np.digitize(temp_array, bins) # 统计每档数量确保训练集和验证集都包含三档样本 for i in range(1, 4): idx np.where(labels i)[0] print(f等级{i}: {len(idx)}个样本)np.digitize的作用是把温度值映射到档位编号之后就可以按档位做随机索引切分。这样划分出来的验证集既能看到模型在低温背景下的误报率也能看到高温目标的召回率。很多项目翻车不是模型不行而是验证集本身偏了这一点红外数据里尤其常见。4. 用YOLO训练红外接头检测模型从最小训练命令到温度回归分支4.1 最小训练命令先把数据铺进项目再跑train.pyYOLO类框架训练前需要把数据整理成固定的目录结构images/train、images/val、labels/train、labels/val四个目录。整理完后再写一个switchgear.yaml数据配置文件里面写明类别数量和数据集路径。这里给出一个最简训练流程# 把图片和标签按YOLO要求摆放 mkdir -p datasets/switchgear/images/{train,val} mkdir -p datasets/switchgear/labels/{train,val} # 训练 python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data switchgear.yaml \ --weights yolov5s.pt--img 640是输入分辨率对于红外接头检测来说不一定最优后面会讲怎么调整。--batch 16在单卡显存有限的情况下比较稳妥如果用的是12GB以上显存的卡可以提到32。--weights用来加载预训练权重但要注意预训练权重是在普通可见光数据集上训出来的红外灰度图与其分布差异大需要更多轮次微调才能稳定。数据配置文件switchgear.yaml至少要包含nc类别数量、train路径、val路径、names列表。这里有一个容易忽略的点如果一张红外图里只有温度略高的正常接头没有过热接头类别不平衡会很明显。训练前先用上一章的统计脚本算一下每个类别的样本数如果某个类别只有几十个框优先考虑做数据增强或合并类别。4.2 训练参数设置红外小目标与可见光检测的三个不同红外接头检测和日常可见光目标检测有三处明显不一样参数得跟着调。我常用的参数配置如下参数可见光常规值红外接头检测建议原因--img6401024 或 1280接头在红外图里往往是几十像素的小目标提高输入分辨率能保留细节--mosaic默认开启关闭或降到0.5红外图像背景单一马赛克增强容易把温度纹理切碎导致误检--multiscale开启谨慎开启不同分辨率下接头尺寸变化大小分辨率输入会让小目标更难学--epochs100150 起步预训练权重域差异大需要更多轮次收敛第一点最关键。很多红外小目标检测算法受益于高分辨率输入接头在640×480原图上可能只有20×20像素下采样到640以后只剩10×10模型很容易漏检。我一般先把输入分辨率调到1024如果显存不够就切patch训练。第二点关于mosaic红外图不像自然图像有丰富的语义多样性马赛克拼接出来的图经常是一半黑一半亮模型反而会去学拼接边界适得其反。第三点容易被人忽略--cache参数在红外数据集上值得用。红外图像是灰度图数据量不大几百张图全量缓存到显存/内存可以大幅缩短训练时间。如果数据有3000多张预读取速度提升非常明显。4.3 温度信息参与训练的两种常见做法硬标签与回归分支温度信息不只能用来划分训练集还能直接参与训练。常见做法有两种。第一种最简单把“正常接头”和“过热接头”做成两个类别模型的输出天然带有温度等级概念。适合快速落地缺点是需要自己决定过热阈值且阈值固定后模型不能感知连续的温升趋势。第二种是加一个温度回归分支让检测头输出框的同时输出一个温度差值。这个做法需要改模型结构但信息利用率更高。下面是一个参考思路基于检测头特征追加回归层import torch.nn as nn class TempRegressionHead(nn.Module): def __init__(self, in_features512): super().__init__() self.fc nn.Sequential( nn.Linear(in_features, 128), nn.ReLU(), nn.Linear(128, 1) # 输出温差值 ) def forward(self, roi_features): return self.fc(roi_features)这个分支的输入是ROI pooling之后的特征向量监督目标是“接头温度减去参考区域温度的差值”。训练时需要单独构造一个回归损失比如SmoothL1Loss再和检测损失相加。这么做的好处是模型不仅会“找接头”还能在推理时输出一个连续温升值运维人员可以直接按这个值排序优先处理温差最大的柜子。代价是温度标注必须可靠否则回归分支学到的是噪声。对于标题里的数据集既然明确带了温度信息这个方向非常值得试。5. 排查与避坑开关柜红外数据集落地时的五个拦路虎5.1 伪彩图被当成普通RGB训练模型过拟合现象用伪彩JPG图直接加载ImageNet预训练权重训练训练集上mAP很快到0.9换一批新场景红外图测试准确率掉一半还多。看起来是模型过拟合其实是模型在学伪彩映射规则。原因红外伪彩图是把温度矩阵映射到色彩空间得到的RGB值与真实温度是查表关系不同相机、不同色带下的伪彩规则可能完全不同。预训练权重在自然图像上提取的是纹理和颜色特征用在伪彩图上抓不到物理热模式。解决优先向数据提供方要灰度图或原始温度矩阵。如果只有伪彩图把图片转成灰度再训练会好很多。具体做法是用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)转灰度再复制成三通道喂给网络这样至少绕开了颜色映射干扰。5.2 坐标越界导致Loss变成nan现象训练前几步正常几十步后loss突然变成nan训练直接中断。换随机种子也一样。原因VOC标注里有框的坐标超出图像边界比如xmax大于图像宽度。YOLO在计算边界框损失时这些非法坐标产生极大梯度导致参数发散。解决转换脚本里加min/max裁剪并在训练前扫描一遍labels目录过滤掉宽度或高度为负的标注。注意裁剪后如果框和另一个框重叠严重还要考虑是否合并否则会影响NMS结果。5.3 温度字段读取不到统计出来全是None现象统计温度信息时3000多个样本里有两千多个显示None没法做温区分层更没法训练回归分支。原因标注工具不同导致字段名不一致有的写temperature有的写Temp还有的写Tmax甚至有的温度写在文件名后缀里没进XML。解决解析时做一个字段名候选列表逐个尝试并输出解析失败的样本清单。更稳妥的做法是让数据提供方给出字段说明没有说明就直接读取文件名的后半段比如IR_0001_T82.5.jpg里的82.5。这个坑在真实数据集里非常常见我踩过一次之后现在解析任何带额外信息的数据集都会先做字段探查。5.4 接头在红外图里只有二十像素全部漏检现象训练完成后测试近处柜子的接头能检测到远处柜子或广角镜头下的接头全部漏掉同时在柜体金属边缘出现大量误报。原因数据集里包含不同拍摄距离的图像远距离接头的尺寸只有几十像素下采样到640后变成5像素以内的点锚框和下采样倍数都不匹配模型基本学不到特征。解决先按框面积统计把样本分成近景和远景两组分别评估然后考虑把输入分辨率提高到1280或者用SAHI这类切片推理工具在推理阶段对图像做分块检测。还有一个直接有效的方式训练时把小样本复制粘贴增强用同一张图的温差区域做局部过采样。5.5 训练集白天多夜间少误报全在夜间场景现象整体mAP不错但按场景分时段评估后发现夜间测试图的误报率高出白天三倍。原因白天红外图背景有太阳辐射干扰夜里环境温度低、背景干净模型在白天数据上学到的阈值在夜间被打破。解决按拍摄时间或背景亮度对数据集做分层保证训练集和验证集里白天、夜间样本比例一致。如果原始数据没记录时间可以用图像整体灰度的均值近似区分环境亮度。在推理端再加上“温差判据”做二次筛选能显著压掉夜间误报。6. 进阶用温差判据验证检测框的可靠性而不是只看mAP6.1 从检测框里取温度框内最高温和背景参考温差的算法模型的输出只是一个矩形框它只能告诉你“这里可能有一个接头”。红外检测的真正价值在于框内温度是否异常所以推理阶段我会再加一道温差校验取检测框内最高温度和柜体背景区域的中位温度做差超过阈值才判为过热。这个逻辑用Python实现很短import numpy as np def check_overheat(thermal_matrix, box, bg_region, threshold20.0): x1, y1, x2, y2 [int(v) for v in box] roi thermal_matrix[y1:y2, x1:x2] if roi.size 0: return False, 0.0 t_max np.max(roi) x_bg1, y_bg1, x_bg2, y_bg2 bg_region bg thermal_matrix[y_bg1:y_bg2, x_bg1:x_bg2] t_bg np.median(bg) delta_t t_max - t_bg return delta_t threshold, delta_t这里的关键是thermal_matrix必须是真的温度矩阵而不是伪彩图。温度矩阵可以通过红外设备的标定公式从灰度图换算常见做法是T gray * slope intercept两个系数从相机厂商提供的数据里取。如果数据集的温度信息以npy格式存放那直接读npy就行。背景参考区域最好选在与接头同一柜面的空白处不要选到柜体边缘因为边缘会有反射干扰算出来的温差参考意义不大。6.2 我的验证习惯按温差分层评估而不是只看平均mAP我的习惯是每次训练完之后不看整体mAP而是把检测结果按温差分成三档温差低于10℃的、10到40℃的、超过40℃的分别看召回率。因为在实际运维场景里真正需要报警的是温差显著超标的样本而这一类在数据集里往往是少数。如果温差超过40℃的档位召回率很高但10到40℃档位召回率很低说明模型学会了“找最亮的点”而不是“找接头这个对象”这在巡检场景里会导致大量漏报。验证集也保持相同的分层逻辑才能真实反映上线后的表现。这个习惯帮我避过好几次“验证集分数好看、现场效果拉胯”的尴尬。希望这篇笔记能让你拿到开关柜接头红外过热检测图像数据集后少踩几个坑把温度信息真正用起来——不只做个框出来。本文还有配套的精品资源点击获取
返回列表