☰
YOLO安全帽手套检测数据集:标签格式、划分与训练避坑指南
2026/10/7 18:18:32 网站建设 项目流程

简介:面向目标检测初学者与工业安全智能化开发者,YOLO安全帽手套检测数据集收录1000张真实作业场景图片,覆盖室内外不同光照、角度与遮挡情况,聚焦安全帽、手套佩戴识别,可直接用于YOLO系列模型训练,助力施工场所违规行为自动监测与预警。压缩包为RAR格式,共2000个文件,以XML标签、TXT标签、HTML教程、Python划分脚本、YAML配置为主要类型,整体约50.13MB,目录结构清晰,便于按需取用。已有544人浏览学习,适合刚接触目标检测的读者在真实数据上体验标注、训练与调参的完整流程。附赠训练集/验证集/测试集划分脚本,支持VOC、COCO、YOLO三种主流标注格式,标注框质量高,配合环境搭建与案例式训练教程,可快速迁移到自己的检测项目中。

1. 安全帽手套检测数据集:1000 张真实场景,三类标签一次给全

做工地场景目标检测的同行应该都碰到过这种尴尬:网上找得到的安全帽数据集,要么是国外工地拍的,要么标注框歪歪扭扭,放到自家摄像头下根本没法看。这份 YOLO 安全帽手套检测数据集,1000 张真实工地场景图片,覆盖安全帽、手套两个类别,用 labelimg 手工标注,同时给出 VOC(xml)、COCO(json)、YOLO(txt)三种标签格式,分类存放在不同文件夹。对刚入门 YOLO 目标检测的人来说,它能让你跳过数据准备这个最磨人的环节,直接跑到环境搭建和训练;对要做业务场景检测的团队,也适合先拿它把整条训练链路跑顺,再换成自己的数据,能少踩很多坑。完整详情与下载入口在原资源页:https://blog.csdn.net/m0_64879847/article/details/132301975

2. 三种标签格式的底细:VOC、COCO、YOLO 各写什么,怎么选

很多刚接触目标检测的人拿到这个资源后第一个疑问是:一个数据集为什么给三种标签格式,是不是重复了。其实不重复,YOLO 系列训练真正读的是 txt,VOC 和 COCO 是生态格式,用来给检查、转换和接其他训练框架。三种格式本质上描述同一批框,只是坐标表达方式不同。这一章把每种格式的结构拆开讲,再给一套质量检查的思路,训练前花二十分钟过一遍,能省后面调 bug 的好几个晚上。

2.1 VOC 的 XML:一个目标一个 bndbox,最直观的人工检查格式

VOC 格式是 labelimg 的默认保存格式,也是这份数据集里最容易被你打开看懂的标注文件。拿一张工人戴安全帽的图举例,xml 内容大致长这样:

<annotation> <folder>JPEGImages</folder> <filename>000001.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>helmet</name> <bndbox> <xmin>312</xmin> <ymin>245</ymin> <xmax>586</xmax> <ymax>489</ymax> </bndbox> </object> </annotation>

读这个 xml 只需要关注三块:<filename>对应哪张图片,<name>是类别名,<bndbox>里是左上角xmin、ymin和右下角xmax、ymax四个绝对像素坐标。注意这里用的是绝对坐标,不是归一化的,这是 VOC 和 YOLO 格式最本质的区别。xml 的优点是直观,一个目标一个 object 块,信息平铺在明处,标注出问题时拿文本编辑器直接改就行;缺点是一张图一个文件,1000 张图就是 1000 个 xml,做数据合并、发布和格式转换时确实啰嗦。

这套数据集把三种格式分别放在不同文件夹下,实际训练时只用 yolo 那个目录就够了,voc 目录主要用来做人工抽查。如果你后面要把自己的数据从 VOC 转成别的格式,转换的坐标逻辑就是从xmin/ymin/xmax/ymax出发的,这一节的 xml 结构是基础,务必记牢。

2.2 COCO 的 JSON:一个文件装下全部信息,适合接训练框架时用

COCO 格式把所有图片信息、类别信息、标注信息都压在一个 json 文件里。读取方式很简单,Python 自带的 json 模块就能处理:

import json with open("annotations.json", "r", encoding="utf-8") as f: coco = json.load(f) print("图片数量:", len(coco["images"])) print("标注数量:", len(coco["annotations"])) for cat in coco["categories"]: print(cat["id"], cat["name"])

输出后会看到三个顶层字段:images存每张图的 id 和宽高,categories是类别 id 与名称的列表,annotations是每个标注框的详细信息。COCO 的一个标注框长这样:

annotation = { "id": 1, "image_id": 1, "category_id": 1, "bbox": [312, 245, 274, 244], # x, y, width, height "area": 274 * 244, "iscrowd": 0 }

COCO 的 bbox 是左上角 x、y 加宽高,VOC 是左上角加右下角,这个差异是 xml 转 json 时最容易翻车的地方。category_id是整数索引,需要和categories里的 id 对应才知道是安全帽还是手套。COCO 格式的好处是接 mmdetection、detectron2 这类框架时不需要额外写转换逻辑,做 COCO 指标评估也方便;坏处是单文件体积大,想查某一个目标时不如 xml 直观。

2.3 YOLO 的 TXT:归一化坐标,训练真正用到的格式

YOLO 训练时读的是和图片同名的 txt 文件,每行代表一个目标,五列数字,空格分隔:

0 0.4321 0.3512 0.2875 0.4567 1 0.7513 0.6234 0.1652 0.2145

从左到右依次是:类别索引、中心点 x、中心点 y、目标宽度、目标高度。后四列都是相对图片宽高的比值,范围在 0 到 1 之间,所以叫归一化坐标。类别索引从 0 开始数,这份数据集里 0 对应安全帽,1 对应手套,具体对应关系看训练配置文件里的 names 列表。

从 VOC 转 YOLO 的公式我习惯写成一个函数备着,以后任何项目都能复用:

def voc_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h return x_center, y_center, width, height

这个函数把 VOC 的左上角、右下角坐标转成归一化的中心点加宽高。分母必须用原图宽高,如果用了 resize 后的尺寸,框的位置会整体偏移,模型训练时 loss 看不出来,推理时框全歪掉。三种格式的对比可以总结成一张表:

格式存放方式坐标形式典型用途
VOC xml每图一个 xml左上角 + 右下角绝对像素人工检查、labelimg
COCO json全部标注在一个文件左上角 x、y + 宽高绝对像素mmdetection、COCO 评估
YOLO txt每图一个 txt中心点 + 宽高归一化YOLO 系列训练

2.4 数据质量检查:类别分布与标注框目测标准

拿到数据集的第一件事不是切分,而是质量检查。先用一段简单脚本统计两类目标的数量,再随机抽几十张图,把标注框画出来人工过一遍。画框的代码不长,用 OpenCV 就能做:

import cv2 img = cv2.imread("01_000001.jpg") W, H = img.shape[1], img.shape[0] # 从 yolo txt 读出来的一行标注 class_id, cx, cy, w, h = 0, 0.4321, 0.3512, 0.2875, 0.4567 x1 = int((cx - w / 2) * W) y1 = int((cy - h / 2) * H) x2 = int((cx + w / 2) * W) y2 = int((cy + h / 2) * H) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite("check_01.jpg", img)

这段代码把归一化的中心坐标转回像素坐标,再画框。注意加框时中心 x 减去一半宽度才是左上角的 x,很多人写成(cx + w) * W,框直接跑到右下角去。检查标准就三条:框边缘是否贴住目标外轮廓,安全帽和手套有没有漏标,类别有没有标反。这类数据是 labelimg 手工标的,整体质量应该不差,但随机抽检这一步不能省,因为一张图标签错位,后面整个训练都在学错误样本。

3. 划分脚本实战:训练集、验证集、测试集怎么切才不出错

资源里附带了三类划分相关文件:一个按文件夹复制的划分脚本、一个生成 ImageSets 下 txt 的脚本、还有 train_list.txt 和 trainval_list.txt。它们对应两种不同的工作习惯,新版 YOLO 喜欢读目录结构,老版 darknet 习惯读文件列表。这一章把每个脚本的逻辑讲清楚,并指出划分时最容易犯的错。

3.1 按文件夹复制的划分脚本:图片和标签成对搬运

这份数据集包含一个把图片和标签一起按比例复制到新文件夹的脚本,适合新版 YOLO 的目录式数据组织。核心逻辑是:列出所有 jpg,随机打乱,按比例切成三段,再成对复制图片和同名 txt 标签。

import os import random import shutil random.seed(42) # 固定随机种子,保证每次划分结果一致 img_dir = "JPEGImages" # 原图目录 label_dir = "labels" # 对应的 yolo 标签目录 out_root = "dataset_split" # 输出根目录 train_ratio, val_ratio, test_ratio = 0.7, 0.15, 0.15 imgs = [f for f in os.listdir(img_dir) if f.endswith(".jpg")] random.shuffle(imgs) n_train = int(len(imgs) * train_ratio) n_val = int(len(imgs) * val_ratio) splits = { "train": imgs[:n_train], "val": imgs[n_train:n_train + n_val], "test": imgs[n_train + n_val:], } for split, names in splits.items(): img_out = os.path.join(out_root, "images", split) label_out = os.path.join(out_root, "labels", split) os.makedirs(img_out, exist_ok=True) os.makedirs(label_out, exist_ok=True) for name in names: stem = os.path.splitext(name)[0] # 去掉扩展名,用来找同名标签 shutil.copy(os.path.join(img_dir, name), os.path.join(img_out, name)) label_file = os.path.join(label_dir, stem + ".txt") if os.path.exists(label_file): shutil.copy(label_file, os.path.join(label_out, stem + ".txt"))

几个关键点说一下。random.seed(42)固定了随机种子,同一份数据每次切分结果完全一致,实验可以复现;shuffle必须在切分前执行,如果按文件名顺序直接切,前几百张可能都是同一个工地的同一时段,训练集和测试集场景重叠,指标虚高;exist_ok=True保证目录不存在时自动创建。比例参数可以按需调整,数据量少时用 0.8、0.1、0.1 也行,但三个比例加起来必须等于 1。

脚本对缺失标签的处理是静默跳过。我建议你跑的时候把缺标签的文件名打印出来单独看,区分是真没标注还是路径配错,别带着一堆空白标签进训练。另外,重复跑这个脚本前最好先清空输出目录,否则上一轮的残留文件会让比例失真。

3.2 split_train_val 脚本:生成 ImageSets 下的 train.txt 与 val.txt

老版 YOLO 的 darknet 训练习惯把样本清单写成 txt,放在 ImageSets 目录下,训练时按列表读取。这份数据里对应的脚本做的是同一件事:

import os import random random.seed(2024) image_dir = "JPEGImages" trainval_ratio = 0.9 # trainval 占全部样本的比例 train_ratio = 0.9 # train 占 trainval 的比例 imgs = [f[:-4] for f in os.listdir(image_dir) if f.endswith(".jpg")] random.shuffle(imgs) n_trainval = int(len(imgs) * trainval_ratio) n_train = int(n_trainval * train_ratio) trainval = imgs[:n_trainval] train = trainval[:n_train] val = trainval[n_train:] os.makedirs("ImageSets", exist_ok=True) with open("ImageSets/train.txt", "w") as f: f.write("\n".join(train) + "\n") with open("ImageSets/val.txt", "w") as f: f.write("\n".join(val) + "\n") with open("ImageSets/trainval.txt", "w") as f: f.write("\n".join(trainval) + "\n") print("train:", len(train), "val:", len(val), "trainval:", len(trainval))

这段脚本生成三个 txt,每一行是不带扩展名的文件名。逻辑是先把 90% 划为 trainval,再从 trainval 里取 90% 作 train,剩下的作 val。这样 train 和 val 之间不会重合,trainval 则是前两者的并集,专门给最后一轮全量微调用。生成文件时用 UTF-8 编码,如果在 Windows 上用记事本打开看到换行异常,不要改文件内容,调读取端的换行处理就行。

3.3 train_list.txt 与 trainval_list.txt:一个训练用、一个微调用

资源里还有 train_list.txt 和 trainval_list.txt,它们和 ImageSets 下的 txt 类似,区别在于内容一般是完整路径或带扩展名的文件名。路径列表的好处是训练时直接读取,不用再扫描整个目录。

# 生成路径形式的列表文件 with open("train_list.txt", "w") as f: for name in train: f.write(f"JPEGImages/{name}.jpg\n") with open("trainval_list.txt", "w") as f: for name in trainval: f.write(f"JPEGImages/{name}.jpg\n")

train 和 trainval 的区别很多人第一次接触会搞混:

文件内容典型用途
train_list.txt仅训练集图片路径正常训练阶段使用
trainval_list.txt训练集 + 验证集并集最后几轮全量微调

如果把 trainval_list 从头用到尾,验证集数据被模型学进去了,最终指标会虚高,这在交付验收的项目里是致命的。正常训练用 train_list,只有训练快结束时想榨干数据,才切到 trainval_list 再跑几个 epoch。

3.4 划分时的边界条件:比例、种子、类别平衡

划分脚本看起来简单,边界问题最折腾人。第一,比例加总必须为 1,浮点数计算时注意不要写0.7 + 0.15 + 0.15 = 0.999999这种误差,导致最后几个样本消失;第二,切分前必须全局 shuffle,否则按时间顺序拍的照片会全部堆在同一段;第三,固定随机种子,换个 seed 就是另一次划分,实验之间没法对比;第四,划分后检查两类目标在三个集合里的分布比例,如果安全帽大量落在训练集而手套大量落在测试集,手套的 mAP 会莫名其妙偏低,这时候优先做类别重分配,而不是怀疑模型结构。

4. 环境搭建与训练:从 GPU 驱动到第一轮 loss 下降

数据集附带的教程里有几个 html 文档,从 Ubuntu 安装、显卡驱动版本到 YOLO 训练教程都有覆盖。我按实际项目跑通的顺序把关键节点拆开讲,每一步都是踩过坑的。环境这关过了,训练本身反而是最省心的。

4.1 GPU 显卡驱动与 CUDA 的匹配

第一步永远不是装最新版驱动,而是先确认当前机器支持什么。Ubuntu 下直接执行:

nvidia-smi # 查看驱动信息与最高支持的CUDA版本

看输出上方的CUDA Version,这是驱动支持的最高 CUDA 版本,不是已经装好的。驱动版本低于 CUDA 要求时,即使强行装上去,运行时也会崩。安装驱动一般用sudo ubuntu-drivers autoinstall,选对镜像源基本不会出错。

装 PyTorch 时按 CUDA 版本选对应 wheel,这个匹配关系是玄学,出问题根本不好排查,所以装完第一件事就是验证:

python -c "import torch; print(torch.cuda.is_available())" # 输出True才能继续

输出 False 就是 torch 和驱动不匹配。常见组合参考下表:

nvidia-smi 显示的 CUDA 版本推荐 torch 版本
11.1 及以下torch 1.8 ~ 1.10
11.6torch 1.12
12.1torch 2.1
12.3 及以上torch 2.2 及以上

教程文档里的环境搭建 html 讲的就是这套流程,跟着表格选版本比直接装最新稳定版稳妥得多。

4.2 修改配置文件:类别数、names 顺序、路径

数据集是两个类别,训练配置里nc=2,names列表要有且只有两个名字。以 YOLOv5 为例,训练命令是:

python train.py --data safety_helmet.yaml --weights yolov5s.pt --epochs 100 --batch-size 16

其中safety_helmet.yaml是数据配置文件:

train: dataset/images/train val: dataset/images/val nc: 2 names: ['helmet', 'glove']

这个文件里最怕改错的是names的顺序。如果 txt 里第一列类别索引 0 对应安全帽,而 yaml 里names写成了['glove', 'helmet'],模型会把两类目标的学习信号完全反转,训练 loss 照样下降,推理结果全反。改配置时唯一的标准是:txt 里第一列的索引值,对应names列表里第几个名字。如果用 YOLOv8,命令写法不同,配置文件通用:

yolo detect train data=safety_helmet.yaml model=yolov8n.pt epochs=100 imgsz=640

两个版本的核心都是改yaml,路径、类别数、names 三处对齐,训练就能跑起来。

4.3 训练启动与损失曲线解读

训练开始后建议同时开 TensorBoard 看曲线:

tensorboard --logdir runs

浏览器打开后重点看三个损失:box_loss是回归框的误差,cls_loss是分类误差,obj_loss是目标置信度误差。前 20 个 epoch 三个 loss 会快速下降,后面逐渐走平。如果obj_loss一直不降,多数是正负样本不均衡,优先检查数据配置里的路径是否把图片和标签对错了。batch-size 根据显存定,8G 显存跑 yolov8n 时 16 左右比较稳,调太小模型震荡,调太大直接 OOM。

学习率默认值够用,不要一上来就调。前几个 epoch 如果 loss 剧烈震荡,先调 batch-size;训练到 80 轮还在明显下降,说明数据量偏大或类别难学,把epochs加到 150 再跑,mAP 通常还能涨一截。

5. 避坑:数据集和训练里最容易翻车的五个地方

很多问题不是模型结构层面的,而是数据流里的意外。这五条我都是实际踩过的,每条按现象、原因、解决对号入座,比看十篇错误日志都管用。

5.1 nvidia-smi 正常但 torch 报 CUDA 不可用

现象:nvidia-smi显示驱动正常,但 Python 里torch.cuda.is_available()返回 False。

原因:驱动支持的 CUDA 版本和 torch 内置的 CUDA 版本不匹配,或者 pip 装到了 CPU 版 torch。最常见的是系统里有多个 CUDA 环境,环境变量被覆盖。

解决:先执行python -c "import torch; print(torch.version.cuda)"看 torch 期望的 CUDA 版本,再对照nvidia-smi顶部的 CUDA Version。我一般直接用指定源重装:

# 用官方cu121源重装torch,版本号按自己需求改 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121

装完再验证一次cuda.is_available(),确认 True 再继续。

5.2 标签路径用绝对路径,换台机器就崩

现象:数据集在 A 机器上跑得好好的,拷到 B 机器后报找不到图片或标签。

原因:脚本里写死了/home/user/data/JPEGImages这类绝对路径,换机器后根目录不同,全部失效。

解决:所有脚本用相对路径,配合os.path.join拼接。这份数据集自带的脚本没有这个问题,但自己改过路径配置后,换机器前记得把脚本里的路径扫一遍,重点看img_dir、label_dir、out_root三个变量。

5.3 类别数改了但输出层和 names 没改

现象:训练正常,loss 也在降,但推理结果全是错类别,某一个类别完全不出。

原因:配置文件里nc=2改了,但载入的预训练权重输出层还是 COCO 80 类;或者names顺序和 txt 里的类别索引对不上。输出层会被重新初始化,这类问题训练时不报错,推理才暴露。

解决:训练前写一句命令统计所有 txt 的类别索引分布:

# 统计labels目录下所有txt第一列的类别索引出现次数 cd labels && cat *.txt | cut -d' ' -f1 | sort | uniq -c

输出应该是0和1两类,数量合理即可。把它和 yaml 里names顺序逐一对应,确认 0 是哪个类别、1 是哪个类别。

5.4 三种格式混用导致训练输入文件损坏

现象:训练时报Label class X exceeds nc=X,或者 txt 解析失败。

原因:把 VOC 的 xml 或 COCO 的 json 当 txt 喂给训练管线。三种格式并存时,训练只认 yolo 目录下的 txt,另外两个是给检查和转换用的。自己转格式时如果只改了扩展名没改内容,dataloader 按 txt 解析必然报错。

解决:训练前用head labels/任意文件.txt抽查几个文件,确认里面是五行空格分隔的数字,而不是<annotation>的尖括号。数据集自带三种标签是分目录存放的,训练时只指向 yolo 标签目录,不要指向 voc 目录。

5.5 训练中途断电,一夜白费

现象:训练了十几个小时,断电导致进程终止,runs 目录里只有零散权重,没有可用的 best.pt。

原因:没开断点恢复,最后一次权重写入没完成。

解决:训练时加--resume参数。YOLOv5 用python train.py --resume runs/train/exp,YOLOv8 用yolo detect train --resume。同时把save_period调成 5,每 5 个 epoch 存一次,断电最多损失 5 个 epoch。自从那次一夜白费之后,我训练超过 6 小时的任务一定开 resume,血泪经验。

6. 训练结束之后:mAP 评估、混淆矩阵与 onnx 导出

训练完不等于项目完,验证、导出、部署每一步都可能再翻车。先做一轮完整评估:

yolo detect val model=runs/detect/train/weights/best.pt data=safety_helmet.yaml

输出重点看三列:mAP50、mAP50-95、Precision。mAP50 是 IoU 阈值 0.5 时的平均精度,安全帽这类大目标一般能到 0.9 以上;mAP50-95 会把阈值从 0.5 逐步提到 0.95,数值低不少,但更严格。如果 mAP50 高而 mAP50-95 低,说明框定位精度不足,优先检查标注框边缘有没有贴住目标。

验证结果里会生成confusion_matrix.png,看安全帽和手套之间有没有互相错分。混淆严重时,先想是不是两类目标在画面里位置太近、遮挡多,再考虑类别不均衡问题。接着导出 onnx,给后续推理引擎用:

yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640

导出后用 onnxruntime 验证一遍输入输出形状:

import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") out = sess.run(None, {"images": np.zeros((1, 3, 640, 640), dtype=np.float32)}) print([o.shape for o in out])

输出形状一般是三元组:batch、候选框数量、4 + 类别数。安全帽手套两个类别就是 6。如果不匹配,检查导出时imgsz是否和训练一致,以及 opset 版本。我现在的习惯是训练到第 80 个 epoch 就去导一次 onnx,早发现问题早改,拖到最后发现格式不对整晚就没了。从那以后,我每次新数据集跑完都强制走一遍“val 评估、混淆矩阵、onnx 导出、输入输出形状打印”这个流程,尤其导出这一步,成本最低但最容易翻车。希望这个流程能帮到你,下载这份数据集后,建议把上面的步骤过一遍,跑通一次再上自己的业务数据。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询