拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于SpringBoot+Vue+Android的课堂人脸识别考勤系统设计与实现

上课铃响老师抱着花名册从第一排喊到最后一排三十多个名字喊完六分钟没了地下还有几声到是替人应的。这是我做这个项目前在课堂上看到的最真实的一幕。当时我就在想能不能做一套东西让学生进教室对着手机刷一下脸出勤记录就自动进系统老师打开后台就能看到谁来了、谁迟到、谁压根没来。于是就有了标题里这套系统SpringBoot做后端、Vue做管理后台、Android端做签到入口核心是人脸识别。我做这套系统的目标很明确不搞花架子要能真的在课堂环境里跑起来。三十到六十人的一个班能在一分钟内完成点名学生手里普通千元机也能流畅识别后台能按课程、按日期、按学生三个维度查考勤。回头整理这篇文章不单是记录这套系统的设计与实现过程更想把我在人脸采集、特征比对、并发签到这些环节踩过的坑一并说清楚给准备做毕业设计或者想入门全栈人脸项目的朋友一条可以复现的路。1. 为什么选人脸识别来解决课堂考勤而不是扫码和指纹很多人刚看到这个题目会问现在扫码签到、NFC打卡也挺成熟的为什么非要人脸识别这个问题我在需求分析阶段被问过很多次但真正做完一版去试运行之后答案反而越来越清晰。1.1 传统课堂考勤的三个死结先看最基础的场景老师点名。一个五十人的课堂完整点一遍名字基本要六到八分钟如果中间有学生没应声还要二次确认时间直接奔着十分钟去。一个老师一周上六节课光点名就耗掉近一个小时这个成本在教学工作量里是看得见的。再看扫码签到和NFC打卡。扫码签到的核心问题是三个字可转交。群里把二维码一丢人在寝室也能到课。这种形式主义比点名更伤——老师并不知道真实的到课情况考勤数据彻底失真。NFC学生卡同样存在转借问题而且学校还要额外承担发卡、补卡、读卡设备的硬件成本。第三个死结在后面考勤数据散落。课堂测验证过纸质签到单学生到课情况需要课后再人工录入Excel再汇总到教务处。整个过程滞后一两天不说录入环节还容易出错。课程结束打印出来的考勤表跟实际课堂情况对不上老师想追溯都没办法。这三个死结指向同一个需求考勤要快、要难代、要自动沉淀。传统手段很难同时满足。1.2 人脸识别在这个场景下的独特优势与代价人脸是少数几个人走哪它跟哪、物理上不太容易转交的凭证。指纹也行但课堂环境下让几十个学生轮流按指纹效率比点名高不了多少。NFC和二维码卡在凭证与本人分离这个漏洞上人脸天然规避了这个问题——除非学生指纹打卡、脸也到场否则很难远程代打。但人脸识别也有它的代价设计时必须正视对光线敏感。教室逆光、暗光场景下识别成功率会明显下降不是模型不好而是采集到的人脸图本身就没信息量。对遮挡敏感。口罩、刘海、低头玩手机都会让检测阶段就把人脸漏掉。涉及个人信息。人脸属于生物特征信息系统需要考虑最小化采集、脱敏展示、权限管理。我在地基阶段就把这些代价列成了设计约束采集端要实时检测人脸质量、后台要支持阈值调节、数据存储要和人脸图分开权限控制。这几点在后面每个模块里都会反复出现。1.3 三端各自的技术使命这套系统的技术分工被我收得很紧每一端只做自己最擅长的事Android端负责看。用CameraX调起摄像头配合ML Kit做人脸检测和关键点定位确认画面里确实有人脸再把质量最好的那一帧截出来压缩上传。识别模型不放手机里能大幅降低包体和内存占用。SpringBoot后端负责认。收到人脸图后丢进特征提取模型得到一个高维特征向量再和学生选课档案里的特征向量做相似度比对得出这个人是谁、可信度多高的结论。Vue管理端负责管。课程、学生、考勤记录、统计报表都在这里老师登录后台完成日常管理。这样拆的好处是每个模块可以独立开发和测试。Android端哪怕暂时没有后端也可以先用模拟数据跑通UI后端也可以不等App直接拿一批照片做接口测试。后面实际开发时这条路线让我的并行进度快了很多。2. 一次课堂签到的完整旅程从摄像头到考勤记录要理解这套系统不要从代码入手而是先跟着一次签到请求走一遍。把这条链路吃透了后面的代码细节都只是实现问题。2.1 三端协作的整体通信链路我用一条最简洁的链路来描述一次正常情况下签到发生了什么学生打开Android APP进入今日签到页面CameraX启动前置摄像头。ML Kit在视频流中检测人脸画出人脸框当人脸足够大且画质满足条件时自动截取人脸区域。APP把裁剪压缩后的图片POST到SpringBoot接口 /api/attendance/checkin同时携带JWT Token。后端先鉴权再根据当前学生和课程信息查出这节课的选课学生清单得到待比对的特征向量集合。后端调用特征提取模型把上传图片转成512维向量与待比对集合逐个人脸算余弦相似度。分数超过阈值判定身份匹配写入考勤记录低于阈值返回未识别请重试。移动端展示签到成功及当前考勤状态同时Vue后台通过轮询或刷新加载出最新考勤记录。这里有一个重要设计步骤5在SpringBoot进程内完成而不是再调一个Python服务。我纠结过要不要单独部署一个Python人脸服务但最终为了部署简单、让项目完全围绕SpringBoot展开选择了onnxruntime-java直接加载模型推理。后文我会详细讲这个选型。2.2 注册流程人脸如何变成一串数字注册流程发生在第一次使用系统时核心是把学生的人脸图处理成一串数字存下来后续签到才能复用。具体流程是学生在Android端拍一张正面照本地检测到人脸后裁剪上传到 /api/face/register。后端把图片保存一份到文件服务器本地磁盘或者OSS同时用特征提取模型得到特征向量再把这张图片的URL和特征向量写入student表的两个字段里。这里我建议把特征向量单独存一张表不要跟学生基础信息混在一起。原因有两个一是特征向量是二进制数据跟文本字段放一起会影响常规查询性能二是后续如果要升级识别模型可以只重建特征表不用动学生信息表。2.3 签到流程1:N比对在哪个环节发生很多人会把人脸识别想象成一次输入图片、输出人名的黑盒操作。实际上课堂考勤场景适合的是1:N对比拿当前拍到的1张脸去和这个课堂里注册过的N张人脸特征做比对找出最像的那个再看相似度够不够。我测试过两种路径一种是把整个年级几千人的特征一次性载入内存做遍历另一种是先按课程过滤只保留选了这节课的学生特征再遍历比对。结果是在五十人课堂里后者耗时只有前者的十分之一左右签到接口P95响应时间从1.8秒降到了400毫秒以内。所以最终实现里签到接口的第一步不是提取特征而是先查出当前课程的学生集合缩小比对范围。2.4 两个核心接口的请求/响应约定为了让三端协作清晰我把接口写得尽量直白。下面看契约的简化示例POST /api/attendance/checkin Header: Authorization: Bearer token Body: { courseId: 12, faceImageBase64: /9j/4AAQSkZJRgABAQAAAQ }响应{ code: 0, data: { studentId: 2024030101, studentName: 张三, status: NORMAL, similarity: 0.9487, checkinTime: 2025-06-12 08:42:03 }, message: 签到成功 }其中status包含NORMAL出勤、LATE迟到两种WHY的判定完全由后端时间窗口决定不依赖客户端时间避免学生改手机时间钻空子。注册接口的核心则是上传图片后返回特征是否入库成功以及用来关联的人脸图片URL。两个接口一前一后分别对应系统的注册身份和验证身份两个基本操作。3. 人脸识别三层拆解检测、特征提取、比对很多人以为人脸识别是一个统一的能力其实它是由三个相对独立的技术环节拼起来的。在我这套项目里这三个环节分别落在Android端和后端搞清楚每个环节的职责是项目成功的关键。3.1 检测与关键点ML Kit在Android端的价值人脸检测Face Detection解决的是画面里哪里有人脸、人脸朝向哪、嘴巴眼睛在哪的问题。我选择的是Google ML Kit的人脸检测它有几个对考勤场景特别贴心的能力本地实时推理不消耗网络流量单帧检测在低端机上也能跑出120ms左右的延迟。输出左眼、右眼、鼻子、嘴巴等关键点坐标方便做人脸裁剪和姿态判断。能返回眼睛睁开概率这个特性后面被我做成了最简单的活体检测。Android端拿到摄像头预览帧后不是把整个画面传给后端而是先用ML Kit检测人脸拿到人脸的边界框在原图上裁剪出人脸区域再上传。这样传输的数据量小、识别特征更聚焦。同时检测阶段会做一个简单的人脸质量打分人脸框宽度占画面宽度的比例低于0.3就提示请靠近一点人脸偏转角度太大就提示请正对屏幕。这一道关卡直接把很多无效请求挡在了App端后端要处理的都是质量还不错的图。3.2 特征提取为什么我把人脸识别模型放进SpringBoot特征提取Face Recognition把人脸图像映射到一个高维向量空间相同的人的向量在空间中距离很近不同的人距离很远。这一层我放在后端处理。我在方案上做过对比方案优点缺点Android本地TFLite模型提取离线可用、隐私最好包体增大、低端机推理慢、模型升级要发版后端Python服务Faces生态成熟、模型多要维护两套运行环境部署复杂后端SpringBootonnxruntime一套Java环境搞定、模型文件随jar走需要自己处理模型输入输出格式我最终选了第三种。具体做法是下载一个MobileFaceNet的ONNX模型放进resources目录用onnxruntime-java加载输入是归一化后的112x112人脸图输出是512维特征向量。Java侧推理一次的耗时在CPU环境下大约是80到200毫秒完全满足课堂考勤的实时性要求。代码核心只有几行OrtEnvironment env OrtEnvironment.getEnvironment(); OrtSession session env.createSession(modelPath, new OrtSession.SessionOptions()); OnnxTensor inputTensor OnnxTensor.createTensor(env, floatArray, new long[]{1, 3, 112, 112}); float[][] result session.run(Collections.singletonMap(input, inputTensor)) .get(0).getValue();这里有个容易踩的坑不同模型的输入张量名称、归一化方式都不一样。MobileFaceNet的输入一般要先将像素值从[0,255]减均值再除以标准差顺序是CHW而不是HWC。我第一次跑的时候没注意归一化出来的特征向量完全不可用排查了将近半天才发现是预处理问题。3.3 比对与阈值余弦相似度的工程细节特征比对环节最简单通常向量维度是512或128课堂场景下遍历几十上百个向量是微秒级操作。我采用余弦相似度来衡量两个人脸的匹配程度。公式是cosine(A, B) sum(Ai * Bi) / (sqrt(sum(Ai^2)) * sqrt(sum(Bi^2)))。在特征已经归一化(模长1)的前提下余弦相似度就是点积。阈值的选择非常关键。设置太低容易把不同的人判定成同一个人设置太高同一个人稍微换个光线就识别失败。我在一个五十人班级的实测中发现阈值设0.75的情况下正常光线的识别准确率能到97%左右设到0.85误识率为零但漏识率会上升到15%以上。最终我采用动态阈值策略为了安全起见系统默认阈值取0.78老师可以在后台按班级调整。另外要注意特征向量在数据库里不能像存普通文本一样存储。我最开始用JSON字符串存float数组代码写起来方便但两张表的数据量一旦上千读出来解析就有明显开销。后来改成二进制BLOB字段配合序列化工具一次性读写效率和可维护性都好很多。3.4 防代签的实用手段从活体检测到时间窗口课堂考勤最大的作弊场景是拿别人手机照片代签。要防这种必须有活体检测。ML Kit能返回人眼的睁开概率我利用这一点做了一个很轻量但有效的活体检测学生进入签到页面后需要在屏幕上出现请眨眼提示的3秒内眨一下眼睛Android端检测到眼睛状态从睁开变成闭上再变成睁开这个完整过程后才允许提交签到。整个过程不超过5秒学生的体验不会太差。这个方案不依赖任何商用的人脸活体SDK成本极低但对翻拍照片和静态图片攻击是有效的。想再进一步防住手机播放视频这种攻击就得引入更复杂的静默活体模型了课堂场景下考虑成本和体验我认为眨眼检测已经够用。再加上后端的时间窗口课前15分钟到课后30分钟内允许签到和可选的地理围栏限制在教室周边200米这几个维度叠加起来代签门槛已经比扫码高了一个数量级。4. SpringBoot后端表结构、并发签到与状态机后端是整个系统的信息枢纽。人脸识别再准考勤数据存不好、查不快、算不对这个系统依然是废的。所以这一章专门讲我把数据库和业务逻辑做成什么样的。4.1 核心表结构设计我的库表设计围绕四个核心实体学生、老师、课程、考勤记录再加上一张选课关联表和一张人脸特征表。下面是几张大表的结构概览CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(50) NOT NULL, class_name VARCHAR(50), face_status TINYINT DEFAULT 0 ); CREATE TABLE face_feature ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, feature BLOB NOT NULL, face_image_url VARCHAR(255), create_time DATETIME, UNIQUE KEY uk_student (student_id) ); CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, teacher_id BIGINT NOT NULL, start_time TIME NOT NULL, end_time TIME NOT NULL, week_day INT NOT NULL ); CREATE TABLE attendance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, student_id BIGINT NOT NULL, checkin_date DATE NOT NULL, checkin_time DATETIME NOT NULL, status VARCHAR(10) NOT NULL, similarity DOUBLE, face_image_url VARCHAR(255), UNIQUE KEY uk_course_student_date (course_id, student_id, checkin_date) );这份设计有几个小心思attendance表加了(course_id, student_id, checkin_date)唯一索引从数据库层就杜绝了同一学生同一天同一门课的重复签到。student表里单独维护一个face_status字段用来标记人脸是否注册。管理端可以快速筛选出还没注册人脸的学生方便老师催办。人脸特征独立成表不和学生基础信息混在一起后续换模型只动feature表不动业务表。4.2 并发签到课前3分钟的48人同时点击课堂签到有一个天然的流量高峰就是上课前两分钟。全班同学几乎同时掏出手机点签到如果不做任何处理一个Tomcat默认线程池很快就可能被打满。我的处理策略是分级减压第一层Android端在网络请求发出前会做一个本地节流同一学生5秒内只能发起一次签到请求避免前端抖动造成重复请求。第二层后端对签到接口做并发控制。用Redis做库存时间窗口判断同一个学生同一门课在同一个签到时间窗口内的重复请求直接返回第一次的结果不做重复的模型推理。第三层模型推理线程池隔离。不能让人脸识别的推理任务和普通的CRUD请求抢占同一个线程池。我单独配置了一个固定大小为当前CPU核数1的线程池处理识别请求队列饱和时快速返回系统繁忙请稍后再试保护主流程。实际压测中50人同时触发签到接口能平稳跑完P95响应时间维持在1.5秒以内。这个表现在课堂场景下完全够用。4.3 出勤、迟到、缺勤的状态机设计考勤状态我一开始想得很简单来就是出勤没来就是缺勤。后来跟老师聊完发现不行课前忘了打个卡、突然身体不适请个假这些现实的边界情况都要处理。最终的状态机如下状态汇总NORMAL出勤、LATE迟到、LEAVE请假、ABSENT缺勤、PENDING待判定。签到窗口开课后5分钟内到课记为NORMAL超出宽限期且在课程结束前签到的记为LATE课程结束后没有签到记录则进入PENDING由老师在后台确认为ABSENT还是LEAVE。这里关键的一个边界是只要学生在Android端成功签到了后端就锁定NORMAL或LATE不允许再被后台清空。后台可以补录LEAVE或ABSENT但必须留操作日志。这一条我踩过坑第一版没做状态锁定某位学生不清楚操作流程签到成功后又尝试重新签到结果接口返回重复签到他以为失败就找老师改了状态数据一下乱了。后来加了状态锁和日志这类问题就消失了。状态机逻辑统一放在一个枚举类里避免散落在各个Service方法里变成意大利面代码。5. Android端实践人脸采集是最容易翻车的环节如果只看后端识别准确率模型选得好一点效果总会不错。但放到真实课堂Android端采集到的人脸图质量才是决定系统成败的上限。这章写几个我花了很多时间才想明白的工程细节。5.1 CameraX集成与人脸框实时绘制Android端我选的CameraX而不是旧式的Camera2。CameraX的生命周期绑定组件让摄像头管理简单很多不至于在界面切换时出现黑屏或OOM。核心页面是签到页布局是一块PreviewView作为取景画布上面叠一层自定义View画人脸框和提示文字。CameraX通过ImageAnalysis配合ML Kit做全流程处理ImageAnalysis strategy new ImageAnalysis.Builder() .setTargetResolution(new Size(640, 480)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); strategy.setAnalyzer(executor, imageProxy - { InputImage image InputImage.fromMediaImage(imageProxy.getImage(), imageProxy.getImageInfo().getRotationDegrees()); faceDetector.process(image) .addOnSuccessListener(faces - { // 拿到faces列表取最大的人脸画框并回调 }) .addOnCompleteListener(task - imageProxy.close()); });这里有个必须注意的点imageProxy.close()一定要在异步回调里调用否则会卡住底层图像管道一旦卡住摄像头预览就会变慢甚至黑屏。我第一版把这个close漏了测试时每隔十几分钟画面就卡死一次还以为是手机设备问题查了很久才发现是资源没释放。预览分辨率我刻意压在640x480而不是直接用2K原图。原因是人脸检测不需要那么高的分辨率压下来之后ML Kit的延迟能稳定在100ms左右而原图模式下低端机经常飙到300ms以上。5.2 绕开手机厂商美颜一定要自己拿原始帧这条是最值钱的实践心得。最初的原型版本我图省事直接调系统相机拍照然后把照片上传后台。测试时发现识别的成功率一塌糊涂关键是有几个宿舍几个人之间居然出现你认成我的情况。后来把上传的照片下载下来看发现问题了——部分Android手机默认开启了美颜模式照片里的脸已经被算法磨皮、瘦脸、提亮了这个美化过的人脸跟注册时拍的人脸在特征空间里距离大幅拉大同一个人也匹配不上更糟的是美颜后的脸趋同化不同的人特征距离反而近了导致误识率上升。所以正确的做法一定是用CameraX的ImageAnalysis通道直接拿到摄像头帧不经过拍照界面不经过厂商的美颜后处理逻辑。这个帧才是真实的脸。这是做移动端人脸识别的基本修养不能图省事用系统相机。5.3 人脸图压缩与上传策略网络传输是签到响应时间的大头之一。一张手机原图动辄3到5MBbase64编码后还要膨胀三分之一直接上传会导致弱网环境下签到体验极差。我设定的标准是人脸区域裁剪后长边缩放到160像素JPEG质量控制在80%最终上传的图片体积一般不超过30KB。在这种尺寸下MobileFaceNet依然能稳定提取出可用的512维特征还远程达不到模型需要的清晰度下限。图片上传用Retrofit OkHttp直接走multipart的file/form字段避免用base64字符串拼到JSON里再传一次省掉一层编码开销。实际弱网测试下30KB的图在4G网络上传输耗时基本能控制在1秒以内。5.4 光照、遮挡与角度的实测修正采集端处理好之后剩下的就是环境适应问题。我拉了一批同学做了三轮测试记录下失败场景的分布逆光场景占失败案例的一半以上。人脸朝向窗户时摄像头拍出来是黑的ML Kit直接检测不到人脸。对策是检测到人脸区域平均亮度低于阈值时在画面上提示光线不足请面向光源。戴眼镜的情况比预想的好很多。普通眼镜和细框眼镜对特征提取影响不大反而是厚重的偏光墨镜会把眼睛区域的特征完全遮挡导致检测丢失。考勤场景这是合理的摘一下眼镜也就几秒。低头族和死亡角度也贡献了不少失败案例。ML Kit能返回头部欧拉角我设定当俯仰角或偏航角绝对值超过20度时提示请正对屏幕而不是等到后端识别失败再返工。5.5 学生端的签到反馈设计聊完技术必须提一下交互层。学生打开App签到等待接口返回的这1到2秒最容易产生焦虑感也最容易误以为系统卡了。我的处理是检测到人脸并截帧后页面立即进入识别中状态展示实时检测界面并有一个转圈动画加一行文字正在比对课堂人脸数据。接口返回成功的瞬间页面从取景画面直接切换成一张全屏的签到成功卡片显示学生姓名、签到时间、课程名称绿色背景。这个先看见结果再看到细节的反馈设计能明显降低测试同学的困惑感。如果失败不能只弹一个干巴巴的Toast而是把失败原因明确给出来未识别到有效人脸和未在课堂名单中是两回事前者要原地重试后者往往意味着学生选课关系没维护好需要去管理后台核查。分开提示能省掉大量客服式的答疑成本。6. Vue管理端考勤数据不能只是能查Vue管理端承担的是老师和管理员的日常操作界面。我一开始只把它当成一个后台CRUD页面但实际使用后才发现它最重要的价值是让考勤数据变得可读、可决策。6.1 页面结构与权限设计管理端我用的是Vue 3 Vite Element Plus Pinia Axios的组合。模块划分成三个一级菜单课程管理创建课程、设置上课时间和周次、维护选课学生名单。考勤管理按课程查看某次课的考勤详情包括已签到、缺勤、迟到学生列表点击迟到/缺勤记录可以查看签到照片。统计分析包含出勤率趋势图、课程出勤对比、个人出勤明细三个维度。权限上系统只分老师和管理员两种角色。老师默认只能看到自己名下的课程管理员可以跨课程查看全校报表。角色在登录接口返回的JWT里通过角色字段区分Vue端路由守卫根据角色过滤菜单项。6.2 考勤报表如何呈现考勤数据的核心场景是一眼看出问题。一个人一个人地翻表格是反人类的我把统计功能作为管理端的一大重点。课程详情页顶部是一行汇总卡片应到人数、实到人数、出勤率、缺勤人数。中间是一张当次课的学生状态列表通过背景色区分出勤、迟到、缺勤。最下面用ECharts画一个本课程过去八周出勤率变化的折线图。个人学生维度的查询可以搜学生姓名或学号直接看Ta在一门课上的所有出勤记录和缺勤次数。这个功能老师用得非常多尤其是期末给分数的时候一张图比翻一个月点名册快得多。6.3 Token管理与接口对接的几个小坑管理端和后端对接大部分是常规操作但有三个点我建议你单独注意第一Axios请求拦截器里把JWT加到Authorization头响应拦截器里根据HTTP 401统一跳转到登录页同时清掉Pinia里的用户状态和本地localStorage。不然Token过期后页面会不断弹登录框体验很差。第二开发环境下Vue CLI/Vite的proxy代理把/api路径代理到SpringBoot地址前端代码里不要写死IP。很多人把baseURL写成localhost:8080等手机连着电脑调试时请求全走不通折腾半天才发现是代理问题。第三文件上传接口要设置较长的超时时间。老师从后台导入一个几百人的Excel名单第一次请求因为后端解析慢、默认10秒超时就直接断掉了。把上传接口的超时单独调到30秒就能避免这种误报。7. 实测数据、避坑清单与后续演进文章最后一部分聊点数字和真实体验。没有真实数据支撑的系统设计都是纸上谈兵。7.1 一组真实环境下的指标我在一个五十人班级的实际课堂环境里做了三轮完整测试结果如下指标数值说明正常光线识别率96.7%统计的是检测和识别全链路成功率弱光/逆光环境识别率88.2%加提示后略有提升单人平均签到耗时1.6秒含检测上传识别全流程全员签到完成时间约40秒相比点名6分钟效率提升明显重复签到拦截率100%唯一索引时间窗口双重保障误识率跨人识别0.2%阈值0.78条件下这个数据看起来不错但我要强调它是一个可控环境下的结果。测试时要求学生摘掉口罩、正对屏幕、保持正常光线。真实课堂上一定会有学生逆光、戴帽子、邻座互相干扰所以实际运行时要做好识别失败可重试、连续失败可求助老师的兜底方案。7.2 我踩过的三个典型坑第一个坑是图像方向问题。Android摄像头拿到的原始帧默认是横向的如果不做旋转校正就送去人脸检测出来的结果要么是侧脸、要么直接检测不到。ML Kit的InputImage从MediaImage创建时一定要正确传入rotationDegrees不能偷懒写死0。这个坑在横屏手机上概率更大我在测试时用一台旋转了90度的平板复现得很稳定。第二个坑是模型输入的归一化标准差。MobileFaceNet的ONNX模型要求把像素值除以255后再做(像素-0.5)/0.5的归一化。我一开始直接传原始[0,255]像素进去特征向量全部异常但模型不会报错问题特别难排查。后来我写了一段单测用同一张图的原始特征和归一化特征做对比才定位到问题。第三个坑是SQL时区导致的签到时间偏移。MySQL默认的时区是服务器时区而SpringBoot的JVM时区是东八区。直接把LocalDateTime传给数据库有时会差8小时签到记录显示在错误的日期上。解决办法是连接串里明确指定serverTimezoneAsia/Shanghai并统一所有时间字段使用DATETIME类型。这种时区小坑出问题得很隐蔽不批量对账根本发现不了。7.3 如果继续演进我会优先做这三件事这套系统跑通后我对它未来的方向也有一些明确的排序第一把点名算力彻底下沉到Android端。用TFLite在手机本地直接跑MobileFaceNet特征提取的结果直接以向量形式上传比对后端连人脸图都不用存了隐私和安全压力小一大截。现在端侧模型已经很容易部署唯一要解决的是低端机的推理性能和包体大小。第二把考勤阈值变成可自动调节。不同光线下的最优阈值其实不同目前只能靠老师手动调。下一步可以收集每次签到的相似度分数分布用小样本自动估算当天的最优阈值减少漏识和误识。第三给缺勤预警加温度。不只是记录谁没来而是在学期进行到一半时自动输出一份出勤预警名单把连续缺勤达到三次以上的学生标记出来方便辅导员提前介入。考勤系统的终点不是记录而是帮助教学管理者采取行动。人脸识别课堂考勤这个项目技术上并没有用到什么高不可攀的算法它的核心工程价值在于把一套本来分散在三端的复杂流程顺畅地串了起来并且每一个环节都做了针对课堂场景的落地优化。希望这篇梳理对你做类似的系统有参考价值尤其是第五章里那些关于人脸采集的细节值得你在动手前多花十分钟想清楚。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门