ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于YOLOv5的网课专注度监测预警系统实战解析

基于YOLOv5的网课专注度监测预警系统实战解析 简介一份面向计算机相关专业毕业生和项目实战学习者的网课专注度监测预警系统完整工程基于YOLOv5目标检测实现抬头/低头、专注状态识别并搭配PyQt5构建桌面交互界面可辅助在线教学场景下的专注度统计与异常提醒也能直接作为毕业设计、课程设计的高分模板。压缩包共122个文件约179.87MB包含39个Python源码文件、YOLOv5模型文件.pt/.onnx、18个YAML配置、UI界面文件、Dockerfile部署配置、项目计划书Word文档以及测试图片与音频样本还附有编译后的pyc文件便于运行调试目录组织清晰解压后即可按说明复现。目前已有128人学习浏览。项目源自导师指导并通过的高分毕业设计评审99分代码完整、接口清晰从模型训练到界面展示均有迹可循适合需要快速搭建目标检测应用或完成毕设系统的读者参考、二次开发与实际部署。1. 网课专注度监测这件事为什么值得用目标检测做在线课堂里老师看不到学生的脸注意力管理基本靠自觉。这套基于YOLOv5的网课专注度监测预警系统解决的就是“屏幕前这个人到底有没有在听课”的问题。它的做法不是用传感器或浏览器插件而是直接调用摄像头画面通过目标检测实时框出人脸再叠加人脸关键点分析头部姿态和视线方向综合判断当前是否处于分心状态并在持续分心时触发预警。这个项目是作者的大四毕业设计评审分99分代码完整、模型已训练好带PyQt5图形界面适合两类人一是正在做毕业设计、课程设计或期末大作业的计算机相关专业学生想直接跑通一个能演示、能答辩的完整系统二是想学习“目标检测人脸关键点桌面应用”这条技术链路的开发者看看一个真实项目如何把模型工程化。整包包含YOLOv5源码、预训练权重、68点人脸关键点模型、PyQt5界面、Dockerfile、项目计划书和提示音文件拿到之后从环境配置到界面交互是一条完整闭环。2. YOLOv5在网课检测场景下的推理链路与工程配置2.1 为什么网课专注度检测选YOLOv5而不是更轻量的模型网课专注度检测的核心任务不是检测书本、手机这类小物体而是稳定地检测画面中的人脸和头部区域。YOLOv5在COCO数据集上对person类别的检测能力已经非常成熟预训练权重里对人脸、头肩的激活响应本身就很好迁移到专注度场景几乎不需要做额外的领域适配。相比YOLOv8、YOLOv9这些后续版本YOLOv5的生态更成熟网上能找到的教程、踩坑记录、模型转换工具最多对毕业设计来说意味着遇到问题时有足够的参考资源。另一个现实原因是硬件约束。网课监测通常跑在普通笔记本或台式机上没有独立显卡的情况很常见。YOLOv5s只有7.2M参数FP16推理在CPU上也能跑到10到15 FPS配合PyQt5的界面刷新足够完成实时检测。如果你在答辩现场用的是一台只有核显的机器YOLOv5s是少数能一边跑检测一边跑关键点分析还不至于卡死的方案。项目中自带的就是轻量级权重训练时用的也是yolov5s.pt作为预训练起点。2.2 推理主流程从视频帧到检测结果系统启动后摄像头或视频文件的一帧画面会先经过letterbox预处理然后送入YOLOv5模型完成前向推理最后通过NMS非极大值抑制去掉重叠框输出每个人脸的边界框。核心推理部分直接基于YOLOv5官方仓库的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保证CPU环境也能运行 model attempt_load(weights/best.pt, map_locationtorch.device(cpu)) model.eval() # 对输入帧做letterbox保持纵横比并填充到640x640 img cv2.imread(frame.jpg) img letterbox(img, new_shape640, stride32)[0] img img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB并调整通道顺序 img torch.from_numpy(img.copy()).float() / 255.0 img img.unsqueeze(0) # 增加batch维度 with torch.no_grad(): pred model(img)[0] # 前向推理获取原始预测 # conf_thres0.25, iou_thres0.45是YOLOv5默认阈值 pred non_max_suppression(pred, conf_thres0.25, iou_thres0.45) for det in pred[0]: # 将边界框坐标从640x640映射回原始图像尺寸 det[:4] scale_coords(img.shape[2:], det[:4], org_frame.shape).round() x1, y1, x2, y2, conf, cls det.tolist() cv2.rectangle(org_frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)这里的配置参数在实际调试时值得注意conf_thres控制漏检和误检的平衡网课场景下摄像头距离人脸通常1到2米人脸尺寸偏小如果阈值设到0.4以上会频繁漏检iou_thres控制重叠框的合并力度两个学生距离近时阈值太低会导致两个框合并成一个。项目中这两个参数分别取0.25和0.45是经过多轮测试的折中值如果你在自己的教室场景测试优先调整conf_thres而不是iou_thres。2.3 依赖环境的版本锁定与常见坑项目在README里给出了完整的requirements.txt这里列出最容易出问题的三个包的版本关系因为它们直接影响能否跑通包名推荐版本说明torch1.10.0cu113 / 1.12.1cpuCPU版本也能跑但推理速度差3到5倍opencv-python4.5.5.62或4.8.0.744.6以上版本对dnn模块改动大影响人脸关键点加载pyqt55.15.75.15.2在Windows高DPI缩放下有已知的模糊问题我在搭建时遇到的第一个坑是numpy版本冲突。YOLOv5官方要求numpy1.24但PyQt5的依赖会自动把numpy升到1.26导致运行时报module numpy has no attribute bool。解决方案是先安装PyQt5再手动降级numpy到1.23.5。另一个坑是torch的CPU版本如果直接用pip默认安装会拉到CUDA版在无显卡机器上启动时会报显存初始化错误需要到PyTorch官网选择CPU only的安装命令。注意Windows环境下YOLOv5的utils目录里部分脚本依赖pycocotools这个包在Windows上需要先安装Microsoft C Build Tools才能编译。项目提供的Dockerfile可以完全避开这个问题后面第5章会展开。3. 专注度判定逻辑人脸关键点、头部姿态与视线估计3.1 从YOLOv5框选出人脸到关键点定位的衔接YOLOv5给出的边界框只是“人在画面中的位置”要判断专注度还需要知道头部的朝向和视线方向。系统采用的是“YOLOv5检测人脸区域68点关键点定位”的两级级联结构先由YOLOv5给出人脸候选框再把候选框裁剪出来送入关键点模型。这样做比直接用OpenCV的Haar级联检测人脸更稳定因为YOLOv5对侧脸、低头、戴眼镜的鲁棒性明显更强。68点关键点模型来自dlib的shape_predictor_68_face_landmarks.dat但项目并没有直接用dlib库而是用OpenCV的Facemark模块加载。原因很简单dlib的依赖编译在Windows上太痛苦而OpenCV的Facemark API在读取这个.dat文件时做了格式兼容。项目中加载关键点模型并提取特征的代码大致如下import cv2 # 加载68点关键点模型对应shape_predictor_68_face_landmarks.dat facemark cv2.face.createFacemarkKazemi() facemark.loadModel(shape_predictor_68_face_landmarks.dat) # face_rect是YOLOv5检测出的人脸边界框 faces facemark.fit(frame, [face_rect]) landmarks faces[0][0] # 68个关键点的坐标数组 # 提取关键部位Indexdlib 68点模型中的定义 LEFT_EYE list(range(36, 42)) # 左眼6个关键点 RIGHT_EYE list(range(42, 48)) # 右眼6个关键点 NOSE_TIP 30 # 鼻尖 CHIN 8 # 下巴这68个点的索引顺序是dlib的标准定义你需要记住几个关键编号36到41是左眼轮廓42到47是右眼轮廓30是鼻尖8是下巴最低点33是鼻根。这些编号在后面的头部姿态解算中会反复用到。项目里已经把关键点坐标统一做了归一化处理除以图像宽和高确保不同分辨率下计算的欧拉角保持一致。3.2 头部姿态估计用solvePnP把2D关键点映射到3D姿态专注度判定中最重要的一个指标是头部是否偏离屏幕中心。实现思路是标准的PnP问题已知人脸关键点的2D坐标以及对应的3D人脸模型坐标通过solvePnP求解相机相对于人脸的旋转矩阵再分解出pitch、yaw、roll三个欧拉角。yaw角表示头部左右偏转pitch角表示上下俯仰roll角表示左右倾斜。import cv2 import numpy as np # 3D人脸模型坐标单位是毫米这是OpenCV官方文档的标准参考值 object_points np.array([ (0.0, 0.0, 0.0), # 鼻尖 索引30 (0.0, -63.6, -12.5), # 下巴 索引8 (-43.3, 32.7, -26.0), # 左眼外角 索引36 (43.3, 32.7, -26.0), # 右眼外角 索引45 (-28.9, -28.9, -24.1), # 左耳 索引0 (28.9, -28.9, -24.1), # 右耳 索引16 ], dtypenp.float64) # 对应的2D关键点坐标来自上一节的landmarks数组 image_points np.array([ landmarks[30], # 鼻尖 landmarks[8], # 下巴 landmarks[36], # 左眼外角 landmarks[45], # 右眼外角 landmarks[0], # 左耳面部轮廓最左侧 landmarks[16], # 右耳面部轮廓最右侧 ], dtypenp.float64) # 相机内参矩阵fx/fy取图像尺寸的近似值cx/cy取中心点 focal_length frame.shape[1] center (frame.shape[1] / 2, frame.shape[0] / 2) camera_matrix np.array( [[focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1]], dtypenp.float64 ) dist_coeffs np.zeros((4, 1)) # 假设无镜头畸变 success, rotation_vector, translation_vector cv2.solvePnP( object_points, image_points, camera_matrix, dist_coeffs, flagscv2.SOLVEPNP_ITERATIVE ) # 将旋转向量转换为旋转矩阵再分解为欧拉角 rotation_matrix, _ cv2.Rodrigues(rotation_vector) pitch, yaw, roll cv2.decomposeProjectionMatrix( np.hstack((rotation_matrix, translation_vector)) )[0].ravel()这里有个容易被忽略的工程点camera_matrix中的fx值直接取图像宽度这在摄像头没有标定的情况下是近似做法对yaw和pitch的估计误差在5度以内用于专注度分级完全够用。但如果你在笔记本上测试摄像头画面默认是640x480换到外接1080p摄像头后focal_length和center都必须按新分辨率重新计算否则欧拉角会产生明显偏移。3.3 视线方向估计与分心判定的综合策略头部姿态只能说明“脸有没有正对屏幕”但不能区分“看着屏幕发呆”和“真正在看课件”。系统的处理方式是再叠加一个视线估计用左右眼的6个关键点计算眼球的纵横比EAREye Aspect Ratio结合头部yaw角综合打分。EAR值的计算逻辑是def eye_aspect_ratio(eye_points): # 计算眼睛垂直方向两组点对的距离均值 vertical_1 np.linalg.norm(eye_points[1] - eye_points[5]) vertical_2 np.linalg.norm(eye_points[2] - eye_points[4]) # 计算眼睛水平方向距离 horizontal np.linalg.norm(eye_points[0] - eye_points[3]) # EAR值通常稳定在0.2到0.35之间闭眼时接近0 return (vertical_1 vertical_2) / (2.0 * horizontal)系统最终的分心判定采用加权打分制而不是单一的阈值判断。基础规则如下yaw角绝对值大于30度或pitch角绝对值大于20度判定为注意力偏离持续1.5秒以上触发一级预警EAR值低于0.18持续2秒以上判定为瞌睡或走神触发二级预警。一级预警只在界面状态栏变色提示二级预警会播放项目内置的fenxin.mp3提示音同时保存当前帧截图到本地。界面上的“专注度评分”是一个滑动窗口内专注帧占总帧数的百分比每10帧刷新一次。4. PyQt5界面集成实时视频流、状态面板与预警交互4.1 界面整体布局与信号槽通信设计项目界面采用PyQt5构建整体布局分三块左侧是实时视频显示区域右侧上方是专注度状态面板包含专注度百分比、当前状态、检测帧率右侧下方是操作按钮区开始检测、暂停、截图、退出。视频流显示用QLabel组件通过setPixmap方法逐帧刷新。界面和检测逻辑之间的通信采用Qt的信号槽机制这直接关系到界面卡顿与否。如果你在回调函数里直接做模型推理UI线程会被阻塞画面会像幻灯片一样。项目中正确的做法是把推理放到QThread线程里通过自定义信号把处理完的帧和检测结果传回主线程更新UI。核心封装如下from PyQt5.QtCore import QThread, pyqtSignal import cv2 class DetectionThread(QThread): # 定义两个信号frame_ready传递带检测框的画面status_ready传递状态数据 frame_ready pyqtSignal(object) status_ready pyqtSignal(dict) def __init__(self): super().__init__() self.running True self.cap cv2.VideoCapture(0) # 0表示默认摄像头 def run(self): while self.running: ret, frame self.cap.read() if not ret: continue # 执行YOLOv5检测和人脸关键点分析略见第2、3章 processed_frame, status self.process_frame(frame) # 通过信号把结果发回主线程 self.frame_ready.emit(processed_frame) self.status_ready.emit(status) # 控制帧率在15FPS左右避免CPU占用过高 self.msleep(66) def stop(self): self.running False self.cap.release() self.wait()信号槽机制在这里的价值是检测线程负责耗时的推理任务主线程只负责把收到的帧绘制到界面上。即使某帧推理耗时达到200毫秒界面也只会表现为帧率下降而不会出现“未响应”的系统弹窗。如果你在跑项目时发现界面拖拽窗口很卡先检查是否把推理直接写在了主线程的定时器里。4.2 视频帧类型转换与刷新策略YOLOv5推理输出的图像是RGB格式而OpenCV默认使用BGR格式PyQt5的QImage则需要RGB。这个转换链路如果处理不好画面会偏蓝偏红或者干脆显示成花屏。项目中的转换代码是标准的四步转换顺序不能错from PyQt5.QtGui import QImage, QPixmap import numpy as np def cv2_to_qpixmap(frame_bgr): # OpenCV默认BGR先转为RGB rgb_image cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape # 构造QImage对象注意bytesPerLine必须等于w*ch qimg QImage(rgb_image.data, w, h, w * ch, QImage.Format_RGB888) # 拷贝一份数据避免原图被释放后指针悬空 return QPixmap.fromImage(qimg.copy())我在实际调试中遇到过一个问题摄像头分辨率为1280x720时QLabel显示区域只有480x360如果直接用setPixmap拉伸检测框的位置和画面内容会错位。解决方法是先缩放QPixmap到控件的实际尺寸再设置到QLabel上代码为pixmap.scaled(label.width(), label.height(), Qt.KeepAspectRatio)。另外项目里状态面板的“帧率”显示不能直接用time.sleep来计算要用QElapsedTimer统计两帧间隔。4.3 预警声音与截图功能的实现细节预警功能在界面上主要有两个交互点状态指示灯的变色和提示音的播放。状态指示灯用一个QLabel设置样式表实现二级预警时背景色切换为红色并闪烁这里要注意避免在检测线程里直接操作UI组件因为Qt的UI操作必须在主线程执行需要再通过一个信号把预警级别传回主线程再由主线程调用提示音播放。提示音播放项目选用的是QSound类而不是pygame或playsound因为QSound是Qt自带的组件不额外增加依赖且支持异步播放不会阻塞推理线程。播放逻辑如下from PyQt5.QtMultimedia import QSound # 项目根目录下的fenxin.mp3为WAV格式提示音 alert_sound QSound(fenxin.mp3) alert_sound.setLoops(2) # 循环播放2次约持续3秒 # 在检测线程的二级预警分支里通过信号触发 self.alert_triggered.emit(level2)注意QSound只支持WAV格式如果你替换成MP3文件会直接播放失败且没有任何报错。项目里带的是已经转换好的WAV文件。另外首次播放时会有300到500毫秒的加载延迟如果预警触发时音效有滞后感可以在程序启动时先执行一次alert_sound.play()再立刻停止让系统预加载音频数据。截图功能相对简单在按钮的槽函数里获取当前QPixmap调用pixmap.save(fscreenshot_{time.time()}.jpg, JPG)。项目在保存时会自动在图片右下角叠加当前时间和专注度评分这个叠加逻辑复用了第2章里画检测框的cv2.putText代码只是把坐标换到图片底部区域。5. 训练自己的网课专注度数据集与系统部署优化5.1 数据准备用已有模型半自动标注如果你想把系统的检测能力迁移到自己的教室场景比如俯拍视角、多排学生、手部检测就需要重新训练YOLOv5模型。项目自带的数据集和标注文件可以用于跑通流程但真正贴合自己场景的数据还是要自己收集。常见做法是先用项目已有的best.pt权重对采集的视频帧做批量推理再把推理结果转成YOLOv5的txt标注格式最后用LabelImg或LabelStudio修正误检和漏检的框这个过程能省掉大约70%的人工标注工作量。YOLOv5要求的标注格式是归一化的类别中心点坐标宽高# labels.txt中每行对应一个目标这里以专注/分心二分类为例 # 类别0: focusing 专注 # 类别1: distracted 分心 0 0.512 0.384 0.156 0.322 1 0.733 0.691 0.118 0.274第一列是类别编号后面四列依次是归一化后的中心点x、中心点y、宽度、高度。标注文件的命名必须和图片完全一致比如img_001.jpg对应img_001.txt并且放在同一目录下。数据集目录结构需要严格遵循YOLOv5的约定dataset/ ├── images/ │ ├── train/ │ │ └── img_001.jpg │ └── val/ │ └── img_100.jpg ├── labels/ │ ├── train/ │ │ └── img_001.txt │ └── val/ │ └── img_100.txt └── data.yaml5.2 训练配置与超参数调整数据集准备好后用YOLOv5官方训练脚本启动训练。项目里针对网课场景对两个超参数做了调整你可以作为初始值参考python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache \ --patience 15--cache参数把图片预加载到内存中训练速度能提升30%左右但如果你的机器内存小于16GB建议去掉这个参数否则会触发OOM--patience 15表示验证集指标连续15个epoch没有提升就提前停止训练对毕设这种时间紧张的场景很实用不需要机械地跑满100个epoch。训练过程中的关键监控指标有三个指标关注点异常表现box_loss边界框回归收敛情况震荡不下降说明学习率过高mAP0.5综合检测精度低于0.85说明数据标注质量问题大val/objectness目标置信度偏高说明大量误检需检查标注框是否包含背景训练完成后导出最优权重替换项目根目录下的weights/best.pt。如果你的训练数据和原始数据分布差异很大建议把模型输入分辨率从640提高到768因为网课场景中桌面上的人脸偏小小目标检测需要更高分辨率的特征图。代价是推理速度下降约一半在CPU上会从15 FPS降到8 FPS左右是否启用取决于你测试机器的性能。5.3 Docker部署与无GPU环境下的推理加速项目根目录提供了Dockerfile目的是绕开Windows环境下依赖难装的问题。这个Dockerfile基于Python官方镜像构建核心思路是先安装PyQt5的系统依赖库包括libgl1、libglib2.0等OpenCV运行所必需的动态库再安装requirements.txt。需要注意的是容器里运行图形界面需要额外挂载X11 socketWindows下不建议直接跑容器取界面更推荐的做法是只用容器做模型训练和推理测试界面程序还是在宿主机上运行。如果你要在无GPU的Windows笔记本上做演示有一个不改变代码就能提升帧率的方法把YOLOv5的推理精度从FP32切换到FP16或INT8。对于FP16只需要在加载模型后加一行model.half()但CPU推理会因为不支持half运算而报错INT8则需要用torch.quantization做量化。更实用的方案是把输入尺寸从640降到416检测速度提升近一倍人脸框精度下降约2%到3%在演示场景下完全可以接受。最后需要验证系统是否在真实场景中稳定工作连续运行30分钟监控两件事一是帧率是否稳定不衰减二是预警触发后UI是否还能正常操作。项目的界面右上角有一个“系统自检”按钮点击后会依次检查模型加载、摄像头访问、关键点模型读取和音频播放四个关键路径任何一步失败都会在状态栏显示具体的错误模块这个功能在答辩现场非常有用能快速定位是外设问题还是环境问题。本文还有配套的精品资源点击获取
返回列表