YOLOv8实现施工安全网破损检测:从数据整理到部署全流程

发布时间:2026/9/11 18:45:27
YOLOv8实现施工安全网破损检测:从数据整理到部署全流程 简介基于YOLOv8的工地安全网漏洞检测项目包是面向计算机视觉方向在校学生、老师及从业者的完整可运行方案包含源码、完整数据集、可视化界面与部署说明可直接用于毕业设计、课程设计或初期项目演示。包内共8个文件包含3个Python脚本分别承担可视化界面、视频检测与模型训练功能、3个YOLO模型权重文件以及2个说明文档压缩包整体约15.91MB结构轻量清晰适合快速下载和部署。项目源码为个人毕设成果代码已经测试通过下载后按README操作即可快速部署无需复杂环境调试。该方案内置可视化页面支持一键生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图能全面展示模型训练效果与检测性能为答辩或评审提供有力支撑同时可作为模板修改以适配其他检测任务。目前已有33人学习使用适合需要获得一个高质量目标检测项目进行复现、二次改进或学习进阶的读者省去从零搭建的麻烦。1. 安全网的破损检测为什么恰好轮到 YOLOv8工地上真正要命的往往不是主体结构而是那些几米高处的密目式安全立网。网面一旦出现撕裂、孔洞或整块脱开高空坠物和高处坠落防护就同时失效。这类破损在画面里通常只是几个像素宽的裂口形状不规则又和背后脚手架的线性纹理高度相似通用检测模型在这种场景下会频繁漏检。YOLOv8 把这件事的成本压得很低单阶段、anchor-free、C2f 骨干、训练和部署管线都集中在同一个库几百张标注图就能跑到可用的准确率配上可视化界面非常适合做成果展示因此也是毕设和课设里的高频选题。下面按数据整理、网络训练、指标评估、界面部署四个阶段完整走一遍每个环节都给出能直接运行的代码和参数。2. 施工安全网数据集从视频抽帧到归一化的完整整理流程2.1 数据来源与场景划分跑工地检测模型第一件事不是下载某个现成的“施工安全数据集”而是先确认检测目标的边界。常见做法是单类检测“破损区域”把网面上大于一定尺寸的孔洞、撕裂、整块脱落都框出来也有项目拆成“完好网面”和“破损网面”两类方便后续统计破损面积。毕设和课设建议用单类标注一致性高达到同等效果需要的数据量更少。数据通常有三个来源工地监控视频抽帧、手机拍摄不同光照角度的照片、公开的施工安全相关数据集。监控视频抽帧最简单ffmpeg 等间隔抽帧就行# 每秒抽1帧质量因子2保证抽出的jpg足够清晰 ffmpeg -i site_camera_01.mp4 -vf fps1 -q:v 2 frames/frame_%04d.jpg参数说明fps1表示每秒钟视频输出一张图10 分钟的视频能得到 600 张-q:v 2是 jpg 质量控制参数范围 2-31数值越小质量越高检测任务里不要低于 2否则小目标的纹理细节会被压缩掉。多路摄像头最好交替抽帧不要连续几千帧都来自同一个机位否则训练集和验证集因为同一场景高度相似而严重过拟合val 指标会虚高。抽完帧后做去重这一步很容易被忽略。监控视频里大量帧只在时间轴上差几十毫秒画面几乎没变化直接标注会浪费大量重复劳动。一般用感知哈希筛重import os import imagehash from PIL import Image hashes {} for f in sorted(os.listdir(frames)): if not f.lower().endswith((.jpg, .png)): continue h imagehash.phash(Image.open(os.path.join(frames, f)), hash_size8) # 8x8 DCT 低频感知哈希 dup [k for k, v in hashes.items() if abs(h - v) 6] if dup: os.remove(os.path.join(frames, f)) # 与已有帧汉明距离小于6视为重复 else: hashes[f] h逻辑说明imagehash.phash先把图片缩到 8x8 再做 DCT取低频系数生成 64 位哈希两张图的哈希汉明距离小于 6 就认为是重复帧。阈值 6 对光照变化较敏感工地监控里同一机位不同时刻的亮度波动大如果发现误删多把阈值降到 4。2.2 用 LabelImg 和 JSON 转 YOLO 格式的标注脚本标注工具建议用 LabelImg虽然老但在这个任务上够用。对安全网这种大图里的细小目标原图普遍在 1920x1080 以上直接标注会漏掉小裂缝建议先切图再标把原图切成 512x512 的滑窗窗口之间留 50-100 像素重叠保证跨窗口的目标至少在其中一个窗口里是完整的。切图时同步记录每个窗口在原图中的偏移量标注时用窗口坐标训练时直接用窗口图像不需要把坐标还原回原图。标注结果保存为 JSON 的 LabelMe 格式时需要转成 YOLO 的 txt 格式。下面的脚本是标准做法import json def labelme_to_yolo(json_path, out_path, categories, img_w, img_h): with open(json_path, r, encodingutf-8) as f: data json.load(f) lines [] for shape in data[shapes]: label shape[label] if label not in categories: continue # 跳过未参与训练的类别 xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) x_c, y_c (x1 x2) / 2 / img_w, (y1 y2) / 2 / img_h w, h (x2 - x1) / img_w, (y2 - y1) / img_h w, h max(w, 1e-6), max(h, 1e-6) # 防止标注退化成一个点 lines.append(f{categories[label]} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}) # 类别ID 归一化坐标 with open(out_path, w) as f: f.write(\n.join(lines))参数说明categories是类别名到整数 ID 的映射比如{hole: 0}或{net_ok: 0, net_broken: 1}img_w和img_h必须取标注时实际加载的图片尺寸不能用切图窗口尺寸去套原图坐标。归一化到 [0,1] 后训练时会按 letterbox 规则重新缩放所以标注框不能超出图像边界如果发现坐标越界标注阶段就要改框而不是靠代码 clip 硬截。2.3 数据划分与离线增强先确定分布再翻倍工地照片里破损经常只占图像面积 1% 甚至更低这种小目标的数量直接决定模型上限。切图完成后验证集里要包含至少 50 个小目标样例否则训练曲线再漂亮部署到现场依然漏检。划分时按视频源分组而不是按单帧随机分一个视频的帧只能全部进 train 或全部进 val否则相邻帧会同时出现在两边测出来的 mAP 没有参考意义。数据增强在此任务上有两条路线建议配合使用。在线增强由 ultralytics 自动执行Mosaic 拼接、HSV 扰动、随机翻转和缩放。Mosaic 会把 4 张图拼成 1 张提升特征提取鲁棒性但它同时会把小目标进一步缩小安全网破洞这种本来就小的目标容易直接消失所以小目标占比高时要把mosaic调低到 0.5并开启close_mosaic10让最后 10 个 epoch 关闭 Mosaic让模型适应真实分布。离线增强可以用 imgaug 库对破损区域做随机旋转、亮度和高斯噪声扰动安全网是规则网格旋转任意角度后网眼纹理依然真实旋转增强对这类任务很友好。不建议用随机裁剪扩容因为破损通常出现在钢管连接处、绳与网的交界等特定位置过度裁切反而让模型学到无意义的局部纹理。增强参数的参考配置如下参数建议值说明mosaic0.5小目标多时取低值防止目标在拼接中消失hsv_h0.02安全网颜色相对固定避免色相偏移过度hsv_s / hsv_v0.5 / 0.4保持默认即可fliplr0.5水平翻转对工地场景安全flipud0.0垂直翻转建议关闭破坏光照方向统计数据量方面300 张有效标注图配合在线增强可以起步600 张以上会有明显质变。这个规模对课程设计来说已经完全够用。3. YOLOv8 网络结构与训练参数从 C2f 到命令行实操3.1 C2f 模块与 anchor-free 检测头的设计意图YOLOv8 相对此前版本的核心变化在骨干和检测头两部分。骨干里之前的 C3 结构换成了 C2f输入先过 1x1 卷积Split 成两路其中一路经过连续 n 个 Bottleneck最后把特征在通道维拼接后再过 1x1 卷积。这个设计的价值是每一层都能直接看到前面所有层的梯度流深层时梯度缩放更平缓网络在训练初期 loss 下降更稳定。YOLOv8 网络结构图上还有另一个细节检测在 P3、P4、P5 三层特征上分别进行Head 是 anchor-free 的 Decoupled Headbox 分支和 cls 分支分开输出box 分支直接回归目标框四条边到中心点的距离而不是去预测预定义 anchor 的偏移。标签分配用的是 TaskAlignedAssigner按分类得分和 IoU 的加权组合挑选正样本而不是过去的 IoU 阈值一刀切这等于给小目标更多匹配机会。损失方面分类用 BCE边框回归用 CIoU 加 DFL。DFL 让模型输出的不是单一距离值而是一个离散概率分布再取期望得到最终边框长度对细长裂口这类极端长宽比的框分布表达比直接回归更灵活。整体算力上yolov8n 参数量约 3MGTX 1660 Ti 这类 6GB 显存的卡能跑得动也为第五章的可视化界面留出了实时推理的余量。3.2 yolov8 环境配置与最小训练命令先看环境版本基线不需要严格照抄但别差太远模块版本建议说明Python3.9-3.11过低和 numpy/opencv 互相约束过高容易出现 wheel 缺失PyTorch2.0CPU 能跑正式训练建议 CUDA 版本ultralyticspip 安装最新即可已内置 yolov8 全部模型和训练器CUDA11.8 或 12.1以 PyTorch 官方对应版本为准环境配置按常规流程走conda create -n yolo8 python3.10 -y conda activate yolo8 pip install ultralytics # data指向包含train/val路径与类别名的yamlmodel用预训练权重 yolo detect train datasafety_net.yaml modelyolov8n.pt epochs100 imgsz640 batch8代码逻辑说明yolo是 ultralytics 提供的命令行入口detect表示目标检测任务train是训练子命令。data指向一个 YAML 文件里面写清 train 和 val 的图片路径、类别数量及类别名model填预训练权重路径yolov8n.pt是 n 版本显存占用最低、速度最快epochs是完整遍历训练集的次数imgsz是训练输入边长YOLO 默认按正方形缩放原图并用 letterbox 补边batch是每个 step 的样本数GTX 1660 Ti 这类 6GB 显存卡建议设 8。如果只有 CPU流程仍然能走通但建议把epochs降到 30、model换成yolov8n、imgsz设 416只做流程验证等有 GPU 再正式训练。3.3 训练参数怎么设imgsz、epochs、batch 与数据增强开关训练参数是最容易随手照抄的部分也直接决定最终指标。我一般会按下表微调参数默认值建议理由imgsz6401280显存足够时安全网破损是小目标640 下裂缝只有几个像素1280 显著提升召回epochs100150-200数据量小时长训练更容易收敛稳定patience10030视觉任务 150 轮内不提升基本不会等来突破batch8 或 16按显存调到能占满过小引入噪声过大会改变小目标分布optimizerautoSGD 或 AdamW显存小用 SGD收敛稳AdamW 适合短训练lr00.010.005-0.01与 batch 大小正相关batch 小时调低mosaic1.00.5防止小目标在拼接中丢失close_mosaic1010最后 10 个 epoch 关闭 Mosaic让模型适应真实分布imgsz这个参数第一轮用 640 定模型结构没问题但真正想拿更高 mAP建议做一次 1280 的微调。把 640 训练得到的best.pt用imgsz1280再跑一次 trainYOLOv8 会以已有权重做 warm start比从头跑 1280 省一半时间小目标的回归精度会明显提升。3.3.1 小目标占主导时的参数微调安全网破损场景基本是小目标主导除了mosaic0.5之外还有两个细节。第一个是hsv_h保持 0.02 附近安全网颜色相对固定过大的色相扰动让网眼颜色失真模型反而学到错误的浅层特征。第二个是关闭高频增强里的垂直翻转真实监控相机安装位置固定垂直翻转会破坏光照从上方来的统计规律等于人为制造训练集和推理集的分布偏移。如果出现 train loss 一直下降但 val mAP 不涨的情况优先检查是不是mosaic1.0且分辨率过低导致小目标被缩放成 1x1 像素其次检查验证集是否和训练集来自同一视频段这是数据划分时最容易埋的雷。4. 训练结果怎么评估才算达标损失曲线、mAP 与失败样例回放4.1 用训练日志画出损失函数曲线ultralytics 每次训练结束后会在runs/detect/train目录下留下results.csv里面包含每轮的 train/box_loss、train/cls_loss、train/dfl_loss 以及对应的 val 指标。画损失曲线是这个任务的标准操作import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(8, 5)) # train/box_loss 下降说明模型在拟合训练集val/box_loss 是真实泛化指标 plt.plot(df[epoch], df[train/box_loss], labeltrain box loss) plt.plot(df[epoch], df[val/box_loss], labelval box loss, linestyle--) plt.xlabel(epoch) plt.ylabel(box loss) plt.legend() plt.grid(alpha0.3) plt.tight_layout() plt.savefig(loss_curve.png, dpi150)逻辑说明results.csv的列名由训练器直接输出不能改。train/box_loss是训练集的边框回归损失val/box_loss是验证集对应损失。两者差距从第 20 轮到第 80 轮保持同一量级说明训练稳定如果持续拉大就是过拟合信号。画图时建议 x 轴从第 5 个 epoch 开始前面几个 epoch 处于 warm-up学习率还没到位曲线抖动大画出来不好看答辩展示时影响观感。4.2 混淆矩阵与 PR 曲线对照验证最佳权重用一条命令yolo detect val modelruns/detect/train/weights/best.pt datasafety_net.yaml命令会在runs/detect/val下生成confusion_matrix.png、PR_curve.png、F1_curve.png。单类任务看混淆矩阵意义不大重点看 PR 曲线曲线越靠近右上角说明精度和召回越均衡如果曲线先陡降再平缓说明模型对难样本的召回很差。工地上最难召回的是两类一类是强光反光把网眼纹理抹平的破洞一类是被钢管阴影压住一半的破洞。前者靠 HSV 亮度扰动很难模拟建议专门收集 10-20 张反光样例做微调后者可以优先尝试调低推理时的 conf 阈值从 0.3 降到 0.2再对比漏检数量。动手做失败样例回放比看指标更重要。写一个小脚本遍历 val 集把所有模型漏检的 GT 框对应图片保存下来人工看一遍是标注本身有歧义破损太小或阴影干扰还是模型真的没学会这个形态。这一步能直接指出数据补充方向。4.3 常见训练问题排查现象可能原因处理loss 波动不降学习率过大或 batch 太小lr0 降到 0.005batch 降到 4 或 8train loss 降、val 不降增强过强或数据太少调低 mosaic 和 hsv补充负样本mAP50 高但 mAP50-95 低回归框精度不够换 yolov8s或 imgsz 提到 1280训练过程显存不足batch 太高或 imgsz 太高减小 batch或开启梯度累积小目标漏检严重特征图下采样后目标消失优先训练大分辨率再考虑 P2 输出层其中“P2 输出”需要改模型 detect 头的结构多数毕设场景不值得硬改优先试imgsz1280效果最直接工作量最小。5. 部署与可视化导出 ONNX、PyQt5 界面与边缘设备移植5.1 导出 ONNX 并处理动态输入训练得到best.pt后部署阶段一般先转成 ONNX让推理环境不再依赖 ultralyticsyolo export modelruns/detect/train/weights/best.pt formatonnx dynamicTrue imgsz640,1280参数说明dynamicTrue让输入尺寸动态可变部署端可以按输入图实际尺寸做 letterbox 后再推理imgsz640,1280表示导出时同时记录两个典型尺寸能加快动态 shape 下的转换效率。导出后部署端只需要用 onnxruntime 加载模型预处理手动做 letterbox、像素乘 1/255、BGR 转 RGB再按[1, 4num_classes, 8400]的格式解析输出张量并做 NMS。注意导出时不要带 NMS 层置信度过滤和 NMS 都在部署代码里自行实现方便后续用不同的阈值调参。5.2 PyQt5 可视化界面的最小实现可视化界面是成果展示的核心常见做法是一个上位机打开图片、播放视频、启动摄像头三个按钮。为了让界面不卡顿推理必须放在 QThread 里下面是标准骨架class DetectThread(QThread): frame_ready pyqtSignal(object, list) def __init__(self, model, source): super().__init__() self.model model self.source source self.running True def run(self): cap cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ok, frame cap.read() if not ok: break # 推理尺寸与训练保持一致conf按场景调整 results self.model.predict(frame, imgsz640, conf0.3, verboseFalse) boxes results[0].boxes.xyxy.cpu().numpy() confs results[0].boxes.conf.cpu().numpy() for (x1, y1, x2, y2), c in zip(boxes, confs): cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) self.frame_ready.emit(frame, [(int(b[0]), int(b[1]), int(b[2]), int(b[3]), float(c)) for b, c in zip(boxes, confs)]) if cv2.waitKey(1) 0xFF ord(q): break cap.release()主窗口把frame_ready信号连接到 QLabel 的setPixmap上重载closeEvent时把running置 False 再wait()避免界面关闭后线程还占着相机。注意model.predict不指定imgsz默认用训练时的尺寸但部署端建议显式传参防止 GUI 里多次调用时偶发 shape 不匹配。5.3 RK3588 等边缘设备的部署要点工地监控经常要求现场推理不能把画面传到云端RK3588 是这类场景的常见选择。流程是把 ONNX 用 RKNN-Toolkit2 转成 RKNN 格式板端加载运行。转换前删掉 NMS 层NPU 不剪这个层会报算子不支持。优先选 int8 量化安全网检测对精度损失承受能力较高量化后能在板端跑到接近实时的帧率。最容易踩的坑是 letterbox 填充值在量化前后不一致以及颜色通道顺序YOLOv8 的 predict 内部默认按 RGB 归一化而 RKNN 输入通常接收 BGR转换时要在 RGA 或预处理里手动做一次通道调整。上板前先在 PC 端用几百张样本对比 ONNX 和 RKNN 的 mAP偏差在 2% 以内可以放行偏差过大时先检查数据分布是否覆盖了阴天、夜间、反光等极端场景。在板端确认同一张图两边的检测框有微小偏移时优先比对预处理后的图像与 ONNX 输入是否像素级一致再回头检查转换日志里的量化表这条排查路径能省掉大部分调试时间。本文还有配套的精品资源点击获取