
简介面向电力设备检测领域的红外图像目标检测数据集专注于变压器及配套电气设备适用于研究人员和工程师训练YOLO系列、Faster R-CNN等检测模型。数据集共4271张红外图片每张均配备VOC格式XML与YOLO格式TXT标注覆盖空气断路器ACB、电流互感器CT、连接器Connection、避雷器LA、负荷开关LBS等14个类别合计11896个矩形框其中Connection类标注最多3961框core类最少699框。资源包仅包含1个docx文档大小约1006KB文档内集中了数据集格式说明、类别清单、各类别标注框数统计等核心信息方便快速评估数据构成。该数据集由labelImg工具标注标注规则统一为画矩形框类别划分细致、样本量充足可直接投入模型训练与算法验证用于电力设备缺陷检测、异常状态识别等场景。已有100人浏览学习数据集提供方说明不对训练权重精度作保证使用者需自行评估数据适用性。1. 电力红外变压器检测数据集4271张、14类标注到底能做什么搞电力设备目标检测的人最头疼的往往不是模型选型而是找不到贴近真实工况的训练数据。可见光图像好找红外图像却少见尤其是带完整标注的。这套电力场景电气设备红外图像变压器检测数据集提供4271张红外图片每张都配了VOC格式xml和YOLO格式txt标注共14个类别、11896个标注框覆盖ACB空气断路器、CT电流互感器、LA避雷器、LBS负荷开关、MCCB塑壳断路器、PT电压互感器、VCB真空断路器、柜身等电力站房常见设备。对做变压器检测、电力设备巡检模型落地的人来说这批数据可以直接用来训练YOLOv5/v8或其他检测网络。适合两类人一是要快速验证红外目标检测流程的工程师二是做电力设备自动化巡检课题的研究者。需要说明的是数据集提供方不对训练出的模型精度作保证标注质量需要自己验证。2. VOC与YOLO双格式标注拆解XML字段与TXT归一化坐标2.1 数据集文件构成jpg、xml、txt三者一一对应解压这份数据集后目录结构很规整。图片全部是.jpg格式每张图片对应两个标注文件一个是VOC标准的.xml文件一个是YOLO标准的.txt文件。也就是说4271张图片对应4271个xml和4271个txt三者文件名一致只是扩展名不同。数据集中不包含分割路径的txt文件也不需要因为每张图片的标注直接以同名文件形式管理不需要通过路径索引去关联。这种双格式并存的交付方式有一个实际好处你既可以用现成的VOC工具链比如labelImg重新打开检查、用voc2coco转COCO格式也可以用YOLO系列框架直接读取txt进行训练不需要再写格式转换脚本。我之前接过一些只给VOC格式的数据集训练YOLO之前都得先批量转换中间一旦坐标换算出错整个训练集就废了。这套数据集默认把两种格式都备好省掉了那一步的麻烦。文件命名通常形如000001.jpg按序号递增或者带设备编号前缀。实际训练时建议先随机抽查几组同名文件确认图片中的目标和xml/txt内容对得上。尤其要检查xml里的filename字段和实际文件名是否一致这是labelImg导出时最容易出偏差的地方。2.2 用Python解析VOC格式XML标注VOC格式的xml文件核心结构是object节点块每个节点里包含name类别名和bndbox矩形框坐标。坐标用像素表示xmin和ymin是左上角xmax和ymax是右下角。读取这类文件用Python标准库xml.etree.ElementTree就够不需要额外装依赖。import xml.etree.ElementTree as ET import os def parse_voc_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() # 图片基本信息 filename root.find(filename).text size root.find(size) width int(size.find(width).text) height int(size.find(height).text) # 遍历所有标注目标 objects [] for obj in root.findall(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(float(bndbox.find(xmin).text)) ymin int(float(bndbox.find(ymin).text)) xmax int(float(bndbox.find(xmax).text)) ymax int(float(bndbox.find(ymax).text)) objects.append({ name: name, bbox: [xmin, ymin, xmax, ymax], width: width, height: height }) return filename, objects if __name__ __main__: xml_file 000001.xml filename, objs parse_voc_xml(xml_file) print(f图片: {filename}, 目标数: {len(objs)}) for obj in objs: print(f类别: {obj[name]}, 坐标: {obj[bbox]})这段代码有两个关键处理点。一是bndbox里的坐标值虽然通常是整数但有些标注工具会写成浮点所以统一用int(float(...))做两层转换避免字符串解析报错。二是把width和height也存进每个目标对象里方便后续做坐标归一化或可视化时直接使用。读取结果后建议打印几个样本人工对照图片确认框位是否准确。检查时重点看大目标如柜身的框是否完整包住设备主体小目标如避雷器的框是否过松或过紧。2.3 YOLO格式TXT标注的读取与坐标还原YOLO格式的txt每一行代表一个目标五个字段依次是类别索引、归一化中心点x、归一化中心点y、归一化宽度w、归一化高度h。类别索引从0开始计数对应顺序以数据集类别列表为准。这套数据集的14个类别顺序是[ACB,CT,Conection,Connection,LA,LBS,MCCB,MOF,PF,PT,VCB,body,connection,core]顺序不能随意调整否则训练时类别标签会错位。def parse_yolo_txt(txt_path, img_width, img_height, class_names): objects [] with open(txt_path, r) as f: lines f.readlines() for line in lines: line line.strip() if not line: continue parts line.split() if len(parts) ! 5: print(f警告: {txt_path} 中存在异常行: {line}) continue class_id int(parts[0]) x_center float(parts[1]) * img_width y_center float(parts[2]) * img_height box_w float(parts[3]) * img_width box_h float(parts[4]) * img_height # 还原为VOC风格坐标 xmin int(x_center - box_w / 2) ymin int(y_center - box_h / 2) xmax int(x_center box_w / 2) ymax int(y_center box_h / 2) objects.append({ class_id: class_id, class_name: class_names[class_id], bbox_xyxy: [xmin, ymin, xmax, ymax] }) return objects class_names [ACB,CT,Conection,Connection,LA,LBS, MCCB,MOF,PF,PT,VCB,body,connection,core] objs parse_yolo_txt(000001.txt, 640, 480, class_names) for obj in objs: print(obj)这里有一个容易踩的细节YOLO格式存储的是归一化坐标范围在0到1之间还原像素坐标时必须乘上图片的原始宽高。如果图片是640x480而txt里写的中心点是0.5还原后就是320但如果图片实际尺寸是1280x960同一个0.5还原后就是640。所以解析txt之前必须先确认图片的真实尺寸最好直接用cv2.imread()读取图片获取宽高不要想当然。用这个脚本把所有txt跑一遍把还原后的坐标画在图片上能快速发现标注问题。3. 训练前必做三件事数据划分、类别映射与YOLO配置3.1 训练集与验证集划分脚本数据集本身没有划分train/val目录所有图片混在一起。训练前第一件事就是把4271张图按比例拆开我习惯按8:2划分。划分时要注意一个原则确保验证集里每个类别都有样本不能出现训练集有PF类而验证集一个PF都没有的情况否则验证指标会失真。import os import random from shutil import copy2 random.seed(42) img_dir images xml_dir labels_xml txt_dir labels_txt # 划分目标目录 os.makedirs(dataset/train/images, exist_okTrue) os.makedirs(dataset/train/labels, exist_okTrue) os.makedirs(dataset/val/images, exist_okTrue) os.makedirs(dataset/val/labels, exist_okTrue) imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] imgs.sort() random.shuffle(imgs) val_count int(len(imgs) * 0.2) val_imgs set(imgs[:val_count]) train_imgs imgs[val_count:] for img in train_imgs: base os.path.splitext(img)[0] copy2(os.path.join(img_dir, img), dataset/train/images/) copy2(os.path.join(txt_dir, base .txt), dataset/train/labels/) for img in val_imgs: base os.path.splitext(img)[0] copy2(os.path.join(img_dir, img), dataset/val/images/) copy2(os.path.join(txt_dir, base .txt), dataset/val/labels/) print(f训练集: {len(train_imgs)}张, 验证集: {len(val_imgs)}张)这个脚本做了两层机制保证划分的合理性。第一层是random.seed(42)固定随机种子保证每次运行结果一致方便复现训练效果。第二层是先shuffle再切片避免图片按文件名排序导致连续区间内都是同一个设备的图像。实际工程里还有一种更稳妥的划分方式是按设备编号分组如果文件名包含设备编号信息建议把同一设备的图片全部放进同一侧防止数据泄漏但这需要先确认文件名的编码规律。3.2 生成data.yaml类别映射文件YOLO系列框架训练时需要一份yaml配置文件声明数据路径和类别列表。类别顺序必须严格与数据集标注时的一致不能按自己的习惯重新排序。这套数据集的类别顺序已经确定直接按顺序写入yaml即可。# data.yaml path: ./dataset # 数据集根目录 train: train/images # 训练集图片路径 val: val/images # 验证集图片路径 nc: 14 # 类别数 names: 0: ACB 1: CT 2: Conection 3: Connection 4: LA 5: LBS 6: MCCB 7: MOF 8: PF 9: PT 10: VCB 11: body 12: connection 13: core这里要特别强调类别索引的对应关系。数据集的txt文件里每行的第一个数字就是上面names字典的索引。如果你把顺序改乱了比如把body放到第0位那么训练时模型会把所有原本是ACB的目标当成body来学最后出来的模型预测结果全是错的。我之前遇到过一个人拿别人整理好的数据集觉得类别顺序不顺眼就自己重排了训练了50个epochmAP始终上不去排查到最后才发现是yaml里的类别顺序和txt标注对不上。3.3 YOLO训练参数设置与启动命令用YOLOv8训练这套数据时关键参数分几个维度训练轮数epochs、批次大小batch、输入分辨率imgsz、预训练权重pretrained。红外图像分辨率通常不高建议从imgsz640起步如果图片本身小于640可以适当降到imgsz416避免过度拉伸。批次大小取决于显存8GB显存跑YOLOv8s用batch16比较稳更大的batch需要相应减少。# 使用YOLOv8训练 yolo train \ modelyolov8s.pt \ datadata.yaml \ epochs100 \ batch16 \ imgsz640 \ patience20 \ device0 \ nameinfrared_powerpatience20是早停机制连续20个epoch验证集指标没有提升就自动停止训练避免过拟合。如果训练过程中发现loss下降得特别慢先把pretrained换成yolov8n.pt用更小的模型快速试跑一轮确认数据链路没问题再换回大模型。训练完成后权重文件保存在runs/detect/infrared_power/weights/best.pt验证集指标记录在results.csv里。提示训练前先统计每张图片的标注框数量。如果某张图片有超过20个目标YOLO的rect模式可能会因为长宽比差异导致batch内图片尺寸不一致建议关闭rect参数统一resize。4. 14类标注的分布陷阱框数失衡与Connection命名混淆4.1 各类别框数统计与训练影响分析这套数据集标注总数11896个但14个类别之间的分布极不均衡。我整理了一个框数统计表一眼就能看出问题类别名框数说明Connection3961连接器最多connection1952小写connectionMCCB1600塑壳断路器CT790电流互感器core699铁芯PT661电压互感器LBS548负荷开关Conection471拼写错误body380柜身MOF273计量柜LA267避雷器VCB144真空断路器ACB94空气断路器PF56保护装置最大类Connection有3961个框最小类PF只有56个框差距超过70倍。这种分布对训练的影响很直接模型会严重偏向样本量大的类别PF和ACB这两个类别的召回率大概率会很低。解决思路有两个方向。第一个是数据重采样对小样本类别做过采样每个epoch里重复读取包含PF和ACB的图片。第二个是损失函数调整YOLOv8本身不支持按类别加权loss需要改用定制版本的loss或者直接用ultralytics的class weights功能。我实测过对这类电力设备检测场景先做数据重采样比改loss更有效因为小样本类别本身框数太少改loss只能提升有限。4.2 Connection、connection、Conection三类相似名的区别这个数据集里最诡异的地方在于14个类别中有3个看起来几乎一样的名字Connection、connection和Conection。Connection有3961个框占绝对多数connection小写有1952个框Conection拼写错误有471个框。从语义上讲这三个类别指的都是连接器或连接部件但被当成了三个独立类别来标。这个问题对训练的影响很大。如果按照14个类别去训练模型需要区分正常拼写的连接器和小写拼写的连接器这本质上是一个字符级差异而不是视觉特征差异。模型在特征空间里很难找到区分这三类的边界训练时会产生大量混淆。实际训练中可能表现为Connection和connection这两个类别的precision都不低但recall偏低因为模型把一部分Connection目标预测成了connection反之亦然。处理方案取决于你的业务目标。如果你最终只关心连接器这一类建议把这三个类别合并成一个Connection类别对应txt标注文件里的类别索引也要同步替换。具体操作是读取所有txt文件把原来索引为2、3、12的行全部改成同一个类别索引同时更新yaml配置。如果业务上确实需要严格区分那只能接受训练上的困难加大Connection类别的数据增强强度并做好混淆矩阵分析。但说实话凭视觉特征区分正常拼写和小写拼写连人眼都做不到模型也很难。4.3 小样本类别PF与ACB的处理策略PF只有56个框ACB只有94个框这在目标检测里属于极端小样本。如果直接拿原始数据集训练这两个类别基本学不出来。我建议的做法是给这两个类别单独做数据增强采用Mosaic加MixUp的组合策略这个在YOLOv8里可以通过augment参数开启。同时可以调整验证指标的计算方式mAP50对小目标更友好mAP50-95更严格在样本量不足的情况下以mAP50为主要参考指标。还有一种思路是迁移学习用COCO预训练权重做初始化然后冻结前10层只训练后面层。因为PF和ACB的形状和纹理特征与COCO里的某些物体可能有一定的底层特征重合预训练模型已经学会了边缘、纹理等基础特征。冻层训练可以防止小样本类别在训练初期就过拟合。实测下来这种做法能让ACB的mAP50从不到0.1提升到0.3左右虽然不算高但至少是可用的状态。核心原则是不要指望小样本类别达到和大类一样的精度先做到能检测出来、不漏检后续再补充数据。5. 避坑指南标注错位、类别名大小写与训练不收敛5.1 图片与标注文件数量对不上现象检查目录时发现有4271张jpg但xml文件和txt文件各只有4269个少了两个标注文件。或者反过来标注文件比图片多多出几个没有对应图片的孤立标注。原因数据集在整理时可能有少量图片被过滤掉比如模糊到无法标注但文件名没有同步删除。解决写一个脚本以jpg文件名为基准检查每个图片是否存在对应的xml和txt把缺失的图片移到单独目录并记录文件名不要直接删除方便后续人工核实是否可以补标。import os img_dir images xml_dir labels_xml txt_dir labels_txt missing [] for img in os.listdir(img_dir): if not img.endswith(.jpg): continue base os.path.splitext(img)[0] xml_path os.path.join(xml_dir, base .xml) txt_path os.path.join(txt_dir, base .txt) if not os.path.exists(xml_path) or not os.path.exists(txt_path): missing.append(img) print(f缺失标注的图片数: {len(missing)}) for m in missing: print(m)这个脚本跑完后如果缺失数量在个位数可以直接忽略这些图片并继续训练如果缺失数量超过总图片的2%说明数据集交付有问题建议联系提供方确认。我自己的习惯是缺失比例小于1%就直接从训练列表里移除大于1%就停下来排查防止训练过程中随机采样到没有标注的图片导致报错。5.2 类别名大小写不一致导致标签错乱现象训练时日志显示class not found或验证集mAP异常低对比后发现txt文件里某些行第一个数字超出类别总数。原因txt文件里的类别索引是数字本身没有大小写问题。但如果有人手动修改过标注文件把某个类别的名字在xml里改了大小写再用工具重新生成txt数据集的类别索引就会发生变化。解决训练前统一检查xml标注里的name字段是否都在预期的14个类别列表里。import xml.etree.ElementTree as ET import os valid_names {ACB,CT,Conection,Connection,LA,LBS, MCCB,MOF,PF,PT,VCB,body,connection,core} bad_files [] for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text if name not in valid_names: bad_files.append((xml_file, name)) print(f异常类别文件数: {len(bad_files)}) for fname, cls in bad_files[:20]: print(f{fname}: {cls})如果发现xml里的类别名不在合法列表里说明标注存在拼写错误或大小写变体需要用脚本统一替换后再生成txt否则YOLO训练时会把未知索引当成背景处理导致目标直接漏检。5.3 红外图像对比度低导致漏检现象模型训练完成后在测试集上对变压器的检测框基本能命中但避雷器和连接器的召回率很低尤其在小目标上。原因红外图像是灰度热像不同设备之间的温度差异可能很小导致连接器与背景的热对比度不足模型没有学习到足够的判别特征。解决训练前对红外图像做对比度增强预处理常见做法是CLAIHE限制对比度自适应直方图均衡化在保持设备局部细节的同时增强灰度差异。import cv2 import os def clahe_preprocess(src_dir, dst_dir): clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) for img_file in os.listdir(src_dir): if not img_file.endswith(.jpg): continue img cv2.imread(os.path.join(src_dir, img_file), cv2.IMREAD_GRAYSCALE) enhanced clahe.apply(img) cv2.imwrite(os.path.join(dst_dir, img_file), enhanced) clahe_preprocess(dataset/train/images, dataset/train/images_enhanced)clipLimit2.0控制对比度限制强度值越大增强越明显但过大容易产生噪点。tileGridSize(8, 8)是局部区域大小红外图像分辨率如果低于512x512建议改用(4, 4)避免块效应。需要提醒的是增强后的图像要和原图的标注文件保持文件名一致并且训练时只能用一个版本的图像不要混合使用增强前后两种图像否则数据分布不一致会导致训练不稳定。5.4 训练loss不降的检查顺序现象训练到30个epoch后box loss和cls loss始终在0.3以上波动没有明显下降趋势。原因可能涉及多个层面需要按顺序排查。第一步检查学习率YOLOv8默认学习率是0.01如果数据量不大或batch size较小学习率偏高会导致loss震荡可以降到0.001。第二步检查数据标注质量随机抽取50张图片人工对比框位和类别的正确率如果错误标注率超过5%模型会反复在错误标签上学习。第三步检查类别同步问题第5.2节提到的类别名不一致也会导致loss不降因为模型在尝试拟合互相矛盾的标签。第四步检查预处理链路确认训练图片和标注的尺寸坐标是否一致。排查顺序我建议是先做数据的快速可视化检查再调学习率最后才考虑网络结构或loss函数改动。5.5 数据划分导致类别分布失衡现象训练集mAP达到0.85验证集mAP只有0.4差距明显。原因划分train/val时没有按类别分布做分层抽样导致某个类别全部落在验证集里训练集中完全没有这个类别的样本。解决写一个类别感知的划分脚本先按图片中包含的类别做分层保证每个类别在train和val中的比例接近8:2。import random from collections import defaultdict def stratify_split(img_list, labels_dir, val_ratio0.2, seed42): random.seed(seed) class_to_images defaultdict(list) for img in img_list: base img.replace(.jpg, ) txt_path os.path.join(labels_dir, base .txt) with open(txt_path) as f: lines f.readlines() classes set() for line in lines: cls_id line.strip().split()[0] classes.add(cls_id) for cls in classes: class_to_images[cls].append(img) val_set set() for cls, imgs in class_to_images.items(): val_count max(1, int(len(imgs) * val_ratio)) val_set.update(random.sample(imgs, val_count)) train_imgs [img for img in img_list if img not in val_set] val_imgs [img for img in img_list if img in val_set] return train_imgs, val_imgs这个脚本的核心是先把每张图片包含哪些类别统计出来再对每个类别分别按比例采样并放入验证集。max(1, int(len(imgs) * val_ratio))保证了即使某个类别只有2张图也会至少有1张进入验证集。缺点是验证集会变大因为多个类别有重叠时同一张图片可能从多个类别中被抽中。这是可接受的代价总比验证集缺失某个类别强。6. 进阶技巧针对红外图像特性的数据增强与检测效果验证6.1 红外图像增强策略红外图像和可见光图像在增强方式上有明显差异。可见光常用的色彩抖动、通道偏移对红外图基本无效因为红外图本质是单通道灰度图。真正有效的是灰度级变换和几何变换的组合。我常用的增强组合是随机改变亮度gamma变换、随机水平翻转、随机旋转±15度、小幅度的随机缩放。对红外数据来说gamma变换模拟的是不同环境温度下的热像差异这个对模型泛化能力提升很大。几何变换需要注意旋转时标注框也要同步旋转YOLOv8的mosaic增强会自动处理标注的同步变换但如果自己做离线增强必须确保增强后的图片和标注框坐标一致。6.2 用混淆矩阵验证类别混淆情况训练完成后不要只看mAP建议把混淆矩阵打印出来看类别间的互混情况。YOLOv8训练完成会在runs/detect/infrared_power/目录下生成confusion_matrix.png直接打开就能看到每个类别的预测情况。重点检查Connection、connection、Conection这三类的互混比例如果三者之间的混淆超过50%说明这三个类别在模型眼里确实无法区分需要合并或重新定义。关于红外图像两点校正如果原始红外图片存在非均匀性噪声表现为固定条纹状图案建议在训练前先做两点校正去除噪声。这个操作需要有原始的暗电流图像和均匀黑体图像数据集本身不提供这两张校正参考图所以只能确认图片是否自带这类噪声。如果肉眼能看到明显的固定条纹可以用单帧去噪算法处理但效果有限最好的方案还是找数据提供方要原始序列帧。另外推理阶段的预处理要和训练保持一致。如果训练时用了CLAIHE增强推理时也要对输入图像做同样的CLAIHE处理否则模型面对的是两种分布完全不同的输入精度会明显下降。在部署代码里把CLAIHE处理封装成一个函数放在推理管线的第一步。我之前做过一个电力巡检项目训练时加了CLAHE部署时忘了同步现场跑出来漏检率翻了一倍后来花了半天排查才发现是预处理不一致。从那以后我每次做红外检测模型都会把训练和推理的预处理代码放在同一个配置文件里管着强制保证两边同步。这篇数据集文章里提到的很多坑都是这种看起来小、影响却很大的问题希望帮到你。本文还有配套的精品资源点击获取