反光衣检测数据集实战:3065张真实工地监控图与VOC/YOLO双格式解析 简介这份资源面向智慧工地、反光衣穿戴检测方向的算法工程师与项目开发者提供真实工地监控场景下的反光背心目标检测数据集可用于训练与验证YOLO系列、SSD、CenterNet、PP-YOLO、YOLOX、PP-PicoDet等主流检测模型支撑穿戴抓拍告警等落地应用。压缩包共7146个文件约943.91MB其中包含3065个xml与3065个txt标签文件分别对应VOC与YOLO两种标注格式另有1015张jpg示例图片方便快速核对标注效果。全部数据由真实工地监控摄像头多视角采集背景与光照多样经labelimg纯手工标注标注精准实际项目验证拟合良好。目前已有1090人学习下载。受上传体积限制包内仅附部分图片完整图片资源提供百度云下载链接适合需要高质量真实场景数据、快速开展模型训练与效果验证的读者使用。1. 反光衣检测数据集怎么选3065 张真实工地监控图能解决什么问题工地出入口的摄像头画面里安全帽和反光衣是两类最常被算法漏检的目标。安全帽颜色固定、轮廓清晰模型容易学反光衣不一样它靠的是高亮反光条在逆光、扬尘、夜间补光下的局部高对比度一旦曝光过度或者被脚手架遮挡标注框里就只剩一团灰。我见过不少团队拿公开数据集训出来的模型在实验室 mAP 能到 0.85拉到真实工地一跑反光衣召回直接掉到 0.5 以下原因就是训练集里全是正面、顺光、单人、干净的样本。这个标题里的数据集核心价值在于「真实工地监控多视角拍摄」这几个字。3065 张图不算大但如果是多机位、多时段、多天气采下来的它的分布覆盖就比几万张同质化网图有用。同时它给了 VOC 和 YOLO 两种标签格式意味着你不需要自己写转换脚本直接能接 Pascal VOC 那套 XML 流程也能直接喂给 YOLO 系列训练。对做智慧工地、做反光衣检测落地的从业者来说这类数据集解决的是「冷启动没有真实场景样本」的问题适合安防算法工程师、工地 AI 产品团队以及正在拿 YOLO 入门目标检测、想找一个有业务含义的实战项目练手的人。需要先明确一点3065 张是「图」的数量不是标注框数量。反光衣检测里一张图可能有三五个人也可能一个人都没有负样本负样本对降低误报很关键所以拿到数据后第一件事是统计框数和类别分布而不是直接开训。2. 拆开这个数据集VOC 与 YOLO 双格式到底差在哪2.1 Pascal VOC 的 XML 结构与反光衣标注习惯Pascal VOC 格式每张图对应一个同名 XML 文件核心节点是annotation下的filename、size、object。反光衣检测通常只有一个类别常见命名是vest、reflective_vest或reflective_clothing具体以数据集里的name字段为准。一个典型的 XML 长这样annotation folderimages/folder filenamesite_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object namevest/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin412/xmin ymin236/ymin xmax508/xmax ymax401/ymax /bndbox /object /annotationbndbox是左上角和右下角的绝对像素坐标不是归一化值。difficult字段在 VOC 评测里表示难样本训练时很多框架默认忽略它但反光衣场景里被遮挡的目标恰恰是难点我一般会把difficult全部当 0 处理让模型硬学。truncated表示目标是否被图像边界截断工地监控里边缘人物很常见这个字段要保留但一般不参与损失计算。2.2 YOLO 的 txt 标签与归一化坐标YOLO 格式每张图对应一个同名 txt每行一个目标格式是class_id x_center y_center width height全部归一化到 0~1。同一个框转成 YOLO 就是0 0.2396 0.2949 0.0500 0.1528计算方式是x_center (xmin xmax) / 2 / widthwidth (xmax - xmin) / width。这里最容易翻车的是类别索引VOC 里写的是字符串vestYOLO 里必须是整数0而且要和data.yaml里的names顺序严格对应。如果数据集里混了安全帽类别索引错一位模型就会把反光衣学成安全帽loss 看着在降实际全错。2.3 两种格式的选型判断维度VOC XMLYOLO txt可读性高字段自解释低纯数字存储体积大单文件冗余多小一行一目标训练框架MMDetection、Detectron2、PaddleDetectionUltralytics YOLO 系列转换成本需脚本转 YOLO需脚本转 VOC反光衣场景适配适合做数据审查适合直接开训如果你只是想把模型跑起来看效果直接用 YOLO 格式如果要做数据清洗、逐张核对标注质量VOC 的 XML 更好读。这个数据集两种都给省掉的是转换和校验的时间但省不掉「你得自己抽查标注对不对」这一步。3. 用 YOLO 格式跑通反光衣检测的最小流程3.1 目录组织与 data.yaml 配置拿到数据后先按 Ultralytics 的约定整理目录不要直接把几千张图堆在一个文件夹里dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml是训练的入口配置字段少但每个都关键path: /home/user/dataset train: images/train val: images/val test: images/test nc: 1 names: 0: vestpath用绝对路径最稳相对路径在不同工作目录下启动训练时经常找不到文件。nc是类别数反光衣单类就写 1。names的键必须从 0 开始连续和 txt 里的 class_id 一一对应。我一般会先跑一遍校验脚本确认每张图都有对应 label、每个 label 的坐标都在 0~1 之间再开始训练。3.2 划分训练集与验证集的比例3065 张图我通常按 8:1:1 切训练 2450 左右、验证 300、测试 300。反光衣检测有个坑如果按随机切分同一个监控机位、同一时段的连续帧可能同时进训练和验证验证集精度会虚高。正确做法是按「拍摄时段或机位」分组切分让验证集里的场景在训练集里没出现过这样测出来的指标才接近上线表现。import os, random, shutil random.seed(42) src_img raw/images src_lbl raw/labels groups {} # 按文件名前缀分组模拟机位/时段 for f in os.listdir(src_img): key f.split(_)[0] groups.setdefault(key, []).append(f) keys list(groups.keys()) random.shuffle(keys) n len(keys) train_k keys[:int(n * 0.8)] val_k keys[int(n * 0.8):int(n * 0.9)] test_k keys[int(n * 0.9):] def move(keys, split): for k in keys: for f in groups[k]: shutil.copy(os.path.join(src_img, f), fdataset/images/{split}/{f}) lbl f.rsplit(., 1)[0] .txt shutil.copy(os.path.join(src_lbl, lbl), fdataset/labels/{split}/{lbl}) move(train_k, train) move(val_k, val) move(test_k, test)这段脚本的关键在key f.split(_)[0]用文件名前缀当分组依据。如果你的数据命名没有规律就退而求其次按文件修改时间分桶。random.seed(42)是为了让切分可复现团队协作时别人能拿到和你一样的划分。3.3 启动训练与关键参数环境用 PyCharm 或命令行都行Ultralytics 装好后一条命令能跑yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/vest \ nameexp1modelyolov8n.pt是 nano 版本3065 张图用 n 或 s 就够上大模型容易过拟合。imgsz640是默认输入尺寸工地监控原图 1920×1080缩到 640 后远处的小目标反光衣可能只剩十几个像素如果漏检严重就提到 960 或 1280代价是显存和速度。patience20表示 20 轮验证指标不升就早停避免无效训练。lr00.01是初始学习率小数据集上如果 loss 震荡降到 0.005 再试。训练过程中重点看metrics/mAP50(B)和metrics/recall(B)。反光衣检测里召回比精度更重要漏检一个人可能意味着安全监管失职误检一个反光条只是多一次人工复核。如果 recall 上不去优先查标注框是不是把反光条裁得太紧适当外扩 5~10 像素往往有效。4. 从 VOC 转 YOLO脚本、校验与四个边界坑4.1 转换脚本与坐标裁剪虽然数据集给了两种格式但实际项目里你经常需要自己转比如补充标注后只更新了 XML。转换脚本核心是解析 XML 并归一化import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 裁剪到图像边界内防止越界坐标 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(w, xmax), min(h, ymax) xc (xmin xmax) / 2 / w yc (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) class_map {vest: 0} for f in os.listdir(raw/xml): voc_to_yolo(os.path.join(raw/xml, f), os.path.join(raw/txt, f.replace(.xml, .txt)), class_map)class_map把字符串类别映射成整数新增类别时只改这里。坐标裁剪那两行是后悔药工地监控里人物贴边时标注框经常超出图像范围不裁的话归一化后会出现负数或大于 1 的值YOLO 训练时直接报错或静默丢弃。4.2 转换后的三项校验转完不要直接开训跑三个检查一是每张图是否有对应 txt二是每行是否恰好 5 个字段三是坐标是否都在 0~1。用下面这段快速过一遍import os bad [] for f in os.listdir(raw/txt): with open(os.path.join(raw/txt, f)) as fp: for i, line in enumerate(fp): parts line.strip().split() if len(parts) ! 5: bad.append((f, i, 字段数不对)) continue vals list(map(float, parts[1:])) if any(v 0 or v 1 for v in vals): bad.append((f, i, 坐标越界)) print(f问题文件数: {len(bad)}) for b in bad[:10]: print(b)bad列表里每一条都对应一个具体问题先打印前 10 条定位规律。如果大量文件都是「坐标越界」说明原 XML 标注本身有问题需要回到标注工具里修而不是在转换脚本里硬裁。4.3 四个边界坑第一个坑是类别名大小写不一致Vest和vest在class_map里会被当成两类转换后一类全丢。第二个坑是 XML 里size的宽高和实际图片尺寸不符常见于图片被压缩过但 XML 没更新归一化后框全偏。第三个坑是空 XML即图片里没有目标但文件存在转换后生成空 txtYOLO 会把它当负样本这是对的但如果你误删空 txt模型就会把「没有反光衣的图」也学成有目标。第四个坑是文件名带空格或中文部分框架读取时会截断路径统一改成英文数字下划线最省事。5. 训练反光衣检测的避坑与排查清单5.1 现象mAP 很高但工地实拍漏检严重原因通常是训练集和真实场景的域差异。数据集里的监控图如果集中在白天模型没学过夜间补光下的反光条过曝形态。解决方法是把实拍漏检的图挑出来人工标 50~100 张加进训练集做一轮微调学习率降到 0.001epoch 30 左右即可不要从头训。5.2 现象loss 正常下降但验证集 recall 一直低先查标注框是不是只框了反光条本身而不是整个人。反光衣检测的业务定义通常是「人是否穿了反光衣」框应该覆盖人体上半身或全身。如果标注只框了细细一条反光带模型学到的是「找亮条」遇到反光条被遮挡就完全失效。解决方法是统一标注规范框到人体躯干范围。5.3 现象训练报错「No labels found」九成是路径问题。data.yaml里的train写的是images/train但实际目录多了一层dataset/或者 txt 和图片没有同名。排查时先确认dataset/images/train/xxx.jpg和dataset/labels/train/xxx.txt成对存在再确认path是绝对路径。Ultralytics 对路径的容错不高错一层就找不到。5.4 现象推理时同一件反光衣出多个框这是 NMS 阈值问题。默认iou0.7对密集人群偏松反光衣在画面里成片出现时容易重复检测。把推理时的iou降到 0.5 左右或者训练时开启agnostic_nms。但要注意降太低会把相邻两个人的框合并工地场景里人和人挨得近0.5 是个需要实测的平衡点。5.5 现象模型把白色安全帽误检成反光衣反光衣和安全帽在颜色和亮度上接近单类模型容易混。如果业务同时要检测两者就做成两类一起训让模型学区分如果只关心反光衣就在数据里把安全帽区域标成负样本背景或者加一批只有安全帽没有反光衣的图作为负样本压掉误报。6. 把 3065 张用到极致小数据集的增强与验证技巧3065 张图在目标检测里属于小数据集直接训容易过拟合验证指标好看但上线就露馅。我一般会做两件事一是离线增强扩样本二是用交叉验证代替单次划分。离线增强不要用太花的策略。反光衣检测里真正有用的是亮度扰动、运动模糊和随机遮挡因为工地监控的真实退化就是这些。Mosaic 和 MixUp 可以用但比例别超过 0.3否则反光条被拼得七零八落模型学不到完整形态。下面这段用 Albumentations 做一批离线增强把训练集扩到两倍左右import cv2, os import albumentations as A aug A.Compose([ A.RandomBrightnessContrast(brightness_limit0.3, contrast_limit0.3, p0.7), A.MotionBlur(blur_limit7, p0.3), A.CoarseDropout(max_holes8, max_height40, max_width40, p0.3), ], bbox_paramsA.BboxParams(formatyolo, label_fields[cls])) src dataset/images/train lbl dataset/labels/train for f in os.listdir(src): img cv2.imread(os.path.join(src, f)) h, w img.shape[:2] with open(os.path.join(lbl, f.rsplit(., 1)[0] .txt)) as fp: rows [list(map(float, l.split())) for l in fp if l.strip()] bboxes [r[1:] for r in rows] cls [int(r[0]) for r in rows] out aug(imageimg, bboxesbboxes, clscls) cv2.imwrite(fdataset/images/train_aug/aug_{f}, out[image]) with open(fdataset/labels/train_aug/aug_{f.rsplit(.,1)[0]}.txt, w) as fp: for c, b in zip(out[cls], out[bboxes]): fp.write(f{int(c)} { .join(f{v:.6f} for v in b)}\n)BboxParams(formatyolo)告诉库输入输出都是归一化坐标省去手动换算。CoarseDropout模拟遮挡MotionBlur模拟运动模糊这两个对反光衣场景最贴。增强后记得把train_aug也加进data.yaml的train列表Ultralytics 支持多路径。验证环节如果数据量实在小用 5 折交叉验证比单次 8:1:1 更可信。做法是把数据按机位分成 5 份轮流拿 1 份做验证其余训练最后看 5 次指标的平均值和方差。方差大说明模型对场景敏感上线风险高。我自己的习惯是任何反光衣模型上线前必须在一个完全没参与训练的独立机位上跑一遍recall 低于 0.8 就不发版。这个习惯帮我挡过好几次「验证集 0.9、现场 0.6」的翻车。数据集只是起点真正决定效果的是你对业务场景的理解和验证的严格程度。希望帮到你。本文还有配套的精品资源点击获取