资讯详情

资讯详情

基于YOLOv5的网课专注度检测系统:从目标检测到PyQt5桌面应用

简介这是一套基于YOLOv5目标检测的网课专注度检测系统完整项目主要面向计算机相关专业正在准备毕业设计或课程设计的学生。系统以Python为核心集成YOLOv5模型、PyQt5图形界面与人脸关键点检测模块能够对网课学习中的注意力状态进行识别分析项目经导师指导获评99分高分。资源包共122个文件包括39个Python源码、YOLOv5预训练模型与ONNX导出文件、界面UI、YAML配置、人脸关键点数据以及项目计划书和Docker部署文件整体大小约179.87MB代码完整、开箱即用。目前已有112人学习下载。除了完整可运行的系统还附带依赖whl包和容器化部署配置便于快速搭建开发环境可直接用于复现实验、二次扩展或作为毕业设计答辩展示的实物支撑。1. 网课专注度检测系统是什么从毕设选题到落地价值的快速判断摄像头对着书桌屏幕里老师讲得正投入屏幕前的人却低头刷手机——这就是网课最常见的翻车现场。基于yolov5目标检测的网课专注度检测系统要做的事很直接用电脑或外接摄像头捕捉学习者的画面实时判断这个人是否还“在状态”并把检测结果和专注度曲线做成一个PyQt5桌面程序。这个方案在毕设里属于典型的“目标检测 可视化界面”组合技术栈清晰演示效果好因此成了电子信息、计算机、人工智能等专业的高频选题。这套系统能解决三个实际问题一是把“专注度”这个抽象概念转成可量化、可回放的行为证据二是不依赖昂贵设备普通摄像头就能跑三是界面交付友好不是黑乎乎的命令行而是带实时检测框、计数器和曲线的窗口程序。适合的人群也很明确——正在做毕业设计的学生、想快速搭建行为检测原型的开发者以及打算把课堂专注度分析作为项目履历的求职者。接下来我按一条完整落地路径拆解算法选型、环境与代码跑通、界面接线、踩坑记录和验收方法。2. 网课专注度检测的方案选型为什么是yolov5而不是姿态估计2.1 行为判断的几种技术路线对比网课专注度检测本质上属于行为分析常见做法有三条路线人脸检测 视线估计、人体姿态估计、目标检测 行为规则。它们都能得出“专注/不专注”的结论但实现成本和可复现性差别很大。视线估计需要标注眼球关键点还要求摄像头清晰度足够否则稍微侧脸就失效姿态估计如OpenPose、MediaPipe能给出骨架但行为语义仍需要自己定义规则而且模型体积和推理耗时都偏高。相比之下基于yolov5目标检测的网课专注度检测系统把问题降了一维不去猜人看了哪里而是检测几个与专注度强相关的目标——正常看屏幕的人、低头玩手机的人、趴桌子的人、空座位。只要这些类别能被稳定检测出来专注度就能按类别和持续时间加权计算。这个思路在工程上非常划算。yolov5属于单阶段目标检测推理速度快、部署简单一个摄像头场景通常只需几十毫秒一帧而且网上有大量预训练权重和开源训练流程可供参考不必从零训练。下面用一个表格对比三种方案的适配度便于你在开题答辩时说明选型依据。技术路线核心任务数据标注成本推理速度落地难度适合场景人脸视线估计回归视线方向高需关键点中高高精度注意力分析人体姿态估计输出骨骼关键点高需17点以上标注低高动作识别、康复评估yolov5目标检测输出行为类别框低只需类别框高低课堂/网课专注度判断需要注意的是“专注度”本身没有标准定义必须把它映射到可检测的行为类别上。我一般会先和指导老师确认是重点检测“低头”行为还是“不在座位”还是“闭眼睡觉”这个定义直接决定标注方案也决定后面答辩时怎么回答“你的专注度算法依据是什么”。2.2 yolov5的网络结构与推理流程选定了yolov5还要理解它为什么能把行为类别检测得又快又稳否则答辩时被问到“网络结构”就会露怯。yolov5的主干网络是CSPDarknet作用是提取图像特征中间是PANet特征金字塔负责把不同尺度的特征融合起来让模型既能认清大目标人体也能识别小目标手机最后是检测头输出每个候选框的类别概率和位置偏移。你可以打开yolov5官方仓库里的yolov5s.yaml看结构里面从depth_multiple、width_multiple到每个模块的通道数都写得清清楚楚。对于网课专注度检测我更推荐使用yolov5s甚至更小的yolov5n作为主干因为场景是固定摄像头目标大小相对均匀不需要用yolov5x那样的大模型去换那零点几个点的mAP反而要保证实时FPS。推理流程也不复杂输入图像先做letterbox缩放保持宽高比并填充边缘经过网络前向计算后得到原始预测框再用NMS非极大值抑制去掉重叠框最终留下置信度最高的框。这一步在yolov5里由detect.py的non_max_suppression函数完成。很多新手会忽略一个细节训练时和推理时的图像尺寸要一致如果训练用了640x640推理时却把图像缩小到320小目标比如手机的召回率会明显下降。2.3 专注度评分需要依赖哪些检测目标跑通模型只是第一步专注度评分才是这个系统的灵魂。我给这个任务设计的类别一般包含studying正常看屏幕、looking_down低头、sleeping趴桌、absent空位。这样设计的理由是四类行为足以覆盖网课场景的绝大多数状态且类别之间语义互斥不容易出现同一个目标既是“看屏幕”又是“低头”的矛盾。评分策略采用时间窗口法设定一个滑动窗口例如30秒统计窗口内各类别出现的帧数占比其中looking_down和sleeping权重为0absent为0studying为1加权累加后映射成0到100的专注度分数。这个策略在论文里容易描述也方便画曲线图。如果你想让系统更细腻还可以引入“低头持续时长”作为惩罚因子——持续低头10秒以上专注度分数额外再扣一档这属于规则优化不改变模型结构。需要特别提醒的是不要把模型输出的置信度直接当专注度。置信度表示“模型有多确信这个框是某类行为”而专注度是“一段时间内的行为占比”二者含义不同。写代码时要把这两个数值分开存储否则后面画曲线时数据含义就乱了。3. 跑通源码并训练自己的数据集从环境搭建到换掉原有权重3.1 用conda创建隔离环境并安装依赖拿到一份完整的网课专注度检测源码第一件事不是急着运行而是创建干净的Python环境。yolov5对依赖版本比较敏感尤其是torch、torchvision和opencv-python的版本组合建议用conda隔离避免和系统里其他项目的Python包互相污染。这也是vscode配置Python环境时踩坑最多的地方——解释器选错了import torch直接报错。conda create -n focus python3.9 -y conda activate focus pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txt先创建名为focus的conda环境指定Python 3.9这是目前yolov5兼容性最好的版本区间。然后安装GPU版PyTorch这里指定了CUDA 11.7对应的版本如果你的显卡驱动版本不同需要去PyTorch官网选择对应命令。最后安装requirements.txt里的依赖。逻辑说明yolov5的推理和训练都依赖torch和torchvision必须匹配opencv用于图像读取和画框pycocotools用于COCO格式评估这些缺一不可。参数说明里最容易被忽略的是torch版本。如果你只有CPU环境把--index-url参数去掉即可但训练速度会慢十倍以上验证代码可以训练正式模型不推荐。安装完成后可以用python -c import torch; print(torch.cuda.is_available())确认GPU是否可用输出True再继续下一步。3.2 先跑通源码再动训练用预训练权重验证全流程拿到“源码模型”的压缩包后一般会有best.pt或yolov5s.pt这样的权重文件还有detect.py和train.py。我强烈建议先不改任何代码用自带权重跑一次推理确认环境没问题再看代码结构。这一步能帮你区分“环境问题”和“代码问题”。import torch # 加载训练好的权重文件 model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, force_reloadTrue) # 读取一张测试图片要求背景是真实网课场景 img test_student.jpg results model(img) # 打印检测结果包含类别名、置信度和坐标 results.print() results.save(save_dirruns/output/)这段代码用torch.hub加载本地权重custom参数表示加载的是自定义训练模型path指向权重文件路径。force_reloadTrue强制重新加载避免权重文件被缓存成旧版本。results.print()会输出每个目标的类别、置信度和坐标results.save()把画好框的结果图存到指定目录。运行逻辑说明如果这张图是网上随便找的人像模型大概率识别不出“looking_down”这类自定义类别因为类别名由训练数据决定。所以跑通验证时最好用源码作者提供的示例图或者你自己录制一段15秒网课画面截几帧出来测试。这样能确认权重和你设定的类别名是一致的而不是空有检测框但类别对不上。这里有个很容易翻车的点如果你的显卡显存只有4Gtorch.hub加载模型时默认加载到CPU还是GPU由torch.device决定。建议在推理代码里显式指定devicecpu或device0避免模型自动选择设备导致显存溢出。CPU推理一张640x640图片大约需要0.3到0.8秒验证流程够用做实时界面时再考虑GPU。3.3 标注自己的数据集从录制视频到labelImg标注网课专注度检测的公开数据集很少所以常见做法是自己动手制作。录制一段20到30分钟的网课实拍视频涵盖不同姿势、光线和时间段然后抽帧、清洗、标注形成自己的数据集。这是整条链路里最耗时的一步但也是决定模型上限的一步标注质量差后面所有训练参数调优都是白费。# 每隔8秒抽取一帧避免连续帧太相似造成数据冗余 ffmpeg -i lecture_video.mp4 -vf fps1/8 -q:v 2 extracted_frames/%04d.jpg # 启动labelImg标注工具 labelImg extracted_frames/ predefined_classes.txtffmpeg抽帧命令的参数含义-i指定输入视频-vf fps1/8表示每8秒输出一帧-q:v 2控制输出图片质量数值越小质量越高。抽帧间隔不能太短否则相邻帧几乎一模一样训练时不仅增加标注量还会让模型对相似样本过拟合。labelImg是常用的目标检测标注工具打开图片后画矩形框并选择类别保存为YOLO格式的txt文件每个txt文件对应一张图内容格式是“类别id x_center y_center width height”坐标全部归一化到0到1之间。标注时要注意类的定义一致性。我见过不少返工案例第一张图把“低头”标成“studying”后面又改成“looking_down”模型训练时同一个目标被赋予不同标签必然学得稀里糊涂。建议先用一个阈值类别的清单例如class 0studying、class 1looking_down、class 2sleeping、class 3absent然后每张图都严格按照这个顺序标注。标注工作完成后把数据集划分成train和val两个目录比例通常为9:1陌生场景越多留出的验证集比例也应适当提高。3.4 修改数据配置并启动训练关键超参数与调优技巧数据集准备好之后接下来就是yolov5训练自己的数据集这个关键操作。你需要新建一个data.yaml文件在里面指定训练集和验证集路径、类别数和类别名然后修改模型结构yaml文件中的nc参数最终启动训练。# data.yaml train: ./datasets/focus/train val: ./datasets/focus/val nc: 4 names: [studying, looking_down, sleeping, absent]python train.py --img 640 --batch 8 --epochs 100 \ --data data.yaml --weights yolov5s.pt \ --hyp data/hyps/hyp.scratch-low.yaml --device 0data.yaml是整个训练流程的索引文件train和val路径必须是训练机器上的绝对路径或相对路径很多新手把路径写错训练启动时报FileNotFoundError其实错误信息已经提示了具体哪个文件缺失照着改就行。nc必须和names列表长度一致否则训练过程会根据不一致自动中断或悄悄修正容易误导后续评估。训练命令的参数里--img 640表示输入图像尺寸数值越大模型能看清的细节越多但显存占用和训练时间同步上升--batch 8是批大小4G显存用88G以上可以提到16或32批大小影响收敛稳定性不要为了追求速度把batch调到1否则BatchNorm层的统计量会很不稳定。--epochs 100是训练轮数对于四类行为数据集100轮基本够用如果loss曲线还在明显下降可以接着训练。--weights指定预训练权重通常用yolov5s.pt做迁移学习--hyp指定超参数文件hyp.scratch-low.yaml代表保守的超参设置适合显存不大、数据量少的情况。训练过程中要盯住两个指标一个是训练集和验证集的loss曲线两者都下降说明模型正常收敛另一个是验证集的mAP0.5这个数值达到0.8以上说明模型在验证集上表现良好。训练结束后runs/train/exp目录下会生成best.pt和last.pt前者是验证集表现最好的权重后者是最后一轮的权重使用时优先选best.pt。把best.pt替换到推理代码里你的专注度检测系统就换上了自己的“大脑”。4. 用PyQt5把检测结果变成可交付的界面五步接线法4.1 界面骨架与视频流接入主窗口和摄像头线程分离命令行推理和带界面的检测系统是两种完全不同的体验。PyQt5界面设计的核心不是把OpenCV的imshow窗口嵌套进去而是用Qt的信号槽机制把视频帧从采集线程送到界面线程。很多人的界面一拖拽就卡死原因就是视频采集、模型推理和界面绘制全挤在主线程里一帧推理耗时0.3秒界面就卡顿0.3秒。import sys import cv2 import torch from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QMainWindow, QLabel, QVBoxLayout, QWidget class DetectWorker(QThread): change_pixmap pyqtSignal(QImage) def __init__(self, model_path): super().__init__() self.model torch.hub.load(ultralytics/yolov5, custom, pathmodel_path, force_reloadTrue) self.cap cv2.VideoCapture(0) def run(self): while True: ret, frame self.cap.read() if ret: results self.model(frame) annotated results.render()[0] # 返回画框后的BGR图像 rgb_image cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qt_image QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.change_pixmap.emit(qt_image)这段代码定义了一个DetectWorker线程类它继承QThreadrun方法里循环读取摄像头帧、执行模型推理、把结果转成QImage并通过信号change_pixmap发给主界面。这样做的好处是推理阻塞不会卡住界面因为主线程只负责接收信号并更新QLabel。参数说明里QImage的构造参数中最关键的是bytes_per_line它表示图像每行占用的字节数如果这个值写错图片显示出来会花屏或斜切。这里用ch * w计算因为图像是连续的内存排列。results.render()返回的是BGR格式的带框图像列表[0]取第一张注意OpenCV和Qt的颜色通道顺序不同必须先用cvtColor转成RGB否则界面里的红色框会显示成蓝色框。4.2 在界面上画实时检测框和专注度分数有了视频流接下来要把检测框、类别名和专注度分数显示在界面上并加上一个实时刷新的进度条或曲线。画框的工作不需要修改检测代码因为results.render()已经在图像上画好了框和标签但如果你想自己控制框的颜色和文字可以在PIL或OpenCV层面重新绘制。class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(网课专注度检测系统) self.label QLabel(self) layout QVBoxLayout() layout.addWidget(self.label) container QWidget() container.setLayout(layout) self.setCentralWidget(container) self.worker DetectWorker(best.pt) self.worker.change_pixmap.connect(self.update_ui) self.worker.start() self.focus_score 0 def update_ui(self, qt_image): self.label.setPixmap(QPixmap.fromImage(qt_image)) self.label.setScaledContents(True) # 这里根据检测结果更新专注度分数 # self.focus_score self.calculate_focus(results)主窗口的重点是信号槽连接self.worker.change_pixmap.connect(self.update_ui)意思是子线程每发一张QImage主线程就在update_ui里刷新标签。setScaledContents(True)让图片自动缩放匹配窗口大小。专注度分数的计算在update_ui里调用但注意calculate_focus需要拿到results对象所以实际项目中应该把results也通过信号传回主线程不能在线程里直接访问界面变量。在实际开发中界面布局建议再加几个控件一个QLabel显示当前专注度分数一个QProgressBar显示分数进度一个QTextBrowser用来打印检测日志。排版做到左边是视频画面右边是统计信息这是PyQt5界面设计的常规结构。另外退出程序时一定要在closeEvent里调用worker.stop()释放摄像头和模型资源否则摄像头一直被占用下次运行会报Device or resource busy。4.3 界面与推理速度的平衡帧率、队列和资源释放网课专注度检测界面对帧率的要求并不高15到20帧每秒已经足够流畅远高于肉眼的判断需求。但模型推理一次约0.1到0.3秒如果每一帧都阻塞等待界面交互就会变得迟钝。提升体验的常见做法是设置一个定时器每隔100毫秒读取一次最新帧而不是让线程无限循环地全速推理。from PyQt5.QtCore import QTimer self.timer QTimer() self.timer.timeout.connect(self.request_frame) self.timer.start(100) # 每100ms取一帧约10FPS这样设计的好处是推理频率可控CPU和GPU占用也低。数据流向变成摄像头线程只负责采集最新帧放入队列推理线程从队列取出帧进行推理界面线程按固定频率刷新。如果队列积压说明处理速度跟不上采集速度此时要做的不是增加线程数量而是降低摄像头分辨率比如从1080p降到720p或换成更轻量的模型否则四个线程一起抢资源只会让整体更卡。值得特别留意的资源释放问题推理过程中torch的Tensor和OpenCV的Mat都会占用内存长时间运行会出现内存缓慢上涨。解决方法是每隔一段时间调用gc.collect()并在每帧处理完后显式dellocal变量。摄像头释放放在closeEvent里通过调用self.worker.stop()让run循环退出随后调用cap.release()和cv2.destroyAllWindows()。这些细节决定了你的系统能不能在答辩现场连续演示半小时而不卡死。5. 避坑指南从拿到源码到答辩的全过程排查5.1 界面拖拽就卡死推理线程阻塞了UI主线程现象PyQt5界面一启动拖动窗口或点击按钮就卡顿画面像幻灯片。原因视频采集和模型推理全写在QMainWindow的初始化或事件回调里导致主线程被阻塞。Qt要求所有界面刷新都在主事件循环中进行任何耗时操作占住主线程界面就失去响应。解决把摄像头读取和yolov5推理移到QThread线程内用信号把处理后的图像传回主线程。如果推理线程也会卡死把它拆成“采集线程推理线程”两级中间用queue.Queue传递帧。我在第四节的DetectWorker已经给出了可用的线程骨架直接照搬即可。5.2 训练loss不降或mAP为0数据标注和配置文件的锅现象训练跑了几十轮loss波动很大或迟迟不收敛验证集mAP始终为0永远检测不出目标。原因90%的这类问题都不是模型结构问题而是数据配置错误。data.yaml路径写错导致训练集为空、标注txt不匹配、类别名顺序不一致、某类样本太少会造成模型完全学不到有效特征。yolov5本身不会告诉你标注有没有错它只会默默把结果反馈在mAP上。解决用debug模式核对三件事。第一在训练代码里打印train加载的图片数量确认路径正确第二随机抽取10张标注过的图片把yaml标注框可视化显示出来看看矩形框是否覆盖目标第三检查每个类别的样本数量把最少的类别扩增到其他类别的三分之一以上。如果数据不平衡在hyp文件中把对应类别的loss权重调大或采用过采样策略复制少数类样本。5.3 换机器后运行报错或检测结果完全不同版本和权重渠道现象在同一台电脑上运行时一切正常拷贝到另一台电脑跑就报ModuleNotFoundError或检测框变得乱七八糟。原因PyTorch和yolov5对Python环境和依赖版本极度敏感换了机器就意味着可能引入不同的依赖版本旧权重和新代码之间的兼容性出问题。另一个隐藏原因是模型结构配置不一致——训练时用的yolov5s.yaml和推理时torch.hub加载的代码版本不同命令行推理或界面推理都会跑偏。解决把requirements.txt和pip freeze requirements_frozen.txt都放进项目包里指明安装固定版本。在代码开头打印torch.version__和yolov5的__version确认两边一致再跑。权重文件最好和源码一起打包用相对路径引用不要靠绝对路径去读取否则换目录就找不到权重。5.4 目标小、光线暗导致漏检严重数据增强与测试时增强现象正常光线下检测很准但到了傍晚或背光场景手机、低头这类小目标大面积漏检甚至完全检测不到。原因摄像头传感器在小目标上的细节本来就少加上光线变化导致目标边缘对比度低模型在训练时没见到足够多这类样本泛化能力不足。小目标检测的果通常在这一步被拉低和模型大小没直接关系。解决训练时开启增强策略。在train.py的hyp文件中设置hsv_h、hsv_s、hsv_v的随机扰动范围把亮度变化范围调宽模拟不同时段的光照开启scale和translate增强让目标在画面中的位置和大小出现变化。推理时给model加上augmentTrue参数yolov5会通过水平翻转和多尺度推理提升小目标召回率代价是推理时间增加30%到50%但界面一般感受不到明显差别。5.5 长时间运行内存不断上涨显存和内存泄漏现象界面运行半小时后内存占用从几百兆涨到几个G笔记本风扇狂转最后程序卡死或被杀掉。原因推理循环中创建的Tensor、Mat和list没有被及时释放尤其是results对象里保留了每一帧的检测信息累积起来非常可观。PyQt的信号槽如果连接方式不当也会让QImage对象堆积在事件队列里同样造成内存膨胀。解决每帧推理后用del显式清理中间变量调用gc.collect()。在信号槽的connect方法中传入Qt.DirectConnection确保信号处理完再继续不要在队列中累计。还可以用torch.no_grad()包住推理代码禁止自动梯度计算这部分操作能省掉大量显存开销。6. 验证方法用一段真实网课视频做端到端验收并规划下一步系统做完后必须证明它真的“有用”而不是只停在“能跑”。我的验证方法是用一段自己录制的25分钟网课视频作为回放输入代替摄像头实时画面然后人工观看视频记录每个时间段的行为状态和系统的专注度曲线做对比。这一步能快速发现漏检类别和评分逻辑的偏差远比在答辩现场用摄像头随机演示更可控。开始回放前先在源代码里把VideoCapture(0)改成VideoCapture(test_video.mp4)然后跑完整段视频得到每帧的检测类别。人工记录部分我按每30秒一个窗口标记状态和系统输出的专注度分数放在同一张表格里比对。时间窗口人工判断系统判断是否一致0:00-0:30看屏幕studying一致5:30-6:00低头looking_down一致12:00-12:30趴桌sleeping一致18:00-18:30短暂离开absent一致如果连续三个窗口不一致就要回溯具体是哪一类检测错了然后补数据重训而不是盲目调阈值。验收通过的标准是90%以上的时间窗口判断一致且系统FPS不低于15帧。跑完回放后我还习惯用runs/detect目录下保存的带框视频重新看一遍确认框的位置没有大尺度偏移类别标签没贴错。进阶方向有三个一是引入目标跟踪用DeepSORT或ByteTrack把同一个学生的检测框串联起来统计单人持续低头时长这个指标比瞬时分类更有说服力二是把专注度评分做成滑动窗口加权平均让曲线更平滑界面显示效果会好很多三是接入数据记录模块把每天的检测结果写进SQLite或CSV文件方便长期统计这在答辩演示“系统完整性”时加分效果明显。每当把模型重训或改动界面代码之前我会习惯性地把当前最好的权重和标注好的数据集打一个压缩包存起来标注文件能和模型一起回滚。这个习惯在好几次数据覆盖后救了我一命。开头提到的这套方案只要按着环境搭建、数据准备、训练调参、界面接线和验证验收的顺序走新手的上手周期大约在一到两周希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →