尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python在线课堂考勤系统:人脸识别从注册到判定全链路实战

发布时间:2026/9/28 16:28:36

资讯中心
01
ARTICLE

Python在线课堂考勤系统:人脸识别从注册到判定全链路实战

Python在线课堂考勤系统:人脸识别从注册到判定全链路实战
简介这份资源是一套基于Python的在线课堂考勤系统完整项目源码面向具备一定Python基础、希望将深度学习落地到教育场景的开发者与学习者。项目以卷积神经网络为核心结合OpenCV与TensorFlow或PyTorch完成人脸特征提取与识别并借助Flask或Django搭建可视化界面覆盖数据收集、预处理、模型构建、训练优化到测试部署的完整流程可用于课程设计、毕业设计或技术练手。压缩包共98个文件约409.43MB包含22个py源码、28张jpg样本图、15个xml配置、6张png截图、5个dat数据文件及3个pt模型权重等目录按功能模块划分便于对照学习。目前已有1461人学习下载读者可从中获取可运行的人脸考勤实现、CNN训练脚本与Web端管理逻辑快速理解从模型到系统的工程化落地思路。1. 在线课堂考勤系统为什么摄像头点名总翻车Python 能不能救线上课最让老师崩溃的不是讲课是点名。五十个学生开着摄像头一半人镜头对着天花板另一半挂着静态头像你喊三遍名字没人应最后只能靠扣平时分威胁。基于 Python 的在线课堂考勤系统要解决的就是这件事把谁在、谁不在、谁在假装在从人工盯屏变成程序自动判定。它适合两类人——一类是正在做课程设计或毕业设计的学生需要一套能跑起来、能演示、能写进论文的系统另一类是培训机构或高校教务的技术负责人想给现有直播平台加一层考勤能力。核心链路其实不复杂摄像头或截图采集 → 人脸检测与识别 → 活体/在场判定 → 写入考勤记录 → 导出报表。Python 在这条链路上的优势是生态成熟OpenCV、face_recognition、Flask、pandas 全都能直接拼起来不需要从零造轮子。但我要先说清楚这套东西的难点从来不在识别人脸而在怎么定义出勤和怎么防止作弊后面几章会把这两件事拆开讲透。2. 技术选型人脸识别方案怎么挑别一上来就上深度学习2.1 三种主流方案的能力边界对比做考勤系统第一步是决定用什么做人脸识别。市面上常见做法有三类能力差距很大选错了后面全是坑。方案依赖库识别精度硬件要求适合场景Haar 级联 LBPHopencv-contrib-python中等受光照影响大CPU 即可课程设计、小班演示face_recognitiondlibface_recognition、dlib较高128 维特征CPU 可跑慢中小规模真实考勤深度学习模型ArcFace 等insightface、onnxruntime高抗角度光照建议有 GPU生产级、大班额我一般会建议如果只是做课设或内部小工具直接用 face_recognition它在精度和上手难度之间平衡得最好。dlib 的 128 维特征向量做欧氏距离比对阈值设在 0.45 到 0.5 之间比较稳。如果班级超过 100 人、或者要求毫秒级响应再考虑 insightface 这类方案但部署复杂度会明显上升。提示不要用 Haar 级联做人脸识别它只能做检测找到脸的位置不能做识别判断是谁。很多新手把检测当识别结果系统只能告诉你画面里有张脸说不出是谁。2.2 环境搭建从 python 安装到依赖库落地环境这一步是新手最容易卡住的地方。python 安装教程、vscode python 环境配置、pycharm 配置 python 环境这些搜索词热度一直很高说明大量人倒在了第一步。我推荐用 conda 建独立环境避免和系统 Python 打架。# 创建独立环境指定 Python 3.9dlib 对 3.10 兼容性偶有问题 conda create -n attendance python3.9 -y conda activate attendance # 安装核心依赖 pip install opencv-python4.8.1.78 pip install face_recognition1.3.0 pip install flask3.0.0 pip install pandas2.1.3 pip install openpyxl3.1.2这里有几个参数值得说明。opencv-python 选 4.8 版本是因为它对 VideoCapture 的摄像头兼容性比较稳定太新的版本在某些 Windows 驱动上会报错。face_recognition 依赖 dlib而 dlib 在 Windows 上需要 CMake 和 Visual Studio Build Tools如果 pip 安装报错常见做法是先装 cmake 再重试pip install cmake pip install dlib19.24.2 pip install face_recognitionLinux 系统安装 python 环境相对省心但要注意 dlib 编译需要libboost-python-dev和cmakeUbuntu 下先apt install build-essential cmake libopenblas-dev libx11-dev再 pip 安装。装完之后跑一句验证import face_recognition import cv2 print(opencv:, cv2.__version__) print(face_recognition ok)如果这行能打印出来环境就算通了。装不上的话八成是 dlib 编译失败去看报错里有没有 CMake 或 boost 字样对症补依赖。3. 核心实现从人脸注册到考勤判定的完整链路3.1 人脸库构建注册阶段怎么存特征考勤系统的第一步是注册也就是把每个学生的人脸特征存下来。我一般会为每个学生采集 3 到 5 张不同角度的照片提取 128 维特征后取平均这样比单张照片鲁棒得多。import face_recognition import numpy as np import os import pickle def register_student(student_id, image_paths): 采集多张照片提取平均特征向量 encodings [] for path in image_paths: image face_recognition.load_image_file(path) # 只取画面中最大的一张脸避免背景人脸干扰 locations face_recognition.face_locations(image, modelhog) if not locations: print(f跳过 {path}未检测到人脸) continue # 按面积排序取最大 locations.sort(keylambda loc: (loc[2]-loc[0])*(loc[1]-loc[3]), reverseTrue) encoding face_recognition.face_encodings(image, [locations[0]])[0] encodings.append(encoding) if not encodings: raise ValueError(f学生 {student_id} 没有可用的人脸样本) # 取平均特征比单张更稳定 avg_encoding np.mean(encodings, axis0) return student_id, avg_encoding def build_database(students_dir, output_fileface_db.pkl): 遍历目录构建人脸特征库 database {} for student_id in os.listdir(students_dir): student_dir os.path.join(students_dir, student_id) if not os.path.isdir(student_dir): continue image_paths [os.path.join(student_dir, f) for f in os.listdir(student_dir) if f.lower().endswith((.jpg, .png, .jpeg))] try: sid, encoding register_student(student_id, image_paths) database[sid] encoding print(f注册成功{sid}样本数 {len(image_paths)}) except ValueError as e: print(f注册失败{e}) with open(output_file, wb) as f: pickle.dump(database, f) print(f人脸库已保存共 {len(database)} 人) return database if __name__ __main__: build_database(./students)这段代码的逻辑是每个学生一个文件夹文件夹名就是学号里面放多张照片。face_locations用 hog 模型速度快CPU 就能跑如果照片质量差、侧脸多可以换成modelcnn但需要 GPU 才跑得动。取最大人脸是为了防止合照或背景里其他人脸被误当成目标。最后用 pickle 序列化存盘下次直接加载不用每次重新提取。参数上face_encodings返回的是 128 维 numpy 数组np.mean做平均后维度不变。如果某个学生只成功提取到 1 张平均就等于它本身也能用但鲁棒性差建议至少 3 张。3.2 实时考勤一帧画面怎么判定谁到了注册完就该做实时判定了。核心逻辑是抓一帧画面 → 检测所有人脸 → 提取特征 → 和库里逐个比对 → 距离小于阈值就算匹配。import face_recognition import cv2 import pickle import numpy as np from datetime import datetime # 加载人脸库 with open(face_db.pkl, rb) as f: face_db pickle.load(f) known_ids list(face_db.keys()) known_encodings list(face_db.values()) # 考勤记录学号 - 首次识别时间 attendance {} # 匹配阈值越小越严格 TOLERANCE 0.48 def mark_attendance(frame): 处理单帧返回本帧识别到的学号列表 # 缩小到 1/4 加速处理坐标记得乘回去 small cv2.resize(frame, (0, 0), fx0.25, fy0.25) rgb_small cv2.cvtColor(small, cv2.COLOR_BGR2RGB) locations face_recognition.face_locations(rgb_small, modelhog) encodings face_recognition.face_encodings(rgb_small, locations) recognized [] for encoding in encodings: # 和库里所有人比对返回距离数组 distances face_recognition.face_distance(known_encodings, encoding) best_idx np.argmin(distances) if distances[best_idx] TOLERANCE: sid known_ids[best_idx] recognized.append(sid) if sid not in attendance: attendance[sid] datetime.now().strftime(%H:%M:%S) print(f[考勤] {sid} 已签到 {attendance[sid]}) return recognized # 打开摄像头 cap cv2.VideoCapture(0) if not cap.isOpened(): raise RuntimeError(摄像头打开失败检查设备索引或驱动) frame_count 0 while True: ret, frame cap.read() if not ret: break # 每 5 帧处理一次降低 CPU 占用 frame_count 1 if frame_count % 5 0: mark_attendance(frame) cv2.imshow(Attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()关键参数说明TOLERANCE0.48是经验值调低到 0.4 会漏识本人被拒调到 0.55 会误识把别人认成你。实际部署时建议先用一批测试照片跑一遍看距离分布再定。fx0.25的缩放是为了提速代价是小脸可能检测不到如果教室画面里人离得远改成 0.5。每 5 帧处理一次是权衡30fps 的摄像头相当于每秒判定 6 次足够考勤用CPU 也不会跑满。face_distance返回的是欧氏距离不是相似度所以是越小越像。很多人第一次写会反过来判断导致全员匹配失败这是个高频翻车点。3.3 考勤数据落库与导出pandas 写入 Excel识别出来只是第一步老师要的是能交差的考勤表。用 pandas 把结果写成 Excel顺便算个出勤率。import pandas as pd from datetime import datetime def export_attendance(attendance, all_students, course_name在线课堂): 生成考勤报表包含出勤状态和签到时间 today datetime.now().strftime(%Y-%m-%d) rows [] for sid in all_students: if sid in attendance: rows.append({ 学号: sid, 姓名: , # 可从学生信息表关联 状态: 出勤, 签到时间: attendance[sid] }) else: rows.append({ 学号: sid, 姓名: , 状态: 缺勤, 签到时间: }) df pd.DataFrame(rows) total len(df) present len(df[df[状态] 出勤]) summary pd.DataFrame({ 课程: [course_name], 日期: [today], 应到: [total], 实到: [present], 出勤率: [f{present/total*100:.1f}%] }) filename f考勤_{course_name}_{today}.xlsx with pd.ExcelWriter(filename, engineopenpyxl) as writer: df.to_excel(writer, sheet_name明细, indexFalse) summary.to_excel(writer, sheet_name汇总, indexFalse) print(f报表已生成{filename}) return filename # 使用示例 all_students list(face_db.keys()) export_attendance(attendance, all_students, Python程序设计)这里all_students必须来自完整的学生名单而不是人脸库的 key否则缺勤的人根本不会出现在表里——这是逻辑上的关键区别。ExcelWriter一次写两个 sheet明细和汇总分开老师一眼能看出出勤率。openpyxl 引擎支持 xlsx 格式如果要用老版 xls 得换 xlwt但没必要。4. 避坑指南考勤系统上线前必须处理的五个问题4.1 照片冒名静态图片也能骗过摄像头现象学生拿手机对着摄像头放一张同学的照片系统照样判定出勤。原因face_recognition 只做特征比对不判断画面里是真人还是照片。解决加一层活体检测。最轻量的做法是要求眨眼或转头用 OpenCV 的 dlib 关键点检测眼睛开合更简单的是随机要求请向左转头连续两帧检测头部姿态变化。如果不想做活体至少加随机抓拍三次、时间间隔大于 10 秒静态照片很难在多个时间点保持同一姿态。4.2 光照翻车背光环境下全员识别失败现象教室窗户在摄像头对面学生全是逆光剪影识别率骤降到 20% 以下。原因hog 模型对光照敏感背光时人脸梯度特征丢失。解决两个方向。一是物理层面让摄像头顺光或加补光灯二是算法层面处理前做直方图均衡化import cv2 def preprocess(frame): gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) equalized cv2.equalizeHist(gray) return cv2.cvtColor(equalized, cv2.COLOR_GRAY2BGR)但均衡化会改变颜色信息对 face_recognition 的特征提取有轻微影响建议只在光照确实差的时候开。4.3 阈值玄学同一个人今天能过明天过不了现象同一个学生昨天识别成功今天怎么都认不出。原因TOLERANCE 设得太贴近边界光照、角度稍有变化距离就超过阈值。解决不要拍脑袋定阈值。写个小脚本用同一个人的多张照片两两算距离看分布import face_recognition import numpy as np def analyze_threshold(student_dir): 分析同一人不同照片的距离分布辅助定阈值 import os encodings [] for f in os.listdir(student_dir): img face_recognition.load_image_file(os.path.join(student_dir, f)) locs face_recognition.face_locations(img) if locs: encodings.append(face_recognition.face_encodings(img, [locs[0]])[0]) distances [] for i in range(len(encodings)): for j in range(i1, len(encodings)): d np.linalg.norm(encodings[i] - encodings[j]) distances.append(d) print(f同人距离均值 {np.mean(distances):.3f}最大 {np.max(distances):.3f}) return np.max(distances)如果同人最大距离是 0.42那阈值至少设 0.5 才安全。同时也要测不同人之间的最小距离两者之间要有间隔否则说明你的照片质量太差得重新采集。4.4 多人同框一张脸被匹配给两个人现象两个学生坐得近系统把 A 的脸同时记到了 A 和 B 名下。原因face_distance对每个 encoding 只取最近的一个但如果两个人特征接近或者库里有重复注册就会串。解决一是注册时去重新注册的特征和已有库比对距离小于 0.4 就提示该人脸已注册二是判定时加第二名距离必须比第一名大 0.1 以上的规则否则标记为不确定人工复核。4.5 摄像头索引错乱代码跑起来黑屏现象cv2.VideoCapture(0)打开成功但读出来全是黑帧。原因笔记本有多个摄像头内置外接索引 0 可能被占用或指向了错误设备。解决遍历索引试import cv2 for i in range(5): cap cv2.VideoCapture(i) if cap.isOpened(): ret, frame cap.read() if ret and frame is not None and frame.mean() 10: print(f可用摄像头索引{i}) cap.release()frame.mean() 10是排除全黑帧正常画面均值不会这么低。找到正确索引再写进代码。5. 进阶技巧把考勤系统做成能交差的产品前面四章把核心链路跑通了但一个能真正拿去用的系统还差几块拼图。这一章讲三个我踩过坑之后总结出来的技巧。第一个是用 Flask 包一层 Web 接口让老师不用装 Python 也能用。核心就一个上传接口加一个查询接口from flask import Flask, request, jsonify, send_file import face_recognition import pickle import numpy as np from datetime import datetime app Flask(__name__) with open(face_db.pkl, rb) as f: face_db pickle.load(f) known_ids list(face_db.keys()) known_encodings list(face_db.values()) app.route(/checkin, methods[POST]) def checkin(): 接收前端上传的截图返回识别结果 file request.files.get(image) if not file: return jsonify({error: 未收到图片}), 400 # 从内存读取避免落盘 import io from PIL import Image img np.array(Image.open(io.BytesIO(file.read())).convert(RGB)) locations face_recognition.face_locations(img, modelhog) encodings face_recognition.face_encodings(img, locations) results [] for enc in encodings: distances face_recognition.face_distance(known_encodings, enc) best np.argmin(distances) if distances[best] 0.48: results.append({student_id: known_ids[best], confidence: round(1 - distances[best], 3)}) return jsonify({recognized: results, count: len(results)}) if __name__ __main__: app.run(host0.0.0.0, port5000)这个接口接收前端传来的截图返回识别到的学号列表。前端可以用 HTML5 的 getUserMedia 抓帧每 10 秒 POST 一次老师端只看到一个网页不用碰命令行。host0.0.0.0是为了局域网内其他设备能访问生产环境记得加鉴权别裸奔。第二个是用 pandas 做周期性统计把每天的考勤记录汇总成周报。思路是每天导出的 Excel 按日期命名周末用一个脚本读进来合并import pandas as pd import glob def weekly_report(pattern考勤_*.xlsx): files sorted(glob.glob(pattern)) all_records [] for f in files: df pd.read_excel(f, sheet_name明细) df[日期] f.split(_)[-1].replace(.xlsx, ) all_records.append(df) merged pd.concat(all_records, ignore_indexTrue) # 透视每个学生每天的状态 pivot merged.pivot_table(index学号, columns日期, values状态, aggfuncfirst).fillna(缺勤) # 算出勤率 pivot[出勤率] (pivot 出勤).sum(axis1) / (pivot.shape[1] - 1) pivot.to_excel(周考勤汇总.xlsx) print(f合并 {len(files)} 天记录已生成周报) return pivot weekly_report()pivot_table把长表转成学号 × 日期的矩阵一眼能看出谁哪天没来。fillna(缺勤)处理的是那天根本没记录的情况默认算缺勤这个逻辑要跟教务确认有些学校请假也算缺勤有些不算。第三个是打包成 exe让完全不懂 Python 的助教也能双击运行。用 PyInstallerpip install pyinstaller pyinstaller --onefile --add-data face_db.pkl;. --name 考勤系统 main.py--add-data把模型文件一起打进去Windows 下分隔符是分号Linux 是冒号。打包后 exe 体积会到 200MB 以上因为 dlib 和 OpenCV 都很大这是正常现象。第一次运行会解压到临时目录启动慢几秒别以为是卡死。最后说个我自己的习惯每次改完识别逻辑先用一段录好的视频回放测试而不是直接开摄像头。摄像头是实时的出了问题你根本不知道是哪一帧、什么光照、什么角度翻的车。录一段包含各种情况的视频跑一遍看日志比对着摄像头反复试效率高十倍。这套系统我前后改了三版前两版都栽在以为阈值调好了上第三版老老实实做了距离分布分析才稳定下来。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。