资讯详情

资讯详情

人脸识别课堂考勤系统:Spring Boot毕设从设计到实现全解析

很多人看到“人脸识别”这四个字第一反应就是“算法太难了”。但作为计算机毕业设计来说人脸识别课堂考勤系统其实是一个非常合适且讨巧的题目它既有场景、有数据、有交互又能把当前热门的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 APIVue Element UI构成管理页面人脸识别模块独立封装成一个service服务。数据流向大致是注册时前端上传照片 → Spring Boot接收 → 调用人脸识别服务提取特征 → 将特征码存入数据库。签到时摄像头捕获照片 → Spring Boot接收 → 调用人脸识别服务搜索相似用户 → 返回学生信息 → 写入考勤记录。这样设计的好处是后续就算你把识别服务从SDK换成APISpring Boot主代码也不需要大改符合高内聚低耦合的设计原则。这里也建议学生在论文里画清晰的结构图这是答辩常问的点。我用Mermaid画图但你们写论文时可以用PowerPoint画框图结构是表现层Vue→ 控制层Controller→ 业务层Service→ 数据层Mapper / DAO旁边独立一个“人脸识别服务”。2. 核心模块拆解与数据库设计2.1 用户角色与权限设计考勤系统至少有三类角色管理员、教师、学生。用Spring Security或者简单的拦截器都可以实现权限控制。实际做的时候我倾向于用JWT做无状态登录这样前后端分离更自然。角色表也可以用一张role表加一张user_role关联表但也可以在user表里加一个role字段因为角色很少没必要做成RBAC那么复杂。用户表设计参考字段类型说明idbigint主键usernamevarchar(50)登录账号passwordvarchar(100)BCrypt加密后的密码real_namevarchar(50)姓名rolevarchar(20)student / teacher / adminstudent_novarchar(20)学号仅学生statusint0禁用1启用密码一定要用BCrypt加密这是答辩安全问题的标准答案。权限控制用Spring Security的PreAuthorize注解或者在Controller里加角色判断按需取舍。2.2 课程、选课与课表模块课堂考勤必然要有“课”的概念否则考勤记录没有归属。课程表、选课表这是最基础的设计。course课程表id、课程名称、教师id、上课地点、上课周数、节次。student_course选课表id、学生id、课程id一个学生可以选多门课一门课有多个学生。课表不必做到像教务系统那样复杂我们只需要能判断“这节课该不该考勤”、“这个学生是否在这个课程里”。这里有一个容易被忽略的点考勤时必须校验学生是否选择了该课程。如果直接把不在课程里的学生也签到成功了那系统就有逻辑漏洞。答辩时老师很可能会拿这个问题来试探。上课时间我是用“周几 第几节”来表示的例如week_day1-7代表周一到周日和period1-8传统作息。如果需要支持按周次区分就再加weeks字段存一个逗号分隔的字符串比如1-16表示第1到16周。这个设计虽然简单但完全够用而且演示效果很直观。2.3 考勤记录设计考勤表是系统的核心数据表。一定要记录的是谁、什么时间、哪门课、结果是什么正常/迟到/缺勤/请假。attendance表字段字段类型说明idbigint主键student_idbigint学生idcourse_idbigint课程idstatusint1正常2迟到3缺勤4请假attendance_timedatetime签到时间image_urlvarchar(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字符串存起来。比对的时候先从库里查出所有特征码解析后逐一计算相似度找到分数最高且超过阈值的即为匹配。特征表设计字段类型说明idbigint主键student_idbigint学生idface_featuretext人脸特征码face_imagevarchar(255)人脸照片存储路径create_timedatetime注册时间一张人脸特征可以对应一个学生也可以同一个学生存多张人脸图取平均特征或者比对时取最高分。毕设可以简单一点一个学生注册一次就覆盖掉旧特征减少冗余。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?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/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: GMT8 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.0JDBC驱动不要混用。如果你用的是MySQL 8driver-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 BaseMapperAttendance { }这个接口就拥有了增删改查和分页能力。在班级、课程那些模块也一样写起来非常快。这也是为什么毕设圈里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); MultiValueMapString, 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); } HttpEntityMultiValueMapString, Object requestEntity new HttpEntity(body, headers); ResponseEntityString 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(namebuffalo_l) face_app.prepare(ctx_id-1) # -1表示用CPU0表示用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, keylambda 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, keylambda 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(host0.0.0.0, port5001)这个实现很简单粗暴但在毕设场景下完全可用。唯一要注意的是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自带的threadedTrue参数就够了if __name__ __main__: app.run(host0.0.0.0, port5001, threadedTrue)再加一个简单的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继续提高精度前端也可以加一个地图展示各教室考勤进度。这些都可以写进“未来展望”部分增加论文篇幅让老师觉得你有思考。但核心代码不要膨胀保持你能掌控的范围毕竟毕设的根本目标是顺利通过而不是做一个完美的商用餐巾纸。根据我个人实际做下来的体验这个题目的成功秘诀就是“模块清晰、识别可演示、数据有闭环”。只要按照上面这套思路走你会发现自己不是死记硬背代码而是真的知道每一行在干什么。最后答辩的时候自信地把你做的系统从头演示到尾把这个过程中的踩坑和思考说出来那才是老师最想看到的“计算机毕业设计”该有的样子。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →