简介:面向电力设备巡检、计算机视觉目标检测研究者的输电线路红外过热检测数据集,针对输电线路运行中红外过热隐患难以快速定位的问题,提供精细标注数据,可直接用于目标检测模型的训练、验证与对比研究,也适合作为高校电力物联网、智能运维相关课程的实验素材。压缩包共2000个文件,以1999个Pascal VOC格式xml标注文件为主,并附1个使用说明txt,整体33.57MB,便于直接接入常见检测框架后按需做格式转换。目前已有120人学习下载。该数据集覆盖多场景下的输电线路红外图像,标注内容包括过热区域位置、温度分布等关键信息,能够支撑模型学习过热特征与故障规律;同时配套说明文档,有助于快速理解数据组织方式,对红外检测算法研究、设备状态评估项目具有实际参考价值。
1. 电力场景输电线路红外过热检测数据集:2253张图能解决什么实际问题
做电力巡检的人都知道,红外热像仪拍输电线路,核心看的是耐张线夹、引流板、接续管这些发热点。以前全靠巡检工人在现场一张一张翻图像,眼睛看花了也容易漏。这个电力场景输电线路红外过热检测数据集VOC+YOLO格式2253张1类别.rar,就是把2253张实地拍摄的红外图像按统一规范整理好,只标注“过热”这一类目标,同时给出VOC的xml和YOLO的txt两套标签,省去自己采集、清洗、标注的漫长过程。适合想做红外视觉检测落地的算法工程师,也适合用YOLOv8、YOLOv5练手单类别检测的学生。拿到手就能直接接进训练管线,不需要从零处理原始热像仪文件。
2. 读懂VOC和YOLO两种格式:2253张图怎么组织、两类标签怎么用
很多第一次拿到这种双格式数据集的人,第一反应是问“我到底该用哪个”。这个问题的答案不是“哪个更好”,而是“你的工具链吃哪个格式”。先把这个数据集内部的布局讲清楚,才能不走弯路。
2.1 VOC格式的目录结构与XML标注字段
解压这个.rar之后,常见的组织方式是VOCdevkit根目录下分Annotations、JPEGImages、ImageSets/Main三个目录。JPEGImages放的是2253张红外图,文件名形如00123.jpg;Annotations里是对应的00123.xml,每个xml文件描述一张图片里所有目标框。
ImageSets/Main下的train.txt、val.txt、test.txt写的是不带后缀的图片名列表,每行一个。训练脚本通过这个列表去JPEGImages和Annotations里取对应的成对文件。这里有个容易看漏的细节:数据集里如果只有train.txt和val.txt,没有单独的test.txt,说明作者把测试集合并进了验证集,你训练时需要在脚本里留出额外拆分,否则最后评估会偏高。
XML的核心字段就几个。<folder>写目录名,<filename>写图片名,<source>和<size>记录图片宽高深度。重点在<object>节点:<name>是类别名,这个数据集只有一个类别,常见写heating或overheat;<bndbox>里是xmin、ymin、xmax、ymax四个整数坐标,表示目标框左上角和右下角。
<annotation> <folder>JPEGImages</folder> <filename>00123.jpg</filename> <size> <width>640</width> <height>512</height> <depth>3</depth> </size> <object> <name>heating</name> <bndbox> <xmin>102</xmin> <ymin>86</ymin> <xmax>158</xmax> <ymax>141</ymax> </bndbox> </object> </annotation>这段XML说明图上有且只有一个发热目标框,坐标都是像素值,没有做过归一化。VOC格式的特点就是人类可读、可视化工具友好,你拿LabelImg重新打开这张图,能直接看到框和类别标签。
2.2 YOLO格式的txt标注与归一化坐标换算
YOLO格式完全不同,它不依赖xml,每张图片对应一个同名的txt文件,放在labels目录里。文件名仍是00123.txt,里面每一行代表一个目标框,格式是class_id x_center y_center width height,注意全部是归一化数值,范围0到1。
class_id从0开始计数,因为这个数据集只有1个类别,所以每行开头固定是0。后面的四个数字是把VOC的像素坐标换算出来的:x_center等于(xmin+xmax)/2/width,y_center等于(ymin+ymax)/2/height,框宽等于(xmax-xmin)/width,框高同理。
0 0.203125 0.221680 0.087500 0.107422用上一节XML里的数值算一遍:x_center是(102+158)/2/640=0.203125,y_center是(86+141)/2/512≈0.22168,宽度是56/640=0.0875,高度是55/512≈0.10742。这个txt不需要保留原始图片尺寸,因为所有数值都是比例,换什么分辨率都通用。
yolo_格式的目录安排,训练工具直接读images和labels两个同级目录。你拿到手的压缩包如果已经按images/train、labels/train分层放好,那就最省事;如果平铺在同一个目录里,需要先按图片文件名把txt对拷到对应子目录。很多训练脚本的data.yaml文件里会写train: path/to/images/train,val: path/to/images/val,它自动去labels目录找同名txt。这个对应关系一旦错位,训练时报错信息又含糊,后面专门说。
2.3 两种格式选哪个:训练目标与工具链决定
不要“哪个熟用哪个”,而要看你的下一步动作。如果你只用Ultralytics YOLO系列训练,直接吃YOLO格式,零转换成本;如果你要做数据增强、用MMDetection、或者用LabelImg重新修改标注,VOC格式更顺。常见做法是把一份数据放在VOC结构里,训练前用脚本批量转成YOLO,这样两头不耽误。
数据增强场景优先选VOC:比如你用imgaug库做随机裁剪,它需要的是像素坐标框;如果只有归一化的txt,裁剪后还得重新归一化,容易算错。反过来,如果你要快速跑通一个YOLOv8的完整训练,用YOLO格式能少踩“标签路径不对”的坑。
选型时还要看显卡显存。YOLO格式配合ultralytics的训练管线,能直接在data.yaml里设cache: True,把整份数据集预加载到内存,减少磁盘IO。VOC格式要先用脚本完成json中间文件转换,多一步就多一个出错点。我的习惯是:如果没有特殊的数据增强需求,一律优先YOLO格式,省心。
3. 用YOLO格式跑通YOLOv8训练:最小命令、参数与验证
拿到这份数据集之后,大多数人真正想做的事就一句话:让模型能识别红外图里的过热区域。这一节给你一条能直接跑的路径,先跑通再调参。
3.1 安装环境与准备数据目录
训练环境建议用Python 3.8到3.10的干净conda环境,GPU显存最低6G,因为红外图像通道单一但尺寸不小。安装ultralytics库,这是目前最常见的YOLOv8训练入口。
conda create -n infrared python=3.9 conda activate infrared pip install ultralytics安装完成后,把数据集解压并按下面的目录结构放好。如果你的压缩包已经分层,直接复制过来即可;如果平铺,就手动创建目录结构。
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容如下,注意names是列表形式,只有一个类别时写['heating'],不要写成字典格式。
path: /home/user/dataset train: images/train val: images/val names: 0: heating这段配置告诉ultralytics三个信息:数据集根目录、训练和验证图片的相对路径、类别名。names列表的索引就是txt里每行的第一个数字。类别名写错不会报错,但最后预测结果里的名字是错的,回头检查时容易懵。
3.2 训练参数设置:imgsz、epochs、batch、workers
直接跑第一版训练,推荐把显存和速度控制在合理范围。下面的命令适合一张12G或24G显卡。
yolo detect train \ data=/home/user/dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ workers=4 \ device=0 \ cache=True这里每个参数都有实际影响。model=yolov8n.pt是预训练权重,虽然红外图和自然图像差距大,但预训练带来的底层特征迁移仍然能缩短收敛时间。imgsz=640是输入分辨率,红外热像仪原始分辨率常见640x512,缩到640既能保住小目标细节,又不至于撑爆显存。batch=16在12G显存下偏稳,如果显存32G以上可以加到32。cache=True把图片提前加载进内存,2253张图全量缓存大约占用内存1.5G左右,完全可接受。
epochs=100是一个相对安全的起点。单类别目标检测收敛速度通常比多类别快,50轮左右损失就开始平稳,100轮足够看完整曲线。如果时间紧,可以先跑50轮,观察val/box_loss曲线,若还在下降就续跑。
训练开始后,终端会输出每轮的box_loss、cls_loss、dfl_loss、precision、recall和mAP50。你的第一关注点是mAP50有没有快速超过0.8。红外过热目标通常比较小,如果前20轮mAP50还在0.5以下,说明anchor或输入尺寸需要调整,这个放到避坑章节细说。
3.3 验证结果:混淆矩阵、热力图与预测输出
训练完的模型以best.pt保存在runs/detect/train/weights目录下。用下面的命令在验证集上跑推理,生成可视化结果:
yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=/home/user/dataset/images/val \ conf=0.25 \ save_txt=True \ save_conf=Trueconf=0.25是置信度阈值,低于这个值的框会被过滤。红外图像的对比度低,背景温度梯度也大,建议先保持0.25,再看结果决定要不要调到0.3或0.2。save_txt=True会生成预测的标签文件,直接用脚本和真实标签做比对,能算出每张图的漏检和误检。
验证集上,Ultralytics会自动生成confusion_matrix.png。单类别模型里,这个混淆矩阵是2x2,除了对角线之外,最需要看的是background被预测为目标的比例,那代表误检有多严重。红外场景里,绝缘子、金具在白天阳光照射下也可能发热,模型容易把边缘温度高的区域框出来,这是正常现象,需要靠后续的min_iou或回归阈值去抑制。
4. VOC格式反向使用与格式互转:一份脚本解决标注迁移
拿到这份数据集,你可能会遇上一种尴尬:数据集的VOC部分想用在自己的老项目里,但老项目只认自己的中间格式。或者你想把YOLO txt转回VOC来编辑标注,发现坐标归一化后图像变了位置。最省事的办法是写一个小脚本,在两种格式之间按需互转。
4.1 为什么还要用VOC:标注工具与数据增强兼容性
YOLO格式虽然训练方便,但手工校正标注很痛苦。你在LabelImg里打开一张图,想调整一个框,不可能直接看txt的0.87 0.45这种数字去心算像素坐标。这时候VOC的像素框有绝对优势:打开xml,看到的就是实际像素坐标,想改就改。
另外,很多经典数据增强管道基于VOC设计。比如用albumentations的BboxParams进行随机平移、旋转时,需要输入像素坐标。你如果手里只有YOLO txt,就得先恢复出xmin、ymin、xmax、ymax,做增强后再重新归一化。跳过这一步直接用归一化坐标做裁剪,通常会出现框偏移半张图的问题。
还有一点是模型集成。你要用MMDetection系列工具时,它虽然支持CocoDataset,但如果你从零准备数据,VOC格式的voc2coco脚本是最成熟的部分。与其在中间格式上折腾,不如直接保留一份VOC原始数据。
4.2 Python脚本:VOC转YOLO
下面这个脚本是VOC转YOLO的标准实现,我处理电力红外数据时一直用它。输入是VOC的Annotations目录,输出是YOLO的labels目录,图片尺寸从xml的size节点读,不依赖文件名。
import os import xml.etree.ElementTree as ET voc_annotations = "VOCdevkit/Annotations" yolo_labels = "VOCdevkit/labels" class_map = {"heating": 0} os.makedirs(yolo_labels, exist_ok=True) for xml_file in os.listdir(voc_annotations): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(voc_annotations, xml_file)) root = tree.getroot() width = int(root.find("size/width").text) height = int(root.find("size/height").text) txt_lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_map: continue box = obj.find("bndbox") xmin = int(box.find("xmin").text) ymin = int(box.find("ymin").text) xmax = int(box.find("xmax").text) ymax = int(box.find("ymax").text) x_center = (xmin + xmax) / 2.0 / width y_center = (ymin + ymax) / 2.0 / height w = (xmax - xmin) / width h = (ymax - ymin) / height txt_lines.append(f"{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") txt_name = xml_file.replace(".xml", ".txt") with open(os.path.join(yolo_labels, txt_name), "w") as f: f.write("\n".join(txt_lines))逻辑说明:先解析xml,拿到图片真实宽高,再遍历所有object节点。每个目标框先把像素坐标算出中心点和宽高,再除以图片宽高得到归一化值。输出写入同名txt,.6f保留六位小数,足够YOLO训练使用。
注意这里的class_map里类别名和数据集里的<name>字段必须完全一致,大小写或空格不同都会导致这一类的目标被丢弃。脚本里continue的逻辑是跳过未知类别,如果你只想保留某一类,把这个映射表改一下就能过滤。反过来转YOLO到VOC,关键是用图片真实宽高把归一化坐标放大回像素值,再去构造xml的bndbox节点,完成后必须检查输出坐标有没有越界,因为有些txt里的框可能超出图像边缘。
4.3 脚本中容易翻车的路径与类别映射细节
路径问题最常见。脚本里的voc_annotations和yolo_labels如果是相对路径,建议在脚本开头打印一次os.getcwd()确认当前工作目录。我实际测试时,在项目根目录运行一次能正常转,第二次换到子目录运行就提示找不到xml,最后定位是相对路径写错了。
类别映射的坑到第二轮才暴露。第一版脚本里我把class_map写成{"heating": 0, "normal": 1},但数据集只有一个类别,结果发现xml里有个<name>写成了Heating,首字母大写,脚本把它当成未知类别直接跳过,那一张图的标签文件内容为空。YOLO训练时空标签文件不会报错,但会少一个正样本,积累多了模型就退化成“什么框都不出”。
所以转格式后一定要做统计校验:数一下生成的txt文件数量是否等于xml数量,再随机抽查几个txt里是否有非零坐标值。在电力红外数据里,一张图往往只有一个目标框,如果某个txt文件里只有一行0 0 0 0 0,说明源xml的bndbox里出现了全零坐标,这种脏数据训练时会持续影响损失,需要提前清洗。
5. 训练红外过热检测的常见坑:3个真实踩坑记录
这一份数据集看着简单,单类别、标准化格式,但实际训练中翻车的地方不少。我把常见的三类问题按现象、原因、解决写出来,你不一定全部遇到,遇到一个就能少查半小时资料。
5.1 标签类别写错导致训练直接报错
现象:训练脚本启动后,刚跑完第一轮迭代就在计算loss时报错,提示index 1 is out of bounds for axis 0 with size 1,或者干脆报Label shape mismatch。
原因:txt标注文件里出现了1这个类别索引,但data.yaml里的names只定义了一个类别。也就是说,数据集的某个或某几个txt文件里写的是别的类别编号,可能是作者整理时混入了旧标签,也可能是你在转换脚本里定义class_map时把映射关系写反了。
解决:写一个Python脚本遍历所有labels文件,打印出现过的所有类别索引,并统计每个索引的框数量。如果发现索引大于len(names)-1,直接用脚本把这行的索引改回0,并检查对应xml确认是不是标错类别。在电力红外场景里,通常只关心过热这一种异常,其他类别一律改为0或删除该框。
import os label_dir = "dataset/labels/train" bad_files = [] for f in os.listdir(label_dir): path = os.path.join(label_dir, f) with open(path) as fp: for line in fp: parts = line.strip().split() cls_id = int(parts[0]) if cls_id != 0: bad_files.append((f, cls_id)) print(bad_files[:10])这个脚本只有二十行,却在多次训练前帮我发现了问题。跑完这一遍,再回data.yaml里确认names只有一个值,训练立刻恢复正常。
5.2 红外图像小目标漏检:anchor与imgsz的坑
现象:训练曲线正常,mAP50在验证集上有0.85,但实际拿去测试几张红外原图,很多明显的发热点没框出来,或者框特别大,把整个绝缘子串都圈进去了。
原因:红外图像里过热区域往往是一个小块,比如耐张线夹的发热区可能只占整张图的2%到5%。YOLOv8默认的anchor尺寸偏大,对这类小目标不敏感。另外imgsz=640虽然和原始分辨率匹配,但如果原始图片本身是800x600,训练时被压缩到640,小目标边缘信息丢失很多。
解决:先把imgsz提高到原始分辨率,比如imgsz=800或1024;其次在data.yaml旁边加一个anchors配置,或者直接打开模型的yaml文件,把小anchor尺寸修改为[4, 6, 8]这一类更小的值。如果不熟悉anchor调参,最省力的办法是改用yolov8s或yolov8m,更大的模型对小目标的特征提取更充分。
这里还有一层玄学:红外图像是单通道温度映射,和自然图像的RGB特征分布差异很大。你用预训练权重的时候,前几层卷积的统计量是按自然图像算的,小目标特征会被背景温度梯度淹没。所以不要省这一步,训练之前先把图像数据统计出来,看一下目标框面积占全图面积的比例分布,如果中位数低于0.03,imgsz调高是必须的。
5.3 过拟合与类别不平衡:2253张单类别的边界
现象:训练到第60轮开始,训练集loss持续下降,但验证集loss先降后升,val/mAP50在0.8附近波动,不再上涨。最终模型在训练集上表现完美,在验证集上却有漏检。
原因:2253张图对于单类别检测来说并不算多,尤其当场景高度相似时——同一个变电站、同一批杆塔角度、相近的温度色标,模型很容易背下训练集的纹理,而不是学到“过热区域”的本质特征。单类别的另一面是负样本很难定义,没有“常温”类,模型对“什么都不框”这件事天生不敏感。
解决:首先在训练命令里加正则和增强参数,hsv_h=0.01、hsv_s=0.5、hsv_v=0.4、degrees=5、translate=0.05,这些增强能从亮度、轻微旋转上扩充数据。其次把训练轮数从100降到60,用早停法在patience=15时停止,避免后期单纯拟合噪声。最后,如果验证集持续不涨,去检查训练集和验证集是否来自同一批数据,很多数据集作者按文件名随机划分,但同一个杆塔的连续帧会同时出现在两边,这会导致验证结果虚高,解决方法是按场景或拍摄条带分组划分。
6. 让模型更抗造:验证集划分、模型导出与实拍部署前检查
训练好模型只是第一步,能不能用起来,取决于你把验证和部署环节做得够不够细。这一步不需要大改代码,但每次做都能避免现场翻车。
先做一次严格的划分检查。用脚本按图片文件名里的拍摄位置或时间戳分组,确保同一个过热点的连续帧不会同时出现在训练和验证里。下面的代码按文件名前六位分组,把同一组的图片放进同一侧。
import os from collections import defaultdict images = os.listdir("dataset/images") groups = defaultdict(list) for img in images: groups[img[:6]].append(img) train_imgs, val_imgs = [], [] for _, imgs in groups.items(): if len(imgs) == 1: train_imgs.append(imgs[0]) else: split = int(len(imgs) * 0.8) train_imgs += imgs[:split] val_imgs += imgs[split:]然后导出ONNX。用yolo export,指定format=onnx,这一步会把Pytorch权重转成通用格式,方便在Jetson、OpenVINO或自己的C++程序里跑。导出后我用Netron看一遍输入输出节点,确认输入尺寸和训练时一致,避免部署时把640的图塞进一个要求1920输入的模型。
最后看loss曲线。训练结束后在runs/detect/train目录下打开results.png,主要看val/box_loss和val/cls_loss是否在最后10轮保持平稳。如果仍有明显的下降趋势,说明欠拟合,可以加载last.pt续跑;如果验证损失一路上扬,回到上一章去检查过拟合。我的习惯是每跑完一轮实验,把results.png按参数名存一份,两周后翻出来对比,比自己记住的靠谱得多。
红外检测这类项目,最大的教训就是别迷信高mAP数字,现场照片和你训练集里的色标可能就差几度,模型立刻变笨。所以拿到任何数据集,第一件事永远是抽10张图人工标注,和预测结果做并排比对,眼见为实。希望帮到你。
本文还有配套的精品资源,点击获取