很多人看到“人脸识别”这四个字,第一反应就是“算法太难了”。但作为计算机毕业设计来说,人脸识别课堂考勤系统其实是一个非常合适且讨巧的题目:它既有场景、有数据、有交互,又能把当前热门的Spring Boot技术栈和人工智能方向结合起来。我当时带过的项目组里,好几个学生就是用这个题目一次性通过答辩的,关键是思路清晰、模块完整、演示效果好。这篇博客我会结合自己实际做过的方案,把整个系统的设计与实现从头到尾捋一遍,从技术选型、表结构设计、人脸识别方案集成,到具体代码实现、部署和答辩避坑,给正准备做这个题目的朋友一份可以直接“抄作业”的参考。
这个题目看似是一个考勤系统,核心其实是两件事:一是如何把“课堂考勤”这个业务场景拆成标准的信息管理模块;二是如何把人脸识别这个AI能力稳定、可控地接入Spring Boot项目,并让它跑起来、演示得通。这两点恰恰是计算机毕业设计最看重的能力。如果你是Spring Boot初学者,或者对AI集成不太熟,看完这篇应该能少踩很多坑。
1. 项目架构与核心设计思路
1.1 系统定位与功能边界
先理清这个系统到底是干什么的。课堂考勤,对比传统的点名、刷卡、二维码签到,它的核心价值在于“无感”和“防代签”。但因为做的是毕业设计,不是商用产品,我们不需要把功能做得极其庞大,而是要抓住几条主线:
- 学生信息管理:学生的基本信息、班级、学号。
- 教师与课程管理:教师可以创建课程,管理选课学生,查看自己课程的考勤记录。
- 人脸注册:学生第一次使用时录入人脸,系统提取人脸特征并存储。
- 课堂签到:考勤开始时,学生通过摄像头拍照或上传照片,系统识别身份,自动完成签到。
- 考勤统计:按课程、按时间统计出勤率、缺勤名单,支持导出。
有了这条线,系统边界就清楚了。不建议一开始就去做复杂的周次节次、调课、代课审批,那些可以留作“拓展功能”在论文里提一嘴,但系统核心先保证闭环。
1.2 技术选型:为什么是Spring Boot + 人脸识别
先说为什么后端必须选Spring Boot。毕设常见的需求就是“传统项目 + 热点名词”,Spring Boot是目前Java Web开发里最主流的框架,它简化了配置,内嵌Tomcat,有一个庞大的生态,写起来比原始SSH、SSM舒服太多。而且答辩时,老师一定会问“为什么选这个框架”,你可以答:自动装配、起步依赖、约定大于配置、便于快速开发等,这些都是加分项。
人脸识别这一层,市面上有几种选择:
- 离线SDK,比如虹软ArcFace,免费、识别精度高,有Windows/Linux版本。
- 在线API,比如百度AI开放平台的人脸检测与搜索,调用简单,但需要联网、有QPS限制。
- 纯开源方案,比如OpenCV + Dlib,或者用Python写个Flask服务,再让Spring Boot调用。
我的建议是:如果毕设要求不高,做人脸识别门禁机那种一体机不方便,那就用离线SDK或者本地Python服务。因为你可以在演示时拔掉网线,这样就不会因为网络问题翻车。而且离线方案能体现你对“算法集成”的理解,而不是纯调第三方接口。如果用在线API,一旦答辩现场网络不好,脸都扫不了,体验就很尴尬。
1.3 总体架构与数据流向
整个系统推荐采用前后端分离:Spring Boot提供RESTful API,Vue + Element UI构成管理页面,人脸识别模块独立封装成一个service服务。数据流向大致是:
- 注册时:前端上传照片 → Spring Boot接收 → 调用人脸识别服务提取特征 → 将特征码存入数据库。
- 签到时:摄像头捕获照片 → Spring Boot接收 → 调用人脸识别服务搜索相似用户 → 返回学生信息 → 写入考勤记录。
这样设计的好处是:后续就算你把识别服务从SDK换成API,Spring Boot主代码也不需要大改,符合高内聚低耦合的设计原则。这里也建议学生在论文里画清晰的结构图,这是答辩常问的点。我用Mermaid画图,但你们写论文时可以用PowerPoint画框图,结构是:表现层(Vue)→ 控制层(Controller)→ 业务层(Service)→ 数据层(Mapper / DAO),旁边独立一个“人脸识别服务”。
2. 核心模块拆解与数据库设计
2.1 用户角色与权限设计
考勤系统至少有三类角色:管理员、教师、学生。用Spring Security或者简单的拦截器都可以实现权限控制。
实际做的时候,我倾向于用JWT做无状态登录,这样前后端分离更自然。角色表也可以用一张role表加一张user_role关联表,但也可以在user表里加一个role字段,因为角色很少,没必要做成RBAC那么复杂。
用户表设计参考:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| username | varchar(50) | 登录账号 |
| password | varchar(100) | BCrypt加密后的密码 |
| real_name | varchar(50) | 姓名 |
| role | varchar(20) | student / teacher / admin |
| student_no | varchar(20) | 学号,仅学生 |
| status | int | 0禁用,1启用 |
密码一定要用BCrypt加密,这是答辩安全问题的标准答案。权限控制用Spring Security的@PreAuthorize注解,或者在Controller里加角色判断,按需取舍。
2.2 课程、选课与课表模块
课堂考勤必然要有“课”的概念,否则考勤记录没有归属。课程表、选课表,这是最基础的设计。
course课程表:id、课程名称、教师id、上课地点、上课周数、节次。student_course选课表:id、学生id、课程id,一个学生可以选多门课,一门课有多个学生。
课表不必做到像教务系统那样复杂,我们只需要能判断“这节课该不该考勤”、“这个学生是否在这个课程里”。这里有一个容易被忽略的点:考勤时必须校验学生是否选择了该课程。如果直接把不在课程里的学生也签到成功了,那系统就有逻辑漏洞。答辩时老师很可能会拿这个问题来试探。
上课时间我是用“周几 + 第几节”来表示的,例如week_day(1-7代表周一到周日)和period(1-8传统作息)。如果需要支持按周次区分,就再加weeks字段,存一个逗号分隔的字符串,比如"1-16"表示第1到16周。这个设计虽然简单,但完全够用,而且演示效果很直观。
2.3 考勤记录设计
考勤表是系统的核心数据表。一定要记录的是:谁、什么时间、哪门课、结果是什么(正常/迟到/缺勤/请假)。
attendance表字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 学生id |
| course_id | bigint | 课程id |
| status | int | 1正常,2迟到,3缺勤,4请假 |
| attendance_time | datetime | 签到时间 |
| image_url | varchar(255) | 签到照片地址,用于追溯 |
这个表看起来简单,但有几个细节要注意:同一个学生在同一门课的同一节课只能有一条考勤记录,所以最好在student_id + course_id + attendance_date上加唯一索引。我们在表里可以用attendance_date字段存日期,再加上course_id和student_id一起做联合唯一索引,避免重复签到。如果允许同一节课多次签到(比如考勤开放期间学生可以补签),那也要在业务上去重;这里最稳妥的做法是“先查后插”,通过唯一索引兜底。
迟到/缺勤规则可以放在业务层定:考勤开始后30分钟内签到的算正常,30分钟到下课时间算迟到,未签到且未请假的算缺勤。当然这个阈值可以做成系统配置,方便演示时调整。
2.4 人脸特征存储方案
很多人对人脸特征怎么存比较疑惑。人脸识别服务输出的通常是一个浮点向量,比如128维或512维。存储方式有几种:
- 直接存
text字段,把浮点数用逗号拼接成字符串。简单,但比对时每次都要解析,效率不高。 - 用专门的向量数据库,比如Milvus、FAISS。效果好,但毕设引入太重。
- 用MySQL的
blob二进制存储。可以,但代码处理稍麻烦,也不是所有同学都熟悉。
对于课堂考勤这种数据量不大的系统,直接用varchar或text存特征字符串完全够用。比如虹软SDK提取的特征值,可以转成Base64字符串存起来。比对的时候,先从库里查出所有特征码,解析后逐一计算相似度,找到分数最高且超过阈值的即为匹配。
特征表设计:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 学生id |
| face_feature | text | 人脸特征码 |
| face_image | varchar(255) | 人脸照片存储路径 |
| create_time | datetime | 注册时间 |
一张人脸特征可以对应一个学生,也可以同一个学生存多张人脸图,取平均特征或者比对时取最高分。毕设可以简单一点,一个学生注册一次就覆盖掉旧特征,减少冗余。
3. 人脸识别方案选型与核心实现
3.1 人脸识别流程拆解
人脸识别不是“一张照片比对另一张照片”这么简单,标准的流程是:人脸检测 → 人脸对齐 → 特征提取 → 特征比对。
- 人脸检测:从图片中定位出人脸的位置,返回矩形框坐标。
- 人脸对齐:将检测到的人脸通过关键点(眼睛、鼻子、嘴角)进行旋转和缩放,统一归一化。
- 特征提取:通过深度学习模型,将人脸图像映射成一个固定维度的特征向量,这个向量可以代表人脸身份信息。
- 特征比对:计算两个特征向量之间的相似度,大于阈值则认为是同一个人。
在答辩时,你能够把这几步说出来,就已经可以证明你不是只会调API。这也是论文里核心章节的重点。
3.2 自选方案对比:SDK、API还是本地服务
我做毕设指导时,给学生推荐过下面几个方案:
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 虹软ArcFace离线SDK | 免费、离线、识别精度高、提供Java调用样例 | 需要注册开发者账号,下载SDK;接口文档较乱 | 高 |
| 百度人脸搜索API | 接入简单、后台有控制台、文档详细 | 需要联网、接口有并发限制、部分功能收费 | 中 |
| OpenCV + Dlib | 完全开源、可控性强 | 识别精度一般、环境配置麻烦,需要C++/Python | 中 |
| Python Flask/FastAPI + InsightFace | 效果好、代码清晰 | 需要额外部署Python服务、内存占用高 | 高(如果你熟悉Python) |
最终我采用的是“本地Python人脸识别服务 + Spring Boot调用”的组合。为什么这么做?因为大多数计算机毕业设计是Java方向,老师希望看到的是Spring Boot的能力,而不是让人用Java去读Python训练好的模型。而且Java端调用Python服务非常自然,通过HTTP接口通信,双方互不干扰,你甚至可以在Spring Boot里声明一个FeignClient或者RestTemplate来调用。
3.3 人脸注册接口设计
注册流程的设计上,我会把所有和人脸识别相关的封装到一个FaceService中,Spring Boot的FaceController只负责接收图片和返回结果。
服务内部逻辑:
- 接收前端上传的图片,保存到本地磁盘或者OSS。
- 将图片发送到Python人脸识别服务的
/face/register接口,并携带一个student_id。 - Python服务检测图片中是否包含人脸,如果包含,则提取特征码并保存到内存/本地文件。
- 将特征码返回给Java端,Java端再写入MySQL的
face_feature表。
这里有一个非常关键的点,就是“图片上传的大小限制”。Spring Boot默认的上传大小是1MB,而一张高清摄像头照片可能2-3MB,很容易报FileSizeLimitExceededException。所以务必要在application.yml里把上传限制调大,同时前端也要做图片压缩,否则答辩时你会被这个错误卡住。
3.4 人脸签到接口设计
签到流程是:
- 学生点击“开始签到”,前端调起摄像头,拍一张照片(或者上传图片)。
- Spring Boot接收照片,调用人脸识别服务的
/face/search接口。 - Python服务提取照片中的人脸特征,与已注册的特征库进行比对。
- 返回最相似的学生ID和相似度分数。
- Spring Boot拿到匹配学生后,再检查选课关系、考勤时间等业务逻辑,写入考勤记录。
需要注意的是,识别和业务校验一定要分开。人脸识别只回答“这个人是谁”,至于“这个人有没有选这门课”“现在能不能签到”,是Spring Boot业务层负责的。这种清晰的职责划分,在论文的模块设计章节也很好写。
4. 从零搭建项目:关键代码与实操过程
4.1 创建Spring Boot项目与依赖配置
我使用的是Spring Boot 2.7.18,因为这是Java 8用户最稳妥的版本,至于Spring Boot 3.x虽然新,但需要Java 17,很多本科生电脑上还没装,容易折腾。建议你直接用 https://start.spring.io 生成项目,或者用IDEA的Spring Initializr。
核心依赖,pom.xml中至少要有:
spring-boot-starter-webspring-boot-starter-validationmysql-connector-java(或者mysql-connector-j,注意版本对应)mybatis-plus-boot-starter(或者用MyBatis,看你自己熟悉哪个)spring-boot-starter-data-redis(可选,用于缓存或会话)lombok(必备,简化实体类)
application.yml关键配置:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/face_attendance?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 10MB jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto face: register-url: http://127.0.0.1:5001/face/register search-url: http://127.0.0.1:5001/face/search similarity-threshold: 0.75关于MySQL版本,建议使用5.7或8.0,JDBC驱动不要混用。如果你用的是MySQL 8,driver-class-name是com.mysql.cj.jdbc.Driver;如果是MySQL 5.7,则用com.mysql.jdbc.Driver,很多报错都是因为这里写错了。
4.2 实体类与Mapper层
以最核心的Attendance表为例,实体类如下:
@Data @TableName("attendance") public class Attendance { @TableId(type = IdType.AUTO) private Long id; private Long studentId; private Long courseId; private Integer status; private LocalDateTime attendanceTime; private String imageUrl; }@TableName是MyBatis Plus的注解,用来映射表名。如果你选了JPA,那就用@Entity。两种方式都可以,我实测觉得MyBatis Plus更适合国内毕设,因为它内置了BaseMapper,你不需要写一堆XML,接口里直接继承即可:
public interface AttendanceMapper extends BaseMapper<Attendance> { }这个接口就拥有了增删改查和分页能力。在班级、课程那些模块也一样,写起来非常快。这也是为什么毕设圈里Spring Boot + MyBatis Plus成了“标准答案”。
4.3 人脸识别服务的封装
Java服务里封装一个FaceClient,用RestTemplate调用Python接口。代码大概长这样:
@Service public class FaceClientService { @Value("${face.register-url}") private String registerUrl; @Value("${face.search-url}") private String searchUrl; @Autowired private RestTemplate restTemplate; public String register(String studentId, MultipartFile image) { // 将图片转成二进制请求体 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); try { body.add("file", new ByteArrayResource(image.getBytes()) { @Override public String getFilename() { return image.getOriginalFilename(); } }); body.add("student_id", studentId); } catch (IOException e) { throw new RuntimeException("图片读取失败", e); } HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.postForEntity(registerUrl, requestEntity, String.class); return response.getBody(); } public FaceSearchResult search(MultipartFile image) { // 类似构造请求,调用/face/search } }这里有一个实操细节:RestTemplate默认不能直接发送MultipartFile,需要把它包成ByteArrayResource,并重写getFilename()方法,否则Python端接收不到文件名,导致类型判断出错。另外,RestTemplate可能需要对超时时间做设置,否则识别过程如果超过1秒,客户端会感到卡顿。识别一个50人课堂的特征库,时间一般不会超过200ms,但如果注册学生上千,还是建议加一层缓存。
4.4 Python端人脸识别服务的参考实现
Python端不需要多复杂,用Flask写一个Web服务即可。我推荐使用InsightFace的buffalo_l模型,它比传统Dlib精度更高,而且安装简单:
pip install flask insightface onnxruntime启动后,通过两个路由处理注册和搜索。关键代码:
import cv2 import numpy as np import insightface from flask import Flask, request, jsonify app = Flask(__name__) face_app = insightface.app.FaceAnalysis(name='buffalo_l') face_app.prepare(ctx_id=-1) # -1表示用CPU,0表示用GPU # 特征数据库,键为student_id,值为特征向量 feature_db = {} threshold = 0.5 @app.route('/face/register', methods=['POST']) def register(): file = request.files['file'] student_id = request.form['student_id'] image = cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) faces = face_app.get(image) if len(faces) == 0: return jsonify({'code': 1, 'msg': '未检测到人脸'}), 400 # 取最大人脸的特征 max_face = max(faces, key=lambda f: f.bbox[2] * f.bbox[3]) feature = max_face.normed_embedding feature_db[student_id] = feature # 这里也可以持久化到文件或MySQL return jsonify({'code': 0, 'msg': '注册成功'}) @app.route('/face/search', methods=['POST']) def search(): file = request.files['file'] image = cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) faces = face_app.get(image) if len(faces) == 0: return jsonify({'code': 1, 'msg': '未检测到人脸'}), 400 max_face = max(faces, key=lambda f: f.bbox[2] * f.bbox[3]) feature = max_face.normed_embedding best_id = None best_score = -1 for sid, fea in feature_db.items(): score = np.dot(feature, fea) if score > best_score: best_score = score best_id = sid if best_score < threshold: return jsonify({'code': 1, 'msg': '匹配失败', 'score': float(best_score)}) return jsonify({'code': 0, 'student_id': best_id, 'score': float(best_score)}) if __name__ == '__main__': app.run(host='0.0.0.0', port=5001)这个实现很简单粗暴,但在毕设场景下完全可用。唯一要注意的是feature_db是内存字典,重启就丢失,所以正规做法是把特征持久化到文件或数据库。如果你想着重展示工程能力,可以把注册时的特征存到MySQL的blob字段中,搜索时加载全部特征。数据量不超过几万人,速度都能接受。
4.5 前端页面与交互Vue实现要点
前端推荐用Vue + Element UI,因为组件现成,表格、弹窗、上传都有现成的。这里不用自己搭脚手架,直接用vue-element-admin精简版,或者用vue-cli创建后手写路由。
比较核心的几个页面:
- 登录页:登录成功后保存JWT令牌,axios拦截器带上
Authorization。 - 学生管理页:管理员维护学生信息,支持人脸注册(上传照片,显示识别结果)。
- 课程管理页:教师创建课程、设置上课时间、维护选课学生。
- 考勤页:教师点击“开始考勤”,页面调起摄像头;学生拍照后,前端把照片发送到后端签到接口。
- 考勤统计页:按课程展示出勤率,用ECharts画饼图或柱状图。
这里有个小坑:摄像头调用必须使用HTTPS或者localhost,否则浏览器会拒绝。如果你在本机演示,访问http://localhost:8080是没问题的,但如果要通过IP地址让其他电脑访问,默认http环境里getUserMedia会直接报错。解决办法是配置Nginx开启HTTPS,或者直接用localhost演示。毕设答辩通常在自己电脑上演示,所以问题不大。
4.6 项目部署与演示环境准备
毕业设计演示时最怕环境出问题,所以部署步骤越简单越好。我的建议是:
- Java后端打成可执行jar包,用命令行启动。
- Python服务用
python app.py启动。 - Vue前端
npm run build后,由Spring Boot的静态资源目录托管,或者单独放在Nginx。
如果想让演示更稳,可以在电脑里装一个能一键启动三个服务的脚本。比如Windows下的.bat文件,先启动Python,再启动Java,然后用浏览器自动打开localhost:8080。答辩前把服务全部启动好,不要现场临时编译。
还有一种更省心的方案:使用Windows的WSL2或Docker,把Python环境和Java环境都装好。但我实测下来,大多数学生电脑Docker运行时内存占用太高,反而容易卡死。不如原生安装干净。
5. 常见问题排查与优化建议
5.1 人脸识别失败率高的原因与对策
最常见的识别失败有几种情况:
- 光照太暗或太亮:摄像头采集到的图片过暗,导致人脸检测不到。对策是做图像增强,或者让演示环境光照均匀。
- 角度倾斜:学生没有正对摄像头,导致特征偏差大。对策是在前端页面提示“正对摄像头”,并且Python端可以用关键点对齐做矫正。
- 口罩/遮挡:课堂场景其实很少要求学生戴口罩,但答辩时可以提一下系统可扩展口罩检测。
在实际测试中,我把相似度阈值设置成0.5,在40人班级中误识别率约为2%;如果调到0.6,误识别率几乎为零,但会有少量学生识别失败。这个值要根据你的特征向量维度调整,建议注册完所有模拟学生后,在测试集上做一个简单的混淆测试,再确定最终阈值。
5.2 Spring Boot集成人脸服务的一些坑
下面这些坑都是我亲眼看到学生踩过的:
- 跨域问题。前后端分离项目,Vue端口和Java端口不同,必会出现CORS错误。解决方案是写一个
WebMvcConfigurer,添加全局CORS映射,允许http://localhost:9528或全部来源。不要只在Controller上加@CrossOrigin,那样容易漏掉。 - 上传文件编码问题。有时候图片上传后Python端Base64解码乱码,大概率是因为前端没有设置
Content-Type: multipart/form-data,检查axios的FormData用法。 - 定时清理临时文件。每次签到都生成新的图片,磁盘会越来越满,定期用Spring的
@Scheduled清理temp目录下的过期图片。 - 教室内多人同屏识别。人脸识别服务可能一下检测到多张脸,我们这边策略是取最大脸。如果实际教室有多人同时入镜,可以改成按照坐标排序,或者前端只从摄像头中裁取一个人脸区域再上传。毕设演示时,最好保证摄像头里只有一个人,这样最稳。
5.3 并发考勤与性能优化
课堂考勤可能同时有几十个学生发起签到请求,如果多人同时请求Python服务,而Python端又是单线程的Flask开发服务器,可能就会出现排队和超时。我建议先在Python端用multiprocessing或者部署成Gunicorn多worker,同时给Spring Boot添加一个线程池,但并发量不大,直接使用Flask自带的threaded=True参数就够了:
if __name__ == '__main__': app.run(host='0.0.0.0', port=5001, threaded=True)再加一个简单的Redis缓存:把已经注册的特征向量加载到内存中,识别前先查缓存,而不是每次都查数据库。这样并发能力提升一个档次。
5.4 毕设论文与答辩常见提问
最后聊聊论文和答辩,因为“设计与实现”这种题目,老师主要看你有没有真的理解。常见提问如下:
- “人脸特征是怎么存储和比对的?”答:特征为由浮点数组成的向量,保存到MySQL对应字段,比对用余弦相似度,先归一化再计算点积。
- “如果两个学生长得很像怎么办?”答:阈值调高;还可以采集多张人脸照片,存入多个模板,取平均特征,降低误识率。
- “为什么用Spring Boot而不用SSM?”答:自动配置、内嵌Tomcat、生态完整、便于快速交付,同时支持与第三方服务(如人脸SDK)集成。
- “系统安全方面做了什么?”答:JWT鉴权、密码BCrypt加密、上传文件大小限制、防止XSS攻击、SQL预编译等。
- “考勤数据有什么统计价值?”答:可以分析出勤率趋势、预警长期缺勤学生、期末自动生成平时成绩等。
这些问题在纸上打个草稿,口头能讲清楚就行。这个项目最大的优势就是每个模块都有实打实的内容,只要你把自己写过的代码讲明白,老师不会为难你。
6. 最后再分享几个实操小技巧
我给学生弄这个项目时,总结了一些非常琐碎但实用的经验。比如,学生注册人脸时如果用的是手机前置摄像头,一定要提醒他摘掉眼镜、正对屏幕,否则特征库录入质量就差,后面上课打卡就总失败。再比如,课程考勤时间最好精确到具体日期,演示时把系统时间调整到上课时间前5分钟,避免解释“为什么现在不能签到”的尴尬。
Python端的人脸服务如果装在Windows上,要注意安装Microsoft Visual C++ Build Tools,否则insightface可能安装失败。还有GPU不一定要用,CPU跑InsightFace在单个检测请求下也就一两百毫秒,课堂演示完全够用。真要追求极致,可以先把摄像头画面切成单人脸正面照,再发送给后端。
另外,这个项目还能很自然地扩展,比如增加钉钉/企微通知,把考勤异常名单推送给辅导员;或者把识别模型换成ArcFace继续提高精度;前端也可以加一个地图展示各教室考勤进度。这些都可以写进“未来展望”部分,增加论文篇幅,让老师觉得你有思考。但核心代码不要膨胀,保持你能掌控的范围,毕竟毕设的根本目标是顺利通过,而不是做一个完美的商用餐巾纸。
根据我个人实际做下来的体验,这个题目的成功秘诀就是“模块清晰、识别可演示、数据有闭环”。只要按照上面这套思路走,你会发现自己不是死记硬背代码,而是真的知道每一行在干什么。最后答辩的时候,自信地把你做的系统从头演示到尾,把这个过程中的踩坑和思考说出来,那才是老师最想看到的“计算机毕业设计”该有的样子。