尧图精选

Python人脸识别考勤系统实战:Flask+Vue全栈开发与避坑指南

🕒 发布时间:2026/9/28 16:40:59 📁 来源:尧图网络
简介这是一套面向高校计算机相关专业学生的Python毕业设计/课程设计完整项目主题为基于人脸识别的上课考勤系统适合需要完成毕设或课设、希望拿到可部署可运行源码的同学。项目包含前端界面与后端录入界面覆盖学生信息输入、人脸数据录入与训练、多条件搜索修改、多选删除、人脸识别追踪签到等完整流程并支持生成签到表格与导出Excel签到表。系统按功能划分为录入端、管理端、监控端三部分在原始项目框架上做了大量补充与优化。压缩包共42个文件约46.97MB以xml级联分类器、png界面截图、ui界面文件、py脚本及whl依赖包为主另含说明文档与配置文件目录结构清晰。资源内附详细部署教程和项目运行图已有1656人学习便于快速搭建环境、理解模块分工并完成二次开发。1. 从一张教室门口的打卡照片说起这套考勤系统到底解决什么问题每年毕业季计算机相关专业的选题里总有一类经久不衰Python 毕业设计做一套人脸识别考勤。原因很现实——它同时踩中了三个能写进论文、又能当场演示的点Python 生态成熟、人脸识别有现成算法库、前后端分离能体现工程能力。但真正动手的人会发现网上能搜到的多半是「跑通一张图」的 demo离「一个班四十个人排队进门、老师打开网页就能看谁到了」还差着十万八千里。这套系统要解决的核心问题其实很朴素把「点名」这件重复劳动自动化。学生走到教室门口的摄像头前系统抓一帧画面识别出是谁写一条带时间戳的考勤记录进数据库老师在浏览器里打开一个录入界面能看到实时到勤名单也能手动补录、导出表格。它适合的人群也很明确——正在做毕业设计、需要一套能演示、能答辩、代码量可控又不太水的同学以及想入门计算机视觉 Web 全栈的开发者。我见过太多人卡在同一个地方算法部分照着教程跑通了一到「前端怎么调后端」「后端怎么存人脸特征」「多人同时识别怎么不卡」就全线崩盘。这篇就按我实际搭过一遍的顺序把这条链路从头到尾讲清楚包括那些教程里不会写的坑。2. 技术选型为什么是 face_recognition Flask Vue而不是别的组合2.1 人脸识别算法这条线怎么选人脸识别落地分两大步人脸检测框出画面里有没有脸、在哪和人脸特征比对这张脸是谁。选型时你面对的是三档方案。第一档是 OpenCV 自带的 Haar 级联或 DNN 检测器优点是零额外依赖、CPU 就能跑缺点是侧脸、戴口罩、光线差时漏检严重答辩现场灯光一暗就翻车。第二档是face_recognition这个库它底层封装了 dlib 的 HOG 检测 ResNet 特征提取调用只要三行代码128 维特征向量做欧氏距离比对准确率对毕业设计这种小规模场景完全够用。第三档是 InsightFace、ArcFace 这类工业级方案精度高但依赖重、模型文件大、部署麻烦对毕设来说是杀鸡用牛刀。我一般会推荐face_recognition理由很实在安装虽然有点折腾dlib 编译是经典血泪点后面避坑章细说但 API 极其友好代码量少到能让你把精力放在前后端联调上而不是调模型。它的核心就两个函数import face_recognition # 加载已知人脸返回 128 维特征向量列表 known_image face_recognition.load_image_file(student_01.jpg) known_encoding face_recognition.face_encodings(known_image)[0] # 对摄像头抓到的帧做识别 unknown_image face_recognition.load_image_file(capture.jpg) unknown_encodings face_recognition.face_encodings(unknown_image) # 比对默认阈值 0.6距离越小越像 results face_recognition.compare_faces([known_encoding], unknown_encodings[0], tolerance0.6) distance face_recognition.face_distance([known_encoding], unknown_encodings[0])这里face_encodings返回的是一个 128 维 numpy 数组compare_faces内部算的就是欧氏距离tolerance默认 0.6——这个值调小更严格容易认不出调大更宽松容易认错人。毕设场景我建议先用 0.5宁可让学生多站一秒也别把张三认成李四答辩时认错人比认不出尴尬十倍。2.2 后端框架Flask 够用别上 Django后端要干三件事接收前端传来的图片、跑识别、读写数据库。这种轻量接口场景Flask 是最优解。Django 自带 ORM、Admin、模板一大堆对毕设来说是负担配置时间够你写完整个 Flask 后端了。FastAPI 也很好异步性能强但如果你对 async 不熟调试起来反而添乱。数据库用 SQLite 就够一个文件搞定不用装 MySQL 服务答辩换台电脑拷过去就能跑。表结构设计上我建议拆成两张表student学号、姓名、人脸特征向量和attendance学号、签到时间、状态。人脸特征向量存成二进制或 JSON 字符串都行别存图片路径——图片文件丢了系统就废了。from flask import Flask, request, jsonify import sqlite3, json, numpy as np import face_recognition app Flask(__name__) def get_db(): conn sqlite3.connect(attendance.db) return conn app.route(/api/recognize, methods[POST]) def recognize(): file request.files[image] # 前端上传的帧 img face_recognition.load_image_file(file) encodings face_recognition.face_encodings(img) if not encodings: return jsonify({code: 1, msg: 未检测到人脸}) conn get_db() rows conn.execute(SELECT student_id, name, encoding FROM student).fetchall() known [np.array(json.loads(r[2])) for r in rows] for enc in encodings: distances face_recognition.face_distance(known, enc) best int(np.argmin(distances)) if distances[best] 0.5: # 阈值和上面保持一致 sid, name rows[best][0], rows[best][1] conn.execute( INSERT INTO attendance(student_id, check_time, status) VALUES(?, datetime(now,localtime), 正常), (sid,)) conn.commit() return jsonify({code: 0, name: name, student_id: sid}) return jsonify({code: 2, msg: 陌生人})这段代码的关键点request.files[image]拿的是前端FormData上传的文件对象不是 base64datetime(now,localtime)用 SQLite 内置函数取本地时间省得在 Python 里处理时区阈值 0.5 和注册时保持一致否则会出现「注册能过、签到认不出」的玄学问题。2.3 前端Vue 做录入界面别用纯 HTML 硬写前端要两个页面学生注册页拍照 填学号姓名 提交和考勤看板页实时名单 手动补录 导出。用 Vue 3 Element Plus 是最省事的组合组件库直接给你表格、上传、按钮样式不用自己调。如果你只会 jQuery 也不是不行但表单校验、列表渲染这些手写起来很累而且「前后端分离项目实战」这个点写进论文也更好看。前端调后端就一个axios的事注意上传图片要用FormData别直接JSON.stringify否则后端request.files拿不到东西——这是新手最高频的翻车点之一。// 学生注册把摄像头截帧 学号姓名一起提交 const formData new FormData(); formData.append(image, blob); // blob 来自 canvas.toBlob() formData.append(student_id, this.sid); formData.append(name, this.name); axios.post(http://127.0.0.1:5000/api/register, formData, { headers: { Content-Type: multipart/form-data } }).then(res { if (res.data.code 0) this.$message.success(注册成功); });Content-Type必须设成multipart/form-data但不要手动加 boundary浏览器会自动补手动加反而报错。这个细节坑过无数人。3. 从零搭起来环境、注册、识别、看板四步走3.1 环境安装dlib 编译是第一个拦路虎face_recognition依赖 dlib而 dlib 在 Windows 上直接pip install大概率编译失败报一堆 CMake 错误。稳妥做法是分平台处理。Windows 上先装 Visual Studio Build Tools勾选 C 桌面开发再装 CMake然后pip install cmake pip install dlib pip install face_recognition pip install flask flask-cors opencv-python如果 dlib 还是编译不过去下预编译的 whl 文件本地安装这是最省时间的后悔药。LinuxUbuntu上简单得多sudo apt-get install build-essential cmake libopenblas-dev liblapack-dev pip install dlib face_recognitionmacOS 用brew install cmake后 pip 装即可。装完验证一下import face_recognition print(face_recognition.__version__ if hasattr(face_recognition, __version__) else ok)能 import 不报错就成。这一步别急着往下走环境不通后面全是白费。3.2 学生注册把一张脸变成 128 个数字注册流程是前端摄像头抓一帧 → 上传 → 后端检测人脸 → 提取特征 → 存库。这里有个关键决策一张脸存几个特征。只存一张照片换个角度就认不出我一般让学生注册时抓 3 到 5 帧每帧提一个特征比对时取最小距离。这样鲁棒性明显提升代价只是存储多一点。app.route(/api/register, methods[POST]) def register(): sid request.form[student_id] name request.form[name] file request.files[image] img face_recognition.load_image_file(file) encodings face_recognition.face_encodings(img) if len(encodings) ! 1: return jsonify({code: 1, msg: 请保证画面中只有一张脸}) conn get_db() conn.execute(INSERT OR REPLACE INTO student(student_id, name, encoding) VALUES(?,?,?), (sid, name, json.dumps(encodings[0].tolist()))) conn.commit() return jsonify({code: 0, msg: 注册成功})encodings[0].tolist()把 numpy 数组转成 Python list 才能 JSON 序列化直接json.dumps(numpy_array)会报not JSON serializable。INSERT OR REPLACE让同一学号重复注册时覆盖旧数据避免脏记录。3.3 实时识别摄像头取帧与性能取舍识别有两种模式前端定时抓帧上传和后端直接读摄像头。毕设演示我推荐前者因为前端抓帧能控制频率不会把后端压垮而且部署时摄像头接在演示电脑上就行。前端用getUserMedia打开摄像头setInterval每 1.5 秒抓一帧转 blob 上传。频率别设太高face_recognition在 CPU 上单帧识别大概 0.3 到 1 秒你 200ms 传一次只会堆积请求。setInterval(() { const canvas document.createElement(canvas); canvas.width video.videoWidth; canvas.height video.videoHeight; canvas.getContext(2d).drawImage(video, 0, 0); canvas.toBlob(blob { const fd new FormData(); fd.append(image, blob, frame.jpg); axios.post(/api/recognize, fd); }, image/jpeg, 0.8); }, 1500);toBlob的第三个参数 0.8 是 JPEG 质量压到 0.8 能显著减小上传体积对识别精度几乎无影响。分辨率别用摄像头原始尺寸缩到 640 宽足够识别还能提速。3.4 考勤看板实时名单与手动补录看板页要展示「已到 / 未到」两个列表还要支持老师手动补录。实时刷新用轮询最简单每 3 秒拉一次接口别一上来就上 WebSocket毕设用不上还容易出 bug。app.route(/api/attendance/today) def today_attendance(): conn get_db() rows conn.execute( SELECT s.student_id, s.name, a.check_time, a.status FROM student s LEFT JOIN attendance a ON s.student_id a.student_id AND date(a.check_time) date(now,localtime) ORDER BY s.student_id ).fetchall() data [{student_id: r[0], name: r[1], check_time: r[2], status: r[3] or 未到} for r in rows] return jsonify({code: 0, data: data})用LEFT JOIN保证没签到的人也出现在列表里status为空时前端显示「未到」。手动补录就是往attendance表插一条注意加唯一约束或先查重否则老师点两次就两条记录。4. 避坑与排查那些让答辩当场社死的细节4.1 坑一dlib 装不上pip 一直报 CMake 错误现象pip install dlib卡在编译满屏红色 CMake Error最后error: metadata-generation-failed。原因Windows 缺 C 编译工具链或 CMake 版本不匹配dlib 需要现场编译 C 源码。解决装 Visual Studio Build Tools 并勾选「使用 C 的桌面开发」单独pip install cmake后再装 dlib实在不行下对应 Python 版本的预编译 whl 本地安装别死磕源码编译。4.2 坑二注册能成功签到死活认不出现象注册时提示成功签到却一直返回「陌生人」。原因注册和识别用了不同的阈值或者注册时存的是归一化后的向量、识别时没做同样处理也可能是注册照片光线和现场差太多。解决把注册和识别的tolerance统一成同一个常量抽到配置文件里注册时多抓几帧取平均或都存下来现场光线尽量和注册时一致别在逆光窗口注册、在暗教室签到。4.3 坑三前端上传图片后端 request.files 是空的现象前端明明传了图后端request.files[image]报 KeyError。原因要么前端用了JSON.stringify而不是FormData要么手动设置了Content-Type但没带 boundary要么字段名对不上。解决前端统一用FormData字段名和后端request.files[xxx]严格一致Content-Type交给浏览器自动设置别手写用浏览器 F12 的 Network 面板看请求体里到底有没有文件。4.4 坑四多人同时签到数据库报 locked现象几个人几乎同时刷脸后端抛sqlite3.OperationalError: database is locked。原因SQLite 默认写操作会锁库Flask 默认多线程处理请求并发写就冲突。解决给连接加超时sqlite3.connect(attendance.db, timeout10)写操作尽量短平快或者干脆在 Flask 启动时加threadedFalse串行处理毕设场景并发量小串行完全够用。4.5 坑五跨域报错前端请求被浏览器拦下现象前端 8080 端口调后端 5000 端口控制台报 CORS 错误。原因前后端分离端口不同就是跨域浏览器默认拦截。解决后端装flask-corsCORS(app)一行搞定生产环境别用CORS(app, origins*)图省事指定前端地址更规范。5. 让识别更稳的几个进阶技巧系统跑通只是及格线答辩想拿高分得在「稳」和「快」上做文章。第一个技巧是特征库缓存每次识别都从数据库读全部学生特征、反序列化人一多就慢。我一般会在 Flask 启动时把特征加载进内存注册新学生时更新这个内存字典识别时直接查内存速度能快好几倍。# 启动时加载一次注册时增量更新 ENCODING_CACHE {} def load_cache(): conn get_db() for sid, name, enc in conn.execute(SELECT student_id, name, encoding FROM student): ENCODING_CACHE[sid] {name: name, encoding: np.array(json.loads(enc))}第二个技巧是活体检测的简化版照片也能骗过纯识别系统答辩老师可能会问「拿张照片能不能打卡」。毕设不用上复杂的活体模型用「连续两帧都识别到同一人」作为判定条件就能挡掉大部分静态照片攻击——举着照片手会抖两帧位置对不上。实现上就是维护一个最近识别结果的队列连续命中才写库。第三个技巧是识别结果去重同一个人站在摄像头前1.5 秒一帧会连续写好几条考勤记录。加个逻辑查这个人今天是否已有记录有就更新最后签到时间而不是新增。这样看板上每个人只有一行干净利落。优化点优化前优化后代价特征读取每次查库反序列化内存缓存启动多一步加载防照片无连续两帧命中多等 1.5 秒记录去重每帧一条每人每天一条需查重逻辑这几个技巧都不复杂但写进论文的「系统优化」章节比空谈「本系统采用先进算法」有说服力得多。我自己踩过的最大教训是别等到答辩前一天才联调前后端分开跑通不代表合起来能跑接口字段名、时间格式、跨域这些破事一定要留出至少三天专门调。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →