
简介这份资源是面向计算机类毕业设计与课程作业的深度学习实战项目聚焦电力作业场景下未佩戴安全帽的检测问题采用YOLOv4目标检测算法实现人员安全帽佩戴状态的自动识别适合具备Python基础、希望将深度学习模型落地到实际场景的学生与开发者参考。压缩包共60个文件约50.86MB以py脚本、jpg示例图片、yaml模型配置、pt权重文件为主另含png效果图、json标注、sh权重下载脚本及md说明文档覆盖数据预处理、模型训练、评估与结果可视化等环节。资源目录包含数据集、模型配置、权重文件与项目文档可帮助读者理解YOLOv4网络结构、训练流程与检测框输出方式并掌握从数据处理到结果分析的完整链路。目前已有89人学习适合作为毕设选题参考或课程实践案例用于快速搭建可复现的安全帽检测系统。1. 从一份毕设压缩包说起YOLOv4 安全帽检测到底能跑出什么电力施工现场的安全帽检测是深度学习课程作业和毕设里被选得最多、也最容易翻车的题目之一。你手上这份「毕设课程作业_深度学习基于YOLOv4算法的电力工作人员未佩戴安全帽检测方法.zip」本质是一套目标检测工程用 YOLOv4 在电力作业场景图像上训练一个二分类佩戴/未佩戴或三分类人、戴帽、未戴帽检测器再部署成能对图片、视频甚至摄像头流做推理的脚本。它解决的不是「识别有没有人」而是「在复杂背景下判断这个人头上到底有没有安全帽」——这中间的难度差了一个量级。适合谁看正在做深度学习课程作业、毕设选题卡在检测类、或者已经拿到类似代码包但跑不起来的人。你需要的基础是会用 Python、装过 PyTorch 或 Darknet、知道什么是标注文件。如果你连虚拟环境都没配过这篇也能跟但每一步都要自己敲一遍别复制粘贴完就指望它跑通。下面从数据、训练、推理到踩坑按我实际做过这类项目的顺序讲清楚。2. 数据准备与标注安全帽检测的成败八成在这里2.1 为什么安全帽数据比通用检测难标安全帽检测的标注难点不在框人而在框「头」。电力场景里工人可能戴安全帽、可能没戴、可能戴了但颜色和背景接近、可能只露出半个头。你如果按 COCO 那种「框住整个身体」的思路标模型学到的特征会偏向人体轮廓而不是安全帽本身。常见做法是标注时以头部区域为主佩戴安全帽的框紧贴帽檐到帽顶未佩戴的框紧贴头发或裸露头部。这样模型关注的是头部区域的颜色、形状、纹理而不是衣服和姿态。另一个坑是类别定义。很多开源安全帽数据集只有两类hat 和 person。但你的标题写的是「未佩戴安全帽检测」那至少要有「未佩戴」这一类。我一般会定义三类helmet戴帽、head未戴帽的头部、person可选用于辅助定位。如果只做两类helmet / no_helmet那 no_helmet 的框就是裸露头部模型容易把「没戴帽的人头」和「其他圆形物体」混淆。三类的好处是 person 类能帮模型先定位人再判断头部状态召回率会稳一些。2.2 从原始图片到 YOLO 格式转换脚本与四个边界坑YOLOv4 用的是 Darknet 格式标注每张图一个同名 .txt每行class_id x_center y_center width height全部归一化到 0~1。如果你手头是 VOC 的 XML 或 LabelImg 存的 XML需要转。下面这个脚本是我常用的转换逻辑处理了四个容易出错的边界。import os import xml.etree.ElementTree as ET # 类别映射根据你的数据集实际类别改 classes [helmet, head, person] def convert_annotation(xml_path, img_w, img_h, out_txt_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 坑1坐标越界裁剪到图像范围内 xmin max(0, min(xmin, img_w - 1)) xmax max(0, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(0, min(ymax, img_h - 1)) # 坑2宽高为0的无效框直接丢弃 w xmax - xmin h ymax - ymin if w 1 or h 1: continue # 坑3归一化时用图像真实宽高不是XML里的size x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w_norm w / img_w h_norm h / img_h # 坑4归一化后仍可能略超1做一次截断 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w_norm min(max(w_norm, 0.0), 1.0) h_norm min(max(h_norm, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))逻辑说明这个函数逐 XML 解析先做类别过滤再做坐标裁剪和无效框丢弃最后归一化并截断。参数上classes列表的顺序决定了 class_id必须和后续训练时的.names文件、.data文件里的classes数量完全一致否则训练时类别索引错位模型会把安全帽学成「人」。img_w和img_h建议从对应图片实际读取不要用 XML 里size的宽高因为有些标注工具会写错。转换完之后你需要生成训练集和验证集的索引文件。常见做法是写两个 txttrain.txt和val.txt每行一张图片的绝对路径。然后建一个obj.data文件内容大致是classes 3 train /path/to/train.txt valid /path/to/val.txt names /path/to/obj.names backup /path/to/backupobj.names每行一个类别名顺序和classes列表一致。这些文件路径建议用绝对路径Darknet 对相对路径的处理在不同版本里不一致容易翻车。2.3 数据增强别把安全帽的颜色增没了安全帽检测里颜色是重要特征红、黄、蓝、白。如果你用 HSV 增强时把饱和度调得太狠红色安全帽可能变成灰色模型就学不到颜色线索。我一般会限制饱和度增强幅度在 0.7~1.3 之间色调偏移不超过 ±10。另外马赛克增强mosaic对安全帽检测有帮助因为它能把多张图拼在一起增加小目标和遮挡场景。但要注意如果拼出来的图里安全帽被裁掉一半标注框也要相应裁剪否则模型学到的是「半个帽子也是帽子」推理时容易误检。数据量方面电力场景的安全帽数据集公开的并不多。如果你只有几百张建议先做迁移学习用 COCO 预训练的 YOLOv4 权重初始化冻结前几层只训练检测头。如果数据到两三千张可以解冻全部层做微调。别一上来就从零训练收敛慢且容易过拟合。3. YOLOv4 训练配置从 cfg 文件到超参数怎么改3.1 选 YOLOv4 而不是 YOLOv5/v8 的理由标题锁定了 YOLOv4那就说 YOLOv4 在这个任务上的实际优势。YOLOv4 的 CSPDarknet53 骨干加 PANet 颈部对小目标检测比 YOLOv3 稳尤其是安全帽这种在画面里可能只占几十个像素的目标。另外 YOLOv4 的 Mosaic 增强和 CIoU 损失在训练时对遮挡场景更友好。如果你用 Darknet 框架YOLOv4 的 cfg 文件结构清晰改类别数、改输入尺寸都很直接。当然YOLOv5 在工程部署上更顺手但既然标题是 YOLOv4下面按 Darknet 的流程走。3.2 改 cfg 文件三个必须改的地方和两个建议改的地方YOLOv4 的 cfg 文件通常从yolov4.cfg复制一份然后改。必须改的三处第一[net]下的width和height。默认 608x608如果你的 GPU 显存不够改成 416x416。安全帽检测里416 通常够用因为头部区域不会太小。如果图片里人很远帽子只有十几个像素那就保持 608 或更大。第二每个[yolo]层前的[convolutional]里的filters。公式是filters (classes 5) * 3。三类就是(35)*3 24。YOLOv4 有三个 yolo 层每个都要改。第三每个[yolo]层里的classes改成你的类别数anchors可以先用默认的但如果你的数据集里安全帽的宽高比和 COCO 差异大建议用 k-means 重新聚类 anchor。我一般会在训练前跑一次 anchor 聚类用darknet自带的anchor工具或者自己写脚本。建议改的两处[net]下的max_batches和steps。max_batches一般设为classes * 2000但不少于 4000。三类就是 6000。steps设为max_batches的 80% 和 90%即 4800 和 5400。学习率learning_rate默认 0.001如果从预训练权重开始可以降到 0.0001 先跑几百轮 warmup。3.3 训练命令与日志观察训练命令很直接./darknet detector train obj.data yolov4-obj.cfg yolov4.conv.137 -gpus 0,1 -map-gpus 0,1指定多卡单卡去掉。-map每 4 个 epoch 算一次 mAP方便看验证集表现。yolov4.conv.137是预训练骨干权重如果你没有可以从零训练但收敛会慢很多。训练日志里重点看几个指标avg loss整体下降趋势class loss和obj loss是否同步下降。如果obj loss震荡不降可能是 anchor 不匹配或学习率太高。如果class loss降但obj loss不降说明模型能定位但分不清类别检查类别标注是否一致。我一般会在avg loss降到 0.5 以下、mAP 连续几个 epoch 不涨时停。提示训练过程中每隔几百轮存一个权重别只存最后那个。安全帽检测容易在某个阶段过拟合中间某个权重可能比最终权重泛化更好。4. 推理与部署把权重变成能用的检测脚本4.1 单张图片和视频推理的最小命令Darknet 自带推理命令./darknet detector test obj.data yolov4-obj.cfg backup/yolov4-obj_best.weights test.jpg -thresh 0.25-thresh是置信度阈值安全帽检测里我一般设 0.25 到 0.4。太低会误检太高会漏检未戴帽的人。视频推理把test.jpg换成demo.mp4加-out_filename output.avi。如果要保存检测结果图加-save_labels或自己写 Python 脚本调 Darknet 的 shared library。4.2 用 Python 调 Darknet 做批量推理实际做毕设或课程作业通常需要批量处理图片并输出统计结果。下面这个脚本用darknet的 Python 接口darknet.py做批量推理并统计未戴帽次数。import cv2 import os from darknet import Darknet # 需要先编译 darknet 的 python 绑定 # 加载模型 net Darknet(yolov4-obj.cfg) net.load_weights(backup/yolov4-obj_best.weights) net.set_gpu(0) # 类别名 class_names [helmet, head, person] def detect_image(img_path, thresh0.3): img cv2.imread(img_path) if img is None: return None, [] # Darknet 需要 RGB 且归一化 rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) detections net.detect(rgb, threshthresh) results [] for det in detections: x1, y1, x2, y2, conf, cls_id det cls_name class_names[int(cls_id)] results.append({ class: cls_name, conf: float(conf), bbox: [int(x1), int(y1), int(x2), int(y2)] }) return img, results # 批量处理 input_dir test_images no_helmet_count 0 for fname in os.listdir(input_dir): if not fname.lower().endswith((.jpg, .png, .jpeg)): continue path os.path.join(input_dir, fname) img, dets detect_image(path) if img is None: continue for d in dets: if d[class] head: no_helmet_count 1 # 画红框标记未戴帽 x1, y1, x2, y2 d[bbox] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, fNO HELMET {d[conf]:.2f}, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(foutput_{fname}, img) print(f未戴安全帽次数: {no_helmet_count})逻辑说明这个脚本先加载 cfg 和权重然后对每张图做推理把head类未戴帽用红框标出并计数。参数上thresh控制置信度net.set_gpu(0)指定 GPU如果没有 GPU 就注释掉。注意 Darknet 的 Python 绑定需要先编译编译时加GPU1和OPENCV1。如果编译报错检查 OpenCV 版本和 CUDA 路径。4.3 部署到摄像头的注意点如果要做实时摄像头检测帧率是瓶颈。YOLOv4 在 416x416 下单张 1080Ti 大概 40~50 FPS但加上视频解码和画框实际可能降到 20 FPS 左右。建议把输入尺寸降到 416或者用 TensorRT 加速。另外摄像头场景里光照变化大训练数据最好包含白天、傍晚、逆光等场景否则模型在傍晚会漏检。我一般会在推理前做一次简单的直方图均衡化提升暗光下的召回。5. 避坑与排查安全帽检测里最常见的五个翻车现场5.1 训练 loss 不降mAP 一直是 0现象训练了几百轮avg loss 在 2.0 以上震荡mAP 始终 0。原因通常是标注格式不对或类别索引错位。检查obj.names的行数和obj.data里的classes是否一致检查每个 txt 里的 class_id 是否在 0 到 classes-1 之间。另一个常见原因是图片路径里有中文或空格Darknet 读不到图但不会报错只是跳过。解决把所有路径改成英文无空格用find命令确认 train.txt 里每行路径都能访问。5.2 模型把所有人都框成 helmet现象推理时不管戴没戴帽都输出 helmet 类。原因通常是训练集里 helmet 样本远多于 head 样本模型倾向于预测多数类。解决在 cfg 里给 head 类加权或者用数据增强多复制一些 head 样本。另一个办法是调整[yolo]层的classes权重但 Darknet 不直接支持类权重需要改损失函数。更简单的做法是过采样 head 类图片让两类比例接近 1:1。5.3 小目标漏检严重现象远处工人头上的安全帽检测不到。原因输入尺寸太小或者 anchor 尺寸不匹配。解决把width和height提到 608重新聚类 anchor让 anchor 的宽高覆盖小目标。另外可以在[yolo]层前加一个上采样层但 YOLOv4 的结构已经固定改起来麻烦。更实际的做法是切图推理把大图切成小块分别检测再合并结果。5.4 推理速度慢达不到实时现象视频推理只有几帧。原因输入尺寸太大、没开 GPU、或者用了 CPU 推理。解决确认编译 Darknet 时GPU1和CUDNN1推理时加-gpus 0。如果还慢把输入尺寸降到 416或者用 TensorRT 转换。另外OpenCV 读视频时用cv2.CAP_PROP_FPS控制帧率别每帧都处理。5.5 换一台机器就跑不起来现象在自己电脑上训练好的权重换到同学电脑上推理报错。原因CUDA 版本、OpenCV 版本、Darknet 编译选项不一致。解决把 Darknet 编译成静态库或者用 Docker 打包环境。如果只是交作业建议在 README 里写清楚依赖版本别让助教猜。我一般会附一个requirements.txt和编译命令省得来回折腾。6. 进阶技巧用 mAP 曲线和混淆矩阵判断模型到底行不行训练完不是看 loss 低就完事。安全帽检测的验收标准是在验证集上helmet 的 AP 和 head 的 AP 分别多少以及误检率。我一般会跑一次darknet detector map输出每个类别的 AP 和 mAP。如果 helmet 的 AP 高但 head 的 AP 低说明模型对未戴帽的样本学得不够需要补数据。如果两个都高但 mAP 波动大说明验证集太小需要扩充。另一个技巧是画混淆矩阵。Darknet 不直接输出混淆矩阵但你可以用推理脚本统计对每张验证图把预测框和真实框做 IoU 匹配然后统计每个类别的 TP、FP、FN。下面这个表格是我在某次安全帽项目里的统计结果你可以照着做自己的。类别TPFPFN精确率召回率helmet41223310.9470.930head18741520.8200.782person29819270.9400.917从表里能看出head 类的精确率和召回率都明显低说明未戴帽的样本是短板。这时候别急着调参先去看 FP 和 FN 的图FP 多半是把安全帽的阴影或圆形物体误判成 headFN 多半是远距离小目标或遮挡。针对 FP可以加一些负样本没有头的背景图针对 FN可以过采样小目标 head 样本。还有一个验证方法是跨场景测试。训练集如果全是白天拿傍晚的图测一下如果 mAP 掉 20 个点以上说明模型过拟合了光照条件。我一般会留一个「困难集」逆光、雨雾、遮挡、小目标各几十张训练完先跑这个集比看整体 mAP 更能发现问题。最后说个习惯每次改完 cfg 或数据先跑 100 轮看 loss 趋势别一上来就训几千轮。安全帽检测的收敛点通常在 2000~4000 轮之间超过 6000 轮还没降大概率是数据或配置有问题。我踩过最深的坑是标注文件里混了一个 class_id 为 3 的框但 classes 只有 3 类0,1,2Darknet 不报错只是那个框被忽略导致模型在某个场景下始终漏检。后来写了个脚本检查所有 txt 的 class_id 范围才揪出来。希望帮到你。本文还有配套的精品资源点击获取