简介本资源面向计算机视觉入门与进阶开发者提供一套完整的YOLOv5打电话行为检测方案可用于课堂演示、安防场景原型验证或毕业设计参考。包内包含训练好的打电话识别权重与配套数据集标注同时提供txt和xml两种格式并分目录存放方便直接训练或迁移到其他检测任务。资源共165个文件以py源码、pyc缓存、yaml配置、jpg与png图像、pt权重、ui界面文件及少量xml、csv、mp4等为主压缩包约433.91MB目录结构清晰便于按模块查阅。项目基于PyTorch框架与Python实现附带PyQt可视化界面支持检测图片、视频以及调用摄像头实时推理并保留训练日志与结果记录便于复盘训练过程与调参思路。目前已有604人学习下载适合希望快速跑通打电话行为检测全流程、对照代码理解数据标注与界面集成的读者参考。1. 从一通没人接的电话说起YOLOv5打电话行为检测到底在做什么监控画面里一个人举着手机贴在耳边这个动作持续超过十秒系统就该报警了。但问题是低头看手机、拿手机拍照、甚至挠耳朵在低分辨率摄像头下和打电话长得几乎一样。这就是YOLOv5打电话行为检测要解决的核心问题不是检测手机而是检测“人-手机-耳朵”三者构成的特定空间关系。整套方案通常包含四个部分YOLOv5负责逐帧定位人体和手机一个轻量分类逻辑判断两者是否构成打电话姿态训练好的权重文件让你跳过最耗时的标注和训练PyQt界面则把这一切封装成非技术人员也能操作的桌面工具。适合谁做工地安全、考场监控、驾驶行为分析的算法工程师或者手里有几百张标注图想快速跑通一个行为识别demo的学生。数据集和预训练模型的存在意味着你不需要从零开始但理解每一部分怎么衔接、参数怎么调、界面怎么和推理线程通信才是能不能真正落地的分水岭。2. 拆开这个技术包YOLOv5、数据集、PyQt界面各自扛什么活2.1 YOLOv5在行为检测里的角色定位很多人第一次接触这个标题会误以为YOLOv5直接输出“打电话”这个类别。实际上YOLOv5是一个目标检测器它输出的是边界框和类别标签。在打电话行为检测中常见的做法有两种第一种是定义两个类别——person和phone然后通过后处理逻辑判断person框和phone框的空间关系第二种是直接定义一个phone_call类别把“人举手机贴耳”的整体区域标注出来。第一种方案更灵活因为你可以复用公开的人体检测权重只需要补充手机标注第二种方案标注成本高但推理后处理简单。我一般推荐第一种方案原因有三人体检测的预训练权重非常成熟YOLOv5s在COCO上对person的AP已经很高手机作为小目标单独训练一个检测头比混合训练更容易收敛空间关系判断的逻辑可以随时调整比如把“手机框中心点在人体框上半部分且水平距离小于人体框宽度的三分之一”作为打电话的判定条件这个阈值可以根据摄像头角度微调。训练数据的组织方式遵循YOLOv5的标准格式每张图对应一个txt文件每行是class_id x_center y_center width height坐标归一化到0到1之间。如果你拿到的数据集已经分好了train、val、test直接检查目录结构是否符合images/train和labels/train的对应关系即可。2.2 数据集的组织结构与标注要点一个能用的打电话行为数据集通常包含以下几个维度的多样性室内和室外场景、白天和夜间红外、不同肤色和衣着、手机拿在左手或右手、以及负样本——也就是看手机但不打电话、拿手机拍照、手机放在桌上等。负样本的数量建议至少是正样本的两倍否则模型会把所有“人手机”的组合都判成打电话。标注时最容易翻车的地方是手机框的粒度。如果手机被手遮挡了一半你是标可见部分还是推测完整轮廓我的血泪经验是标可见部分但保证同一批数据里标注标准一致。YOLOv5对遮挡目标本身就有一定的鲁棒性但标注不一致会让模型学到一个模糊的边界。数据增强方面YOLOv5内置了Mosaic、HSV增强、随机翻转等。对于打电话行为检测我建议关闭上下翻转因为“手机在耳朵上方”和“手机在耳朵下方”是完全不同的语义。左右翻转可以保留因为左手打电话和右手打电话都是正样本。Mosaic增强在小目标检测上效果明显但如果你的数据集里手机本身就很小Mosaic拼接后手机可能变成几个像素反而有害。这时候可以把mosaic概率从默认的1.0降到0.5。2.3 PyQt界面与推理线程的通信方式PyQt界面负责三件事选择输入源本地视频文件或摄像头、显示检测结果、控制推理的启停。难点在于YOLOv5的推理是计算密集型的如果直接在主线程里跑界面会卡死。正确的做法是把推理放在QThread里通过信号槽机制把带框的图像传给主线程的QLabel显示。一个常见的错误是直接在子线程里操作UI组件这在Qt里是不允许的。必须定义一个信号比如frame_ready pyqtSignal(np.ndarray)在子线程里emit这个信号主线程连接到槽函数里做QPixmap.fromImage的转换和显示。另外视频帧的读取和推理速度不匹配时需要设置一个帧队列或者跳帧策略否则延迟会越积越大。界面布局上我习惯左边放一个大的视频显示区域右边放参数控制面板置信度阈值滑块、IOU阈值滑块、是否显示手机框的复选框、以及一个文本框显示当前帧的检测结果统计。这些控件通过valueChanged信号连接到推理类的属性实现运行时动态调整。3. 从零跑通训练YOLOv5打电话行为检测的完整命令链3.1 环境搭建与数据集目录规范假设你已经拿到了一个标注好的数据集目录结构应该整理成下面这样。YOLOv5对目录结构有约定不按这个来会在训练启动时报“No labels found”的错误。# 数据集根目录结构 phone_call_dataset/ ├── images/ │ ├── train/ # 训练集图片jpg或png │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片可选 ├── labels/ │ ├── train/ # 训练集标注txt与图片同名 │ ├── val/ │ └── test/ └── data.yaml # 数据集配置文件data.yaml的内容如下注意nc是类别数names的顺序必须和标注文件里的class_id对应# data.yaml path: ./phone_call_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 test: images/test # 测试集相对路径 nc: 2 # 类别数0person, 1phone names: [person, phone] # 类别名称顺序不能错环境安装用conda创建一个独立环境然后安装YOLOv5的依赖。注意PyTorch版本要和CUDA版本匹配否则训练时会报“CUDA error: no kernel image is available”。conda create -n yolov5_phone python3.8 -y conda activate yolov5_phone # 安装PyTorch以CUDA 11.3为例 pip install torch1.12.1cu113 torchvision0.13.1cu113 --extra-index-url https://download.pytorch.org/whl/cu113 # 克隆YOLOv5仓库并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt3.2 训练命令与关键超参数设置YOLOv5的训练入口是train.py最简命令只需要指定数据和模型配置。但打电话行为检测有几个参数需要特别关注。python train.py \ --data ./phone_call_dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml \ --name phone_call_v1 \ --cache逐项说明--weights yolov5s.pt表示从COCO预训练权重开始微调这比从头训练收敛快得多尤其在你数据集只有几千张的时候。--img 640是输入分辨率如果手机在画面里占比很小可以提到1280但显存占用会翻倍。--batch-size 16在12G显存下跑640分辨率比较稳如果OOM就降到8。--hyp指定超参数文件hyp.scratch-low.yaml适合小数据集学习率初始值较低避免过拟合。训练过程中重点看三个指标metrics/mAP_0.5、metrics/precision、metrics/recall。打电话行为检测对召回率更敏感因为漏检一个打电话的人比误检一个看手机的人代价更高。如果recall上不去先检查标注里有没有漏标手机再把--conf-thres在推理时调低。3.3 推理脚本与PyQt界面的对接训练完成后权重文件在runs/train/phone_call_v1/weights/best.pt。写一个独立的推理类封装YOLOv5的加载和预测方便PyQt调用。import torch import numpy as np import cv2 class PhoneCallDetector: def __init__(self, weights_path, conf_thres0.4, iou_thres0.5): # 加载YOLOv5模型指定设备 self.model torch.hub.load(./yolov5, custom, pathweights_path, sourcelocal) self.model.conf conf_thres # 置信度阈值 self.model.iou iou_thres # NMS的IOU阈值 self.model.classes [0, 1] # 只保留person和phone两类 def detect(self, frame): # frame是BGR格式的numpy数组 results self.model(frame) # 获取检测框、置信度、类别 detections results.xyxy[0].cpu().numpy() phone_call_boxes [] person_boxes [] phone_boxes [] for det in detections: x1, y1, x2, y2, conf, cls det if int(cls) 0: person_boxes.append([x1, y1, x2, y2, conf]) elif int(cls) 1: phone_boxes.append([x1, y1, x2, y2, conf]) # 空间关系判断手机框中心在人体框上半部分且水平距离近 for p in person_boxes: px1, py1, px2, py2, pconf p p_center_x (px1 px2) / 2 p_top_half py1 (py2 - py1) * 0.5 # 人体框上半部分的底边 for ph in phone_boxes: phx1, phy1, phx2, phy2, phconf ph ph_center_x (phx1 phx2) / 2 ph_center_y (phy1 phy2) / 2 # 手机中心在人体上半部分且水平偏移小于人体框宽度的1/3 if ph_center_y p_top_half and abs(ph_center_x - p_center_x) (px2 - px1) / 3: phone_call_boxes.append([px1, py1, px2, py2, max(pconf, phconf)]) return phone_call_boxes, person_boxes, phone_boxes这段代码的逻辑说明先分别收集person和phone的检测框然后遍历每一对“人-手机”组合判断手机中心点是否落在人体框的上半部分并且水平方向足够靠近人体中心。满足条件的就认为是一个打电话行为。参数conf_thres控制检测器输出的最低置信度iou_thres控制NMS的合并阈值。如果误检多提高conf_thres到0.5如果漏检多降到0.3。PyQt界面里把这个检测器实例放在QThread的run方法里每读一帧就调用detect然后用cv2.rectangle画框通过信号把画好框的帧发回主线程显示。4. 避坑与排查打电话行为检测落地时最容易翻车的五个地方4.1 现象模型把“看手机”全部判成“打电话”原因训练集里负样本不足或者负样本的标注里手机位置和正样本太相似。YOLOv5本身不区分“手机在耳边”和“手机在胸前”它只学到“人手机”的共现关系。解决补充至少两倍于正样本的负样本特别是“低头看手机”“手机放在桌上”“手持手机拍照”这三类。如果补充数据后仍然误检在空间关系判断里加一个角度约束手机框中心到人体框中心的连线与垂直方向的夹角小于30度。4.2 现象训练loss正常下降但mAP卡在0.3上不去原因标注文件里的类别id和data.yaml里的names顺序不一致。比如标注里0是phone、1是person但yaml里写反了。YOLOv5不会报错但模型学到的类别是乱的。解决用脚本统计所有标注文件里每个class_id出现的次数和yaml里的names对照。另外检查有没有标注框的宽高为0这种无效标注会拉低mAP。4.3 现象PyQt界面显示视频时卡顿延迟越来越大原因推理速度低于视频帧率帧在队列里堆积。比如视频是30fps但推理一帧要50ms每秒只能处理20帧剩下的10帧就积压了。解决在读取视频的循环里加一个跳帧逻辑当队列长度超过阈值时丢弃旧帧。或者用cv2.CAP_PROP_POS_FRAMES直接跳到最新帧。更彻底的做法是把推理分辨率从640降到416速度能提升一倍左右。4.4 现象换一个摄像头后检测效果急剧下降原因模型过拟合了训练时的摄像头角度和光照条件。打电话行为检测对视角很敏感仰拍和俯拍下手机和耳朵的相对位置完全不同。解决在数据增强里加入随机透视变换和随机裁剪模拟不同的拍摄角度。如果目标场景已知最好在目标摄像头上采集几百张图做微调冻结骨干网络只训练检测头学习率设小一点。4.5 现象导出ONNX后推理结果和PyTorch不一致原因YOLOv5的导出脚本里NMS是包含在模型里的但ONNX的NMS实现和PyTorch有细微差异。另外如果导出时没有指定--dynamic输入尺寸被固定换分辨率会报错。解决导出时用python export.py --weights best.pt --include onnx --img 640 --dynamic然后在推理端用onnxruntime加载自己实现NMS。对比两边在相同输入下的输出如果框的坐标差异在1到2个像素内是正常的差异过大说明导出时的opset版本不对试试--opset 12。5. 让检测更稳的几个进阶技巧从滑动窗口滤波到界面参数热更新训练和界面跑通之后真正决定这套系统能不能长期稳定运行的往往是后处理。原始YOLOv5的逐帧检测结果会有抖动同一部手机在连续帧里可能一会儿被检出、一会儿丢失导致界面上的框闪烁。我一般会加一个滑动窗口滤波器维护一个长度为N的队列只有当某个打电话行为在最近N帧里出现次数超过阈值时才最终判定为“持续打电话”。from collections import deque class SlidingWindowFilter: def __init__(self, window_size10, threshold6): self.window deque(maxlenwindow_size) # 固定长度的队列 self.threshold threshold # 触发报警的最小命中次数 def update(self, detected): # detected是当前帧是否检测到打电话行为的布尔值 self.window.append(1 if detected else 0) # 窗口内命中次数超过阈值才返回True return sum(self.window) self.threshold这个滤波器的参数需要根据实际帧率调整。如果摄像头是25fpswindow_size10意味着0.4秒的窗口threshold6表示至少0.24秒内持续检测到才报警。窗口太长会导致响应迟钝太短则起不到滤波作用。我通常会在PyQt界面上放一个滑块让用户实时调整threshold这样在不同场景下不用重新训练模型。另一个技巧是界面参数热更新。PyQt的滑块valueChanged信号可以直接连接到检测器实例的属性比如self.detector.model.conf value / 100。但要注意YOLOv5的model.conf是浮点数滑块传过来的是整数需要除以100。这个改动是立即生效的不需要重启推理线程。验证滤波效果的方法很简单录一段包含“拿起手机-打电话-放下手机”全过程的视频分别开启和关闭滤波器跑一遍统计误报次数和漏报次数。我的经验是滑动窗口滤波能把误报率降低60%以上代价是报警延迟增加0.2到0.3秒。对于工地安全这种场景这个延迟完全可以接受。最后说一个我踩过的坑PyQt界面长时间运行后内存缓慢增长。原因是每次推理返回的numpy数组没有及时释放Python的垃圾回收在Qt事件循环里触发不及时。解决办法是在子线程的循环里显式调用del frame和gc.collect()或者用torch.cuda.empty_cache()清理显存。这个习惯让我避免了好几次半夜被叫起来重启服务的尴尬。希望帮到你。本文还有配套的精品资源点击获取