简介:这是一套面向高校计算机相关专业毕业设计场景的完整项目源码,主题为基于Python的人脸表情识别课堂行为检测系统,适合正在准备毕设、需要可运行参考项目的学生与开发者。资源包共261个文件,以90个py源码、75个pyc编译文件、24个html页面、21个css样式及17个mp4演示视频为主,另含数据库、配置与说明文档,压缩包约118.46MB,目录结构清晰,便于按模块查阅与二次开发。项目已获导师指导并通过答辩,代码完整可直接下载运行,涵盖人脸检测、表情分类、课堂行为判定与前后端交互等核心环节,配套模型文件与演示素材,能帮助读者快速理解系统架构、复现实验流程并对照完善自己的毕设方案。目前已有582人学习下载,可作为毕业设计选题落地与功能扩展的实用参考。
1. 从一份课堂行为检测系统源码说起:表情识别怎么落到教学场景
带过毕业设计的同学大概都有体会:选题时觉得“人脸表情识别”听起来很唬人,真动手才发现,光是把摄像头画面里的人脸框出来、判断是高兴还是走神,就够折腾好几周。这份基于 Python 的课堂行为检测系统源码加模型,解决的正是这个从算法到可演示系统的断层。它把表情识别模型、后端逻辑和一套带登录、课程管理、学生管理的 Web 界面打包在一起,拿到手就能跑出一个能演示的完整系统,而不是只有一段孤零零的推理脚本。适合正在做毕业设计、课程设计,或者想快速搭一个表情识别演示原型的同学。CSS 文件里出现的 admin_login、student_index、course_index 这些命名,说明前端页面是按角色分好的,后端接口和模型推理是接在一起的,不是拼凑的 demo。
2. 拆开压缩包先看什么:目录结构与技术栈判断
2.1 从文件命名反推系统分层
拿到一个源码包,我习惯先不急着装环境,而是把目录树拉出来看一遍。这份资源里反复出现的 CSS 文件名其实透露了不少信息:admin_login.css 对应管理员登录页,student_index.css 对应学生首页,course_index.css 对应课程页,teacher_index.css 对应教师端。这说明系统至少有三类角色——管理员、教师、学生,是一个典型的教学管理系统骨架,表情识别是嵌在课堂环节里的功能模块,而不是整个系统的全部。
判断一个毕业设计值不值得深入,关键看它有没有把“算法”和“业务”接起来。很多表情识别项目止步于一个 predict.py,输入一张图输出一个标签,这种拿去做答辩很容易被问“你的系统在哪里”。而这份源码带了完整的页面样式和角色划分,意味着它有一个可交互的 Web 层,模型推理是被业务逻辑调用的。常见做法是 Flask 或 Django 做后端,前端用模板渲染或前后端分离,具体用哪个框架需要进代码里确认,但目录结构已经能让你对工作量有个预期。
2.2 环境依赖与版本确认
在动手之前,先把 Python 环境理清楚。这类项目通常对版本敏感,尤其是深度学习框架和 NumPy 的兼容性。我一般会先找 requirements.txt 或者 README,如果没有,就根据 import 语句反推。
# 先看项目根目录有没有依赖清单 ls -la | grep -iE "requirements|readme|environment" # 如果没有依赖文件,进代码里搜关键 import grep -rE "^import |^from " --include="*.py" . | grep -iE "torch|tensorflow|keras|flask|django|opencv|cv2|numpy" | sort -u上面第一条命令是找现成的依赖清单,第二条是从所有 Python 文件里提取顶层 import,筛出深度学习框架、Web 框架和图像处理库。这样你能快速知道这个项目到底依赖 PyTorch 还是 TensorFlow,用的是 OpenCV 还是 PIL。参数上没什么可调的,重点是看框架大版本——PyTorch 1.x 和 2.x 的模型加载方式有差异,TensorFlow 1.x 和 2.x 更是两套写法,版本对不上,模型加载那一步就会直接报错。
提示:如果代码里用的是
torch.load加载 .pth 模型,注意 PyTorch 2.6 之后weights_only默认值变了,老代码可能加载失败,需要显式传参或降版本。
2.3 模型文件的识别与加载路径
模型文件是这份资源的核心资产之一。压缩包里通常会有一个 .pth、.h5 或 .onnx 文件,大小从几 MB 到几十 MB 不等。先确认模型格式,再确认代码里加载模型的路径写的是相对路径还是绝对路径。
import os import torch # 常见的模型加载写法,先确认路径 MODEL_PATH = os.path.join(os.path.dirname(__file__), "weights", "emotion_model.pth") # 如果原代码写的是绝对路径,比如 D:/xxx/model.pth,必须改成相对路径 # 否则换一台机器就找不到模型 if not os.path.exists(MODEL_PATH): raise FileNotFoundError(f"模型文件不存在: {MODEL_PATH}") # PyTorch 加载,注意 map_location 防止 GPU 模型在 CPU 机器上报错 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") checkpoint = torch.load(MODEL_PATH, map_location=device) print("模型加载成功,设备:", device)这段代码做了三件事:用os.path.dirname(__file__)把模型路径锚定到脚本所在目录,避免绝对路径换机失效;用os.path.exists做前置检查,让报错信息更明确;用map_location把模型映射到当前可用设备。参数上,map_location是最容易被忽略的一个——如果模型是在 GPU 上训练保存的,直接torch.load到没有 GPU 的机器上会抛 CUDA 相关错误,加上这个参数就能自动落到 CPU。模型加载路径和设备映射这两点确认好,后面推理才不会在第一步就翻车。
3. 把系统跑起来:从环境安装到表情推理链路
3.1 依赖安装与虚拟环境隔离
毕业设计项目最容易出的问题不是代码逻辑,而是环境冲突。同一台机器上装了三四个项目的依赖,numpy 版本互相打架是常事。我一般会强制用虚拟环境,把这份项目的依赖单独隔开。
# 创建虚拟环境,Python 版本建议 3.8 到 3.10 python -m venv venv # 激活,Windows 和 Linux/macOS 命令不同 # Windows: venv\Scripts\activate # Linux/macOS: source venv/bin/activate # 安装依赖,有 requirements.txt 就按文件装 pip install -r requirements.txt # 没有依赖文件就手动装核心包,版本按代码实际需要调整 pip install torch torchvision opencv-python flask numpy pillow虚拟环境这一步看着基础,但它是后面所有排查工作的前提。参数上,Python 版本选 3.8 到 3.10 是因为这个区间对 PyTorch 和 TensorFlow 的兼容性最好,3.11 以上有些老版本框架的 wheel 包可能找不到。pip install的时候如果卡在某个包上,先看是不是需要编译的包缺系统依赖,比如 opencv 在某些 Linux 发行版上需要额外的 libGL。装完之后用pip list核对一下关键包的版本,和代码里的 import 对得上再往下走。
3.2 表情识别推理链路拆解
表情识别的核心链路其实就四步:检测人脸、裁剪对齐、送进模型、输出表情标签。这份源码里这套链路应该是封装好的,但你要能看懂每一步在哪,出问题才知道去哪找。
import cv2 import numpy as np import torch # 假设模型输入是 48x48 灰度图,这是 FER 类模型的常见配置 IMG_SIZE = 48 def preprocess_face(face_img): """把人脸区域处理成模型需要的输入格式""" # 转灰度,表情识别常用灰度图减少计算量 gray = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) # 缩放到模型输入尺寸 resized = cv2.resize(gray, (IMG_SIZE, IMG_SIZE)) # 归一化到 0-1,再转成模型需要的张量维度 normalized = resized.astype("float32") / 255.0 tensor = torch.from_numpy(normalized).unsqueeze(0).unsqueeze(0) return tensor # 人脸检测用 OpenCV 自带分类器做演示,实际项目可能用 MTCNN 或 RetinaFace face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) img = cv2.imread("test.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5) for (x, y, w, h) in faces: face = img[y:y+h, x:x+w] tensor = preprocess_face(face) # 这里接模型推理,输出表情类别 cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imwrite("result.jpg", img)这段代码把推理链路串了一遍。detectMultiScale的两个参数值得说:scaleFactor=1.1控制每次缩放图像的步长,值越小检测越细但越慢;minNeighbors=5控制一个候选框要被多少个邻居认可才算真脸,调高能减少误检但可能漏掉侧脸。预处理里转灰度、缩放、归一化三步的顺序不能乱,模型训练时怎么处理,推理时就得怎么处理,这是血泪经验——训练用灰度推理用 RGB,准确率能掉一大截。实际项目里人脸检测可能用的是 MTCNN 或 RetinaFace,比 Haar 分类器稳,但依赖也更重,看你的环境能不能扛住。
3.3 Web 层与模型推理的对接
系统能演示的关键,是 Web 页面上的操作能触发模型推理并把结果返回。这部分通常在 Flask 或 Django 的视图函数里。
from flask import Flask, render_template, request, jsonify import os app = Flask(__name__) @app.route("/detect", methods=["POST"]) def detect(): """接收上传的图片,调用表情识别,返回结果""" file = request.files.get("image") if not file: return jsonify({"error": "没有收到图片"}), 400 # 保存到临时目录 save_path = os.path.join("static", "uploads", file.filename) file.save(save_path) # 调用推理函数,返回表情标签和置信度 result = run_emotion_recognition(save_path) return jsonify({"emotion": result["label"], "score": result["score"]}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)这个视图函数是前端和模型之间的桥。request.files.get("image")拿上传的文件,存到 static 目录,再调推理函数。参数上,host="0.0.0.0"让局域网内其他设备也能访问,方便答辩时用手机或另一台电脑演示;debug=True开发阶段开着方便看报错,但正式演示前记得关掉,否则出错会暴露堆栈信息。如果你的系统是前后端分离的,这里返回的就是 JSON,前端拿到再渲染;如果是模板渲染,返回的就是render_template。对接这一步最容易出的问题是文件路径和静态资源目录配置对不上,图片存进去了但前端访问不到,检查 Flask 的 static 配置。
4. 避坑与排查:跑不起来时先看这几条
4.1 模型加载报错:路径与设备不匹配
现象:运行推理脚本时报FileNotFoundError或者RuntimeError: Attempting to deserialize object on a CUDA device。原因通常是模型路径写成了原作者机器的绝对路径,或者模型在 GPU 上保存而当前机器只有 CPU。解决方法是把路径改成基于__file__的相对路径,加载时加map_location=torch.device('cpu')。如果用的是 Keras/TensorFlow,检查.h5文件是否完整,有时候下载中断会导致模型文件损坏,重新解压一次。
4.2 人脸检测不到:分类器文件缺失或参数过严
现象:图片里明明有人脸,但detectMultiScale返回空列表。原因可能是 OpenCV 的 haarcascade 文件路径不对,或者minNeighbors设得太高。先确认cv2.data.haarcascades目录下有没有对应的 xml 文件,没有就手动指定路径。参数上把minNeighbors降到 3 试试,scaleFactor调到 1.05 增加检测密度。如果还是不行,换 MTCNN 做检测,它对侧脸和光照的鲁棒性更好,代价是推理慢一些。
4.3 表情标签对不上:类别顺序错位
现象:模型输出的表情总是反的,高兴识别成生气。原因是训练时的类别索引和推理时的标签列表顺序不一致。比如训练时0=angry, 1=happy,推理代码里写成了0=happy, 1=angry。解决办法是找到训练时的类别映射,通常在训练脚本或数据集加载部分,把推理端的标签列表改成一样的顺序。这个坑很隐蔽,因为模型能跑通、有输出,只是结果不对,不仔细看发现不了。
4.4 Web 页面样式丢失:静态资源路径问题
现象:登录页能打开但没样式,或者图片上传后预览不出来。原因是 Flask/Django 的静态文件目录配置和 CSS 里引用的路径不匹配。检查static目录结构,确认 CSS 文件在static/css/下,HTML 里引用的是/static/css/admin_login.css。如果用了蓝图或子应用,静态路径前缀可能还要调整。浏览器按 F12 看 Network 面板,哪个资源 404 一目了然。
4.5 依赖版本冲突:numpy 与框架不兼容
现象:pip install装完,import 时报numpy.dtype size changed或module compiled against API version。原因是 numpy 版本和 torch/tensorflow 编译时用的版本不一致。解决方法是先装框架,让 pip 自动解析依赖,再按需降级 numpy。或者直接按 requirements.txt 里的版本锁死,不要手动升级。虚拟环境在这里就是后悔药,搞崩了删掉重建,不影响其他项目。
5. 进阶玩法:把表情识别结果用起来
5.1 从单帧识别到课堂行为统计
单张图片识别表情只是起点,课堂行为检测的价值在于统计。比如一节课 40 分钟,每隔 10 秒抓一帧,统计每个学生的表情分布,就能看出整体专注度曲线。实现上不需要改模型,只需要在推理外面套一层循环和计数器。
import time from collections import defaultdict # 模拟一节课的采样统计 emotion_counter = defaultdict(int) sample_interval = 10 # 秒 total_duration = 60 # 演示用 60 秒,实际可设 2400 start = time.time() while time.time() - start < total_duration: # 这里替换成实际的抓帧和推理 # label = run_emotion_recognition(frame) label = "happy" # 占位 emotion_counter[label] += 1 time.sleep(sample_interval) # 算占比 total = sum(emotion_counter.values()) for emotion, count in emotion_counter.items(): print(f"{emotion}: {count/total*100:.1f}%")这段代码把单次推理扩展成了时间维度上的统计。defaultdict(int)省去了初始化键的麻烦,sample_interval控制采样频率,太密了计算量大,太疏了漏掉表情变化。实际部署时抓帧可以用 OpenCV 的VideoCapture,推理结果存数据库或内存,最后按学生 ID 聚合。参数上,采样间隔和课程时长要根据实际场景调,演示用 10 秒够了,真实课堂可能 30 秒一次更合适。
5.2 模型微调与准确率验证
如果发现预训练模型在你的场景下准确率不够,可以拿自己的数据微调。但毕业设计时间有限,微调之前先做一件事:在一个标注好的测试集上跑一遍,看看到底是模型不行还是数据不行。
| 验证项 | 做法 | 判断标准 |
|---|---|---|
| 整体准确率 | 测试集全部跑一遍,算正确率 | 低于 60% 考虑微调 |
| 单类召回率 | 按表情类别分别统计 | 某类特别低说明数据不均衡 |
| 混淆矩阵 | 看哪些类别互相混淆 | 相邻表情混淆正常,对立表情混淆是 bug |
| 推理耗时 | 单张图片计时 | 超过 500ms 考虑换轻量模型 |
验证的时候注意测试集要和训练集分开,别拿训练数据当测试,那样准确率虚高没有意义。如果某一类表情召回率特别低,先看训练数据里这类样本是不是太少,再考虑数据增强或者换损失函数。微调的话,冻结前面的卷积层只训练全连接层,学习率设小一点,比如 1e-4,训练几个 epoch 看验证集准确率有没有提升。
5.3 一个我踩过的坑
有次帮人调一个类似的表情识别项目,模型能跑、准确率也还行,但 Web 端上传图片后一直返回“未检测到人脸”。查了半天发现是前端上传的图片经过了压缩,分辨率降到了 100x100 以下,人脸检测器在这么小的图上直接失效。后来在前端加了限制,上传原图或者至少保证短边 300 像素以上,问题就没了。从那以后我每次接图像类项目,都强制走一遍“上传-存储-读取-检测”全链路,确认每个环节的图片尺寸和格式没有被意外改变。希望帮到你。
本文还有配套的精品资源,点击获取