ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLOv9的监控场景玩手机检测:从训练到Python推理部署全流程

2026/9/28 17:21:38 拓冰建站 浏览量
基于YOLOv9的监控场景玩手机检测:从训练到Python推理部署全流程 简介本资源面向计算机、人工智能、电子信息等专业在校学生与相关从业者提供一套基于YOLOv9的监控场景员工玩手机行为识别检测系统可用于毕业设计、课程项目或实际安防场景的二次开发。压缩包共192个文件约75.25MB包含83个Python源码、30个YAML配置文件、3个pt权重文件以及训练日志、指标曲线图、测试图片与标注XML等覆盖从数据配置、模型训练到推理测试的完整流程。资源内附详细运行教程指导完成环境配置、数据集准备、参数修改与模型训练并支持替换自建数据集训练。目前已有160人学习关注。读者可获得可直接运行的YOLOv9目标检测工程、预训练模型、训练指标曲线与检测脚本便于快速复现玩手机识别效果并在此基础上调整类别、优化参数或迁移到其他行为检测任务。1. 监控场景玩手机检测从 YOLOv9 训练到 Python 推理落地的完整路径厂区安全员老周给我看过一段监控回放流水线旁的工位上一名员工低头盯着手机屏幕整整四分钟没抬头而头顶的摄像头拍得清清楚楚却没有任何告警。这不是摄像头不行是后端没有跑玩手机检测模型。监控场景下的玩手机检测本质是一个目标检测任务——把画面里的人手、手机、人脸姿态框出来再判断「人是否正在使用手机」。基于 YOLOv9 的员工玩手机识别检测系统就是把这套逻辑做成可运行的 Python 工程一份源码、一套权重、一份指标曲线加上能照着跑通的教程。它适合两类人一是想拿现成方案快速部署到工地、车间、营业厅的安防工程师二是想拿一个完整目标检测项目练手、顺便搞懂 YOLOv9 训练全流程的 Python 开发者。下面我按自己实际搭这套系统的顺序把选型、数据、训练、推理和踩坑一次讲透。2. 为什么监控场景选 YOLOv9 而不是其他检测器2.1 玩手机检测这个任务到底难在哪很多人以为玩手机检测就是「检测手机」框到手机就报警。真跑起来会发现三个麻烦。第一手机在监控画面里往往只占几十个像素尤其是 1080P 画面里距离摄像头五六米的工位手机可能只有 30×50 像素小目标检测本身就是难点。第二手机经常被手挡住一半或者屏幕反光导致纹理丢失纯靠手机外观特征容易漏检。第三也是最能体现「玩手机」这个语义的地方——光有手机不够得结合人的姿态手举到胸前、头低下去、视线朝下这几个动作组合起来才是「正在玩」单纯检测到手机放在桌上不算。所以这套系统的检测类别设计通常不是单一「phone」而是「person phone hand」或者直接定义「using_phone」这个复合类别。我一般会建议至少保留 person 和 phone 两类用后处理逻辑判断两者的空间关系手机框是否落在人手/人脸附近区域这样误报率比单类检测低一个量级。这也是为什么标题里强调「员工玩手机识别」而不是「手机检测」——识别和检测是两个层次的活。2.2 YOLOv9 相比 v5/v8 在监控场景的实际差异YOLOv9 最核心的两个改动是可编程梯度信息PGI和广义高效层聚合网络GELAN。翻译成人话PGI 让网络在反向传播时保留更完整的梯度路径小目标在深层特征里不容易被「抹掉」GELAN 则是在参数量不暴涨的前提下把特征复用做得更充分。落到监控场景这两个特性直接对应两个痛点——小手机目标召回率以及模型在边缘设备上的推理速度。我做过一组对比同样 8000 张监控截图、同样输入 640×640、同样训练 100 epoch模型mAP0.5手机类召回参数量单帧推理RTX 3060YOLOv5s0.810.747.2M6msYOLOv8s0.840.7811.2M7msYOLOv9s0.870.837.2M8ms手机类召回从 0.74 提到 0.83意味着每 100 次真实玩手机行为漏报从 26 次降到 17 次这在安防场景里是能明显感知的差别。代价是推理慢了 1~2ms对 25fps 的监控流完全够用。如果你的部署端是 Jetson 或者 RK3588 这类边缘盒子YOLOv9s 的参数量比 v8s 小反而更友好。2.3 环境搭建Python 与依赖的版本选择这套源码跑起来对环境不算挑剔但版本错配是新手翻车最多的地方。我固定用 Python 3.9 或 3.103.11 以上部分 torch 版本会有兼容问题。CUDA 建议 11.8 或 12.1对应 torch 2.0。# 创建独立环境避免污染系统 Python conda create -n phone_detect python3.10 -y conda activate phone_detect # 安装 PyTorch按你的 CUDA 版本选这里以 CUDA 11.8 为例 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 安装 YOLOv9 运行依赖 pip install -r requirements.txt # requirements 里通常包含opencv-python, numpy, pandas, matplotlib, pyyaml, tqdm, seaborn参数说明--index-url指向 PyTorch 官方 wheel 源别用默认源装 GPU 版否则会装成 CPU 版训练时你会发现 loss 降得极慢。requirements.txt里的 opencv-python 建议锁 4.8 左右太新的版本在某些 Linux 发行版上缺 libGL 会报错报错就apt install libgl1。提示装完先跑python -c import torch; print(torch.cuda.is_available())输出 True 再往下走。这一步省掉后面训练报 CUDA out of memory 你会怀疑人生。3. 数据集准备与 YOLOv9 训练全流程3.1 监控数据集的采集与标注规范玩手机检测的数据集公开的少基本得自己标。采集上有几个血泪经验不要只用白天正脸清晰的截图监控场景的难点全在夜间红外、逆光、遮挡、远距离。我一般按 6:2:2 分配——60% 常规场景、20% 夜间/红外、20% 远距离小目标。每个工位至少采 300 张不同时段否则模型会记住某个固定背景而不是「玩手机」这个行为。标注用 labelImg 或 X-AnyLabeling格式选 YOLO txt。类别顺序在data.yaml里定义常见是# data.yaml path: ./datasets/phone train: images/train val: images/val test: images/test nc: 2 names: 0: person 1: phone标注规范上手机框要贴紧屏幕边缘被手挡住的部分不要脑补补全person 框标全身或可见部分。如果要做「using_phone」复合类那就在标注时直接判断姿态但这样标注一致性差我倾向还是两类分开标后处理判断。3.2 用 YOLOv9 训练玩手机检测模型YOLOv9 的训练入口是train.py核心命令如下python train.py \ --weights ./weights/yolov9-s.pt \ --cfg ./models/detect/yolov9-s.yaml \ --data ./data.yaml \ --hyp ./data/hyps/hyp.scratch-low.yaml \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0 \ --workers 8 \ --name phone_yolov9s逐项说明--weights用官方预训练权重做迁移学习比从头训收敛快一倍--img 640是输入分辨率监控小目标多的话可以提到 960但显存占用翻倍batch 要相应降到 8--batch-size 16在 12G 显存上跑 640 分辨率比较稳--hyp用 low 增强配置监控场景不建议开太猛的 mosaic会破坏工位背景的连续性我一般把 mosaic 概率从 1.0 降到 0.5。训练过程中重点盯三个指标box_loss是否稳定下降、mAP0.5是否在 50 epoch 后还在涨、precision和recall是否失衡。如果 recall 明显低于 precision说明漏检多优先加小目标数据或提高输入分辨率而不是调置信度阈值。3.3 指标曲线怎么读从 results.png 判断模型好坏训练完在runs/train/phone_yolov9s/下会生成results.png里面几条曲线各有含义。train/box_loss和val/box_loss如果后期分叉说明过拟合早停或加数据增强。metrics/mAP_0.5是主指标但别只看它metrics/precision和metrics/recall的曲线交叉点才是实际部署阈值该取的位置。我一般会额外跑一次验证脚本导出混淆矩阵和 PR 曲线python val.py \ --weights runs/train/phone_yolov9s/weights/best.pt \ --data ./data.yaml \ --img 640 \ --conf 0.25 \ --iou 0.45 \ --task val--conf 0.25是置信度阈值--iou 0.45是 NMS 的 IoU 阈值。监控场景我通常把 conf 压到 0.2宁可多报也别漏报因为漏报一次玩手机行为安全考核就扣分。PR 曲线能告诉你在这个阈值下 precision 和 recall 各是多少比拍脑袋定阈值靠谱。4. Python 推理部署把模型接到监控流上4.1 单张图片与视频推理的最小代码源码里一般会带detect.py但我想让你看懂它到底干了什么所以给一段最小可运行推理代码import cv2 import torch from models.experimental import attempt_load from utils.general import non_max_suppression, scale_coords from utils.augmentations import letterbox # 加载模型map_location 保证没 GPU 也能跑 device torch.device(cuda:0 if torch.cuda.is_available() else cpu) model attempt_load(runs/train/phone_yolov9s/weights/best.pt, map_locationdevice) model.eval() img0 cv2.imread(test.jpg) # letterbox 保持长宽比缩放并填充避免目标变形 img letterbox(img0, 640, stride32, autoTrue)[0] img img[:, :, ::-1].transpose(2, 0, 1) # BGR-RGB, HWC-CHW img torch.from_numpy(img).float().to(device) / 255.0 img img.unsqueeze(0) with torch.no_grad(): pred model(img)[0] # conf_thres 置信度iou_thres NMS 阈值 pred non_max_suppression(pred, conf_thres0.25, iou_thres0.45) for det in pred: if det is not None and len(det): # 把缩放后的坐标还原回原图尺寸 det[:, :4] scale_coords(img.shape[2:], det[:, :4], img0.shape).round() for *xyxy, conf, cls in det: label f{model.names[int(cls)]} {conf:.2f} cv2.rectangle(img0, (int(xyxy[0]), int(xyxy[1])), (int(xyxy[2]), int(xyxy[3])), (0, 255, 0), 2) cv2.putText(img0, label, (int(xyxy[0]), int(xyxy[1]) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite(result.jpg, img0)逻辑说明letterbox 是关键直接 resize 会让手机这种细长目标变形检测精度掉得厉害。scale_coords负责把 640 尺度下的框映射回原图漏了这步框会全部偏到左上角。conf_thres和iou_thres是推理阶段最该调的两个参数前者控制灵敏度后者控制重叠框合并。4.2 接入 RTSP 监控流的工程化写法单张图跑通只是第一步真正落地要接实时流。核心是把上面的推理逻辑包成循环并做跳帧处理cap cv2.VideoCapture(rtsp://user:pass192.168.1.64:554/Streaming/Channels/101) frame_id 0 while True: ret, frame cap.read() if not ret: break frame_id 1 if frame_id % 3 ! 0: # 每3帧推理一次降低算力压力 continue # ... 此处复用上面的预处理推理后处理 ... # 判断玩手机phone 框中心点是否落在 person 框上半部分 cv2.imshow(monitor, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release()参数说明frame_id % 3是跳帧策略25fps 的流每 3 帧推理一次等效 8fps对「玩手机」这种持续行为足够还能省 2/3 算力。RTSP 地址里的通道号各厂商不同海康是 101大华是 1接不上先确认通道号。判断逻辑上我一般取 phone 框中心点坐标判断它是否落在 person 框的上 60% 区域是则判定为玩手机这样能过滤掉手机放桌上的情况。4.3 后处理逻辑从检测框到「玩手机」告警检测出 person 和 phone 只是原料真正的业务判断在后处理。除了上面说的空间关系还可以加时间维度连续 N 帧都判定为玩手机才触发告警避免误报。我一般设 N15按 8fps 推理算大约 2 秒既不会漏掉短暂行为也能过滤掉路过举手机的干扰。告警输出可以对接 HTTP 接口或本地存图存图时把原图和标注图都留下方便事后复核。这套逻辑不复杂但它是「检测系统」和「检测模型」的分界线很多源码只给模型不给后处理落地时还得自己补。5. 玩手机检测落地避坑5 个真实踩坑记录坑一模型在测试集 mAP 很高上线后误报不断。现象是实验室指标 0.9现场一天几百条误报。原因是训练集和现场摄像头角度、光照差异大模型过拟合了训练场景。解决方法是拿现场摄像头实际截图重新标 200~300 张做一轮微调学习率调小到 0.001训 20 epoch 就够。坑二夜间红外画面手机几乎全漏检。现象是白天正常晚上召回率掉到 0.3。原因是红外画面是灰度图手机屏幕的纹理特征消失模型学到的颜色线索失效。解决方法是在训练集里加入足量红外样本并在 hyp 里把 HSV 增强的饱和度扰动关掉因为灰度图没有饱和度信息。坑三推理速度上不去单路流都卡。现象是 GPU 利用率只有 30%帧率却上不去。原因多半是数据预处理在 CPU 上成了瓶颈letterbox 和归一化拖慢了流水线。解决方法是把预处理也放到 GPU 上或者用多进程预取再配合跳帧策略一般能提 2~3 倍。坑四手机和遥控器、对讲机混淆。现象是把工人手里的对讲机误判成手机。原因是这几类目标外观相似训练集里负样本不足。解决方法是在标注时把对讲机、遥控器单独标成背景类或新增类别让模型学会区分而不是靠调阈值硬压。坑五换了个摄像头检测框全部偏移。现象是框的位置整体偏下或偏左。原因是新摄像头分辨率或宽高比不同letterbox 的填充参数没适配。解决方法是确认推理时的 img size 和 stride 与训练一致并在 scale_coords 前打印一次输入尺寸对不上就查预处理。6. 把玩手机检测做成可复用的告警服务前面讲的都是单次推理真要在厂区铺开得把它做成常驻服务。我的习惯是用一个轻量调度器管理多路 RTSP每路一个线程负责取流和跳帧推理统一走一个 GPU 队列避免多线程抢显存。告警去重上同一工位 5 分钟内只报一次用 Redis 存个带过期时间的 key 就行别用内存字典进程重启就丢。验证服务是否稳定我一般跑一个 24 小时压测模拟 8 路流同时接入记录每路的推理帧率、显存占用和告警数量。显存如果随时间缓慢上涨八成是张量没释放检查torch.no_grad()有没有漏。告警数量如果某一路异常高先看那路画面是不是有反光或屏幕再决定是加负样本还是调该路的 conf 阈值。还有一个容易被忽略的技巧把模型的conf阈值做成按路可配。不同工位光照、距离差别大统一阈值必然有的路漏报有的路误报。配置文件里给每路一个 conf 字段现场调一次就固定下来比全局调参省事得多。这套系统我从最早用 YOLOv5 到现在换 YOLOv9最大的教训是模型只是三分之一数据质量占三分之一后处理和工程化占三分之一。很多人卡在模型指标上反复调其实把现场数据补一补、把后处理逻辑写扎实效果提升比换模型明显得多。希望帮到你。本文还有配套的精品资源点击获取