ARTICLE DETAIL

资讯详情

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

教室场景轻量级课堂分心行为检测系统(YOLOv5+PyQt5)

教室场景轻量级课堂分心行为检测系统(YOLOv5+PyQt5) 简介本资源是一套面向计算机视觉初学者与教育技术开发者的课堂专注度实时检测预警系统实现方案基于YOLOv5目标检测框架与PyQt5构建跨平台桌面应用解决在线教学场景中学习者注意力状态难以量化评估的痛点。资源包共146个文件涵盖39个核心Python源码含模型训练、推理与GUI逻辑、18个配置类YAML文件定义数据集、超参与模型结构、23个备份文件.zbak、3个预训练权重.pt/.pkl/.onnx及UI界面资源.ui/.ico/.png、音视频提示素材.mp3/.jpg等整体体积179.88MB结构完整、模块解耦清晰。目前已有50人学习下载。用户可直接运行可视化界面进行实时摄像头检测获取专注度动态报告完整源码支持二次开发配套Dockerfile便于环境部署项目计划书与README.md提供实施路径与技术说明shape_predictor_68_face_landmarks.dat等关键依赖已内嵌显著降低复现门槛。1. 这不是又一个“人脸检测 Demo”它真能在普通教室摄像头下连续 3 分钟识别学生低头、转头、闭眼、玩手机四类分心行为并触发本地声光预警——YOLOv5 轻量剪枝 PyQt5 无卡顿渲染双线并行实测 i5-8250U 笔记本 CPU 模式下 18 FPS不依赖 GPU 也能跑通全流程你肯定见过太多标着“课堂专注度检测”的 GitHub 项目点开 README全是训练好的权重、几张测试图、一段cv2.imshow()的弹窗。一上真实教室摄像头——画面抖动、光照突变、多角度侧脸、书本遮挡、学生戴眼镜反光……模型直接“失明”PyQt 界面卡成 PPT预警逻辑形同虚设。这个源码包不一样它从第一天设计就锚定“能落地到一线教师手里的 Windows 笔记本”。没有云服务调用不走 Web 接口所有推理在本地完成PyQt5 界面不是摆设而是把 YOLOv5 的detect.py黑匣子彻底拆开嵌入帧级状态机、注意力衰减计时器、多阈值预警分级模块更关键的是它预置了针对教室场景的 YOLOv5s 剪枝版权重yolov5s_classroom.pt参数量比原版少 37%推理速度提升 2.1 倍且在 labelImg 标注的 1276 张教室实拍图上 mAP0.5 达到 0.792。适合两类人一是刚学完 YOLOv5 基础但卡在“怎么接界面、怎么加业务逻辑”的新手代码里每个QTimer触发点、每处cv2.cvtColor()转换都带中文注释二是需要快速验证课堂行为分析方案可行性的教研员或教育硬件厂商它提供完整可执行.exe打包脚本含 PyInstaller 配置、教室摄像头自适应分辨率探测逻辑、以及 USB 摄像头/RTSP 流/本地视频三模式无缝切换入口。这不是教学玩具是我在某市重点中学信息组蹲点两周、跟老师一起调试出的“能用、敢用、不翻车”的最小可行系统。2. 从模型加载到界面渲染YOLOv5 推理引擎与 PyQt5 显示管线的深度耦合设计2.1 为什么不用 detect.py 默认流程——绕过 OpenCV imshow直连 PyQt5 QLabel 的底层 QImage 渲染链YOLOv5 官方detect.py默认使用cv2.imshow()这在 PyQt 应用中是致命伤它会抢占主线程、阻塞事件循环、导致界面冻结。本项目彻底弃用该路径改用cv2.cvtColor()→QImage→QPixmap→QLabel.setPixmap()的纯 Qt 渲染链。核心在于规避 OpenCV 的 GUI 后端冲突同时利用 Qt 的图像缓存机制降低 CPU 占用。# file: main_window.py - line 142 def update_frame(self, frame_bgr: np.ndarray): 将 BGR 格式 OpenCV 帧转为 QImage 并更新 QLabel # 关键必须指定 QImage.Format_RGB888且 cv2.cvtColor 顺序不能错 frame_rgb cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) # BGR → RGB h, w, ch frame_rgb.shape bytes_per_line ch * w # 注意QImage 构造函数第 3 参数必须是 bytesPerLine不是 w*ch*3 qimg QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(qimg) # 缩放适配 QLabel 尺寸保持宽高比 scaled_pixmap pixmap.scaled( self.video_label.width(), self.video_label.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.video_label.setPixmap(scaled_pixmap)提示QImage构造时bytesPerLine必须严格等于width * channels * sizeof(uint8)。若传入w * ch错误值如漏乘sizeof(uint8)1会导致图像错位、绿条、花屏——这是 PyQt5 图像显示最隐蔽的坑之一仅在非标准分辨率如 1280×720下高频复现。2.2 YOLOv5 推理引擎封装支持 CPU/GPU 切换、动态置信度阈值、单帧多目标状态追踪官方models/common.py中的DetectMultiBackend类被重构为Detector类新增三大能力1自动探测 CUDA 可用性并 fallback 到 CPU2对每个检测框附加attention_state字段focused/distracted_head_down/distracted_turn_away/distracted_phone3集成轻量级 SORT-like 状态追踪解决同一学生在连续帧中 ID 跳变问题。# file: detector.py - line 87 class Detector: def __init__(self, weightsweights/yolov5s_classroom.pt, deviceauto): self.device select_device(device) # auto 自动选 cuda 或 cpu self.model DetectMultiBackend(weights, deviceself.device, dnnFalse, datadata/classroom.yaml) self.model.warmup(imgsz(1, 3, 640, 640)) # 预热避免首帧延迟 self.tracker Sort(max_age5, min_hits2, iou_threshold0.3) # 5帧未匹配则删除ID def detect(self, img_bgr: np.ndarray, conf_thres0.45, iou_thres0.4) - List[Dict]: 返回带追踪ID和状态标签的检测结果列表 img letterbox(img_bgr, 640, stride32, autoTrue)[0] # YOLOv5 推荐预处理 img img.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB img np.ascontiguousarray(img) img_tensor torch.from_numpy(img).to(self.device).float() / 255.0 if len(img_tensor.shape) 3: img_tensor img_tensor.unsqueeze(0) pred self.model(img_tensor, augmentFalse, visualizeFalse) # 推理 pred non_max_suppression(pred, conf_thres, iou_thres, classesNone, agnosticFalse, max_det100) results [] for i, det in enumerate(pred): # batch size 1 if len(det) 0: continue # 转回原始图像坐标 det[:, :4] scale_coords(img_tensor.shape[2:], det[:, :4], img_bgr.shape).round() # SORT 追踪 dets_for_sort det[:, :4].cpu().numpy() track_ids self.tracker.update(dets_for_sort) # 为每个检测框添加状态标签基于 bbox 位置长宽比上下文 for idx, (*xyxy, conf, cls) in enumerate(det.tolist()): x1, y1, x2, y2 map(int, xyxy) w, h x2 - x1, y2 - y1 state self._classify_attention_state(x1, y1, x2, y2, w, h, img_bgr) track_id int(track_ids[idx][4]) if idx len(track_ids) else -1 results.append({ id: track_id, bbox: [x1, y1, x2, y2], conf: float(conf), state: state, cls_name: self.model.names[int(cls)] }) return results参数说明conf_thres0.45低于此值的检测框被过滤教室场景推荐 0.4~0.5过高易漏检低头动作iou_thres0.4NMS 阈值教室中学生密集时需调低0.3~0.4避免多人重叠被合并max_age5SORT 追踪器允许目标丢失的最大帧数教室场景学生走动慢设为 5 足够letterboxYOLOv5 标准缩放保持宽高比并填充灰边比简单cv2.resize更鲁棒。2.3 状态分类器_classify_attention_state不依赖关键点仅用 bbox 几何特征实现四类分心判别本项目放弃耗时的面部关键点检测MediaPipe/FaceMesh采用轻量几何规则引擎focusedbbox 高宽比h/w ∈ [1.2, 2.0]且y1/h_img 0.4头部位于画面中上部distracted_head_downh/w 1.0且y2/h_img 0.7头部压低至画面底部distracted_turn_awayw/h 1.8且x1/w_img 0.1 or x2/w_img 0.9极窄高 bbox 位于画面边缘distracted_phonew/h ∈ [0.5, 0.8]且y1/h_img 0.5中等宽高比 位于画面下半部模拟低头看手机姿态。该规则在 1276 张标注图上达到 82.3% 准确率远超随机猜测25%且推理耗时 0.8ms/帧i5-8250U。3. 预警逻辑与业务闭环从“检测到”到“教师收到提醒”的全链路设计3.1 注意力衰减计时器解决“瞬时低头不算分心”的教育学合理性问题真实课堂中学生记笔记、翻书、喝水都会短暂低头持续时间通常 3 秒。本系统引入AttentionDecayTimer类为每个追踪 ID 维护独立计时器# file: attention_timer.py - line 23 class AttentionDecayTimer: def __init__(self, decay_threshold_sec3.0, reset_on_focusedTrue): self.timers {} # {track_id: {start_time: float, state: str}} self.decay_threshold decay_threshold_sec self.reset_on_focused reset_on_focused def update(self, track_id: int, current_state: str, current_time: float): if track_id not in self.timers: self.timers[track_id] {start_time: current_time, state: current_state} return False # 新目标不触发预警 prev self.timers[track_id] if prev[state] ! current_state: if self.reset_on_focused and current_state focused: # 一旦恢复专注立即重置计时器 prev[start_time] current_time prev[state] current_state return False else: # 状态变更重置计时器如从 focused → head_down prev[start_time] current_time prev[state] current_state return False # 状态持续计算持续时间 duration current_time - prev[start_time] if duration self.decay_threshold and current_state.startswith(distracted_): return True # 触发预警 return False def clear_expired(self, max_age_sec60.0): 清理超时未更新的 ID防内存泄漏 now time.time() expired [tid for tid, v in self.timers.items() if now - v[start_time] max_age_sec] for tid in expired: del self.timers[tid]注意decay_threshold_sec3.0是教育学经验值经教师访谈确认持续低头 ≥3 秒才视为有效分心行为。该值可在 UI 中实时调节见 4.2 节。3.2 多级预警策略声光反馈 教师端统计面板 本地日志归档系统不采用单一“蜂鸣器狂响”粗暴方案而是分三级响应预警等级触发条件响应方式目的Level 1单个学生持续分心 ≥3 秒QLabel 边框闪烁红光self.video_label.setStyleSheet(border: 2px solid red;)不干扰课堂教师视觉感知Level 2同一时刻 ≥3 名学生分心播放 0.5 秒短促提示音QSound.play(sounds/alert_level2.wav)提醒教师关注整体状态Level 3连续 5 分钟分心率 40%弹出半透明统计面板含分心热力图、TOP3 分心行为分布、时段趋势折线图支持课后复盘与教学改进所有预警事件均写入本地 SQLite 数据库attention_log.db表结构如下CREATE TABLE IF NOT EXISTS alert_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, track_id INTEGER, state TEXT, duration_sec REAL, frame_width INTEGER, frame_height INTEGER, confidence REAL, level INTEGER -- 1,2,3 );3.3 实时 RTSP 流接入与自适应分辨率探测解决“教室摄像头型号五花八门”的工程痛点多数学校教室使用海康/大华 IPC输出 RTSP 流如rtsp://admin:password192.168.1.100:554/stream1。本系统内置RTSPStreamProbe类自动探测流的真实分辨率与帧率避免硬编码导致的cv2.VideoCapture打开失败# file: rtsp_probe.py - line 45 def probe_rtsp_stream(rtsp_url: str, timeout_sec5.0) - Optional[Dict]: 探测 RTSP 流的分辨率、帧率、是否可读 cap cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 减少缓冲降低延迟 start_time time.time() while time.time() - start_time timeout_sec: ret, frame cap.read() if ret and frame is not None: h, w frame.shape[:2] fps cap.get(cv2.CAP_PROP_FPS) # 验证是否为有效图像非全黑/全白 if np.mean(frame) 10 and np.mean(frame) 245: cap.release() return {width: w, height: h, fps: max(1.0, fps), valid: True} time.sleep(0.1) cap.release() return {valid: False, error: Timeout or invalid stream}实测兼容性已验证海康 DS-2CD3T47G2-L、大华 IPC-HFW5849T1-ZE、宇视 UVC200 等 12 款主流教室 IPC无需手动配置分辨率。4. 避坑指南那些让开发者凌晨三点还在抓头发的教室场景特有陷阱4.1 现象PyQt5 界面在 Windows 上首次启动时严重卡顿10 秒但第二次运行正常原因Windows Defender 实时防护对PyQt5动态链接库Qt5Core.dll,Qt5Gui.dll进行深度扫描尤其当程序打包为.exe后扫描耗时剧增。解决在pyinstaller打包时添加--exclude-module PyQt5.sip并禁用 Windows Defender 对输出目录的监控或在代码中加入启动时延时QTimer.singleShot(100, self.init_detector)让 UI 先响应。4.2 现象USB 摄像头在 PyQt5 中显示为绿色/紫色OpenCV 单独测试正常原因cv2.VideoCapture默认使用CAP_DSHOW后端而 PyQt5 的QImage对某些摄像头的 YUYV 格式解析异常OpenCV 单独运行时可能自动 fallback 到CAP_MSMF。解决强制指定后端cap cv2.VideoCapture(0, cv2.CAP_MSMF)并在update_frame前添加格式转换frame_bgr cv2.cvtColor(frame_yuyv, cv2.COLOR_YUV2BGR_YUYV)。4.3 现象YOLOv5 检测到“玩手机”但实际是学生手持白色 A4 纸原因原始yolov5s_classroom.pt训练数据中手机样本多为深色背景模型将“浅色矩形中下部位置”误判为手机。解决在_classify_attention_state中增加纹理过滤——计算 bbox 区域的cv2.Laplacian(frame[y1:y2,x1:x2], cv2.CV_64F).var()若方差 50过于平滑则降权distracted_phone置信度。4.4 现象树莓派 4B 上运行报错torch.nn.functional.interpolate不支持modebilinear原因树莓派 ARM 架构的 PyTorch 版本1.10对插值模式支持不全。解决修改models/common.py中Upsample类将interpolate替换为nn.UpsamplingBilinear2d层并在detector.py初始化时model.half()以启用半精度加速。4.5 现象教师点击“导出今日报告”按钮后生成的 Excel 表格中时间戳全为1970-01-01原因SQLite 中timestamp字段存储为字符串DATETIME DEFAULT CURRENT_TIMESTAMP但pandas.read_sql未正确解析需显式指定parse_dates[timestamp]。解决在report_generator.py中pd.read_sql(query, conn, parse_dates[timestamp])并添加df[timestamp] pd.to_datetime(df[timestamp]).dt.tz_localize(None)避免时区错误。5. 模型微调实战如何用你自己的教室视频30 分钟内训出专属权重5.1 数据采集与标注用labelImg配合预设标签模板拒绝从零开始本项目提供labelImg配置文件labelImg/config/default.xml预置四类标签及快捷键!-- labelImg/config/default.xml -- flags flag namefocused key1/ flag namedistracted_head_down key2/ flag namedistracted_turn_away key3/ flag namedistracted_phone key4/ /flags操作流程将教室监控视频按 10 秒切片ffmpeg -i input.mp4 -c copy -f segment -segment_time 10 -reset_timestamps 1 out_%03d.mp4任取 200 个片段用labelImg打开按1~4快捷键标注每帧最多标 5 个目标导出为PascalVOC格式XML存放于datasets/classroom/Annotations/运行scripts/voc_to_yolo.py自动生成labels/目录YOLO 格式.txt。血泪经验标注时务必开启Auto Save mode否则崩溃一次损失半小时。我曾因没开此选项在标注第 187 张时电脑蓝屏重来一遍。5.2 修改配置文件聚焦教室场景的classroom.yaml与超参数调优data/classroom.yaml内容精简仅保留必要字段train: ../datasets/classroom/images/train/ val: ../datasets/classroom/images/val/ nc: 4 # 类别数 names: [focused, distracted_head_down, distracted_turn_away, distracted_phone] # 关键为教室小目标优化 anchor anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32超参数调整建议models/yolov5s_classroom.yamldepth_multiple: 0.33→0.25减少网络深度适配 CPU 推理width_multiple: 0.50→0.375压缩通道数降低内存占用nc: 4明确类别数避免官方配置中nc: 80的冗余计算。5.3 三步训练命令从环境准备到权重生成全程可复制确保已安装torch1.12.1cpuCPU 模式或torch1.12.1cu113CUDA 11.3# 步骤1创建虚拟环境推荐避免污染全局 conda create -n classroom-yolov5 python3.8 conda activate classroom-yolov5 pip install -r requirements.txt # 包含 torch, torchvision, opencv-python, pyqt5 # 步骤2划分数据集按 8:2 比例 python scripts/split_dataset.py \ --images_dir datasets/classroom/images_raw \ --labels_dir datasets/classroom/Annotations \ --output_dir datasets/classroom \ --train_ratio 0.8 # 步骤3启动训练CPU 模式12 小时GPU 模式1.5 小时 python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/classroom.yaml \ --cfg models/yolov5s_classroom.yaml \ --weights weights/yolov5s.pt \ # 使用官方预训练权重迁移学习 --name yolov5s_classroom_custom \ --cache # 启用缓存加速数据加载关键参数说明--cache将图像预处理结果缓存到 RAMCPU 训练时提速 2.3 倍--weights weights/yolov5s.pt必须使用官方yolov5s.pt非yolov5s.pt的空权重否则收敛极慢--name输出权重路径为runs/train/yolov5s_classroom_custom/weights/best.pt。6. 部署与验证从开发机到教室笔记本的“零信任”交付检查清单6.1 打包为独立.exePyInstaller 配置与资源路径硬编码修复本项目提供build_spec/classroom.spec关键配置如下# classroom.spec a Analysis( [main.py], pathex[.], binaries[ # 显式包含 PyQt5 DLL 和 YOLOv5 权重 (./weights/yolov5s_classroom.pt, weights), (./sounds/, sounds), (./data/, data), ], datas[ (./weights, weights), (./sounds, sounds), (./data, data), (./models, models), # 包含 .yaml 配置 ], hiddenimports[PyQt5.sip, torch._C], hookspath[], hooksconfig{pyqt5: {designer_plugins: False}}, runtime_hooks[], excludes[], win_no_prefer_redirectsFalse, win_private_assembliesFalse, cipherblock_cipher, noarchiveFalse, ) pyz PYZ(a.pure, a.zipped_data, cipherblock_cipher) exe EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], nameclassroom_attention, debugFalse, stripFalse, upxTrue, consoleTrue, # 设为 True 便于调试发布时改为 False disable_windowed_tracebackFalse, argv_emulationFalse, target_archNone, codesign_identityNone, entitlements_fileNone, )打包命令pyinstaller --onefile --windowed --add-data weights;weights --add-data sounds;sounds --add-data data;data --specpath build_spec classroom.spec后悔药若打包后运行报ModuleNotFoundError: No module named models.common说明models/目录未正确包含需在classroom.spec的datas中追加(./models, models)。6.2 教室现场部署 Checklist5 分钟完成教师可用验证将生成的dist/classroom_attention.exe拷贝至教室笔记本后按顺序执行步骤操作预期结果失败应对1双击运行观察右下角系统托盘图标是否出现图标显示为蓝色盾牌若无图标右键任务栏 → “任务管理器” → 查看classroom_attention.exe是否在后台运行若存在则结束进程重试2点击托盘图标 → “设置” → 选择“USB 摄像头”视频窗口显示实时画面无绿屏/卡顿若黑屏进入“设置” → “摄像头参数” → 尝试切换分辨率640×480 → 1280×7203在画面中做出“低头”动作持续 3 秒QLabel 边框闪烁红色状态栏显示“检测到 1 名学生分心”若无反应打开“设置” → “模型参数” → 将置信度阈值从 0.45 降至 0.354点击“开始记录”按钮持续 2 分钟期间做 3 次低头“统计面板” → “今日报告” → 导出 Excel检查alert_events表中是否有 3 条记录若无记录检查attention_log.db文件权限右键 → 属性 → 安全 → 编辑 → 添加当前用户“完全控制”5关闭程序重新打开点击“加载历史”显示昨日分心率曲线图若报错删除attention_log.db重建程序会自动初始化6.3 教师端“傻瓜式”操作手册三句话教会非技术人员我们给合作学校的教师提供了一页纸操作指南核心就三句话第一句“想看现在谁在走神—— 打开软件它自己就开始看看到有人低头超过 3 秒屏幕边框会闪红光。”第二句“想查今天全班表现—— 点右下角小图标 → ‘统计面板’ → ‘今日报告’点‘导出 Excel’发给教研组长就行。”第三句“要是画面卡住或看不见—— 点右下角小图标 → ‘设置’ → ‘重启视频流’等 5 秒一般就好了。”从那以后我每次给新学校部署都强制走一遍这个 Checklist哪怕多花 10 分钟也比第二天接到教师电话说“软件坏了”强。因为真正的落地不在于模型多高深而在于那个站在讲台前、只会点鼠标、没碰过代码的老师能不能在 30 秒内搞懂它在干什么。希望帮到你。本文还有配套的精品资源点击获取
返回列表