资讯动态

基于深度学习的课堂专注度分析与作弊检测系统:Python实现与调优指南

发布时间:2026/10/2 22:01:32 来源:尧图企业网站定制
简介这份资源是一套基于深度学习实现的课堂专注度分析与考试作弊检测系统包含完整Python源码与配套文档说明面向计算机相关专业正在做毕业设计的学生以及需要项目实战练习的学习者。项目经导师指导并通过评审难度适中源码均经本地编译与严格调试可正常运行。压缩包共626个文件约87.63MB以383个py源码文件为核心辅以41个md文档、32个yaml配置、25张jpg与21个gif图示以及cfg、cu、cpp、sh等模型与脚本文件覆盖数据配置、网络定义、训练推理与结果展示等环节。内容预览中可见yolov3、RetinaFace等模型配置与权重文件说明系统涉及目标检测与人脸分析等技术路线。目前已有163人学习下载适合希望快速搭建可运行系统、理解深度学习落地流程并完成毕设或课程实践的读者参考使用。1. 课堂专注度分析与作弊检测从一段监控视频到可用告警的完整链路一间装了摄像头的教室一节课四十五分钟视频里同时存在几十张脸。教务真正想要的不是把视频存下来而是想知道这节课有多少人走神了、哪个时间段集体低头、有没有人在考试时频繁转头或传递东西。基于深度学习开发的课堂专注度分析和考试作弊检测系统本质就是把这段原始视频拆成人脸检测 → 头部姿态与视线估计 → 时序行为判定 → 告警输出四段流水线用 Python 串起来跑。它适合两类人一类是手里有课堂或考场视频、想快速搭出可演示原型的在校开发者另一类是拿到一份 Python 源码加文档说明、却卡在环境配置和参数调不对的工程新手。这篇笔记按先讲清每个模块为什么这么选再给能直接抄的命令和参数来写重点放在能复现、能调参、能排错上而不是泛泛谈深度学习有多强。2. 系统拆成四段流水线为什么不能一个模型端到端硬扛很多人第一反应是找个行为识别模型把视频丢进去输出专注或作弊。真做起来会发现这条路在课堂场景里几乎走不通作弊动作样本极少、标注成本极高端到端模型没有足够数据去学而专注度本身是个连续量不是离散标签。所以常见做法是拆成可解释的四段每段用成熟模型中间结果可单独调试。2.1 人脸检测与跟踪先解决谁是谁第一段负责在每帧里框出人脸并给同一张脸分配稳定的 ID。检测用轻量模型保证帧率跟踪用简单的 IoU 匹配或卡尔曼滤波即可不必上重型 ReID。原因是课堂里人基本不动跟踪难度低把算力留给后面的姿态估计更划算。import cv2 import numpy as np # 用 OpenCV 自带的人脸检测器做最小可跑版本替换成 YOLO 系检测器时接口保持一致 detector cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) def detect_faces(frame, scale1.1, min_neighbors5, min_size(60, 60)): gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # scaleFactor 越小越慢但越不容易漏检课堂远景建议 1.05~1.1 # minNeighbors 越大误检越少但小脸容易被过滤5 是常用折中 faces detector.detectMultiScale( gray, scaleFactorscale, minNeighborsmin_neighbors, minSizemin_size ) return faces # 返回 (x, y, w, h) 列表这段代码里scaleFactor和minNeighbors是最常被调的两个参数。教室后排人脸只有几十像素min_size设太大直接漏检设太小又会把窗户、投影仪误判成人脸。我的经验是先用一段真实课堂视频跑一遍把min_size定在最远那排脸的实际像素高度再乘 0.8然后微调minNeighbors压误检。跟踪部分给每个框维护一个 ID 和丢失帧计数连续几帧匹配不上才注销避免人脸被遮挡时 ID 频繁跳变。2.2 头部姿态与视线估计专注度的核心信号专注度最可靠的信号是头部朝向和眼睛开合。头部姿态用 6D 或欧拉角表示常见做法是检测人脸关键点后做 PnP 解算或用直接回归姿态的轻量网络。视线估计则看瞳孔相对眼眶的位置。两者结合能区分低头看书和低头玩手机——前者头低但视线在桌面后者头低且视线频繁下移。# 用关键点做头部姿态解算的骨架landmarks 为 68 点或 5 点 import numpy as np # 3D 人脸模型参考点简化版单位任意比例正确即可 MODEL_POINTS np.array([ (0.0, 0.0, 0.0), # 鼻尖 (0.0, -63.6, -12.5), # 下巴 (-43.3, 32.7, -26.0), # 左眼左角 (43.3, 32.7, -26.0), # 右眼右角 (-28.9, -28.9, -24.1), # 左嘴角 (28.9, -28.9, -24.1), # 右嘴角 ], dtypenp.float64) def estimate_head_pose(landmarks_2d, frame_size): h, w frame_size[:2] focal w # 粗略焦距真实标定可换成相机内参 center (w / 2, h / 2) cam_matrix np.array([[focal, 0, center[0]], [0, focal, center[1]], [0, 0, 1]], dtypenp.float64) dist np.zeros((4, 1)) ok, rvec, tvec cv2.solvePnP( MODEL_POINTS, landmarks_2d, cam_matrix, dist, flagscv2.SOLVEPNP_ITERATIVE ) if not ok: return None rmat, _ cv2.Rodrigues(rvec) # 从旋转矩阵提取俯仰角负值通常代表低头 pitch np.degrees(np.arcsin(-rmat[2, 1])) return pitchfocal用图像宽度近似是常见偷懒做法误差在可接受范围但如果要精确到几度得用棋盘格标定相机内参。pitch的阈值不是固定的前排学生和后排学生因为俯仰视角不同同样的低头动作算出来的角度不一样。稳妥做法是按人脸框在画面中的纵向位置做一次线性补偿或者干脆对每个跟踪 ID 记录一段基线用相对变化判断。2.3 时序行为判定把逐帧信号变成事件单帧姿态没有意义专注度是时间维度上的统计量。常见做法是滑动窗口每 1 秒聚合一次统计窗口内低头帧占比、视线偏离占比、头部转动方差。作弊检测则关注短时间内的异常模式比如头部在 2 秒内左右摆动超过阈值、或手部区域频繁进入画面下方。from collections import deque class FocusTracker: def __init__(self, window_sec30, fps25): self.window deque(maxlenwindow_sec * fps) self.fps fps def update(self, pitch, gaze_off): # 低头超过 20 度或视线偏离记为不专注帧 unfocused 1 if (pitch -20 or gaze_off) else 0 self.window.append(unfocused) def focus_ratio(self): if not self.window: return 1.0 return 1.0 - sum(self.window) / len(self.window) def is_suspicious(self, turn_events): # 30 秒内头部大幅转动超过 5 次判为可疑 return turn_events 5window_sec决定告警灵敏度窗口太短一次正常低头就触发告警太长作弊动作被平均掉。课堂专注度统计我一般用 30 秒窗口作弊检测用 5 到 10 秒的短窗口。turn_events的计数要在跟踪 ID 维度上做不能全画面累加否则一个人频繁转头会污染所有人的统计。2.4 告警输出与可视化让结果能被人看懂最后一段把事件写成结构化记录并叠加到视频上。输出通常包括每个学生的专注度曲线、整节课的专注度均值、可疑时间段列表。可视化用 OpenCV 画框和文字即可不必上重型前端。def draw_overlay(frame, faces, tracker): for (x, y, w, h) in faces: ratio tracker.focus_ratio() color (0, 255, 0) if ratio 0.7 else (0, 165, 255) cv2.rectangle(frame, (x, y), (x w, y h), color, 2) cv2.putText(frame, f{ratio:.2f}, (x, y - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return frame颜色阈值 0.7 是经验值实际按班级基线调整。输出记录建议同时写 CSV 和 JSONCSV 给教务做统计JSON 给前端做回放定位。3. 环境配置与源码跑通Python 依赖、模型权重、目录结构拿到一份 Python 源码加文档说明第一步不是读代码而是把环境跑起来。这一步翻车的人最多血泪经验是先确认 Python 版本再装依赖最后放权重文件顺序错了会浪费大量时间排查。3.1 Python 环境与依赖安装推荐 Python 3.8 到 3.10太新的版本某些深度学习库轮子还没跟上。用虚拟环境隔离避免和系统包打架。# 创建并激活虚拟环境 python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate # 安装核心依赖版本按源码文档说明为准 pip install opencv-python4.8.0.76 pip install numpy1.24.3 pip install torch2.0.1 torchvision0.15.2 pip install onnxruntime1.15.1opencv-python和numpy版本要匹配否则会出现numpy的 ABI 报错。torch装 CPU 版还是 GPU 版看机器有 NVIDIA 显卡且装了对应 CUDA 就装 GPU 版否则 CPU 版也能跑只是帧率低。onnxruntime用于加载导出的 ONNX 模型比直接跑 PyTorch 推理快适合部署。3.2 模型权重与目录结构源码通常不会带大权重文件需要按文档说明放到指定目录。常见结构如下目录用途注意事项weights/存放检测、姿态、视线模型权重文件名要和代码里加载路径一致data/测试视频和输出结果视频建议先转成 25fps 统一帧率configs/阈值、窗口大小等参数改参数优先改这里别硬编码logs/运行日志和告警记录定期清理避免占满磁盘权重文件缺失时程序一般会在加载处报FileNotFoundError按报错路径补文件即可。如果文档说明里给了下载方式按说明操作没有的话用代码里指定的模型名去对应框架的官方模型库找同结构权重。3.3 跑通最小示例先用一段短视频验证整条链路别一上来就处理整节课。python main.py --source data/test_5min.mp4 --output logs/result.csv --show--source指定输入视频--output指定结果 CSV--show打开实时预览窗口。第一次跑建议关掉--show用--save-video输出带标注的视频方便逐帧检查。如果报显存不足把批处理大小调到 1或改用 ONNX 推理。4. 参数调优与阈值设定专注度和作弊判定的边界在哪模型跑通只是开始真正决定系统好不好用的是阈值。阈值定错要么满屏误报要么什么都检测不到。4.1 专注度阈值的分层设定不要用一个全局阈值。按人脸在画面中的位置分层前排俯仰角天然偏下后排偏上。可以按人脸框中心 y 坐标分三段每段用不同的 pitch 阈值。分层人脸中心 y 范围低头阈值说明前排画面高度 0~33%-25 度视角偏下阈值放宽中排33%~66%-20 度标准阈值后排66%~100%-15 度视角偏上阈值收紧这张表是起点实际要用自己教室的视频标定。方法是让几个学生做标准低头动作记录每层的 pitch 值取平均值再减 5 度作为阈值。4.2 作弊检测的时序参数作弊检测靠的是短时间异常参数比专注度更敏感。核心三个参数转动角度阈值、时间窗口、触发次数。CHEAT_CONFIG { yaw_threshold: 35, # 头部左右转动超过 35 度记为一次转头 window_sec: 8, # 8 秒窗口 trigger_count: 4, # 窗口内转头 4 次触发告警 hand_region_ratio: 0.3, # 手部进入画面下方 30% 区域 }yaw_threshold设太小正常看黑板也会被算成转头设太大真作弊动作漏检。trigger_count和window_sec要一起调窗口短、次数少误报多窗口长、次数多漏报多。我的经验是先用 8 秒 4 次跑几段真实考场视频看误报率再微调。4.3 用验证集校准而不是拍脑袋有标注数据的话画一条 ROC 曲线选阈值。没有标注数据就人工看几段视频统计误报和漏报手动调。关键是记录每次调整后的误报数和漏报数别凭感觉。5. 避坑与排查那些让系统跑不起来的常见问题这一章全是踩过的坑按现象 → 原因 → 解决写遇到问题直接对号入座。5.1 检测框疯狂闪烁、ID 频繁跳变现象同一个人的人脸框忽大忽小跟踪 ID 每几帧换一次。原因检测器在相邻帧输出不稳定跟踪匹配阈值太严。解决给检测结果做一次指数平滑或把跟踪的 IoU 匹配阈值从 0.5 降到 0.3并允许丢失 5 帧内保持 ID。5.2 低头阈值在真实教室完全不准现象按文档默认阈值跑全班都被判成不专注。原因默认阈值是在正面平视的测试视频上调的真实教室摄像头有俯角。解决按 4.1 的分层方法重新标定或对每个跟踪 ID 记录前 10 秒的 pitch 均值作为个人基线用相对变化判断。5.3 显存不足或帧率低到无法实时现象跑几分钟后程序崩溃报 CUDA out of memory。原因批处理太大或模型没释放中间张量。解决把批大小降到 1推理时用torch.no_grad()或导出 ONNX 用 onnxruntime 跑。帧率低的话把检测间隔改成每 3 帧检测一次中间帧用跟踪结果。5.4 作弊告警全是误报现象一节课触发几十次作弊告警。原因转头阈值太低或没排除正常动作如捡笔、看时间。解决提高yaw_threshold到 40 度以上增加持续时间要求转动必须持续超过 0.5 秒并把手部区域检测加上只有头部异常加手部异常才告警。5.5 输出 CSV 中文乱码现象用 Excel 打开结果文件学生姓名或备注是乱码。原因CSV 默认编码不是 UTF-8 with BOM。解决写文件时用encodingutf-8-sigExcel 就能正确识别。6. 从能跑到好用把专注度曲线做成可回放的教学反馈系统能跑通、告警能出来只算完成一半。真正有价值的是把逐帧结果变成教师能看懂的反馈。我一般会做两件事一是把整节课的专注度按分钟聚合成曲线标出低谷时间段二是把告警时间点对应到视频时间戳支持点击跳转回放。import pandas as pd def build_minute_curve(csv_path): df pd.read_csv(csv_path) # 假设有 timestamp 和 focus_ratio 两列 df[minute] (df[timestamp] // 60).astype(int) curve df.groupby(minute)[focus_ratio].mean().reset_index() # 标出低于 0.5 的低谷分钟 curve[low] curve[focus_ratio] 0.5 return curve def export_report(curve, outlogs/report.csv): curve.to_csv(out, indexFalse, encodingutf-8-sig)groupby(minute)把秒级数据聚合成分钟级low列直接标出低谷教师一眼就能看到哪几分钟学生集体走神。回放功能用时间戳做锚点前端拿到告警列表后按timestamp定位视频即可。验证系统好不好用别只看准确率数字。找三位老师各看一段带标注的结果视频问两个问题告警时间点对不对、有没有明显漏掉的。如果老师觉得告警太频繁就调高触发次数如果觉得漏就降低阈值。这个反馈循环比任何离线指标都实在。最后说个我自己的习惯每次调完参数一定把配置和对应的误报漏报数记在一个表格里攒够十几组再回头看就能摸出这套参数在自己场景下的合理区间。别指望一次调对也别每次凭感觉改。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑