简介:本资源是一个基于OpenCV的人脸识别课堂抬头率检测系统,面向计算机专业本科生及毕业设计选题者,解决课堂教学中学生专注度量化评估的实际问题。系统通过调用摄像头实时捕获教室画面,结合预训练的Haar级联分类器(含5个XML模型文件)进行人脸检测,并依据选课人数数据库自动计算并显示实时抬头率,配套PyQt/TKinter风格GUI界面便于教学管理人员操作与监控。压缩包共22个文件,含4个Jupyter Notebook(.ipynb)主程序、3个Python可执行脚本(.py)、5张测试图像(.jpg)及Excel课程数据表(.xlsx),总大小2.88MB;其中code1_window_and_face_recognition.py.ipynb为完整可运行版本,camera.ipynb支持单帧图像采集,code0_initial.ipynb保留原始英文注释与环境配置说明,便于理解底层逻辑与调试排错。目前已有736人学习下载,提供从图像采集、人脸定位、UI集成到结果统计的全流程代码实现,无需额外配置即可直接运行,适合作为毕业设计核心方案或课程实践参考。
1. 课堂抬头率这个需求,到底在检测什么?——需求分析与技术选型
每年到毕业设计开题季,"基于OpenCV的人脸识别课堂抬头率检测系统"就会出现在大量选题清单里。这个题目看起来不高不低,既能体现图像处理能力,又不需要太深的深度学习背景,加上GUI界面就能把工作量撑起来,很多本科同学一眼相中。但真正动手做的时候,问题就来了:抬头率到底怎么定义?用人脸识别还是人脸检测?GUI用哪个框架?搞清楚这些,项目才能真的立住。
1.1 抬头率检测的业务逻辑:从"低头"到"抬头"的判定标准
先别急着写代码,把业务规则定清楚。所谓课堂抬头率,在监控摄像头画面里,我们要对每一位入镜的同学判断他是处于"抬头看黑板/看老师"状态,还是"低头看手机/看书/趴桌"状态。然后统计一段时间内抬头人数占总识别人数的比例,得到一个百分数。这跟门禁人脸识别是完全两回事:门禁要回答"你是谁",抬头率只关心"头抬没抬,脸朝没朝前"。
所以这个系统的核心不是识别身份,而是判断姿态倾向。最直接的思路是:人脸检测模型给出每张人脸的位置,如果检测到的人脸足够完整、尺寸够大、包含清晰的眉眼区域,那就认为是"正脸抬头"状态;如果只能检测到侧面或者检测不到脸,但画面中确实有人,那就认为是"低头/非抬头"状态。从这个角度理解,你需要的不是人脸识别,而是人脸检测加姿态推断。
这里有个容易混淆的点:很多人以为要用人脸识别库(比如face_recognition、dlib)才能做。实际上对毕设来说,OpenCV自带的Haar级联分类器就够了。因为你的目标是"有没有抬头的人脸",而不是"这张脸是谁"。我见过不少同学一开始就上MTCNN、RetinaFace,最后被环境依赖折磨得半死,精度提升却微乎其微——课堂摄像头普遍离人两三米,人脸分辨率很小,重型模型并不会比轻量模型好到哪去,反而把实时性和部署难度都搞砸了。
1.2 为什么用OpenCV人脸检测而不是深度学习模型?
这个选题既然写了OpenCV,那你就不必纠结要不要换成PyTorch。OpenCV的人脸检测主要有两种内置方案:Haar特征级联分类器(CascadeClassifier)和DNN模块加载Caffe/TensorFlow模型。毕设场景下我推荐用Haar,理由有三点:
- 模型文件是
haarcascade_frontalface_default.xml,OpenCV自带,不需要额外下载,代码开箱即用; - 检测速度飞快,普通笔记本CPU跑640x480视频,每帧人脸检测耗时几毫秒到几十毫秒,实时性完全够;
- 你需要的是"检测到人脸的区域",Haar给出的框足够稳定,配合后续姿态判断逻辑,在课堂这种相对固定的环境下精度完全够用。
如果坚持用深度学习模型,比如OpenCV DNN加载人脸检测模型(SSD或YOLO),确实在遮挡和侧脸场景下更好,但安装模型文件、配置框架版本、调整置信度阈值,这些都会成为毕设开发周期里的不确定因素。我的建议是:先用Haar把整个流程跑通,如果后期时间充裕,再作为"改进方向"在论文里提出,这是最稳妥的毕设策略。
1.3 系统整体架构和模块划分
整个系统按功能拆分,至少包括四个模块:
| 模块 | 职责 | 关键依赖 |
|---|---|---|
| 视频采集模块 | 打开摄像头或读取视频文件,逐帧取图 | OpenCV VideoCapture |
| 人脸检测模块 | 对每一帧图像做人脸检测,返回人脸框坐标 | OpenCV CascadeClassifier |
| 抬头判定模块 | 根据人脸框数据判断抬头/低头,计算统计指标 | 自定义算法 + NumPy |
| GUI展示模块 | 显示实时画面、抬头率曲线、统计报表,提供控制按钮 | PyQt5 / Tkinter |
这四个模块各干各的,GUI只负责展示和操作,检测模块独立成类,这样你在写论文画架构图的时候也方便。模块之间的数据传递用简单的类属性或队列就能完成,不需要上多复杂的框架。后面所有代码我都按照这个结构来写,你拿到之后可以直接用,也可以按自己需求改。
2. 人脸检测与抬头判定的核心算法解析
这个系统的技术核心就两块:人脸检测和抬起头判定。前面说了用人脸检测,但具体怎么检测、检测到之后怎么判定,细节都在这一节。
2.1 Haar特征级联分类器的工作原理
Haar级联分类器是Viola-Jones算法的经典实现。它的核心思想是用一系列简单的矩形特征(类似卷积核)去图像上滑动扫描,计算白色区域和黑色区域的像素和差值,这个差值能反映人脸局部区域的灰度变化规律。比如眼睛区域比脸颊区域暗、鼻梁比两侧亮,这些特征组合起来就能区分人脸和非人脸。
因为是级联结构,分类器会把大量简单特征按顺序排列,早期阶段用少量特征快速排除明显不是人脸的区域,只有通过前面所有阶段的区域才被认为是人脸。所以它速度飞快。OpenCV里加载和调用就三行代码:
import cv2 face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' ) faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60))参数含义要理解清楚:scaleFactor=1.1表示每次搜索窗口扩大10%,越小检测越慢但越精确;minNeighbors=5表示每个候选框周围至少要满足多少个邻近检测框才保留,越大漏检越多、误检越少;minSize=(60, 60)是目标最小尺寸,课堂场景中如果人脸太小,检测意义也不大,调大这个值还能顺便过滤一部分误检。
2.2 抬头/低头的姿态判定:基于人脸关键点与面积比例的方法
Haar检测出来只是一堆矩形框,怎么判断这个框里的人抬没抬头?最朴素但好用的方法是看检测框的宽高比和面积变化。
当人头抬起正对摄像头时,人脸轮廓完整,检测框通常是近似正方形或宽度略大于高度,宽高比在0.8到1.3之间;当人低头时,脸部是俯视角度,眉毛眼睛可能被遮挡,Haar检测到的框会变得扁长或者检测不到,宽高比就会明显偏离正常范围。另外还有一个辅助特征:抬头时人脸在画面中占的面积相对稳定,低头时下巴或额头离摄像头更近,检测框面积会发生突变(变很大或很小)。
所以我的判定逻辑是这样设计的:
判定为抬头状态的条件: 1. 该帧检测到该人脸区域 2. 人脸框的宽度/高度比例在0.7~1.4范围内 3. 人脸框面积在合理区间(比如帧面积的5%到50%) 以上条件同时满足,则计为"抬头帧"这只是一个经验公式。实际课堂中,学生低头但手机放在桌面上,摄像头还是可能拍到半张脸,这时Haar可能会检测到不稳定的框。为了减少这种抖动,我加了连续帧平滑:只有当一个人脸框连续N帧满足"抬头条件"时,才把这一段时间标记为抬头。这个N可以设成3到5,相当于一个迟滞阈值,避免单帧误判造成抬头率曲线毛刺。
2.3 用OpenCV实现人脸检测的核心代码与参数调优
把上面的逻辑落实到代码,我写了一个HeadUpDetector类,核心部分不长:
class HeadUpDetector: def __init__(self, cascade_path=None): if cascade_path is None: cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' self.face_cascade = cv2.CascadeClassifier(cascade_path) self.frame_width = 640 self.frame_height = 480 self.up_frames = {} # 每人脸ID的抬头连续帧计数 self.seen_persons = 0 self.up_persons = 0 def detect(self, frame): gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 可选:直方图均衡化提升低光照下的人脸检出率 gray = cv2.equalizeHist(gray) faces = self.face_cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60)) return faces def is_head_up(self, face_box, frame_area): x, y, w, h = face_box ratio = w / h area = w * h area_ratio = area / frame_area if 0.7 <= ratio <= 1.4 and 0.05 <= area_ratio <= 0.5: return True return False这里我额外调用了cv2.equalizeHist做直方图均衡化。因为教室监控普遍存在背光、偏暗的情况,均衡化能增强人脸区域对比度,显著提高Haar的检出率。不过要注意:equalizeHist只接受单通道灰度图,而且如果图像本身就很好,均衡化反而可能增加噪声。我当时调试时发现,加了均衡化之后,靠窗座位学生的检出率明显提高,但偶尔背景窗帘也会被误判成人脸,所以必须配合minNeighbors参数抑制误检。
2.4 静态图像测试与实时视频流的处理差异
写算法时可以先在静态图片上测试,但真实场景是实时视频流,两者有差别。静态图测试只需要对着图片跑一次detectMultiScale;视频流则是逐帧调用,需要考虑三件事:
- 不需要每帧都检测。典型摄像头是30fps,但对课堂场景来说,每秒检测2到3次已经完全足够。如果每帧都做检测,不仅CPU占用高,而且同一人会在连续帧中被重复计数。我采用的方法是每5帧检测一次,其他帧直接复用上一次的人脸框坐标。
- 需要做人脸去重。连续帧中同一个人的脸框位置会有小幅移动,如果直接按帧统计,很容易把同一个人重复计数。我用最简单的IOU重叠判断:当前检测到的人脸框如果与上一帧某个框的交并比大于0.5,就认为是同一个人,延续之前的抬头状态。
- 要处理"人离开画面"的情况。当一个脸框连续多帧消失,比如超过30帧,就判定这个人已经出画,重置他的状态。
这些处理逻辑直接关系到抬头率计算的准确性。很多同学调试时感觉统计数目不对,往往就出在没做去重和帧间隔处理上。
3. 从零搭出GUI界面:PyQt5还是Tkinter?
毕设里"GUI界面"四个字是很多老师的硬性要求,它既是展示窗口,也是工作量体现。GUI框架的选择、布局设计、线程处理,每一步都有讲究。
3.1 GUI框架选型:为什么选择PyQt5
Python的GUI框架选项很多,Tkinter、PyQt5/PySide6、wxPython、Kivy。对于这个项目,我强烈建议你选PyQt5。理由很实际:
- PyQt5的
QTimer可以极方便地驱动视频帧刷新,而Tkinter虽然也能做,但处理视频更新的延迟明显。 - PyQt5自带
QChart等图表组件,抬头率实时曲线可以直接画,不用额外装matplotlib嵌入,少折腾很多。 - PyQt5的信号槽机制天然适合多线程更新UI,检测线程一回调,界面马上更新,不会出现Tkinter那种"界面假死"的问题。
- 毕设答辩时,PyQt5的界面相比Tkinter原生控件看起来更现代、更专业,印象分就上去了。
缺点就是PyQt5安装包比较大,而且license是GPL(如果只是毕设自己用,没任何问题)。你也可以考虑PySide6,API基本一样,官方支持,但对新手来说PyQt5的社区资料更丰富,出问题好搜。
3.2 界面布局设计:摄像头画面、抬头率曲线、统计表格
一个合格的GUI不只是放个视频窗口,应该包含几个区域:
- 左侧主区域:摄像头实时画面,并在画面中把人脸框画出来,抬头的人脸框用绿色,低头/未检测到的人脸框用红色。这个视觉反馈非常重要,答辩时老师一眼就能看出系统在干什么。
- 右上区域:抬头率实时曲线,横轴时间,纵轴抬头率百分数。这里我用的是PyQt5的
QChart或者直接用matplotlib嵌入都可以,为了减少依赖,我用QChart。 - 右下区域:统计信息面板,包括当前画面中检测到的人数、抬头人数、当前抬头率、课堂平均抬头率、检测总时长。再放一个"导出统计结果"按钮,把结果存成CSV。
- 底部控制栏:开始检测、暂停检测、停止检测、重置统计、退出程序。
这个布局不需要炫酷,但要功能清晰。下面是我用的主窗口简化代码:
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('课堂抬头率检测系统') self.setMinimumSize(1000, 600) self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignCenter) self.curve_widget = QChartView() self.info_text = QTextBrowser() self.btn_start = QPushButton('开始检测') # ... 布局代码省略,用QVBoxLayout / QHBoxLayout组合3.3 多线程处理:让界面不卡顿的关键
这里有个新手必踩的大坑:直接在UI线程里跑视频帧循环,一跑界面就卡死,因为视频检测是耗时操作,UI线程被阻塞,控件没法响应点击和刷新。
解决办法是把视频采集和检测放到子线程,通过信号把处理完的帧和统计结果发回主线程更新UI。PyQt5的信号槽做这个非常自然。我的实现是一个DetectThread(QThread),里面运行视频处理循环:
class DetectThread(QThread): frame_signal = pyqtSignal(QImage, list, list) # 帧图 + 人脸框 + 抬头标记 stat_signal = pyqtSignal(dict) # 统计信息 def run(self): cap = cv2.VideoCapture(self.camera_index) while self.running: ret, frame = cap.read() if not ret: continue # 每隔几帧检测一次 if self.frame_count % self.detect_interval == 0: faces = self.detector.detect(frame) up_flags = self.detector.judge_up(faces, frame.shape) # 绘制人脸框 for (box, up) in zip(faces, up_flags): color = (0, 255, 0) if up else (0, 0, 255) x, y, w, h = box cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2) # 转换成QImage发信号 rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb.shape qimg = QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888).copy() self.frame_signal.emit(qimg, faces, up_flags) self.frame_count += 1 cap.release()主线程里连接信号:
self.detect_thread.frame_signal.connect(self.update_frame) self.detect_thread.stat_signal.connect(self.update_stat)记住,子线程里绝对不要直接操作UI控件,所有UI更新必须通过信号回到主线程。这是多线程GUI开发的铁律。
3.4 GUI和检测逻辑的完整联调
联调时最容易出现的问题是信号刷新频率过高,导致UI线程忙不过来。解决方法是控制检测线程的发帧频率,比如只发20ms一帧,或者如果检测结果没变化就不发新帧。也可以在信号里做一个简单的节流:信号发出后主线程用QTimer设置最小更新间隙,如果上一次处理还没完成就跳过本次更新。
联调完成后,你点击"开始检测"按钮,视频开始播放并且实时画出人脸框,右上角的抬头率曲线开始滚动,右下角的统计数字持续刷新。到这一步,整个GUI才算真正和检测逻辑结合起来了。
4. 抬头率计算模型与课堂场景的边界问题
抬头率计算是整个系统的"产出物",如果统计口径不统一,结果就失去了参考意义。同时,真实课堂有很多边界情况,必须提前设计好应对策略。
4.1 抬头率计算公式:帧级统计与时间窗口
我采用了基于时间窗口的统计方式,而不是简单地把所有帧平均。具体方法是:
- 维护一个滑动时间窗口,默认长度是30秒。
- 窗口内每完成一次检测,记录两个数字:本次检测到的总人数
total、本次判定为抬头的人数up。 - 当前时刻抬头率 = 窗口内所有
up之和 / 窗口内所有total之和 × 100%。
这样计算的好处是平滑,不会因为某一帧有人低头捡笔就瞬间拉低抬头率。窗口大小可以做成GUI上的可调参数,方便实验对比。如果你希望输出整堂课的抬头率,就取消窗口,变为累计统计。
4.2 多人场景下的检测:多个脸如何跟踪
用Haar检测单张图可以返回多个人脸框,但问题是这些框没有身份ID,帧与帧之间无法对应。所以我在代码里维护一个简单的tracker列表:
- 每一帧检测到的人脸框与当前tracker中已有脸框做IOU匹配;
- 如果IOU大于0.5,就更新原tracker的脸框位置;
- 如果IOU小于0.5,则认为是一个新出现的人,新增tracker;
- 如果某个tracker连续多帧匹配不到,就标记为离开,并从列表中删除。
这样做的目的是统计"有多少不同的人被识别到",以及"每个人抬头状态持续了多久"。如果你论文里需要展示课堂互动热力分析,这个tracker还可以扩展成记录每个位置的抬头占比。
4.3 常见误判:遮挡、低头看笔记、趴桌、侧面脸等
真实场景远比理想模型复杂。以下几个是我在测试中频繁遇到的误判:
- 遮挡:手托腮、用手挡脸、前面同学后脑勺挡住后排人脸。Haar检测遇到遮挡很容易丢失目标。解决办法是用
detectMultiScale的检测结果还是不可靠,可以考虑在论文中提出"结合前景分割或姿态估计"的改进方向,但毕设能做好现有方案就已经合格了。 - 低头看笔记:低头但前额对着摄像头,Haar可能检测到半张脸,宽高比异常,容易被误判为低头,但实际是在认真做笔记。这个不好解决,除非引入脸部关键点(比如用dlib的68个关键点)看眼睛和鼻子的连线角度,但会增加不少工作量。我建议毕设阶段接受这个误差,并在论文局限中说明。
- 趴桌:整个脸完全被手臂遮挡,Haar检测不到,系统就认为这个人"低头",逻辑上是没问题的,准确说应该叫"未抬头",可以接受。
- 侧面脸:学生转头和同桌说话,Haar对纯侧脸的检测能力较弱,可能检测不到,导致漏检。这也是Haar的固有缺点,可以在代码里加入
haarcascade_profileface.xml作为补充,但效果也不稳定。
你看,边界问题其实很多,但只要你能在论文中分析清楚,反而体现了你的思考深度。不要试图把所有问题都完美解决,那是不现实的。
4.4 基于OpenCV的辅助改进:眼睛位置与面积突变
如果想让系统稍微"聪明"一点,可以不引入额外的大型模型,只用OpenCV内置的人眼检测器做辅助判断。haarcascade_eye.xml可以检测眼睛区域,思路是:在一个已检出的人脸框内,如果能检测到眼睛,并且眼睛的位置在人脸框的上半部,说明此人大概率在抬头看前方。如果检测不到眼睛,或者眼睛位置异常,说明可能低头或闭眼。
此外,还可以利用面积突变:当一个人从抬头变为低头时,检测框面积通常会先增大(因为低头时额头离摄像头更近)然后消失。这个突变特征可以用于修正帧级判定的延迟。我在代码里做了个简单实现:记录每个人脸框面积的历史,如果面积连续两帧变化超过50%,则强制翻转该目标的抬头状态。
这些改进不需要额外安装库,效果虽然有限,但作为论文里的"算法优化"模块是很加分的。
5. 完整代码运行指南与毕设答辩经验
到了最后这一节,把代码跑起来、把论文写顺、准备答辩,才是大家最关心的。我把整个项目从环境配置到答辩需要注意的坑都列出来。
5.1 环境配置与依赖安装
我推荐使用Python 3.8到3.10版本,太新的版本有时会遇到某些库没有预编译wheel的问题。需要安装的依赖有:
- opencv-python(4.5.5.64,我用这个版本很稳定)
- opencv-contrib-python(可选,如果你要用到额外模块)
- numpy(opencv会自动带,但最好指定1.21以上版本)
- PyQt5(或者PyQt5-tools)
- 可选:matplotlib(如果不用QChart而用matplotlib绘制曲线)
安装命令:
pip install opencv-python==4.5.5.64 numpy PyQt5建议用venv建一个独立环境,不要往系统Python里乱装,避免和其他项目互相污染。
5.2 代码结构与关键模块逐段讲解
我的完整项目结构如下:
headup_system/ ├── main.py # 程序入口,启动GUI ├── detector.py # 人脸检测与抬头判定算法类 ├── worker.py # QThread子线程,视频采集与检测循环 ├── ui_mainwindow.py # 主窗口界面定义(可用Qt Designer生成再转) ├── ui_mainwindow.ui # Qt Designer的界面文件(可选) ├── haarcascade_frontalface_default.xml # 如果cv2.data找不到可放同级 ├── output/ │ └── records.csv # 导出的统计结果 └── README.md # 项目说明和运行方法每个模块的职责我都写得很清晰。detector.py里是HeadUpDetector类,worker.py里是DetectThread类,main.py里创建QApplication和MainWindow。全部代码加起来大概300到400行,对毕设来说工作量适中。
关键的部分我多写几句。detector.py里除了检测,还负责维护全局统计字典,数据格式如下:
self.stats = { 'total_persons': 0, # 累计识别到的不同人数 'total_detect_frames': 0, # 累计检测帧数 'up_count': 0, # 抬头人帧数 'current_up_rate': 0.0, # 当前窗口抬头率 }worker.py每处理完一帧,就把这些统计信息通过信号发出去。GUI里的update_stat方法负责更新界面显示。这样就实现了逻辑和界面的解耦。
5.3 运行效果展示:控制台输出、日志记录、数据导出
程序运行时,控制台可以打印每1秒汇总一次的检测信息,方便调试:
if self.frame_count % 30 == 0: logger.info(f'当前检测人数 {len(faces)} 抬头人数 {up_num} 抬头率 {rate:.1f}%')同时,我建议在GUI上增加"导出CSV"按钮,把每个时间段(如每10秒)的统计结果导出,格式类似:
时间, 检测人数, 抬头人数, 抬头率 14:30:00, 28, 24, 85.7% 14:30:10, 28, 22, 78.6%这个CSV可以直接导入Excel做图表,答辩时展示一下,说服力很强。
5.4 毕设论文中的技术路线与创新点包装
很多同学代码跑通了,但论文不会写。这个系统怎么包装技术路线?我的建议是:
- 研究背景写"高校课堂教学质量评估缺乏客观量化手段,传统人工观察耗时费力",顺势引出自动抬头率检测。
- 技术路线按照"视频采集→图像预处理→人脸检测→抬头状态判定→实时统计与可视化"的逻辑展开,把每一章的图表画好。
- 算法对比上,可以简单用Haar和深度学习模型在自建数据集上做个检测率对比实验,得出Haar在实时性上的优势,而且不回避其遮挡问题。
- 创新点可以集中在"基于宽高比与面积稳定性的轻量级抬头判定算法""多线程处理机制""实时统计与导出功能"这些实际落地的点。
不要写"创新设计了XX模型"这种大话,老师一眼就知道是假的。把你的实现细节、遇到问题、解决过程写清楚,比啥都强。
5.5 实际部署中踩过的坑与对策
最后,把我自己测试时踩过的几个坑分享给你,这些在毕设答辩时如果被问到也能体现你真做过项目。
cv2.data.haarcascades路径问题。有时你打包程序或换机器运行,cv2.data.haarcascades指向的路径不存在。稳妥做法是在程序运行时检测该文件是否存在,不存在则从项目目录加载XML文件,并把模型文件复制到项目目录一份。- PyQt5的
QImage转换后图像翻转。OpenCV用的是BGR,QImage默认是RGB,转换时务必cv2.cvtColor(frame, cv2.COLOR_BGR2RGB),否则显示出来肤色是蓝紫色的,看起来很假。 - 摄像头占用问题。调试时如果程序异常退出,摄像头可能没被释放,下次运行会报错。在
DetectThread.run()最后一定要cap.release(),并且主窗关闭时发信号让线程退出。 - 运行路径不要有中文。曾经把项目放到
D:\课堂项目\下面,OpenCV读取视频和写入CSV都出问题,把文件夹改成英文就正常了。这算是OpenCV老毛病了。 - 不同机器摄像头索引不同。有些笔记本摄像头是0,外接摄像头可能是1。我在GUI上加了个"摄像头序号"输入框,默认0,省得每次改代码。
把这些问题都处理掉,你的系统就已经是一个可以直接运行、逻辑自洽、展示效果完整的毕设项目了。要是你在后期想扩展,还可以往里面加课堂专注度预测、学生出勤统计、人脸签到等功能,框架搭好了,加功能并不难。希望这篇分享能帮你把系统从0到1做起来,少走点弯路。
本文还有配套的精品资源,点击获取