安全背心识别数据集构建与YOLOv8训练实战:从标注到部署
发布时间:2026/9/12 13:31:31 锦皓数字建站

简介面向安全背心识别任务的目标检测数据集类别设为vest与no-vest两类分别表示穿戴安全背心与未穿戴安全背心两种目标状态适合施工现场、交通路口、工厂作业区等需要进行人员安全监测的应用场景也可用于YOLO系列、Faster RCNN、SSD等主流目标检测模型的训练与验证。压缩包共2000个文件以1999个txt格式的YOLO标注文件为主每个txt文件记录对应图片中目标框的类别编号及归一化的中心坐标、宽度和高度另附1个yaml类别配置文件用于定义数据集路径、类别列表等信息整体压缩包大小约254.18MB。txt标签已经划分好训练集、验证集和测试集下载后可直接用于YOLOv5至YOLOv10等YOLO系列算法的训练无需再自行拆分数据或转换标注格式可节省数据预处理时间。目前已有242人学习下载适合需要快速获取标准化安全背心标注数据、开展作业人员安全监测或目标检测算法研究的深度学习开发者和研究人员。1. 安全背心识别数据集是目标检测落地的“第一块短板”在工地、工厂和路政养护场景里安全背心是最显眼的视觉标志却也是目标检测最容易翻车的地方。反光条在强光下过曝、深色背心在阴影里和水泥墙混在一起、人员密集时互相遮挡直接拿公开 COCO 权重去推理通常只能把目标识别成“人”无法回答“这个人穿没穿背心”的问题。安全背心识别数据集目标检测要做的就是把“穿背心的人”作为独立类别为模型提供包含边界框和类别标签的训练语料让 YOLO、RT-DETR 这类目标检测算法能训练出真正可用的着装合规判断模型。接下来按数据集搭建、训练和评估的顺序每一步都给到能直接复制的参数和脚本。适合做智慧工地、安监算法验证的工程师也适合第一次想从零准备目标检测数据集的人照着跑通。2. 构建安全背心识别数据集采集、标注与格式转换2.1 数据采集的三种来源与数量底线安全背心识别数据集的第一优先级是场景多样性而不是单纯堆数量。理想样本需要覆盖不同时间白天、黄昏、夜间、不同距离2 米内近景、5 到 10 米中景、15 米以上远景、不同姿态正面、背面、侧身、弯腰以及不同遮挡程度半身被挡、多人重叠、被脚手架或车辆挡住。只在一个固定机位拍出来的数据验证分数再高换一个工地就会原形毕露。常见做法是先自己拍 3000 到 5000 张现场照片再补公开的施工安全数据集。这类公开数据大多包含安全帽、反光衣等类别可以只抽取其中置信度较高的图像来扩充背景多样性。另一个被低估的来源是视频抽帧把一个固定机位拍摄的 10 分钟工地视频按每秒 1 帧抽图可以快速得到几百张动作不同的样本。抽完帧之后必须做去重连续帧之间场景高度相似放进训练集只会让模型记住机位而不是记住背心。数量底线建议按目标个数来定不按图像张数来定。训练集中的背心目标应不低于 5000 个加上未穿背心的负样本目标总量在 8000 个以上。如果每张图只有一两个人3000 张图的绝对目标数仍然不够尤其是后面要提升小目标 AP 时远景样本会被数据增强一再压缩根本剩不下几个有效像素。2.2 用 labelImg 标注反光背心的边界框标注工具可以从 labelImg 开始它输出 Pascal VOC XML 格式转换脚本容易写。标注规则有一条必须坚持框的是“背心”本身不是整个人。边界框要紧贴荧光面料和反光条范围领口以下到背心下摆不要把头和腿包进去。你把背心框得越准模型学到的是“背心”而不是“人”。实际协作时如果有人习惯先标一个人框再随手改成背心框在目标很小的情况下框往往缩到只有上身训练时正样本的边界框会非常不稳。建议只设置两个类别vest表示穿了反光背心的人person_no_vest表示没有穿背心的人。如果同一个工地上还要管安全帽可以再加一个helmet类但不要一次塞六七个类。类别多了背心和马甲、反光条和反光贴纸之间的边界会互相干扰训练时长也会明显拉长。标注人员打开图片后按W画框按D切换下一张。框的边要贴近背心实际轮廓反光条不单独标注。同一个目标被遮挡超过 30%除非还能清楚判断背心颜色否则跳过不标避免把模棱两可的样本喂给模型。2.2.1 标注过程里最常见的三个错误第一把背心框画成整个人框最终模型学出来的是人检测器。第二对远距离小目标直接跳过测试集里的小目标漏检从标注阶段就埋下了。第三把荧光绿和荧光橙拆成两个类别。背心颜色并不影响“是否穿戴背心”这个判断拆开只会让每个类别的样本量被稀释类别合并成vest即可颜色多样性交给训练数据本身去学。2.3 把 VOC 标注批量转换成 YOLO 格式YOLO 系列需要的标签文件是一张图片对应一个同名 txt 文件每行格式为“类别ID cx cy w h”。其中cx cy是归一化后的框中心坐标w h是归一化后的框宽高。把 VOC XML 转成 YOLO 格式核心代码只有几十行下面这段脚本可以直接放在数据集根目录运行。import os import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_file, img_w, img_h, classes): tree ET.parse(xml_file) root tree.getroot() yolo_lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 防止标注越界或反向 x1 max(0, min(x1, img_w)) y1 max(0, min(y1, img_h)) x2 max(0, min(x2, img_w)) y2 max(0, min(y2, img_h)) if x2 x1 or y2 y1: continue cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h yolo_lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return yolo_lines classes [vest, person_no_vest] if __name__ __main__: xml_dir annotations/voc out_dir annotations/yolo img_dir images/train os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue xml_path os.path.join(xml_dir, xml_name) img_name xml_name.replace(.xml, .jpg) img_path os.path.join(img_dir, img_name) with Image.open(img_path) as im: img_w, img_h im.size yolo_lines voc_to_yolo(xml_path, img_w, img_h, classes) if yolo_lines: out_path os.path.join(out_dir, xml_name.replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(yolo_lines))代码里先解析 XML 的bndbox再把x1 y1 x2 y2换算成 YOLO 需要的格式。两个细节值得注意图片宽高必须用真实图片读取到的值不能手写固定分辨率x2 x1或y2 y1的脏标注要做非法判断否则生成的 txt 会出现负宽度训练时 loss 直接变成 nan。对于 COCO 格式的 JSON换算逻辑完全一样只是把iter(object)改成遍历 annotations 数组并提前建好图片 id 到宽高的映射。YOLO 格式对文件摆放位置很敏感图片和标签要分别放在 images 和 labels 目录下层级保持一致。2.4 类目设计与目录结构最终目录按 YOLO 的训练习惯整理成下面这样避免在训练时写一堆自定义路径逻辑。safety-vest-dataset/ ├─ data.yaml ├─ images/ │ ├─ train/ │ ├─ val/ │ └─ test/ └─ labels/ ├─ train/ ├─ val/ └─ test/字段推荐值说明类别数量2vest、person_no_vest训练集图片2500-4000每张平均 2-3 个目标验证集图片400-600与训练集不同机位测试集图片400-600来自未参与训练的工地图片分辨率1280x720 及以上避免直接缩到 640 导致小目标消失正负样本比1:0.8 ~ 1:1.5没有背心的人也要有一定数量这里把“没穿背心的人”设成显式负类而不是把所有未被标记为背心的人统统当背景。很多施工安全数据集只标正样本模型会把工地上大量未穿背心的人当成背景处理对“人”这个目标的敏感度会下降。显式负类相当于让模型先找到人再去判断这个人有没有背心这个思路更适合安全监测场景。3. 用 YOLOv8 训练安全背心识别模型最小可复现流程3.1 组织 data.yaml假设代码放在safety-vest-dataset的上一级目录data.yaml 可以写成这样path: ../safety-vest-dataset train: images/train val: images/val test: images/test names: 0: vest 1: person_no_vestpath是数据集根目录train、val、test指向的是图片目录而不是 labels 目录。YOLO 会自动在同级目录下找 labels 文件夹。如果你把图片和标签混在同一个文件夹里训练启动后通常会报no labels found。这个报错在第一次跑 YOLO 的时候出现频率很高先检查目录结构再检查数据转换脚本。3.2 训练命令与参数含义在安装好ultralytics包之后跑通一个安全背心识别模型的最少命令是yolo detect train data../safety-vest-dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ projectruns/detect \ namevest_exp1这 6 个参数是起步必调的。参数推荐值说明modelyolov8n.pt用 nano 预训练权重初始化先跑通流程epochs100自制数据集够用了反向传播到收敛即可imgsz640显存不够就保持 640小目标场景再往上调batch16小显卡 8 也能跑3090 可以到 64projectruns/detect每次实验独立目录防止权重覆盖namevest_exp1实验名配合 project 生成完整输出路径如果后台挂着训练可以在命令里加cacheTrue把图片提前加载到内存省去训练时反复读磁盘的时间。另外注意 YOLOv8 的 CLI 参数用传值和 YOLOv5 的--epochs风格不同。看到unrecognized argument或语法报错时先检查是不是把两种写法混用了。3.3 训练日志里看哪几个值训练启动后不用频繁盯进度条重点看三个指标box_loss、cls_loss、dfl_loss。box_loss负责边框回归cls_loss负责分类dfl_loss是 YOLOv8 的分布焦距损失决定框边缘的锐利程度。背心数据集最常见的现象是cls_loss降得很快、box_loss还在缓慢下降。原因是“背心”的颜色和位置相对固定但边界并不总是清晰。这时候不要急着换模型等box_loss在验证集上进入持平区间再做早停。训练结束后用 best.pt 在验证集上输出指标重点看metrics/recall(B)。背心识别和普通物体检测不一样漏检比误检更危险recall 比 precision 更值得关注。如果 precision 高但 recall 低说明模型只在“非常像背心”的地方输出预测漏掉了很多被挡住的背心。后续调优应该把重心放在数据增强和小目标处理上而不是盲目加大模型尺寸。4. 安全背心识别目标检测的短板小目标、遮挡与类不平衡4.1 为什么小目标容易漏检工地远端背心在原图上往往只有 30×30 像素输入尺寸缩到 640 后它就变成 10×10 像素再经过骨干网络 32 倍下采样特征图里只剩一个点。小目标在浅层特征图上还有响应但 YOLOv8 默认只从 P3 开始输出预测小目标的框回归基本靠猜。提升小目标检测有三条路提高输入分辨率、在训练时减少缩小类增强、增加小目标复制增强。第一条最直接把imgsz从 640 提到 1280小目标 AP 通常能涨 3 到 5 个点代价是显存占用翻倍。如果你的部署环境只能跑 640那重点做第二条也就是调整增强参数。YOLOv8 默认的mosaic1.0会把 4 张图拼接成一张每张图都被缩小到原来的二分之一远距离背心会被进一步压没。建议把 mosaic 概率降下来同时把 scale 抖动范围收窄给中小目标留出更多生存空间。下面是可以在训练时通过augmentaugment.yaml加载的配置示例# augment.yaml mosaic: 0.5 scale: 0.5 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 degrees: 10 translate: 0.1 fliplr: 0.5 copy_paste: 0.3copy_paste会把当前 batch 里一张图的背心目标随机复制到另一张图上。对反光背心这种颜色和纹理特征相对固定的目标copy-paste 几乎不会制造语义错误也不需要额外跑外部工具。注意测试阶段不要套用这些增强配置否则验证集分数会失真。4.2 遮挡场景的样本策略施工人员经常被立柱、脚手架、货运车辆遮住半边身体目标检测没有显式遮挡建模模型只能靠背心的局部纹理做判断。如果数据集里遮挡样本太多边框会变得不稳定太少遇到遮挡就丢。常见做法是把遮挡超过 50% 的图像单独放进一个子目录训练时按固定概率混入正常样本。抽样代码可以挂在自定义 DataLoader 里也可以简单在拼 batch 前做一次随机替换import os import random normal_dir images/train occluded_dir images/occluded normal_files [f for f in os.listdir(normal_dir) if f.endswith(.jpg)] occluded_files [f for f in os.listdir(occluded_dir) if f.endswith(.jpg)] # 当前批次里按20%概率拿一张遮挡图替换正常图 chosen normal_files.copy() if occluded_files and random.random() 0.2: replace_idx random.randint(0, len(chosen) - 1) chosen[replace_idx] random.choice(occluded_files)替换逻辑不复杂但要注意遮挡样本的标签已经放进了 YOLO 格式并且 image 和 label 的文件名必须保持一一对应否则训练时这张图会找不到标签而直接被跳过。4.3 处理正负样本不平衡如果数据集中未穿背心的人占 70%模型会产生惰性倾向于输出person_no_vest导致背心的 recall 偏低。YOLOv8 的cls_loss默认没有按类别加权解决思路有三个在数据划分层面控制两类目标数量接近修改分类损失为 focal 形式推理阶段对vest降低阈值。最推荐的是第一个思路。把不穿背心的人抽样控制在背心数量的 1.2 倍以内不追求完全均衡但要避免负样本占绝对主导。如果你不想动数据可以考虑把cls_loss的 gamma 从 0 调到 2。Ultralytics 没有直接暴露这个参数需要在源码loss.py的分类损失部分做小改动改动时只动分类分支不要碰回归分支。处理方式对安全背心任务的影响注意事项imgsz 640 - 1280小目标 AP 提升明显显存占用翻倍mosaic 1.0 - 0.5中小目标保留更多全局上下文变少copy_paste 0.3背心目标数量增加可能制造语义重叠框控制正负样本比背心 recall 更稳需要重新划分数据集5. 评估安全背心识别模型不只看 mAP5.1 逐类 AP 和混淆矩阵怎么拿训练日志里的mAP50只给出整体水平但安全背心识别更关心vest这一类的表现。用下面命令在验证集上输出每个类别的 AP并生成混淆矩阵图yolo detect val \ modelruns/detect/vest_exp1/weights/best.pt \ data../safety-vest-dataset/data.yaml \ plotsTrue执行完后会在runs/detect/val下生成confusion_matrix.png和results.csv。更直接的数值读取方式是用 Pythonfrom ultralytics import YOLO model YOLO(runs/detect/vest_exp1/weights/best.pt) metrics model.val(data../safety-vest-dataset/data.yaml, plotsTrue) print(metrics.box.map50) print(metrics.box.maps) # [vest_AP, person_no_vest_AP]metrics.box.maps是一个列表第一个值是背心的 AP第二个值是无背心人的 AP。如果背心 AP 比无背心人的 AP 低 3 个点以上基本可以确认是正样本数量或场景多样性不够不是简单调参能解决的。5.2 用检测结果反查标注质量验证集分数低不一定是模型的问题。背心框如果标得偏大把反光条边缘切掉一半模型输出紧贴背心的框和 GT 的 IOU 只有 0.6会被误判成 false positive。建议写一个可视化脚本把置信度高于 0.85 的检测框和 GT 框同时画在原图上人工抽查几百张。把标注误差从模型误差里分离出来之后再决定是清洗数据还是调模型。5.3 部署前的一个具体技巧按框面积动态调整置信度工地部署不要用训练时的默认conf0.25一把梭。白天和夜间的最佳阈值可能差 0.1 以上远端小目标的置信度天然偏低。可以采用双阈值策略框面积小于全图 0.1% 的远端目标允许置信度降到 0.15 再输出近处大目标用 0.35减少误报。import cv2 from ultralytics import YOLO model YOLO(runs/detect/vest_exp1/weights/best.pt) frame cv2.imread(site_night.jpg) image_area frame.shape[0] * frame.shape[1] results model(frame, conf0.25, classes[0]) for box in results[0].boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf box.conf[0].item() area (x2 - x1) * (y2 - y1) threshold 0.15 if area / image_area 0.001 else 0.35 if conf threshold: # 进入告警逻辑该目标已穿戴安全背心 pass这个技巧不用重训模型只改推理阈值就能让远端背心的召回率小幅提升同时保持近处告警精准度不下降。实际接入告警系统时还可以把框宽度和视频流帧号一起写入消息队列方便事后复盘漏检。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。