
简介这是一套面向建筑工地、工厂车间等实际作业场景的安全帽与反光背心佩戴检测数据集采用YOLOV9格式标注共包含2000余张已标注图片适合智能安全监管系统的算法开发者与研究人员直接用于模型训练与效果验证。数据集已完成自动定向、统一缩放至1280x720黑边填充等预处理工作每个标注文件包含对应图片中安全帽、反光背心等目标的位置与类别信息可直接接入YOLO系列训练流程。资源包共2000个文件其中txt标注文件1955个、jpg图片44个、yaml配置文件1个压缩包整体约193MB结构清晰便于按需取用。图片内容覆盖不同拍摄角度、光照与人员姿态有助于提升模型在真实场景中的泛化能力。目前已有855人学习使用适合需要快速获取高质量标注数据进行安全检测算法开发的用户。1. 安全帽与反光背心检测为什么这份 YOLOv9 标记数据集值得动手训练做工地安全监控的人都有一个共识安全帽检测不难难的是“什么时候都能检测出来”。白天还行一遇到夜间反光背心、背光环境和远距离小目标泛化能力立刻见底。这套资源提供了 2000 多张经 YOLOv9 标注的工地/道路场景图片同时覆盖安全帽和反光背心两类目标标注格式是 YOLO 同款 txt图片已经做过自动定向和 1280x720 黑边预处理。这意味着你拿到的不是一堆杂乱素材而是一份可以直接丢进训练流程的数据集。适合做安全生产检测的算法工程师、做智慧工地项目的研发团队也适合想用 YOLOv9 入门目标检测但不想从标图开始的开发者。接下来我会从数据格式、训练配置、推理部署到避坑把这套资源的完整用法拆开讲。2. 数据集结构与预处理解析自动定向、1280x720 黑边和 YOLO 标注格式2.1 拆解文件名与 YOLO 标注先搞清楚手里有什么看到那一长串文件名第一反应别去猜直接当成一个规范来读Recording-2024-05-17-232902_000125_jpg.rf.88697464184b98bc697eabac5362d809.jpg拆开看Recording-2024-05-17-232902_000125是原始视频抽帧来源说明这张图来自 2024-05-17 的录像_jpg是 Roboflow 在导出时把原始图片文件名里的点替换成下划线避免路径解析出问题rf是 Roboflow 导出标记88697464184b98bc697eabac5362d809是图像内容的哈希值用来保证文件名唯一。另一类像File2_000023_...和1000_F_478827740_...则是现场拍摄或图库来源的静态图片。整个数据集的来源混合了监控录像和实拍照片后者为模型提供了更多视角变化前者提供了真实的现场光影和遮挡情况。对应的标注是txt文件与图片同名放在 labels 目录下。YOLO 格式每行五个数含义非常直接class_id center_x center_y width height前四个数都是相对于图片宽高的比例值取值在 0-1 之间。第 1 个数是类别 id从 0 开始。这里有个容易忽略的地方Roboflow 导出时坐标归一化是拿预处理之后的图来算的。也就是说如果图片经过了 1280x720 黑边填充标注坐标里已经包含了黑边的影响。你后面做推理预处理时只要和它不一致预测框就会偏移这一点我在第 5 章会专门展开。2.2 自动定向和黑边填充预处理对训练结果的影响摘要里提到的“自动定向”和“调整大小适合黑边1280x720”是两点关键预处理不建议自行改动。自动定向指的是读取图片 EXIF 中的旋转参数把图片纠正到正向。手机拍摄的现场照片经常带旋转角度监控平台导出的 JPEG 有时也会写入 Orientation 标记。如果不去正同一张图在不同软件里朝向不一致模型训练时等价于数据分布不稳定推理时输出的检测框会横七竖八。Roboflow 在导出前已经把 EXIF 方向修正过所以图片本身是正的。黑边填充是另一种思路。工地监控画面宽高比五花八门常见的有 1920x1080、1280x720、960x540甚至还有竖屏的手机拍摄图。目标检测模型一般会用固定输入尺寸训练如果直接把不同宽高比的图拉伸到 1280x720人会被拉扁安全帽形状也会变形模型学出来的特征就不稳定。标准做法是保比例缩放后在短边两侧补黑边也就是 letterbox。这套资源固定到 1280x720视野比例偏向 16:9正好匹配绝大多数监控画面模型训练时的有效细节也远高于 640x640 的常规配置。2.3 上手第一步目录核验脚本与类别检查拿到数据集后我习惯先做一次完整性核验而不是直接开训练。因为导出过程有时会因为网络中断或漏标出现某些图片没有 txt、或者 txt 是空文件。空文件意味着这张图一个目标都没有训练时会被当作负样本数量多了会把模型带偏。import sys from pathlib import Path def check_dataset(img_dir: str, label_dir: str): img_dir Path(img_dir) label_dir Path(label_dir) imgs sorted(img_dir.glob(*.jpg)) sorted(img_dir.glob(*.png)) missing, empty [], [] for img in imgs: lb label_dir / (img.stem .txt) if not lb.exists(): missing.append(img.name) elif lb.stat().st_size 0: empty.append(img.name) print(f图片总数: {len(imgs)} 张) print(f缺失标签: {len(missing)} 张, 空标签: {len(empty)} 张) seen set() for lb in label_dir.glob(*.txt): for line in lb.read_text().splitlines(): parts line.strip().split() if parts: seen.add(int(parts[0])) print(出现的类别 id:, sorted(seen)) if missing or empty: sys.exit(1) if __name__ __main__: check_dataset(sys.argv[1], sys.argv[2])运行方式很简单python check_dataset.py train/images train/labels。脚本会输出图片总数、缺失标签和空标签数量然后把所有标注里出现的类别 id 去重打印一遍。如果你看到[0, 1]说明标注包含两大类如果只有[0]那反光背心的标注可能在某目录下缺失了需要单独确认。这个检查能挡掉八成“训练到一半发现类别对不上”的问题尤其是数据集从网盘下载、文件夹层级出现变动的时候。注意脚本里img.stem取的是去掉.jpg后的完整文件名而 Roboflow 导出后的图片和 txt 恰好同名所以直接用img.stem .txt拼接路径可行。如果你后续重新扁平化过文件图片名和标签名可能变得不一致就需要额外做 basename 映射而不是直接拼接。提示如果下载包只提供 train 和 valid 两个目录test 可以从 valid 中划分别从 train 里抽否则验证集失去独立性。3. YOLOv9 训练实操数据配置、训练命令和关键参数的选择逻辑3.1 环境与目录结构准备先把训练环境准备好。YOLOv9 基于 PyTorch显卡建议至少 8G 显存因为输入尺寸 1280x720 比 640x640 多消耗近一倍显存。环境搭建按常规流程走先装对应 CUDA 版本的 PyTorch再安装依赖常见依赖是torch、opencv-python、pillow、pyyaml、tqdm、matplotlib这几个。目录结构建议按 Roboflow 导出的默认布局放不要乱动层级dataset/ ├── data.yaml ├── train/ │ ├── images/ │ └── labels/ ├── valid/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/如果你的下载包里只有 train 和 valid没有 test问题不大。valid 可以拆一部分当 test也可以用整个 valid 做验证只是最后报告的泛化数字略乐观。我一般会在训练前先看一眼 train/valid 各自的类别分布避免训练集里两类都有、验证集却只有安全帽这种情况否则验证阶段的 mAP 会失真。3.2 写 data.yaml类别 id 与标注严格对齐data.yaml 是 YOLOv9 读取数据集的入口。文件内容很简单但三个字段最容易出错。path: /your_abs_path/dataset train: train/images val: valid/images test: test/images names: 0: hard_hat 1: safety_vestpath建议写绝对路径YOLOv9 会把train/images拼接成{path}/{train}相对路径在切换工作目录时会找不到数据。names列表的长度和标注中的类别 id 最大值需要对应。这套数据集标注里出现过的 id 是 0 和 1对应安全帽和反光背心。如果你的项目里还希望区分“戴安全帽的人头”和“没戴安全帽的人头”那要重新做标注这里就不适用了。提醒一个细节数据集文件名里有.rf.hash段train/images目录里图片后缀统一是.jpgYOLOv9 解析时不会对文件名做正则匹配纯粹按目录扫所以带不带rf标记不影响读取。但如果你用自己的脚本批量改文件名千万不要改动.txt的文件名主体只改后缀位置会直接干掉同名对应关系。3.3 训练命令与超参数取舍显存、批次、图像尺寸数据配置就绪后我用类似下面的命令启动训练python train.py \ --data data.yaml \ --cfg models/detect/yolov9-c.yaml \ --weights yolov9-c.pt \ --batch-size 16 \ --imgsz 1280 \ --epochs 150 \ --device 0拆开讲几个关键参数。--cfg指定模型结构yolov9-c 是速度和精度的折中版本。如果你对实时性要求更高换yolov9-t或yolov9-s分辨率 1280 下仍然能保持较高帧率如果更看重精度、且 GPU 显存足够可以换yolov9-e。--weights是预训练权重建议从官方仓库下载对应结构的 COCO 预训练权重不要随机初始化从头训。工地场景的目标和 COCO 有部分语义重叠预训练权重能明显加快收敛。--imgsz 1280要和数据集预处理对齐。原因是标注坐标基于 1280x720 计算如果你把--imgsz改成 960 或 640训练时 YOLOv9 会先做自己的 resize黑边会被二次缩放坐标虽然是归一化的不会错但实际参与训练的目标像素变小小目标检测能力会下降。反过来如果你显存只够跑 640那建议重新导出数据集而不是强行用这套 1280 的标注跑 640 的输入。--batch-size 16是 16G 显存显卡的保守值。显存不够时优先降 batch 到 8而不是先降 imgsz因为输入分辨率对检测精度的影响远大于 batch size。梯度累积可以在 batch 过小时用但我不建议用 1 或 2 的 batch 硬训 YOLOv9batchnorm 层的统计量会非常不稳。训练过程中关注两个曲线训练 loss 和验证 mAP。正常情况前 20 个 epoch loss 快速下降后面缓慢收敛。如果你在 70 个 epoch 时 loss 还没掉下来优先排查第 5 章里的预处理不一致问题而不是继续加 epoch。我一般取runs/train/exp/weights/best.pt作为最终模型它是验证集上 mAP 最高的权重不是最后一轮的权重。4. 推理与部署从单图检测到 RTSP 监控视频流接入4.1 用训练好的模型跑单张图片检测训练完成后最快验证的方式是直接用 YOLOv9 仓库自带的 detect.pypython detect.py \ --source test.jpg \ --weights runs/train/exp/weights/best.pt \ --imgsz 1280 \ --conf-thres 0.45输出写到runs/detect/exp/图片上的检测框和类别已经画好。这里两个参数要盯紧--imgsz 1280必须和训练一致--conf-thres根据现场漏检和误检的平衡来调。工地监控场景我先把阈值定在 0.45后续根据误报率再往上提或往下压。想在自己的 Python 脚本里集成推理写法也不复杂import torch model torch.hub.load( WongKinYiu/yolov9, custom, runs/train/exp/weights/best.pt, force_reloadTrue ) model.conf 0.45 model.iou 0.45加载后模型对象接受原始图片数组内部会自动做 resize 和 padding。这里的关键在于torch.hub 加载自定义权重时预处理完全由模型配置决定。如果你训练时用的是 1280x720 的黑边方案而推理端按默认方形尺寸处理检测框的坐标换算就会出现偏差。我的做法是每次训练完把imgsz相关配置写进一个常量文件推理端直接引用同一个数值保证两端一致。4.2 接 RTSP 监控视频流做连续检测真实项目最终都要接到现场摄像头。下面是一个最小可用的 RTSP 推理脚本import cv2 import torch model torch.hub.load(WongKinYiu/yolov9, custom, runs/train/exp/weights/best.pt) cap cv2.VideoCapture(rtsp://admin:password192.168.1.64:554/Streaming/Channels/1) cap.set(cv2.CAP_PROP_BUFFERSIZE, 3) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, size1280) det results.xyxy[0].cpu().numpy() for x1, y1, x2, y2, conf, cls in det: if conf 0.45: continue color (0, 255, 0) if int(cls) 0 else (0, 0, 255) cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) label f{model.names[int(cls)]} {conf:.2f} cv2.putText(frame, label, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow(safety-detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码每帧推理一次。注意两点cap.set(cv2.CAP_PROP_BUFFERSIZE, 3)是把解码缓冲降到 3避免画面堆积导致推理延迟越来越严重model(frame, size1280)这里的 size 必须是训练时的输入尺寸不是摄像头原始分辨率。画框逻辑上我给安全帽和反光背心分别用了绿色和红色便于值班人员区分。颜色本身没有技术含量但在做值班大屏时很有用红色框代表反光背心目标值班人员能直观判断哪个工人着装不合规。如果你部署的监控点比较多记得每个 RTSP 流对应一个进程或线程不要在同一个进程里串行跑多路流否则解码会成为瓶颈。4.3 后处理策略目标过滤与业务规则绑定模型输出的每一帧检测框不能直接用还要套业务规则。我做工地项目时的规则一般有三条一是检测框置信度低于阈值直接丢弃避免地面反光或车牌误检二是反光背心的检测框如果中心点落在安全帽框的下方判定为同一个人这时只需要记录一次三是同一工人持续超过 N 秒未戴安全帽才触发告警避免单人瞬时低头造成误报。这些逻辑写在推理端而不是模型端好处是模型保持干净规则调整不用重新训练。选型上如果现场并发路数超过 20 路我建议把模型服务和推理分离用队列接视频帧不要每个摄像头都起一个完整 Python 进程。4.4 部署前的性能评估不要拿 mAP 直接推断部署速度。我先看三个指标单帧耗时、GPU 利用率、CPU 预处理耗时。实操方法是写一个计时脚本把model(frame, size1280)包在time.perf_counter()里连跑 200 帧取中位数。如果单帧耗时大于 100ms先看是不是 batch 设置过大再排查有没有在循环里做无意义的 BGR/RGB 反复转换最后看解码帧率是否低于模型推理帧率。我遇到过一种情况模型推理只要 40ms但整个循环跑下来要 120ms原因出在cv2.imshow在无显示器环境下会阻塞等待窗口刷新。去掉显示函数帧率立刻回到 20 以上。现场工控机一般不接显示器所以这个坑很常见。5. 避坑手册安全帽数据集中最容易翻车的五个场景5.1 标注坐标偏移预处理不一致是最隐蔽的坑现象训练 loss 正常收敛但推理时检测框整体往右下或左上偏移尤其是目标位于画面边缘时偏移最明显中心点目标基本准确。原因数据集的标注坐标是基于 1280x720 黑边图计算的。训练时如果用了不同尺寸或者推理前自己写了一个 resize 把图直接拉伸到 640 再送进模型模型输出的框是按它的输入坐标系来的画回原图时坐标换算错了框自然偏。这个坑最隐蔽因为很多人只看到训练能跑通、loss 能下降根本不会怀疑预处理。解决我后来强制规定数据集的 imgsz 参数在训练和推理两端必须保持一致。推理时用模型自带的 letterbox 逻辑不要手动 resize。如果一定要自定义预处理也要重复模型内部的 letterbox 过程等比缩放、补黑边然后把框坐标按比例映射回原图。黑边宽度计算公式为(1280 - 原图宽/原图高 * 720) / 2映射时要处理这个偏移量否则边缘目标的框全是歪的。5.2 类别 id 错位安全帽戴上反光背心的帽子现象训练出来的模型0 类安全帽检测正常1 类反光背心的框经常套在安全帽区域或者反过来。原因data.yaml 里的 names 列表顺序和标注文件中的 class_id 不对应。Roboflow 导出时会在每个 txt 里写入类别 id如果标注顺序和 YAML 里写的不一致模型学到的语义就错位。这类情况常见于多份数据集合并两份数据的 0 类一个是安全帽、一个是反光背心直接拼接就会混乱。解决合并数据集前跑一个统计脚本把每个 labels 目录里所有 txt 文件的类别 id 出现次数打印出来和 data.yaml 对比。更稳的做法是选几张图把标注可视化出来确认 0 类是安全帽、1 类是反光背心再开始训练。不要只看类别 id 数字对不对要看数字对应的语义对不对。5.3 夜间/逆光漏检增广策略不奏效时怎么办现象白天能检出 95% 以上夜间测试时反光背心在真实强反射下检测框乱跳甚至直接丢失逆光条件下安全帽和背景融为一体。原因数据集里夜间、逆光样本占比少模型对高动态范围场景的学习不够。盲目加大 mosaic、hsv 增强反而会让白天样本颜色变化过大影响基础特征。解决先统计数据集里夜间图片的数量如果小于 20%不要继续调增广直接扩充数据集。用现场摄像头夜间视频抽帧重新标注每类夜间样本增加 300 张就能让模型产生质变。也可以把预处理里加一个 CLAHE 自适应直方图均衡单独处理低照度帧但 CLAHE 必须同步用在训练端和推理端否则又引入预处理不一致。5.4 小目标漏检远距离安全帽看不见现象高空摄像头或者远处目标安全帽在 1280x720 的图中只有三四十像素高训练收敛后这类目标经常检不出来中近距离目标精度很高。原因小目标平均尺寸小与背景信息混合度高同时数据集里小目标样本占比偏低loss 计算时这部分目标的贡献权重本来就少。解决先别急着换大模型用--imgsz 1280保持住再检查数据集里小目标的比例。如果小目标确实稀缺补标数据比换模型更有效如果小目标占比已经接近真实场景换yolov9-e能提升一点但训练时间和推理开销增长明显。实际生产里更依赖的是高分辨率输入和针对性增广把小目标裁剪放大作为额外训练样本效果会好很多。5.5 训练正常但部署卡顿先从预处理管线找问题现象训练时 mAP 不错部署到工控机上帧率只有 3 FPSGPU 利用率很高CPU 也打满了整个循环都被拖住。原因多线程推理脚本里每帧都做了读图、resize、颜色转换RGB/BGR 转换重复操作CPU 预处理成为瓶颈。叠加cv2.imshow这类阻塞调用帧率会被进一步拉低。解决把预处理从推理线程里剥离出来固定使用 OpenCV 直接读入 BGR 帧送到模型resize 参数固定成常量不要每帧动态计算。更彻底的做法是转 TensorRT engine模型在前处理上不再使用 numpy 逐帧复制。实际部署中把输入尺寸固定为训练尺寸后解码到推理的流水线稳定在 15 FPS 以上才允许交付现场。6. 进阶技巧TensorRT 加速与数据闭环迭代6.1 ONNX 导出与 TensorRT 引擎转换当模型在 PyTorch 跑通但部署环境帧率不够时我会先转 ONNX再用 TensorRT 构建 engine。YOLOv9 仓库自带 export.py导出时指定权重和动态输入python export.py --weights runs/train/exp/weights/best.pt --imgsz 1280 --batch-size 1 --dynamic得到best.onnx后在装有 TensorRT 的机器上执行trtexec --onnxbest.onnx --saveEnginebest.engine --fp16如果显卡支持 FP16精度几乎没有肉眼可见下降推理速度提升一倍以上。一个可以参考的经验值GTX 1660 SUPER 上跑 FP16 的 yolov9-c1280x720 输入稳定在 12 FPS 左右换 RTX 3060 能到 25 FPS 左右。如果现场还用 CPU 做视频解码记得把解码和推理放到不同线程否则帧率会卡在解码侧。6.2 数据闭环让模型在使用中自我增强部署不是终点。三个月后回头看模型会过时因为现场环境变了太阳角度变了工人也换了不同类型的反光背心。我的习惯是每隔两周把误检和漏检的视频截下来按时间戳重新标注每次补 100-200 张放到原数据集里重新训练一轮。这个过程我称之为数据闭环。2000 多张的起始数据集一年下来会自然长到 8000 张模型精度反而越用越高。最后想说的是下载资源只是起点把数据集当成一个持续演化的工程资产来维护才是这类安全检测项目真正的核心竞争力。从那以后我每次拿到新数据集都会强制走一遍预处理核验、类别映射、训练推理尺寸对齐这三步再开训练踩过的坑一次都没再踩过。希望帮到你。本文还有配套的精品资源点击获取