简介:面向目标检测与计算机视觉任务,提供一套可用于YOLOv5/YOLOv8训练的安全帽佩戴识别数据集,覆盖室内、室外、人群等常见施工场景,解决戴帽与未戴帽的自动检测问题。资源内含近6000张图像及对应标注,压缩包共2000个文件,其中包含1999个txt格式的YOLO标签和1个yaml配置文件,标签由作者手动标注,整体包体约900MB,适合有一定YOLO基础、需要现成数据训练或微调检测模型的开发者直接使用。已有364人学习下载,经作者在yolov5与yolov8上实测,mAP@0.5达到0.9以上,能够有效支撑安全帽佩戴检测相关项目落地、算法验证与毕设课题。数据集按images和labels目录划分,配合yaml文件即可快速接入训练流程,免去自行采集与标注的重复工作。
1. 6000张安全帽图片配YOLO:这个数据集方向到底值不值得投入
6000张图片的安全帽检测数据集,配合YOLO系列做目标检测,是计算机视觉领域里工业落地最成熟的方向之一。它解决的是工地、厂区、检修通道里“工人是否佩戴安全帽”的自动巡检问题,输入一路监控视频,输出每一个人的头部状态,所有带帽子的目标被框出来并给出置信度。它的价值也不在于数据量本身,而在于场景高度集中:安全帽颜色相对固定,目标大小跨度大,漏检和误检的边界清晰,非常适合拿来验证检测模型从数据清洗、格式转换、模型训练到现场阈值调节的完整链路。
先说结论:6000张图的量级不大不小,直接用默认参数训练大概率能跑出可用结果,但现场效果好不好,主要取决于你对标签质量的检查、训练增强的配置和推理阈值的调节,而不是换一个更大的backbone。换句话说,这份数据集的瓶颈在数据侧和部署侧,不在模型侧。下面按一套可复现的流程,把从拿到数据集到现场部署的每个环节讲清楚,新手能照着跑,熟手可以重点看参数边界和几个容易翻车的现场调参细节。
2. 安全帽数据集的底细与标签校验:训练前必须处理好的三类隐患
拿到6000张安全帽图片之后,第一步永远不是急着训练,而是把数据集当成一个黑匣子拆开看。很多训练翻车不是模型的问题,而是标签格式、类别定义、切分方式从一开始就不干净。安全帽数据集通常来自不同采集渠道,有的用VOC的XML标注,有的用COCO的JSON,有的已经是YOLO格式的TXT,同一个压缩包里还可能混着乱码文件名、空标签文件、类别名字带空格等问题。这个环节花的时间,通常决定了后面训练过程是顺滑还是反复返工。
2.1 先看文件数:图片与标签一一对应检查
先统计图片数量和标签数量,确认没有“有图无标签”或“有标签无图”的情况。对于6000张图片的数据集,常见错误是某个子目录里混入了隐藏文件、缩略图文件夹,或者classes.txt这类非标注文件被当成标签计数。用下面一组命令检查:
# 统计图片数量,jpg/jpeg/png 按实际后缀调整 find ./safety_helmet_dataset -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" \) | wc -l # 统计标注文件数量,先把 classes.txt 这类文件排除 find ./safety_helmet_dataset/labels -type f -name "*.txt" ! -name "classes.txt" | wc -l两个数字应当一致。如果图片数量比标签多,说明有一部分图没被标注,这些图要么删掉,要么留作负样本;如果标签比图片多,说明有重复标注或孤儿标签,需要逐一核对该标签对应的图片是否存在。注意,有些数据集把标注文件按“图片名.txt”存放在labels目录下,目录结构不同时,把find的路径换成实际路径即可。检查完毕后再用同样的方式确认train、val目录下分别的文件数,避免训练集和验证集文件交叉。
这一步的意义在于:YOLO训练时如果存在标签文件但图片缺失,训练进程一般不报错,只会跳过该样本,但会造成实际训练图片数少于预期,模型在验证集上表现波动。缺标签的图片则会被当成纯背景输入,模型会在这些位置学到“无目标”的负向信号,干扰安全帽检测的置信度输出。
2.2 标签格式统一:从VOC/COCO坐标到YOLO归一化TXT
常见的VOC标注是XML格式,保存的是xmin、ymin、xmax、ymax的绝对像素坐标;COCO的JSON则保存了分割多边形或bbox的[x, y, width, height]。YOLO训练需要的是TXT文件,每行一个目标,一行五个值:类别ID、归一化中心点x、归一化中心点y、归一化宽w、归一化高h。三者互不兼容,必须先统一成YOLO格式再做训练。下面这段脚本处理VOC XML转YOLO TXT:
import os import xml.etree.ElementTree as ET # 类别名到ID的映射,按数据集实际类别修改 class_name_to_id = {"helmet": 0, "head": 1} def convert_voc_to_yolo(xml_path, out_txt_path): tree = ET.parse(xml_path) root = tree.getroot() 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.strip() if name not in class_name_to_id: # 类别不在映射表里就跳过,避免把噪声写进标签 continue bndbox = obj.find("bndbox") x1 = float(bndbox.find("xmin").text) y1 = float(bndbox.find("ymin").text) x2 = float(bndbox.find("xmax").text) y2 = float(bndbox.find("ymax").text) # 边界防止出现 w=0 或 h=0 的非法框 x1, x2 = max(x1, 0), min(x2, img_w) y1, y2 = max(y1, 0), min(y2, img_h) cx = (x1 + x2) / 2.0 / img_w cy = (y1 + y2) / 2.0 / img_h bw = (x2 - x1) / img_w bh = (y2 - y1) / img_h lines.append(f"{class_name_to_id[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") with open(out_txt_path, "w", encoding="utf-8") as f: f.write("\n".join(lines))这段脚本的逻辑是:通过XML根节点找到图片宽高,遍历每一个object,把左上角和右下角的绝对坐标换算成相对图片宽高的中心点与宽高。关键参数是img_w和img_h,很多标注工具会把图片的EXIF旋转信息写进size,如果训练时统一转成RGB再去读,容易读错宽高,导致所有框偏移。另一个常见问题是x1、x2、y1、y2超出图片边界,转出来会是负数或大于1的归一化值,YOLO训练时该框会被忽略,但验证时mAP会莫名其妙掉,所以在脚本里做了边界截断。
如果数据集已经是COCO的JSON格式,思路一样:从annotations里读出每张图片的width、height和对应的bbox,把[x, y, w, h]换成[cx, cy, w, h],除以图片宽高。转换完随机抽5张图,画框可视化检查一遍,这一步不要省,格式转换脚本的错误很隐蔽,等训练完才发现就晚了。
2.3 分层切分:验证集里必须保留黑夜与逆光样本
6000张图怎么切分训练集、验证集和测试集,直接决定你评估出来的mAP可信不可信。最常见的错误是把所有图片混在一起random.shuffle后按比例切分。安全帽数据集的采集场景通常不是均匀分布的,有些拍摄于白天正光,有些是傍晚逆光,有些是夜间补光,还有大量室内通道。如果夜间图片只有50张,随机切分可能把它们全分进训练集,验证集里全部是白天图片,训练过程的mAP看着很高,一到现场夜班就断崖式失效。
正确做法是按场景分层切分。我一般会先用文件名或目录名做场景分组,再在每个场景组内部按比例采样,代码逻辑如下:
import os import random from collections import defaultdict random.seed(42) image_root = "safety_helmet_dataset/images" image_paths = [ os.path.join(image_root, f) for f in os.listdir(image_root) if f.endswith((".jpg", ".jpeg", ".png")) ] # 按文件名里的场景关键词分组,实际项目里根据数据集命名习惯调整 buckets = defaultdict(list) for path in image_paths: name = os.path.basename(path).lower() if "night" in name or "dark" in name: buckets["night"].append(path) elif "indoor" in name or "corridor" in name: buckets["indoor"].append(path) else: buckets["day"].append(path) train, val, test = [], [], [] for bucket in buckets.values(): random.shuffle(bucket) n = len(bucket) train += bucket[: int(n * 0.8)] val += bucket[int(n * 0.8) : int(n * 0.9)] test += bucket[int(n * 0.9) :] print({k: len(v) for k, v in [("train", train), ("val", val), ("test", test)]})这段代码的核心是buckets分组,分组依据可以是文件名关键词、子目录名,或者一个额外的scene.csv。每一组内部再按8:1:1切分,保证验证集和测试集都覆盖到白天、黑夜和室内场景。随机种子固定成42,方便后续复现。做完切分后,把划分出的图片路径分别写到train.txt、val.txt、test.txt,YOLO训练框架可以直接读文件列表,也可以按目录结构把图片分别放好,配合后续的data.yaml使用。
3. 用YOLOv8在本地跑通安全帽训练:最小命令集与三个必调参数
数据准备完毕,接下来进入训练环节。安全帽检测用YOLOv8的训练器是当前上手门槛最低的方案,单卡就能跑,6000张图的规模在消费级显卡上几十轮就能收敛。这一章给出最小可复现的训练命令,以及训练前必须想清楚的三个参数:模型尺寸、输入分辨率、batch大小。这三个参数的组合,决定了你的训练时间、显存占用和最终对远距离小目标的检出能力。
3.1 搭建虚拟环境与YOLO训练器安装
训练环境建议用Python虚拟环境,避免和系统Python包相互污染。安装训练框架后,用yolo命令验证安装是否成功:
# 创建并激活虚拟环境 python -m venv .venv source .venv/bin/activate # 安装YOLOv8训练框架,带全部依赖 pip install ultralytics # 查看版本,出现完整帮助信息即安装成功 yolo --help安装完成后,yolo命令和Python API是等价的,后面都用yolo命令行操作。注意,pip安装的是CPU版PyTorch,如果机器有NVIDIA显卡,需要先装对应版本的PyTorch再安装ultralytics,顺序不能颠倒,否则会装上CPU版而浪费显卡。显存小于6GB的机器建议只用CPU跑推理,训练就用云端或者换卡。
3.2 组织数据集目录与safety_helmet.yaml配置
YOLOv8训练时通过一个YAML文件告诉训练器数据集在哪里、有哪些类别。目录结构按images和labels分开,train和val完整分离。下面是标准结构和配置示例:
mkdir -p safety_helmet_dataset/images/train safety_helmet_dataset/images/val mkdir -p safety_helmet_dataset/labels/train safety_helmet_dataset/labels/val# safety_helmet.yaml train: ./safety_helmet_dataset/images/train val: ./safety_helmet_dataset/images/val # 类别数量,按标签文件里实际出现的最大ID+1填 nc: 2 names: 0: helmet 1: head这段配置里nc和names必须和标签文件严格一致。安全帽数据集有的只标helmet一个类别,有的把未戴帽子的头也标成head,有的把不同颜色安全帽拆成多个类别。训练前统计所有TXT标签里出现的类别ID,再回填配置。train的路径如果写相对路径,要以执行训练命令的目录为基准;把路径写错是最低级的报错信息,但也是最常见的。写完配置后用下面命令快速校验图片与标签能被正常读取:
python -c "from ultralytics.data import YOLODataset; ds=YOLODataset('safety_helmet.yaml'); print(len(ds))"能打印出数据集长度,说明路径和标签格式没问题;报KeyError或FileNotFoundError,就按报错路径一层层检查。这个校验步骤能帮你省掉训练跑了两小时才发现数据没加载进去的尴尬。
3.3 第一次训练命令:yolov8s、imgsz与batch怎么选
数据集和配置文件就绪后,直接启动训练。以yolov8s为例:
yolo detect train \ model=yolov8s.pt \ data=safety_helmet.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/helmet \ name=exp01这里几个参数是安全帽场景下最值得花时间调的地方。model=yolov8s.pt是从预训练权重开始训练,s版本在精度和速度之间最均衡;6000张图用n版本训练更快但远距离小目标会更容易丢,用m版本精度更高但训练时间翻倍。imgsz=640是默认值,如果数据集中大量安全帽目标在图像里占据面积很小,建议改成imgsz=960甚至1280,代价是显存占用和训练时间上升。batch=16是按单张16G显存设的,8G显存改成8或4,显存溢出时优先降低batch而不是降低imgsz,因为imgsz对检测精度的贡献通常比batch大。train和val路径如果包含中文,某些旧版本会报编码错误,建议统一改成英文路径。
3.4 看训练产物:results.csv、混淆矩阵与mAP曲线
训练结束后,所有产物在runs/helmet/exp01目录下。weights里有best.pt和last.pt,best.pt是验证集表现最好的权重,部署时优先用这个。直接用下面命令评估:
yolo detect val \ model=runs/helmet/exp01/weights/best.pt \ data=safety_helmet.yaml评估结果会打印mAP50和mAP50-95。安全帽这类单目标检测,mAP50达到0.9以上不稀奇,但mAP50-95通常低不少,它衡量的是不同IoU阈值下的综合表现,定位不准或置信度分布差都会暴露在这里。更细的信息在results.csv里:
import pandas as pd df = pd.read_csv("runs/helmet/exp01/results.csv") # 打印最后十轮的验证指标,观察是否还在上升 print(df[["epoch", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]].tail(10))此外runs/helmet/exp01下还有confusion_matrix.png和PR_curve.png。混淆矩阵里“true_helmet被预测成background”那格数字高,说明漏检严重;“background被预测成helmet”那格高,说明误检严重。不要只盯着mAP数字看,这两个图才是定位问题方向的入口。
4. 安全帽检测常见问题排查:五个让模型翻车的现场
模型训练完成后,进入现场测试阶段才是真正头疼的开始。下面五类问题,是安全帽检测在工地和厂区视频流里最常见的翻车现象,每一类我都按现象、原因、解决的顺序拆开讲。这些问题不是模型结构层面的bug,而是数据分布、标签质量和推理参数综合作用的结果,排查路径清晰,照着做基本都能缓解。
4.1 现象:离镜头远的安全帽全部漏检
监控画面里靠近通道远端的人,安全帽只有十几像素大小,模型完全无输出。原因有两个:一是输入分辨率不够,640x640下小目标经过骨干网32倍下采样后只剩下不足一个像素的特征;二是数据集里近距离大目标样本占比高,小目标样本数量少,训练时小目标对损失的贡献微乎其微。解决方法是把imgsz调大到960或1280,显存不够就用batch=8;如果现场机位固定,可以把原图按320x320重叠切块分别推理再合并结果,相当于用计算量换小目标召回。同时给训练集做copy-paste增强,把已有的小目标框剪切出来,在训练图空白区域复制几份,提高小目标样本比例。
4.2 现象:黄色塑料桶、黄色安全绳被误检成安全帽
安全帽最常见的颜色是黄色,工地现场的黄色警示桶、安全网、吊装带也是黄色,模型在颜色相近、形状模糊的区域会输出假框。原因在于正样本里黄色背景物体太少,模型学到的主要是“黄色+类圆形”的弱特征,没有足够负样本告诉它哪些黄色物体不是安全帽。解决方法是专门收集几百张现场没有安全帽的图片,放进训练集里,对应标签文件写成空文件,让模型在这些图上学习“无目标”。训练时data.yaml不用改,目录结构里image和空labels配对即可。加入负样本后误检数量通常会明显下降,但注意负样本图片不要全来自同一机位,否则模型只是记住了那个角度。
4.3 现象:训练loss在降,验证mAP一直不动
训练进程里train/box_loss每个epoch都在降,看起来一切正常,但val的mAP50从第30轮开始就稳定不变。原因通常是验证集和训练集分布过于接近,模型很容易把验证集里的安全帽“背下来”,继续训练只是在记忆训练样本的纹理细节,现场换个角度就失效。解决方法是重新检查验证集切分,确认是否做了场景分层;在val命令里打开augment=False,避免不必要的数据扰动;训练时设置早停参数patience=20,如果验证指标连续20轮无提升就停止,不浪费训练时间。更关键的是不要只看mAP50,把PR曲线的召回率拐点记下来,现场阈值设置更依赖recall曲线而不是单一平均值。
4.4 现象:逆光和阴影场景下安全帽被当成背景
现场下午低角度阳光照射时,帽檐处在阴影里,帽子颜色偏暗,模型检测不到。原因是训练集里均匀光照图片占绝大多数,模型对亮度变化不敏感。解决方法是训练时加大颜色增强参数,在训练命令里加hsv_h=0.02、hsv_s=0.6、hsv_v=0.5,让模型见过更多低亮度、低饱和度的变体;如果现场机位固定,也可以在推理前做CLAHE对比度均衡,把暗部细节拉出来再送进模型。注意hsv_v不要调到0.7以上,否则会产生大量过曝的失真样本,反而干扰正常场景识别。
4.5 现象:同一顶安全帽被同时输出两个类别框
如果数据集把安全帽按颜色拆成“white_helmet”“yellow_helmet”两个类别,同一种帽子在中午和傍晚颜色漂移后,会被分类头同时判为两个类,输出两个重叠框。原因是类别定义本身模糊,颜色在不同光照下的特征分布不分离。解决办法是:业务上如果只关心“戴没戴”,就把所有颜色类别合并成单一helmet类,统一改写标签里的类别ID为0;推理时设置agnostic_nms=True,让NMS在去重时不区分类别,重叠度高的框不管是不是同个类都会被压掉。
5. 现场部署阈值怎么调:置信度与NMS的配合策略
训练出来的模型在验证集上表现良好,不等于在监控视频流里直接可用。推理时的置信度阈值和NMS阈值是最后一道开关,调得不合适,要么误报满天飞,要么漏检收不住。这一章讲清楚这两个阈值的调节依据,给出一组能直接上手的参数组合,以及如何用验证集的反推结果代替拍脑袋定阈值。
5.1 为什么先校准置信度:训练分布和现场分布不是一回事
训练时模型从预训练权重学到的是通用特征,微调阶段又完全依赖安全帽数据集的分布。现场机位角度、光照色温、遮挡程度和数据集分布存在差异,这会导致模型输出的置信度与真实置信概率不对齐。常见情况是验证集上0.5置信度就能压住误检,现场0.6置信度下误检依然很多,不是模型坏了,而是现场样本落在了训练分布的低置信区。校准方法是用少量现场图片先跑一遍推理,把预测结果的置信度从高到低排列,人工标记每个框是TP还是FP,找到precision和recall的交点。如果现场暂时没有标注数据,就先用极低置信度跑几段视频,把所有候选框都用0.05的conf导出,再在标注工具里统计TP/FP分布。
5.2 一组能直接抄的阈值表:conf与iou的组合
下表是安全帽场景下常用的初始阈值组合,基于单类别检测和普通监控机位视角设置:
| 参数 | 作用 | 建议值 |
|---|---|---|
| conf | 低于该值的预测框直接丢弃 | 0.25~0.35 |
| iou | NMS去重时判断重叠的IoU阈值 | 0.45~0.60 |
| agnostic_nms | 跨类别执行NMS去重 | True |
| max_det | 单张图最多输出的目标数 | 20~50 |
conf越高误检越少但漏检风险越大;iou越高保留的框越多,两个相近框同时输出的概率也越大。单类别安全帽任务里建议固定agnostic_nms=True,避免快慢帧之间同一个目标输出两个相邻框导致重复报警。max_det设到50足够覆盖大多数监控通道里的人数规模,设太大反而会把低置信度误检也保留下来。
5.3 agnostic_nms与max_det:多路视频推理时的两个隐藏参数
多路视频流并发推理时,算力有限,每路视频能分到的推理帧率有限。降低max_det能减少输出后处理耗时,但效果不如直接在推理命令里限制输入尺寸。下面是一组实际可用的推理命令:
yolo detect predict \ model=runs/helmet/exp01/weights/best.pt \ source=site_video.mp4 \ conf=0.3 \ iou=0.5 \ agnostic_nms=True \ max_det=50这里conf=0.3是常规起点,i ou=0.5是NMS去重阈值的常见值。如果现场远端小目标多,把conf降到0.2、iou降到0.45;如果误报无法容忍,把conf升到0.35,但不要高于0.4,否则暗光环境漏检会明显增加。推理命令还可以把source指向摄像头RTSP流或图片文件夹,输出会在保存目录里生成每帧的检测结果。注意,视频流推理时模型输出存在帧间抖动,单帧NMS只能压同一帧内的重复框,帧与帧之间的闪烁需要叠加跟踪算法,比如在检测结果上按框中心距离做简单关联,避免同一目标在相邻几帧被反复计数报警。
6. 从数据增强到二次迭代:用6000张图撑起更稳的现场识别
6.1 只增强场景盲区:mosaic、亮度扰动与负样本回归
第一轮模型稳定后,二次迭代的方向不是增加数据量,而是针对现场反馈的盲区做定向数据增强。YOLOv8默认开启的mosaic增强,把四张图拼成一张训练,对安全帽这种小目标密集的场景很有效,目标密度提高了,小目标被模型见到的机会也多了。亮度扰动方面,hsv_v设到0.5左右能让模型适应逆光和暗光,但不要超过0.6,否则安全帽颜色被过度漂移,模型会学到错误的颜色关联。旋转和透视增强要谨慎,安全帽的帽形是弧面,旋转超过30度后形状严重畸变,反而干扰特征学习。
我的一般做法是,第一轮训练只做基础增强,拿到现场首版结果后,从现场返回的误检和漏检样本里挑出200张左右,手工修正错误标签并补标漏检目标,再混进原始数据集做第二轮训练。新增样本数不超过总量5%,这6000张图的首版模型已经具备不错的泛化能力,一次补充过多的现场样本会破坏原有特征分布。混合增强比例也要控制,mosaic开启但mixup关闭,因为安全帽是刚性物体,不同目标的像素混合会产生不真实的纹理。
6.2 第二轮训练的两条铁律:标签修正优先于参数堆叠
二次训练前先把上一轮用到的conf和iou参数记下来,新模型跑完用同一组参数对比,才能区分性能提升来自数据还是来自阈值变化。过程中如果发现现场远距离漏检依旧,先回去检查是不是小目标标签被漏标了,不要急着加大imgsz——标签里的漏标会让模型学到“小目标不是目标”的错误先验。标注工具里把6000张图按置信度排序,优先检查置信度最低的那一批预测结果,往往能找到之前人工标注时被忽略的远处目标。
我的习惯是每次训练前先花半小时翻标签,而不是直接跑训练;标签里的玄学问题,后面都会在mAP上找回来。识别精度卡住时,先别急着换模型,回头检查数据文件和现场阈值设置,往往比换backbone更省时间。希望帮到你。
本文还有配套的精品资源,点击获取