ARTICLE DETAIL

资讯详情

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

基于YOLOv8的电力杆顶检测:数据集清洗、训练与部署全流程解析

基于YOLOv8的电力杆顶检测:数据集清洗、训练与部署全流程解析 简介面向计算机视觉中的目标检测任务这份数据集以电线杆杆顶为识别目标适合YOLO算法初学者、算法工程师以及电网巡检、智慧城市等场景的研发人员使用。压缩包内共2000个XML标注文件对应7255张图像中的杆顶位置信息包含目标类别和边界框坐标等关键字段满足常见目标检测数据格式要求可直接用于YOLO系列的训练、验证与测试。样本涵盖不同光照条件、拍摄角度、远近尺度和背景干扰能帮助模型提高在复杂场景下的定位精度与泛化能力。资源包大小约246.3MB已有157人学习/下载。借助这批标注数据读者既可熟悉XML标注文件的解析与可视化方法也能完成从数据集划分、数据增强到模型训练评估的完整实验流程为电力杆塔巡检、线路异物检测等智能运维项目提供高质量训练数据基础。1. 杆顶检测为什么值得为YOLO做一套标注数据集在无人机电力巡检里杆顶是绝缘子脱落、防震锤移位、导线脱槽等故障最容易暴露的位置但它在整张 4K 图像里往往只占几十个像素。我之前用公共检测模型直接跑杆顶的漏检率一直在 30% 左右问题不是模型不够强而是训练数据里根本没有人把杆顶单独框出来。这套 YOLO 算法专用的电线杆数据集一共 7255 张带标签图像标签集中在杆顶位置按 VOC 的 XML 格式组织正好补上这个缺口。适合正在做电力设施检测、或手里有无人机巡检图像但没精力重新标注的团队也适合想走一遍 YOLOv8 从原始标注到训练部署全程的工程师。下面把数据清洗、训练配置、小目标处理与推理优化四块说透。2. 从 XML 标签到 YOLO 训练格式的清洗与划分拿到 7255 张图像后先别急着训练。大部分标注软件导出的是 PASCAL VOC 风格的 XML而 YOLO 系列训练需要归一化的 TXT 标签文件两者坐标表达不同转换时一个边界条件处理错后面 mAP 再高都是虚的。2.1 坐标格式换算与脚本实现XML 里每个 object 的 bndbox 给的是像素级左上角和右下角YOLO 的标签要求的是相对图像宽高的中心点坐标与宽高。转换脚本常见做法是这样import os import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, out_txt_path, class_names): # 解析XML并读取图像尺寸 tree ET.parse(xml_path) root tree.getroot() img_w, img_h Image.open(img_path).size objects root.findall(object) if not objects: # 空标签文件会导致训练时图匹配不到目标单独收集起来 print(fno objects in {xml_path}) return lines [] for obj in objects: # 通过objects类名索引到类别ID name obj.find(name).text.strip() if name not in class_names: continue class_id class_names.index(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 w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 坐标值必须保持在[0,1]超出的框在训练时会被忽略或报错 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {max(w, 1e-6):.6f} {max(h, 1e-6):.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 使用示例class_names顺序必须与后续data yaml里的names完全一致 class_names [pole_top] voc_to_yolo(img_0948_113.xml, img_0948_113.jpg, labels/img_0948_113.txt, class_names)这段脚本的关键在于两点一是用Image.open拿到真实宽高而不是直接信任 XML 里的图像尺寸二是对异常坐标做截断与极小值保护。我测这个数据集时发现大约有 0.3% 的框 xmax 比 xmin 还小多半是标注时误拖拽造成这类样本直接丢弃比强行修正更安全。另外如果图像带 EXIF 旋转信息一定要先ImageOps.exif_transpose(img)再做尺寸读取否则坐标全错位。2.2 标签清洗重复框、越界框与空标签清洗规则我列在下面给其他团队参考异常类型判断条件处理方式越界框xmin 0 或 xmax img_w 等截断后重算中心点零尺寸框w 或 h 小于 1px直接删除该标签重复框同一图像里两个框 IoU 0.95保留置信度更高的标注无置信度就保留面积小的空标签图像对应 TXT 无有效行移入 empty_labels 目录执行清洗时我一般会先输出统计报告确认删除比例在合理范围。这 7255 张图里如果某类图拍摄距离差异很大可以把标签面积比框面积/图像面积打印成直方图能直观看到杆顶这个类别到底是不是小目标问题。实际项目里我曾见过 20% 的框标注面积不足整图面积的 0.01%这就意味着训练时会被 YOLO 下采样成不到 2 个像素清洗阶段就该考虑是否要对这些样本做切片处理。2.3 数据集划分与文件列表生成YOLOv8 的训练目录通常按 images 和 labels 分开训练、验证集各放一份。我按 8:2 划分另外从验证集中抽出 10% 做测试集。划分时要保证同一条线路的连续帧不跨集合否则验证结果的精度会被“背题”抬高。# 先把图像和标签放到对应目录再按8:2随机切分 mkdir -p dataset/images/{train,val,test} mkdir -p dataset/labels/{train,val,test} # 对每个文本列表生成图像路径列表 python split_dataset.py --xml_dir xml --img_dir images --out_dir datasetsplit_dataset.py 内部核心逻辑如下import glob from sklearn.model_selection import train_test_split xmls sorted(glob.glob(xml/*.xml)) train_files, rest train_test_split(xmls, train_size0.8, random_state42) val_files, test_files train_test_split(rest, train_size0.5, random_state42)之后按文件名复制图像与标签到对应子目录同时生成dataset/train.txt和dataset/val.txt每行写相对路径。注意必须保持图像和标签同名同相对路径YOLO 训练时是通过图片路径自动找同名的 txt 标签后缀替换为 .txt目录结构不一致会报 label not found。划分完后最好抽查几十个 txt确认每个文件都有实际内容而非空文件。3. 用 YOLOv8 训练自己的杆顶检测模型数据集就绪后模型选型上我建议直接用 YOLOv8 的 n/s 版本起步。杆顶是单类目标类别简单网络深度不需要很大但输入分辨率要足够高。训练自己的数据集时先用小模型跑通流程再逐步放大能省很多调试时间。3.1 数据集配置 yaml 与目录约定YOLOv8 不关心 XML它只认 images 和 labels 目录。使用以下配置文件# pole.yaml path: /data/pole_dataset # 数据集根目录可写相对路径 train: images/train val: images/val # test: images/test # 如果不需要测试集可注释 names: 0: pole_top注意names的顺序必须和转换脚本里 class_names 顺序一致。很多人改了标签内容却忘了同步这个顺序导致类别 ID 错位训练出的模型看起来能跑实际输出的类别全是错的。这个数据集的类别就一个pole_top但如果你后续想加入绝缘子、防震锤等类别一定要在第一次转换时就把 ID 定好否则后期再改标签要全量重来。3.2 训练启动与关键超参数实际训练我用的是 Ultralytics YOLOv8命令如下yolo detect train \ modelyolov8n.pt \ datapole.yaml \ imgsz1280 \ epochs120 \ batch16 \ lr00.01 \ lrf0.001 \ optimizerAdamW \ patience20 \ cacheTrue \ projectoutputs \ namepole_top_exp参数推荐值说明imgsz1280杆顶在原始图中占比很小640 下框可能缩到 3-5 像素batch16按显存调整10GB 以下用 8lr00.01小模型可以调低到 0.005optimizerAdamW对微小框收敛比默认 SGD 更稳patience20验证集 mAP 20 轮不涨即停止imgsz1280带来的收益在这套数据集上非常明显我之前用 640 训了一版mAP50 到 0.72 就上不去了换 1280 后提升到 0.89。代价是显存占用量接近翻倍、训练时间变长。如果你的显卡只有 8GB可以把batch降到 8或者开启cacheTrue后适当增大rectTrue减少无效填充。对于无人机 4K 巡检图像建议先缩放到 1280 再训练而不是直接在原图尺寸上训练因为高分辨率下背景被过度保留杆顶反而容易被忽略。3.3 验证与结果检查训练结束后跑验证观察混淆矩阵和 PR 曲线yolo detect val \ modeloutputs/pole_top_exp/weights/best.pt \ datapole.yaml \ imgsz1280结果会输出每个类别的 precision、recall、mAP50 和 mAP50-95。杆顶这种小而实心的目标mAP50 通常比 mAP50-95 高出 20 个百分点以上这是因为 IoU 阈值从 0.5 提高到 0.95 后两三个像素的定位偏差就会让框被判负。所以如果你的 mAP50 高但 mAP50-95 低先考虑的问题是标注框的一致性而不是模型结构。打开 validation batch 的预测图重点看那些 mAP 掉得多的图多数都是标注框偏大或偏移。4. 小目标与遮挡场景下的训练与推理优化7255 张图覆盖了不同光照、角度与遮挡但杆顶在图像中依然属于小目标。这里给出几条从训练和推理两侧同时优化的路径。4.1 使用 SAHI 切片推理解决超小目标漏检在巡检原图上直接检测 4K 图像时YOLOv8 内部会先把图缩放为 1280杆顶就会缩小到不足 10 个像素。常见做法是先用 SAHI 做切片把原图切成 512×512 的重叠块每块单独推理再通过 NMS 合并。这类似图像超分辨率重建的思路等于先把目标放大再做检测。from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathoutputs/pole_top_exp/weights/best.pt, confidence_threshold0.35, image_size1280, devicecuda:0 ) result get_sliced_prediction( imagedrone_frame_0001.jpg, detection_modeldetection_model, slice_height512, slice_width512, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_typeNMM, postprocess_match_threshold0.5, ) result.export_visuals(export_diroutputs/sahi_result)这里overlap_height_ratio取 0.2 是为了让切断的杆顶目标至少有一个完整切片能覆盖到太大会产生大量冗余计算postprocess_typeNMM是比 NMS 更严格的多模型融合能减少重复框。使用切片后漏检率下降明显但单帧推理时间从 30ms 涨到 200ms适合做离线巡检记录处理。在无人机实时巡检里我更倾向于用下面的增强参数来缓解漏检而不是直接上切片。4.2 数据增强参数调整YOLOv8 默认开启 Mosaic、HSV 扰动和随机翻转。对于电线杆这种长条形目标垂直翻转会改变杆顶遮挡关系Mosaic 在切片小目标上效果不错但会引入过度拼接痕迹。我在训练时针对性调整了增强参数yolo detect train \ modelyolov8n.pt \ datapole.yaml \ imgsz1280 \ epochs120 \ batch16 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.3 \ flipud0.0 \ fliplr0.5 \ mosaic1.0 \ mixup0.2flipud 置 0 是怕把“杆顶在上”这种语义破坏掉虽然在目标检测任务里模型不一定依赖绝对方向但数据里的标签全部按实际物理位置标注垂直翻转后杆顶朝下的场景在真实巡检里不存在。mixup 可以轻微保留因为它帮助模型对背景干扰鲁棒。hsv 增强不要开太大否则杆顶在夜色或逆光下颜色会被过度扭曲导致白天训练的模型在阴天场景失效。4.3 损失函数与置信度阈值YOLOv8 默认使用 CIOU 损失但我建议在训练时开启fliplr0.5的同时保留mosaic1.0——这套数据集中杆顶经常被电线遮挡Mosaic 能让模型学会用上下文补全目标。另一个容易被忽略的点是推理时的置信度阈值。验证时 mAP 会把所有置信度都算进去实际部署时如果你设 conf0.1会看到大量误检设成 0.4 又会漏掉被电线遮挡的杆顶。我通常先在验证集上做一次置信度扫描python tools/confusion_matrix_analysis.py --model best.pt --data pole.yaml --conf-list 0.1 0.2 0.3 0.4 0.5从结果里选一个误检率可以接受的平衡点。对本数据集我选 0.3 左右比较合适杆顶本身视觉特征强不像行人检测那样需要很低的阈值。5. 推理部署时提升杆顶检测速度与精度的两个实操点很多团队拿到 best.pt 就直接用但原图 4K 下直接预测既慢又不准。我在这套数据集上验证过的做法是把模型导出为 ONNX并结合输入尺寸与后处理做两级提速。导出命令yolo export modeloutputs/pole_top_exp/weights/best.pt formatonnx imgsz1280 opset12 simplifyTrue导出后可以用 onnxruntime 替代 pytorch 推理单帧延迟在 3060 上能降 30%。注意 imgsz 要和训练时一致否则不好直接说精度损失。实际部署时如果追求更高吞吐可以导出 TensorRT engine但这里不展开。第二个点是针对单帧视频做时序二次确认。由于杆顶在连续帧中位置变化很小我把相邻 3 帧的检测框做 IoU 跟踪只有连续两帧都在的框才上报。这个技巧比单独调 conf 更有效地降低了误检from ultralytics import YOLO model YOLO(best.pt) prev_boxes [] for frame in frames: results model.predict(frame, conf0.3, imgsz1280, verboseFalse) boxes results[0].boxes.xyxy.cpu().numpy() if len(prev_boxes) 0: prev_boxes boxes continue # 和上一帧做IoU匹配保留IoU0.8的框作为确认结果 confirmed match_boxes(prev_boxes, boxes, iou_thr0.8) prev_boxes boxes if len(confirmed) 0: save_detection(frame, confirmed)match_boxes内部用 simple IOU 计算两帧框的重叠重叠度超过 0.8 就认为同一杆顶。这种方式在杆塔静止时效果最好无人机悬停拍摄时几乎能消除所有单帧噪声框。最后提一个标注层面的验证技巧如果杆顶框与绝缘子边界非常贴近可以统计每个框的 aspect ratio 分布。正常杆顶标注宽高比集中在 0.8-1.2如果发现大量面积小且比例异常的框多半是标注时把整根杆体都框了进去。把这部分重新校正后再回去训练mAP50-95 通常还有 3-5 个点的提升。本文还有配套的精品资源点击获取
返回列表