睡岗玩手机检测数据集:VOC转YOLO与YOLOv8训练全流程
发布时间:2026/9/28 16:48:58来源:尧图网络
简介面向安防监控、行为识别方向的算法工程师与高校研究者这份睡岗与玩手机检测数据集可直接用于目标检测模型的训练与验证。资源包含4653张原始图像并配套VOC XML格式标注文件覆盖睡岗、玩手机两类典型违规行为适合课堂考勤、工位监管、值班室智能巡检等场景的算法开发与效果评估。压缩包内共2000个文件以XML标注文件为主与原始图片一一对应整体约140.37MB体积适中便于快速下载与本地解压使用。目前已有773人学习下载具备一定的社区验证基础。借助该数据集读者可省去繁琐的标注环节直接投入模型训练、数据增强与迁移学习实验也可用于对比不同检测网络在真实监控画面下的精度表现为后续部署与优化提供可靠的数据支撑。1. 睡岗玩手机数据集4653 张原始图怎么变成能训的目标检测集工地值班室、工厂中控室、矿区调度台这类场景的监控画面里最常出现的两类违规行为就是睡岗和玩手机。想用视觉模型自动识别第一步不是选模型而是先有一批标注好的图。这个标题讲的是一份 4653 张原始图、带 VOC XML 标注的数据集目标场景就是睡岗与玩手机检测。它解决的是「从零标注成本太高」的问题适合两类人一类是想快速验证睡岗/玩手机检测可行性的算法工程师另一类是手里有监控视频、想自己攒数据集但不知道标注格式怎么定的开发者。VOC XML 是这套数据最常见的载体理解它、清洗它、转成 YOLO 能吃的格式是落地绕不开的三步。2. VOC XML 标注到底存了什么字段拆解与选型理由2.1 一张 XML 里哪些字段真正影响训练VOC 格式的标注文件本质是一个 XML每张图对应一个同名.xml。很多人拿到数据集直接转格式转完发现框全错位问题往往出在没看懂字段含义。下面是一份典型的睡岗标注文件结构我按实际会读到的字段拆开讲。annotation folderimages/folder filenamesleep_0001.jpg/filename !-- 图片文件名必须和 jpg 同名 -- size width1920/width !-- 原图宽坐标换算的基准 -- height1080/height !-- 原图高 -- depth3/depth /size object namesleep/name !-- 类别名大小写敏感 -- poseUnspecified/pose truncated0/truncated !-- 是否被截断0/1 -- difficult0/difficult !-- 是否难样本训练时可过滤 -- bndbox xmin412/xmin !-- 左上角 x绝对像素 -- ymin236/ymin xmax890/xmax !-- 右下角 x -- ymax701/ymax /bndbox /object /annotation逻辑说明size里的宽高是坐标换算的唯一基准如果标注时用的是缩放后的图而size写的是原图尺寸框就会整体偏移。bndbox四个值是绝对像素坐标不是归一化值这点和 YOLO 的.txt完全不同。name是类别字符串睡岗场景常见写法有sleep、sleeping、shuigang混用转换前必须先统一。参数说明truncated和difficult这两个字段在睡岗/玩手机场景里很关键。监控画面里人经常被桌子、设备挡住一半truncated1的样本如果直接丢掉模型对遮挡场景的鲁棒性会明显下降。我的做法是保留truncated1但把difficult1的样本在训练时降权而不是一刀切删除。2.2 为什么这类场景优先选 VOC 而不是直接上 YOLO txt常见做法是标注阶段用 labelImg它默认导出 VOC XML。原因很实际XML 可读性强出错了肉眼能查改一个框不用重跑转换脚本而 YOLO txt 是纯数字五列数据错一位很难定位。4653 张这个量级标注周期通常要一到两周中途换人、换工具的概率很高XML 的容错性更好。另一个理由是复用。VOC 是通用格式转 COCO、转 YOLO、转 CSV 都有成熟脚本而 YOLO txt 想转回带类别名的可读格式反而麻烦。所以我的习惯是原始标注永远存 VOC训练前再转转换脚本固定下来不手工改 txt。提示如果数据集里同时有sleep和phone两类务必确认每张图的 XML 里 object 数量是否和实际一致。漏标比错标危害更大模型会把漏标的人当成背景学进去。3. 从 4653 张原始图到可训练集清洗、划分与转换3.1 先做一致性体检再谈转换拿到数据集别急着转格式先跑一遍体检脚本把命名不匹配、坐标越界、类别混乱三类问题揪出来。这三类问题在真实数据集里出现频率极高尤其是多人协作标注的。import os import xml.etree.ElementTree as ET IMG_DIR images XML_DIR annotations VALID_CLASSES {sleep, phone} # 统一后的类别白名单 def check_dataset(): imgs {os.path.splitext(f)[0] for f in os.listdir(IMG_DIR)} xmls {os.path.splitext(f)[0] for f in os.listdir(XML_DIR)} # 1. 图片和标注是否一一对应 print(缺标注的图:, imgs - xmls) print(缺图的标注:, xmls - imgs) for name in imgs xmls: tree ET.parse(os.path.join(XML_DIR, name .xml)) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) for obj in root.findall(object): cls obj.find(name).text.strip() if cls not in VALID_CLASSES: print(f{name} 非法类别: {cls}) xmin int(obj.find(bndbox/xmin).text) xmax int(obj.find(bndbox/xmax).text) ymin int(obj.find(bndbox/ymin).text) ymax int(obj.find(bndbox/ymax).text) # 2. 坐标越界检查 if xmin 0 or ymin 0 or xmax w or ymax h or xmax xmin or ymax ymin: print(f{name} 坐标异常: {xmin},{ymin},{xmax},{ymax}) check_dataset()逻辑说明脚本先比对图片和 XML 的文件名集合差集就是缺失项。然后逐文件解析检查类别是否在白名单内、坐标是否越界或宽高为负。坐标越界通常来自标注时手滑或图片被裁剪过。参数说明VALID_CLASSES要根据你的实际类别改。如果数据集里出现sleep、Sleep、sleeping三种写法先在这里列全再统一映射成一个不要指望模型自己区分大小写。3.2 训练/验证集划分别用随机划分坑自己睡岗和玩手机的数据集有个特点同一段监控视频截出来的帧高度相似。如果按帧随机划分训练集和验证集里会出现几乎一样的图验证指标虚高上线就翻车。正确做法是按视频源或时间段划分。import random from collections import defaultdict # 假设文件名前缀能标识视频源如 cam01_0001.jpg groups defaultdict(list) for f in os.listdir(IMG_DIR): src f.split(_)[0] # 按摄像头/视频源分组 groups[src].append(f) sources list(groups.keys()) random.seed(42) random.shuffle(sources) val_ratio 0.2 n_val max(1, int(len(sources) * val_ratio)) val_sources set(sources[:n_val]) with open(val.txt, w) as fv, open(train.txt, w) as ft: for src, files in groups.items(): target fv if src in val_sources else ft for f in files: target.write(os.path.join(IMG_DIR, f) \n)逻辑说明先按视频源分组再对源做随机划分保证同一来源的帧只出现在训练或验证一侧。这样验证指标才接近真实泛化能力。参数说明val_ratio设 0.2 是常规值但如果视频源总数少于 10 个建议提到 0.3否则验证集覆盖的场景太少。random.seed固定住保证每次划分一致方便复现。3.3 VOC 转 YOLO归一化坐标的四个边界坑转换的核心是把绝对像素坐标变成归一化的中心点加宽高。公式不复杂但边界处理容易出错。import xml.etree.ElementTree as ET def voc_to_yolo(xml_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): cls obj.find(name).text.strip() if cls not in class_map: continue xmin float(obj.find(bndbox/xmin).text) ymin float(obj.find(bndbox/ymin).text) xmax float(obj.find(bndbox/xmax).text) ymax float(obj.find(bndbox/ymax).text) # 边界裁剪防止越界导致归一化后超出 [0,1] xmin, xmax max(0, xmin), min(w, xmax) ymin, ymax max(0, ymin), min(h, ymax) cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_map[cls]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) return lines class_map {sleep: 0, phone: 1}逻辑说明先裁剪坐标到图像范围内再算中心点和宽高。class_map把类别名映射成从 0 开始的整数顺序一旦定下就不能改否则训练和推理的类别对不上。参数说明保留 6 位小数足够YOLO 读取时精度损失可忽略。如果某张图转换后某行宽或高为 0说明原始框退化成一个点这种样本要单独挑出来删掉否则训练时会报 NaN。注意转换后务必抽查几张用可视化脚本把框画回原图看一眼。我见过太多「转换成功但框全偏」的情况肉眼验证五分钟能省掉后面几小时的排查。4. 用这份数据集训 YOLOv8配置、参数与首轮验证4.1 目录结构和 data.yaml 怎么写YOLOv8 对目录结构有约定转换完的 txt 要和图片分开放。推荐结构如下dataset/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml是训练的入口配置字段不多但每个都关键path: /data/dataset train: images/train val: images/val nc: 2 names: [sleep, phone]逻辑说明path是数据集根目录train和val是相对路径。nc是类别数必须和names长度一致。names的顺序必须和转换时的class_map完全对应sleep是 0phone是 1写反了模型学出来的类别就是错的。参数说明如果数据集放在移动硬盘或网络盘path用绝对路径更稳。names里不要用中文YOLOv8 读取时可能编码出错用英文或拼音。4.2 首轮训练命令与必调参数首轮训练不要一上来就调大 epoch先用小轮次验证流程通不通。yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ lr00.01 \ patience15 \ projectruns/sleep_phone \ namebaseline逻辑说明modelyolov8n.pt用 nano 版先跑通速度快能快速暴露数据问题。imgsz640是默认输入尺寸睡岗和玩手机的目标通常占画面比例不小640 够用。patience15表示验证指标 15 轮不提升就早停避免无效训练。参数说明batch16要按显存调8G 显存跑 640 尺寸大概能到 16爆显存就降到 8。lr00.01是初始学习率如果 loss 一开始就震荡降到 0.001。epochs50只是首轮确认流程没问题后再加到 200 以上。4.3 看什么指标判断数据集质量训练跑起来后重点看三样mAP50、每一类的precision/recall、以及混淆矩阵。如果sleep的 recall 明显低于phone通常是睡岗样本里遮挡多、或者标注时把趴桌子的人漏标了。混淆矩阵里如果sleep和phone互相误判说明两类在画面里姿态接近需要补充区分度高的样本而不是继续加轮次。提示验证集指标好但实际监控画面漏检八成是划分时没按视频源隔离。回头检查第 3.2 节的划分逻辑。5. 避坑与排查睡岗玩手机数据集最常见的五个翻车点5.1 现象训练 loss 正常下降但验证 mAP 一直是 0原因data.yaml里的names顺序和转换脚本的class_map不一致或者nc写成了 1 但实际有两类。模型学到的类别索引和验证时对不上指标自然为 0。解决打印一张转换后的 txt确认第一列类别索引再对照data.yaml的names顺序。两者必须严格一致改完重新训练。5.2 现象框的位置整体偏移且偏移量随图片尺寸变化原因XML 里的size宽高和实际图片尺寸不符。常见于标注时用了压缩图但size填的是原图尺寸或者图片被批量裁剪过但 XML 没更新。解决跑第 3.1 节的体检脚本用 PIL 读实际图片尺寸和 XML 里的size比对不一致的直接修正 XML 或重新标注。5.3 现象模型把坐着低头的人也判成睡岗原因睡岗和「低头看手机」「趴着休息」在单帧画面里姿态接近标注时边界没划清。数据集里如果sleep包含了大量低头样本模型学到的就是「低头睡岗」。解决明确标注规范睡岗限定为闭眼且头部低垂或趴伏玩手机限定为手持设备且视线朝下。对边界样本单独建一个uncertain类训练时排除而不是硬塞进两类之一。5.4 现象小目标玩手机检测 recall 很低原因监控画面里手机目标本身很小640 输入尺寸下可能只剩十几个像素。加上玩手机的人通常坐着手机被手和身体遮挡。解决把imgsz提到 960 或 1280 再训一轮对比或者在数据增强里开启mosaic和scale增加小目标出现频率。如果还是不行考虑对画面做区域裁剪只对工位区域做检测。5.5 现象同一段视频的相邻帧分别进了训练和验证集原因划分时按文件名随机没按视频源分组。相邻帧几乎一样验证集等于变相泄漏。解决回到第 3.2 节按视频源或时间戳分组划分。如果文件名里没有来源信息用感知哈希对图片去重后再划分。6. 让这份数据集更值钱增量标注与难例回流4653 张能跑通 baseline但真正上线后你会发现漏检集中在几类场景夜间红外画面、人员密集遮挡、手机屏幕反光。这些靠原始数据集覆盖不到得靠难例回流补。我的习惯是部署一个轻量推理服务把置信度在 0.3 到 0.5 之间的检测结果自动存图每周人工过一遍确认是漏检还是误报然后按 VOC 格式补标再合并进训练集重训。这样数据集是活的模型迭代才有源头。具体操作上回流样本的命名要带日期和来源比如cam03_20240612_001.jpg方便追溯。补标时沿用同一套class_map不要中途改类别顺序。重训时新旧数据按 1:1 混合避免模型只学新场景而遗忘旧场景。验证方法很简单固定一个包含新旧场景的测试集每次重训后对比各类别的 recall只要没有明显下降就说明增量是正向的。我踩过最深的坑是早期图省事回流样本直接覆盖进原训练集结果类别分布被新场景带偏老场景的睡岗漏检率反而上升。后来改成独立存放、按比例混合才稳定下来。数据集不是标完就完事它是个需要持续喂养的东西。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网