简介面向光伏发电板红外巡检与目标检测场景这份数据集收录了173张真实红外图像并针对鸟粪这一典型遮挡物提供了完整标注。图像统一为jpg格式配套Pascal VOC标准的xml标注文件和YOLO标准的txt标注文件可直接用于Faster R-CNN、YOLO系列等主流检测模型的训练与评估省去自行采集数据和标注的环节也可作为VOC与YOLO格式对照学习的样例。数据集中仅设guano一个类别共574个矩形框全部统一使用labelImg工具绘制标注边界清晰、格式规范便于开展算法对比和迁移学习。压缩包采用7z压缩共521个文件含173张jpg图片、173个xml标注、173个txt标注等整体大小仅7.92MB下载和存储都很轻量。目前已有446人学习使用适合光伏运维智能化、计算机视觉方向的研究者与学生用于遮挡目标检测、数据格式转换及标注质量分析等实践。1. 光伏板红外图像鸟粪检测173张图能训练出一个能用的模型吗光伏电站巡检拍到一张红外图组件上有几块鸟粪温度异常区域看着刺眼但人眼盯屏幕找太慢想用目标检测模型自动找。手头这份数据集只有173张图、1个类别还是VOC和YOLO双格式的压缩包第一反应往往是“这么点数据能训出什么”。实际做过就知道红外场景下能用、敢用的模型不一定需要几千张图173张认真处理的小样本足够跑通完整流程关键在格式转换不出错、训练策略不踩坑。这篇文章就把这套数据集从解压到训练到验证的完整路径拆开讲适合手上有一批电力红外图像、正准备用YOLO起步的从业者。2. VOC与YOLO双格式拆解173张图的标注信息到底长什么样拿到.7z压缩包第一步不是急着训练而是先把两种格式的目录结构和标注内容摸清楚。VOC和YOLO是目标检测领域最常用的两种标注格式前者是XML描述边界框后者是纯文本归一化坐标同一份数据同时给两种格式等于把标注信息重复存放了一份方便切换不同的训练框架。2.1 VOC格式的目录与XML字段原始标注长什么样解压后常见做法是看到Annotations、JPEGImages、ImageSets三个目录这是VOC系列数据集沿袭下来的目录约定。JPEGImages放红外图像Annotations放同名XML标注文件ImageSets/Main下的txt文件记录哪些图划分到train、val或test。打开一个XML文件重点看这几个字段annotation filenameIR_0001.jpg/filename size width640/width height512/height depth3/depth /size object namebird_dropping/name bndbox xmin120/xmin ymin88/ymin xmax156/xmax ymax124/ymax /bndbox /object /annotation这里要注意size里的depth字段。很多红外数据集虽然是灰度图但保存成JPG时被转成了三通道depth写3也正常。后面转YOLO格式时要以实际读图结果为准不能只看XML头。bndbox四个值是像素坐标xmin和ymin是左上角xmax和ymax是右下角。一个XML里可能有多个object节点对应多块鸟粪区域。做这个数据集标注时通常会遵循“只要遮挡引起的温度异常区域都框”的原则所以边界框之间允许有重叠训练时模型也能学到多目标同时出现的场景。2.2 YOLO格式的txt与类别映射转换后每一行代表什么YOLO格式的标签文件与图像同名扩展名换成.txt每行描述一个目标字段依次是class_id x_center y_center width height。注意后四个值全部做了归一化除以图像的宽和高所以取值范围在0到1之间。类别只有1个class_id固定为0navmapping文件里通常写0: bird_droppingYOLO格式之所以流行是训练时直接按行读取归一化坐标不用每次去解析XMLIO效率高很多。但代价是如果原图尺寸不固定归一化之后的小目标坐标很容易丢精度这一点在4.4节再展开。有几点容易忽略VOC标注越界的框边界坐标超出图像宽高在YOLO格式里必须裁剪否则训练会报错反向转换时还要从归一化坐标乘以原图宽高取整得到像素框。2.3 VOC转YOLO的最小脚本拷贝即用的转换与检查我一般会在项目里放一个voc2yolo.py一次性完成XML解析、坐标归一化、txt写盘和空标注检查。为了不依赖第三方库只用了Python标准库xml.etree.ElementTree在任何装了Python的环境里都能跑。import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, output_dir, class_map): tree ET.parse(xml_path) root tree.getroot() # 读取图像实际尺寸来自XML的size节点 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_map: continue # 只保留需要的类别 box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 坐标越界裁剪防止负数或超出宽高 xmin max(0.0, min(xmin, img_w)) xmax max(0.0, min(xmax, img_w)) ymin max(0.0, min(ymin, img_h)) ymax max(0.0, min(ymax, img_h)) # 防止标注退化成一个点 if xmax - xmin 1 or ymax - ymin 1: continue 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_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) output_path Path(output_dir) / (Path(xml_path).stem .txt) output_path.write_text(\n.join(lines), encodingutf-8) return len(lines)调用时只需要准备好class_map和目录class_map {bird_dropping: 0} xml_dir Path(Annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) empty_count 0 for xml_file in xml_dir.glob(*.xml): count voc_to_yolo(xml_file, out_dir, class_map) if count 0: empty_count 1 print(f警告: {xml_file.stem} 没有有效目标) print(f转换完成共处理 {len(list(xml_dir.glob(*.xml)))} 个xml空标注 {empty_count} 个)这段脚本的逻辑不复杂但有三个参数值得说明。class_map决定类别ID目前只有1类写死{bird_dropping: 0}即可坐标裁剪能避免训练时报“bbox坐标越界”的错保留6位小数是给后续训练精度留余量。转换完一定要跑一遍空标注统计因为173张图里大概率存在没有鸟粪的负样本图负样本在目标检测里同样重要不能因为空txt而把它删掉。3. 用YOLO在本地训练鸟粪检测模型环境、命令与参数设置格式转换之后最焦虑的一步就是“跑通训练”。其实YOLO的训练流程已经被工具链封装得很简单常见做法是选YOLOv8或YOLOv5系列以YOLOv8为例整个流程只需要三步建环境、写data.yaml、敲一条train命令。3.1 环境准备在Anaconda里装好训练依赖用Anaconda建独立环境是避免依赖冲突最省心的方式。我习惯建一个Python 3.10的环境然后安装ultralytics包它会把训练、验证、导出一次打包好。conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics如果机器有NVIDIA显卡先确认PyTorch是否拿到CUDA版本。pip install ultralytics默认会装一个CPU或通用版PyTorch训练速度差别很大。检查方式是在Python里执行import torch print(torch.cuda.is_available())输出True再继续否则去PyTorch官网选对应CUDA版本的安装命令。这个环境配置要求不算高1080Ti级别的显卡训173张图绰绰有余只有CPU也能跑只是慢一些。3.2 整理目录与生成data.yaml训练前的最后一步训练前要把数据组织成YOLO约定的目录结构并准备一个data.yaml描述路径和类别信息。目录结构一般是datasets/ bird_dropping/ images/ train/ val/ labels/ train/ val/ data.yaml划分比例建议按9:1来做173张图里约156张训练、17张验证。因为只有1个类别且目标稀疏不要按5:5切验证集太少会让指标波动很大这一点第4章还会展开。data.yaml内容如下path: datasets/bird_dropping train: images/train val: images/val names: 0: bird_dropping这里path建议写绝对路径避免YOLO在相对路径解析上出幺蛾子。类别顺序必须与class_map一致不能改。names是字典形式YOLOv8要求写一个list或dict如果只写names: [bird_dropping]也能跑但配合key写更清晰。3.3 启动训练命令与参数说明核心训练命令如下yolo detect train \ datadatasets/bird_dropping/data.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch16 \ patience30 \ optimizerAdamW \ seed42 \ projectruns/detect \ namebird_dropping_trial参数说明写在下面这张表里。参数推荐值作用与说明modelyolov8n.pt预训练权重小样本迁移学习的起点n是轻量版训练快、显存低epochs200配合patience早停实际跑不到200轮imgsz640与红外原图分辨率一致最好但显存紧张可降到512batch16受显存限制单卡16较稳小于8时注意BN崩溃风险patience30验证集指标连续30轮不提升就停止optimizerAdamW小数据集上比SGD收敛更稳不容易出现loss震荡seed42固定随机种子保证结果可复现modelyolov8n.pt会第一次运行时自动下载预训练权重这是正常的下载的是COCO预训练权重迁移到红外场景能明显提升早期收敛速度。如果网络受限也可以先手动下载权重文件放到项目目录再把model参数改成权重文件的路径。3.4 训练输出物哪些文件值得看哪些不用管训练结束后runs/detect/bird_dropping_trial目录下会生成一堆文件。值得关注的是weights/best.pt验证集指标最优的权重后续推理、部署都用它。weights/last.pt最后一轮权重有时best过拟合而last泛化更好可以两个都试。confusion_matrix.png预测类别与真实类别的混淆矩阵。results.png训练损失、验证mAP、precision、recall随epoch变化的曲线。val_batch_pred.jpg验证集预测可视化快速判断模型效果。剩余一堆labels_*.jpg、results.csv之类的文件属于过程产物初次跑通时不用每个都研究先看val_batch_pred.jpg和results.png有没有异常再决定是否调参。4. 红外图像鸟粪检测避坑五条必须提前知道的血泪经验小样本红外检测的坑比常规可见光目标检测多得多。这五条经验来自实测中使用这份数据集或类似电力红外数据集的常见问题每一条都按“现象、原因、解决”写清楚。4.1 黑白红外图被当成三通道数据增强全部失效现象是训练时results.png里训练损失能正常下降但验证mAP始终在低位徘徊而且跑完看val_batch_pred.jpg发现检测框和图像内容对不上。根源在于红外图本质是单通道灰度保存成JPG时如果被强行转成三通道三个通道内容完全相同模型等于在看冗余输入而YOLO默认的随机颜色扰动、HSV增强会把这种“伪三通道”搅得面目全非。解决方法是训练前统一图像通道策略。如果原图是灰度就保持单通道读取并在推理时也走同样的预处理。用OpenCV读取时用cv2.imread(path, cv2.IMREAD_GRAYSCALE)再扩展成三通道复制避免颜色增强破坏温度信息。这个处理最好写成一个预处理脚本把173张图全部过一遍确认像素值分布正常后再进训练。4.2 盲元被学习成鸟粪先做盲元检测与替换现象是模型在干净的无鸟粪光伏板上频繁输出假阳性而且这些假阳性位置在每张图里几乎相同。原因是红外探测器存在盲元表现为固定位置的亮点或暗点人眼能分辨但模型会把它们当作小目标学进去。尤其鸟粪区域在红外下也是亮点盲元和鸟粪的灰度特征高度相似。解决思路是做盲元检测与替换。如果机芯支持两点校正在采集端先跑校正流程生成两点校正参数后再出图这是最理想的。如果数据集已经形成无法回溯采集端就在预处理阶段记录全图像素值长期不变的固定点对每个盲元用周围3×3邻域的中值替换。这个操作不影响鸟粪标注框又能显著降误检。4.3 batch太小导致BN崩溃loss变成nan现象是训练到第几个epoch时loss突然跳到nan之后指标全部归零怎么调学习率都没用。原因是173张图本身数据量小如果batch设成4或更小BatchNorm层的均值方差统计量在少量样本上极不稳定出现数值溢出。解决方法是把batch至少开到16如果显存放不下就用梯度累积YOLOv8里没有直接暴露累积参数但可以通过batch8加上更大的imgsz间接模拟。另一个更稳妥的思路是前20轮冻结骨干网络只训练检测头。预训练权重已经在COCO上学到了基础特征冻结骨干相当于用小样本只学“鸟粪长什么样”的差异部分能大幅减少BN统计量的漂移。4.4 小目标归一化坐标精度不足标签退化现象是训练结束后用val_batch_pred.jpg看小目标检测结果发现预测框总比真实框偏几个像素尤其针对远处光伏板上很小的鸟粪区域。原因是YOLO标签用归一化坐标保存一个4×4像素的鸟粪在640×512图像里宽度只占0.006txt里保留6位小数已经接近精度上限反复训练迭代后边界更容易偏移。解决方式有两条路。一是提高输入分辨率把imgsz从640调到1024再看效果二是在不牺牲分辨率的前提下对红外大图做切图训练把每张图切成带重叠的图块相当于变相放大目标尺度。后一种做法会在第6章展开对这张只有173张的小数据集提升明显。4.5 验证集不足20张指标飘得离谱现象是每次训练完重新划分验证集mAP50在0.3到0.85之间反复横跳无法判断模型到底有没有进步。原因不复杂173张里切出17张验证验证集里有没有难样本直接决定了指标高低样本量决定了方差。解决方法是做K折交叉验证。把173张图随机分成5份每份约35张训练5次每次换一份做验证最后取mAP的均值和标准差。虽然训练时间变成原来的5倍但得到的指标才真正接近模型能力。另外一个折中办法是固定随机种子并锁死划分文件保证每次对比实验用同一批验证集至少能看到相对变化。5. 模型验收与置信度门限单类别模型怎么看懂验证结果训练跑完后不能只看一个mAP数字就收工。单类别检测的指标解读和可见光多类别场景不太一样重点在于确认模型到底漏了多少鸟粪、误报集中在那些区域。5.1 单类别模型优先看recall和mAP50对鸟粪检测来说漏检的代价远高于误检。漏检意味着热斑继续发展可能损伤光伏板误检最多让巡检人员多看一眼。所以评估时要优先看recall召回率和mAP50mAP50-95反而不是最关键的因为鸟粪边界本来就不清晰标注框在红外图上存在较大主观性追求高IoU下的精度意义不大。YOLOv8训练结束会打印指标表直接看all类那一行R是recallmAP50是IoU阈值0.5下的平均精度。如果recall低于0.7说明模型漏掉了一批鸟粪不要急着加数据先查看漏检样本是目标尺寸太小还是对比度太低。5.2 混淆矩阵读出误检来源总合不唯一不奇怪训练目录下confusion_matrix.png看起来直观但很多人对着图发蒙矩阵数字加起来对不上。原因在于混淆矩阵默认按归一化计数负样本背景被模型预测成目标的样本数量来自整张图统计口径和正样本不同总合不唯一是正常的不用怀疑代码写错了。对单类别模型重点看两个格子bird_dropping这一行里被分成background的比例代表漏检率background这一行里被预测成bird_dropping的比例代表误检率。如果误检率偏高去val_batch_pred.jpg里看误检位置如果集中在光伏板边框或组件缝隙就要考虑增加负样本或调整温度对比度归一化。5.3 推理时调整置信度门限从默认0.25到0.1训练权重导出后推理默认置信度门限是0.25。这个门限对常规可见光目标合适但对红外鸟粪检测偏保守。鸟粪区域红外特征有时很弱置信度算出来只有0.1到0.2但真实存在。直接把门限从0.25降到0.1能明显提升召回代价是误检增加。实际操作中先跑一遍验证集的验证命令把不同置信度门限下的precision和recall打印出来选一个recall不低于0.85、误检可接受的门限值。YOLO验证命令支持传入conf参数yolo detect val \ modelruns/detect/bird_dropping_trial/weights/best.pt \ datadatasets/bird_dropping/data.yaml \ conf0.1观察输出指标里precision和recall的变化如果precision掉到0.5以下说明误报太多需要配合后处理过滤而不是继续降门限。6. 进阶把单帧检测扩展成场站巡检的自动化流程单帧检测模型只是起点真实场站巡检里更多是视频流或大图巡检。把这条链路串起来比继续刷模型指标更有工程价值。6.1 用切图推理拾回小目标光伏板红外图通常分辨率不低鸟粪目标只有几十个像素直接塞进模型等于把目标缩成几个像素。常见做法用SAHI库做切图推理把大图切成带重叠的图块每个图块单独检测再把结果合并成原图坐标小目标召回率能提升一个档次。pip install sahi切图推理的切片尺寸、重叠率需要根据目标尺度来调对鸟粪这种小目标切片256到512、重叠0.2是合理的起点。合并时如果同一个目标被多个切块检出用NMS按IoU过滤输出的是原图坐标框。6.2 多帧时序确认降低误报单张图的误报难以完全消除但相机连续拍摄时真实鸟粪位置稳定盲元位置固定而随机噪声产生的误检帧间不连续。常见实践是连续3帧都在同一区域检出目标才触发告警这会去掉一大半随机误报。帧间做简单的框匹配中心点距离小于目标框宽度就视为同一目标。6.3 把温度信息作为后处理过滤条件红外图像如果同时带温度矩阵可以在检测框内统计最高温度与周边平均温度差。鸟粪遮挡区域与正常光伏板的温差通常有几度而误检区域温差往往不明显。引入温差阈值做二次判断比单纯堆模型参数更稳定。我自己养成的一个习惯是每次跑完一个新版本先对着验证集的每张图看一遍误报和漏检把检错的位置截下来贴进台账连续看两周才会放心让模型参与巡检。模型迭代到后期瓶颈往往不在算法而在对红外设备特性和现场环境的理解。希望帮到你。本文还有配套的精品资源点击获取
