
简介本资源是一个基于Python开发的多人人脸识别课堂考勤系统源码包面向计算机视觉初学者、高校教学实践者及教育信息化开发者旨在解决传统人工点名效率低、易代签等问题提供可运行、可拓展的自动化考勤技术方案。压缩包共36个文件含10个核心Python脚本如app.py、core.py、attendance.py等实现人脸检测、特征提取与考勤逻辑、21个HTML模板与CSS样式文件构成完整Web界面、1个SQL数据库结构定义文件schema.sql及README.md等说明文档整体仅29KB轻量易部署。已有921人学习下载代码结构清晰模块职责分明——前端模板与后端逻辑解耦数据库操作封装于db.py人脸识别核心集成OpenCV人脸定位与Dlib/深度学习模型特征比对支持实时视频流处理与SQLite本地存储附带requirements.txt便于环境快速复现是深入理解人脸识别工程落地的优质入门级实战项目。 做课堂考勤这个需求说实话一开始是我接的一个“人情活”。在某高校帮忙带实验课老师每次上课点名都要花十几分钟五十多号人的大课喊到声此起彼伏还有人替答。后来我提议直接上一套基于Python和多人人脸识别的课堂考勤系统教室里放个普通USB摄像头学生进教室自动识别人脸不用点名记录自动生成。断断续续折腾了两周整套源码整理成zip发出去后收到不少同学私信问怎么跑起来、怎么改。今天就拿这套“Python基于多人人脸识别的课堂考勤系统源码.zip”当引子从技术选型、环境搭建、核心代码到实战排错完整拆一遍。1. 项目概述课堂考勤与人脸识别的碰撞1.1 传统课堂考勤的痛点在哪大学课堂点名这事看起来简单实际上谁点谁知道。老师喊名字学生挨个回“到”一轮下来十几分钟就没了。大课往往还是跨班合上名单重复、漏点错点常有更麻烦的是代答到室友之间帮喊一声老师坐讲台上压根分辨不出来。纸质签到表更是形同虚设一张纸传一圈上面能多出七八个笔迹。事后统计出勤率全靠人工翻表期末算平时分时光核对签到记录就够喝一壶。这些痛点拆开来看其实都指向同一个需求能不能用机器自动完成“谁来了、谁没来、几点来的”这三件事并且把结果沉淀成结构化数据期末直接拉表格。顺着这个需求往下走技术选型就变得很清晰了——课堂场景下人脸识别要比刷卡、指纹、扫码都更贴合实际原因我下面详细对比。1.2 这套源码项目交付了什么适合谁参考这套源码zip解压后你看到的不是一个只能“识别到人就拍照”的demo而是一个完整的考勤闭环学生人脸批量注册、摄像头实时多人识别、自动判定正常/迟到/缺勤、考勤记录存储与导出最后落到一张Excel表里期末直接能用。我把它定位成“可落地”而不是“玩具”就是因为它在设计时考虑了真实教室的多项约束多人同时入镜、不同光线、学生坐姿和侧脸、识别结果去重、点名窗口期等。如果你正准备做毕业设计、课程设计或者只是对OpenCV和face_recognition库感兴趣想找个完整项目练手这套代码的结构和排错思路都值得参考。哪怕你零基础只要照着文章把环境搭起来也能在半小时内看到程序框出人脸并报出学生姓名。2. 技术选型为什么是Pythonface_recognition2.1 课堂考勤场景下的方案对比决定做这个系统之前我先把市面上能用的考勤方式过了一遍列了个对比表方案硬件成本识别速度防代刷能力用户体验典型短板校园卡/刷卡需读卡器快弱可代刷易忘带卡学生要排队刷指纹考勤需指纹机中等强需逐个按压人多时排队体验差二维码签到几乎为零快极弱截图满天飞要掏手机代扫严重人脸识别一个普通摄像头快多目标并行强无感通过对光线角度敏感课堂是个很特殊的场景人多、流动性大、时间集中。刷卡和指纹都要求学生主动靠近设备一节课五十多人排队下来不比点名快多少。二维码签到就更别说了群里的码截图一发没来的人也能签到成功。人脸识别最大的优势是“无感”和“批量”学生不用排队、不用掏卡、不用掏出手机走进教室的一瞬间摄像头就能在画面里同时识别出多个人脸。这种体验和课堂场景天然契合。2.2 多人人脸识别的技术链路这套源码里多人人脸识别不是把单个人脸识别跑多遍而是一次性完成整帧画面的多目标处理。核心链路如下摄像头画面采集 → dlib人脸检测返回画面中所有人脸框 → 关键点对齐 → 提取128维特征向量 → 与库中已知特征逐一比对 → 输出姓名或unknown → 写入考勤记录这里的关键点是128维特征向量。face_recognition库底层调用的是dlib的ResNet模型每张人脸会被映射成一个128维的浮点向量。同一个人在不同角度、不同光线下的向量距离很近不同人的向量距离则很远。比对时用欧氏距离判断距离小于阈值就认为是同一个人否则判为陌生人。我项目里默认阈值是0.5比face_recognition官方建议的0.6更严格。原因很直接课堂考勤如果误判成别人会把出勤记录写错宁可偶尔漏判成unknown也不能让A的签到记到B头上。缺勤可以人工补错位记录处理起来反而更麻烦。后面我会解释这个阈值怎么调。3. 源码包结构与核心模块拆解3.1 解压后看到的目录结构拿到zip先别急着跑把结构认清楚。我在整理源码时按“数据、逻辑、界面”三层组织解压后大概是这样的classroom-attendance/ ├── main.py # 程序主入口启动GUI或命令行菜单 ├── add_students.py # 学生人脸注册脚本 ├── realtime_attendance.py # 实时考勤主逻辑摄像头识别循环 ├── export_report.py # 考勤记录导出Excel ├── config.py # 全局配置上课时间、阈值、轮询间隔等 ├── requirements.txt # 依赖清单 ├── README.md # 使用文档 ├── utils/ │ ├── __init__.py │ ├── db_helper.py # SQLite数据库封装 │ ├── face_utils.py # 人脸加载、编码、比对的工具函数 │ └── camera.py # 摄像头初始化与帧读取封装 ├── data/ │ ├── students/ # 存放学生照片命名格式学号_姓名.jpg │ ├── encodings.json # 已注册人脸的128维特征缓存 │ └── attendance.db # SQLite考勤数据库 └── output/ └── 考勤记录_2025-xx-xx.xlsx这个结构是我个人比较推荐的做法把配置单独抽出来把通用操作放进utils把入口脚本保持在顶层。这样做的好处是后期想加功能时不需要在一大坨代码里翻找比如你想把考勤数据发到钉钉或企微只需要在export_report.py旁边加一个新脚本就行完全不碰识别逻辑。3.2 各模块职责与数据流转整份源码的数据流可以这样理解add_students.py负责“建库”它扫描data/students目录下的照片提取出所有人的128维特征写入encodings.json同时把学生信息写入attendance.db的students表。运行real-time考勤时realtime_attendance.py读取encodings.json到内存然后打开摄像头在每一帧里做检测、编码、比对。一旦匹配到某个学生就查今天的考勤记录表如果还没有该学生的记录插入一条如果已经有了就不重复插入。config.py里的轮询间隔默认是每5分钟扫一轮迟到判定和时间窗口都在这里配置。导出阶段export_report.py把attendance表里当天的数据按课程名、学号、姓名、签到时间、状态排序用pandas直接生成Excel表格。这个数据流设计有一个值得抄作业的点识别结果和考勤判定是解耦的。识别模块只负责输出“某个学生在什么时间被看到”判定模块才负责写“正常/迟到/缺勤”。这两个逻辑如果混在一起后面想调整签到窗口会非常痛苦。4. 环境搭建从零跑通这个项目的完整步骤4.1 Python版本与依赖清单第一步是环境这里有个坑必须先说Python版本别贪新。face_recognition库本身跨平台没问题但它依赖的dlib在Windows下的编译体验版本越新越糟心。我的建议是使用Python 3.8或3.9兼容性最稳。依赖清单requirements.txt内容如下opencv-python4.8.0.76 face-recognition1.3.0 numpy1.24.3 Pillow9.5.0 pandas2.0.3 openpyxl3.1.2安装前强烈建议建一个干净的虚拟环境别一股脑装进base环境。命令行操作如下conda create -n attendance python3.8 -y conda activate attendance pip install -r requirements.txt如果你连conda都没装用venv也可以但Windows下venv切换环境没conda直观。我实际用下来conda对dlib这种带编译依赖的库友好很多。4.2 dlib安装实战与避坑这是整份源码环境搭建里最容易卡住的地方。人脸识别库底层依赖dlib而dlib在Windows上默认是从源码编译的需要CMake和Visual Studio Build Tools。如果你直接执行pip install dlib很可能看到一长串编译日志后报错退出。我的处理方式分两步。第一步优先用conda装编译好的版本conda install -c conda-forge dlibconda-forge源里有Windows预编译的dlib装上就能用省去本地编译的痛。第二步装完dlib再装face_recognitionpip会自动识别已安装的dlib不会再触发编译pip install face-recognition这套组合拳我帮好几台机器装过还没失手过。如果conda源拉不动也可以去dlib的GitHub Release页面找对应Python版本的wheel文件下载后pip install本地文件。4.3 解压到启动的完整操作流程环境弄好后按下面步骤操作能最快把项目跑起来解压源码zip注意路径不能带中文。Windows下dlib对中文路径的支持很坑放到D:\attendance这种纯英文目录下。在data/students目录放入学生正面照命名格式统一为学号_姓名.jpg比如20230001_张三.jpg。照片尽量用清晰的正面照背景简单点。运行录入脚本python add_students.py脚本会遍历照片目录、提取特征并把特征写入encodings.json同时把学号和姓名写入数据库。插上摄像头运行考勤主程序python realtime_attendance.py程序启动后会弹出摄像头画面窗口并实时标注识别到的人脸框和姓名。默认按q键退出。课程结束后运行导出脚本python export_report.py会在output目录下生成当天的Excel考勤表。整个流程跑通后你会看到这样的输出程序先加载所有已知人脸特征然后打开摄像头等学生进入画面控制台逐行打印“张三 09:02:33 detected”窗口画面里的人脸框顶部会显示“张三 0.41”之类的距离值。5. 核心实现多人识别与考勤判定逻辑5.1 数据库与图片数据的组织方式考勤系统涉及到两类数据学生静态信息和每日动态考勤。我用了SQLite轻量、免安装、单文件即可携带。关键建表语句如下import sqlite3 conn sqlite3.connect(data/attendance.db) conn.execute( CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT UNIQUE NOT NULL, name TEXT NOT NULL, photo_path TEXT, encoding BLOB ); ) conn.execute( CREATE TABLE IF NOT EXISTS attendance ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT NOT NULL, course_name TEXT NOT NULL, date TEXT NOT NULL, check_in_time TEXT, status TEXT, UNIQUE(student_no, course_name, date) ); )注意attendance表里有个很关键的约束UNIQUE(student_no, course_name, date)。这能保证同一个学生同一天同一门课只会有一条考勤记录。后面识别模块反复扫帧即使一个学生被框到十几次也不会把数据库塞满重复记录。这就是工程细节比算法本身更影响稳定性。编码特征我既存了encodings.json也存了数据库BLOB实际运行时从json加载到内存因为内存比对比查数据库快几个数量级。数据库里的encoding字段更多是为了备份和数据迁移。5.2 人脸注册流程与特征提取add_students.py的核心逻辑不长但它决定了后面识别率的生死。代码如下import os import json import face_recognition student_dir data/students known_encodings [] known_names [] for filename in os.listdir(student_dir): if not filename.lower().endswith((.jpg, .jpeg, .png)): continue # 文件名格式学号_姓名.jpg student_no, name os.path.splitext(filename)[0].split(_) image_path os.path.join(student_dir, filename) image face_recognition.load_image_file(image_path) encodings face_recognition.face_encodings(image) if not encodings: print(f警告{filename} 中未检测到人脸已跳过) continue # 关键取第一个人脸照片里最好只有一个人 known_encodings.append(encodings[0].tolist()) known_names.append({student_no: student_no, name: name}) with open(data/encodings.json, w, encodingutf-8) as f: json.dump({encodings: known_encodings, names: known_names}, f, ensure_asciiFalse) print(f注册完成共 {len(known_names)} 名学生)这里有一个我在实践中踩过的坑如果一张照片里出现多个人脸face_encodings会返回多组向量默认取第一个很可能取错人。所以在注册阶段我严格要求每张照片只有一个目标人脸。如果实在只有合影可以做训练数据你必须用face_locations定位后按人脸框大小排序取最大的那个框——通常就是离镜头最近的那个人。5.3 实时摄像头多人识别循环实时识别是整套系统的核心也是“多人”两个字最集中的体现。核心代码如下import cv2 import face_recognition # 预先加载已知特征 with open(data/encodings.json, r, encodingutf-8) as f: data json.load(f) known_encodings [np.array(enc) for enc in data[encodings]] known_student_nos [item[student_no] for item in data[names]] known_names [item[name] for item in data[names]] video_capture cv2.VideoCapture(0) frame_skip 3 # 每3帧处理一次降低CPU压力 frame_count 0 while True: ret, frame video_capture.read() if not ret: break frame_count 1 if frame_count % frame_skip ! 0: continue # 缩小帧能显著加快检测速度人脸检测对分辨率不敏感 small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) rgb_small_frame cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB) # 一帧内检测所有人脸 face_locations face_recognition.face_locations(rgb_small_frame) face_encodings face_recognition.face_encodings(rgb_small_frame, face_locations) for face_encoding in face_encodings: if len(known_encodings) 0: break # 计算当前人脸与所有已知人脸的欧氏距离 distances face_recognition.face_distance(known_encodings, face_encoding) min_index distances.argmin() if distances[min_index] 0.5: matched_no known_student_nos[min_index] matched_name known_names[min_index] # 调用考勤写入函数带时间戳 record_attendance(matched_no, matched_name) else: matched_name unknown # 绘制人脸框和姓名 top, right, bottom, left face_locations[0] cv2.rectangle(frame, (left*2, top*2), (right*2, bottom*2), (0, 255, 0), 2) cv2.putText(frame, matched_name, (left*2, top*2 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(Attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break video_capture.release() cv2.destroyAllWindows()帧缩放到0.5倍很好理解人脸检测关心的不是高清细节而是足够表达人脸结构的像素。缩到一半后HOG检测速度能提升三到四倍编码阶段的计算量也成倍下降而识别精度几乎没有损失。代价是距离阈值在线上的偏移可以被接受实践下来没问题。多人同框的识别本质上就是for循环遍历face_encodings逐个人脸与库里比对。画面里三个人就比三次五个人就比五次。5.4 考勤判定规则与导出实现考勤判定不是“识别到就算正常”而是要结合上课时间做窗口判断。我在config.py里定义了几个关键参数COURSE_START 09:00 # 上课时间 NORMAL_DEADLINE 09:15 # 正常签到截止时间 LATE_DEADLINE 09:30 # 迟到截止时间 SCAN_INTERVAL 5 # 每5分钟轮询一次判定逻辑如下在COURSE_START到NORMAL_DEADLINE之间首次识别到 → 状态为“正常”在NORMAL_DEADLINE到LATE_DEADLINE之间首次识别到 → 状态为“迟到”超过LATE_DEADLINE仍未识别到 → 课程结束后统一补写为“缺勤”写考勤记录时用INSERT OR REPLACE保证同一学生在同一课程的记录只会被更新、不会重复插入def record_attendance(student_no, course_name): now datetime.now() date_str now.strftime(%Y-%m-%d) time_str now.strftime(%H:%M:%S) if now.time() datetime.strptime(NORMAL_DEADLINE, %H:%M).time(): status 正常 elif now.time() datetime.strptime(LATE_DEADLINE, %H:%M).time(): status 迟到 else: return # 迟到截止后不再写入等待批量补缺勤 conn.execute( INSERT OR REPLACE INTO attendance (student_no, course_name, date, check_in_time, status) VALUES (?, ?, ?, ?, ?) , (student_no, course_name, date_str, time_str, status)) conn.commit()导出Excel的代码也很简单pandas一把梭import pandas as pd import sqlite3 conn sqlite3.connect(data/attendance.db) df pd.read_sql_query( SELECT s.student_no, s.name, a.course_name, a.date, a.check_in_time, a.status FROM attendance a LEFT JOIN students s ON a.student_no s.student_no ORDER BY a.date DESC, a.status, a.student_no , conn) df.to_excel(output/考勤记录.xlsx, indexFalse)6. 常见问题与排查实录6.1 常见问题速查表这套源码我分发出去后收到的反馈问题五花八门但高度集中在下面几个点整理成速查表症状可能原因解决方案安装face_recognition时报dlib编译错误缺CMake或VS Build Tools用conda先装dlib再装face_recognitionModuleNotFoundError: No module named face_recognition依赖装错环境检查conda activate是否生效pip list确认包存在face_encodings返回空列表照片没有人脸或人脸太小换清晰正面照照片中人脸高度至少达到100像素摄像头打开黑屏或报错设备索引错误或权限被占把VideoCapture(0)改1试试关闭其他占用摄像头的软件中文乱码或路径报错源码路径含中文整个项目迁移到纯英文路径如D:\attendance识别特别卡CPU占用高每帧都做检测编码跳帧处理frame_skip调到3或5缩小帧到0.5解压zip时提示file is not a zip file下载不完整文件损坏重新下载检查文件大小是否与发布页一致常见同学被识别成unknown现场光线差、角度偏或者注册照太旧扩大注册照片集每人提供2到3张不同角度照片6.2 几个真实的排错案例第一个案例是dlib编译失败。一个同学在Windows 10上装face_recognitionpip直接开始编译dlib跑了十分钟后报错提示缺少visual studio c 14.0。我远程看了一眼建议他先装conda然后执行conda install -c conda-forge dlib两分钟装完再pip install face-recognition就顺利通过。Windows平台下能用预编译包就别折腾本地编译这是省时间的第一原则。第二个案例是识别率极低。用户把摄像头挂在教室黑板旁边正对整排学生结果系统只能认出第一排的人后几排全显示unknown。这个不是代码bug是物理距离问题。人脸识别对画面中人脸的像素尺寸很敏感后几排的人脸在1080p画面里可能只有二三十像素高远低于face_recognition能稳定工作的最低尺寸。解决办法是摄像头放在讲台前侧斜向下俯拍前六排区域同时把识别区域裁剪到走廊入口附近而不是对整间教室开地图炮。课堂考勤不是要覆盖整间教室只需要在入口处抓拍到每个进教室的人这个思路很多人一开始没转过弯。第三个案例是考勤记录重复。有同学把系统连续开了一上午结果数据库里同一个学生出现几十条记录。这个一般是因为他改了表结构去掉了UNIQUE约束或者用了INSERT而不是INSERT OR REPLACE。我后来把唯一约束写进建表语句并在record_attendance里加了判断问题才彻底解决。6.3 阈值到底怎么调很多人喜欢问tolerance阈值调多少合适我直接给个实践参考。face_recognition官方默认是0.60.5到0.55之间误识别人脸的概率较低。我自己的做法是先在教室环境里跑一遍记录识别同一个学生时的最小和最大距离如果某个学生一次没识别出来就看他对应的最小距离是不是落在0.5到0.6之间。如果是把这个学生添加到注册特征库而不是一味调低阈值。因为调低到0.4虽然能降低误识别但会损失更多有效识别导致考勤记录大面积缺失。7. 优化与扩展从“能用”到“好用”7.1 识别性能优化的几个方向源码默认跑的是CPU上的HOG人脸检测在普通笔记本上每帧大约需要200到400毫秒。如果不做跳帧处理画面会明显卡顿。我在代码里已经把帧缩放到0.5倍并设置每3帧处理一次实际体验已经比较流畅。如果想进一步优化可以从这几个方向入手第一换用OpenCV的DNN人脸检测器。face_recognition库用的是dlib的HOG检测器精度不错但速度一般。OpenCV的DNN模块支持加载Caffe或TensorFlow的人脸检测模型在GPU或现代CPU上的速度会快很多。改动的代价是DNN检测器输出的人脸框格式和dlib不完全一样需要一层坐标转换。第二只在有人移动时才触发识别。教室摄像头多数时候是静止画面可以先用帧差法或背景减除判断画面是否有变化没有变化就直接跳过识别。这个优化能把系统闲置时的CPU占用降到接近零。第三把特征比对改成向量化批量计算。目前代码里对每个encoding都调用face_distance如果学生库超过几百人这个开销会变大。可以把known_encodings变成一个numpy二维数组一次性计算所有距离速度会快很多。7.2 从单机脚本到Web端考勤如果你想把这套系统做成管理端可查看的Web应用方向也很清晰。后端用FastAPI或Flask把识别逻辑封装成接口前端页面显示学生列表和实时考勤状态视频流通过WebRTC或MJPEG推流到浏览器。这样你不用坐在教室在办公室就能看到学生进出情况。不过我不建议一上来就直接上Web先保证单机版能稳定运行一个学期。原因很实际Web化牵扯到并发、流媒体、前端交互工作量大很多而考勤系统的核心价值在数据准确性和稳定性界面反倒是其次。我自己在实际项目里是先跑通单机确认考勤判定的准确率足够再考虑要不要加管理界面。7.3 考勤数据还能怎么用考勤数据一旦沉淀下来能做的就不只是“查谁没来”。最简单的是按周汇总每个学生的出勤率结合辅导员系统做预警进阶可以把考勤数据与成绩做关联分析看期末考试排名和出勤率有没有相关性再进一步还可以在课程进行到一半时生成缺勤学生名单定时推送给任课老师。这些扩展点都绕不开一件事数据模型要一开始就设计好。attendance表里我特意存了course_name和date两个字段就是为了后续做多维统计。如果你拿到源码想改造成自己的需求优先考虑保留这个表结构后面的分析SQL会好写很多。最后再分享一点个人体会当我真正把这套系统放进教室连续跑起来之后最深的一个感受是这种项目从来不是“堆模型”那么玄乎真正的坑全在工程细节。测试环境里跑得好好的一上真实场景就各种掉链子——有人侧脸、有人戴口罩、有人坐在角落背光你永远不知道下一帧会出现什么情况。识别准确率能到九成以上剩下的那不到一成靠的是数据库去重、时间窗口规则、人工补录这些“不性感”的工程手段兜底。所以别只看人脸识别的精度多高多花时间在数据流和异常处理上这才是考勤系统能不能真正用起来的分水岭。还有个小技巧如果你注册阶段能收集每个学生3张不同角度的照片识别率会比单张照片提高一个档次这是性价比最高的优化没有之一。本文还有配套的精品资源点击获取