ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python多人人脸识别课堂考勤系统实战解析

Python多人人脸识别课堂考勤系统实战解析 简介本资源是一个基于Python开发的多人人脸识别课堂考勤系统完整源码包面向计算机视觉初学者、高校课程设计学生及教育信息化开发者旨在解决传统人工点名效率低、代签漏洞多等教学管理痛点。压缩包共36个文件含10个核心Python模块如app.py、core.py、attendance.py实现主逻辑与人脸识别流程、21个HTMLCSS前端页面含管理员看板、考勤记录页等、1个SQL数据库脚本及README等辅助文档整体仅29KB轻量易部署。已有921人学习下载代码结构清晰分层后端采用Flask框架集成OpenCV人脸检测、Dlib关键点定位与预训练深度特征模型前端提供简洁UI配套SQLite数据库支持学生信息与人脸模板管理并包含异常处理与实时视频流适配逻辑。读者可直接运行调试深入理解从图像采集、特征提取到考勤入库的全流程实现细节。1. 课堂考勤这个场景给开发者出了哪些难题先说一个我自己的经历。有段时间帮学校教务处做课程支撑每次去代课进门看到老师在点名四十多人的班级点一轮下来五六分钟就没了底下学生该玩手机玩手机点到自己名字就抬头应一声压根儿不知道是谁答的。更有意思的是有些人明明没来室友帮忙喊一声到老师这头就把名字记上了。纸质签到更不靠谱一张纸传下去传到后面能多出好几个不存在的名字。从那时候起我就觉得考勤这事看着简单真要做得可靠光靠流程管是管不住的。后来我拿到手一个很有意思的题目Python基于多人人脸识别的课堂考勤系统源码。这个名字拆开看就是三件事——Python、多人人脸识别、课堂考勤。核心难点不是人脸识别这四个字怎么写而是多人和课堂这两个词叠加在一起之后系统的真实约束条件变了。课堂考勤和门禁考勤完全是两种玩法。门禁机前的人是一个一个排着队过来的摄像头正对一张脸距离固定、角度固定、光照基本稳定而课堂教室里摄像头挂在黑板正上方镜头里是全班几十号人有人正对着镜头有人低着头有人侧着身子跟同桌说话后排人脸小到可能只有几十个像素这还没算窗外阳光照进来把半张脸打上阴影的情况。所以你要解决的不是人脸识别准不准而是在一堆人脸里能不能把每一个真人都准确找到并认出来。这个场景对系统提出了几个非常具体的需求。第一必须是多人同时识别而不是一次只识别一个人。镜头一开全班人脸都在画面里程序要先把人脸一个个找出来再逐个判断身份不能只对焦在最中间的那个人身上。第二识别结果要自动关联到学生姓名、学号和课程信息生成一条可查询、可导出的考勤记录而不是只弹一个识别成功的提示框就算完事。第三要防止重复打卡。一个学生在一节课里被识别到十几次数据库里不能出现十几条出勤记录要有去重机制按学号、课程和日期做唯一约束。第四要有迟到、缺勤、正常出勤的状态判定逻辑。上课后 10 分钟内识别到算迟到整节课都没有识别到课后自动标记缺勤。这几个需求放在一起才构成了一个能真正用在课堂上的考勤系统而不是实验室里跑通一个算法就完事。下面我按照这套源码项目的实际设计思路把技术选型、核心模块、踩坑细节和交付整理挨个拆开讲。2. 技术选型与系统架构这套方案是怎么搭起来的2.1 人脸识别技术方案对比为什么用 face_recognition人脸识别这个领域可选的开源方案太多了选错一个后面全是坑。我基于这个项目的定位把市面常见的几个方案拉出来对比了一遍。方案实现方式优点不足适合场景OpenCV Haar 级联传统特征匹配部署简单代码量少误检率高对角度、光照极敏感入门演示不适合考勤Dlib HOG 68点传统机器学习轻量CPU 运行快小尺寸人脸检出率低需要快速定位人脸的场景face_recognitionDlib 封装128维特征向量API 简单底层 dlib 在 Windows 下编译麻烦中小型项目本系统首选MTCNN多任务级联 CNN小脸检测能力好带五点对齐需要 GPU 加速才能流畅跑对定位精度要求高的场景DeepFace / ArcFace深度神经网络识别精度最高模型体积大加载慢推理慢工业级应用数据量很大对比完基本就清楚了。这个课堂考勤项目用的是face_recognition库原因也很直接它把 dlib 底层封装成了几个函数调用起来非常简单不需要自己搭神经网络也不需要懂复杂的模型训练流程对Python开发者来说几乎是上手最快的一条路。它输出的是一张人脸对应的 128 维特征向量这个向量在做比对的时候非常方便存进数据库占用的空间也不大。顺带说一句人脸识别行业的方案其实一直都在升级像人脸识别门禁机、人脸打卡机现在用的基本都是深度学习的方案识别速度和精度都要好很多。但课堂考勤这种场景网络环境、硬件配置都不一定可控face_recognition这个库在普通笔记本 CPU 上跑起来速度完全够用做教学项目和中小型课堂考勤是合适的。2.2 系统整体架构与流程设计这套系统的整体链路从摄像头采集画面到最终生成考勤记录按顺序分成六个环节摄像头实时采集视频帧。在视频帧中检测所有人脸的位置。对检测到的每个人脸提取 128 维特征向量。将每个特征向量和数据库里已注册的学生特征做距离比对。根据比对结果和当前时间判定该学生的出勤状态。将出勤结果写入数据库并在界面上展示识别情况。这个流程里有一个关键设计点系统不保存人脸照片做直接匹配而是把人脸转换成一个 128 维的特征向量再存进数据库。这样做的好处有两个。第一是速度快两张脸做向量距离计算比做图像像素级比对快了几个数量级。第二是隐私性好数据库里存的不是学生的照片而是一串数字特征即使数据库被导出也无法直接还原成清晰的图像。前端界面这块考虑到源码包要方便使用者快速跑起来我建议用 Tkinter 或者 PyQt5 做桌面界面。桌面版的好处是不依赖 Web 服务双击就能运行对大多数想测试这个系统的人来说更友好。PyQt5 做出来的界面确实好看一些控件丰富布局灵活代价是打包出来的文件体积会明显变大。如果你只是想快速验证效果Tkinter 就够了。2.3 数据存储与统计报表设计数据库我选了 SQLite。有人会问为什么不用 MySQL课堂考勤的数据量其实很小一个班级几十人一天几节课一年撑死也就几千条考勤记录SQLite 完全撑得住而且零配置、单文件、随系统走使用者拿到源码包不用额外装数据库服务这是交付时省心的关键。系统里主要设计了四张表学生信息表学号、姓名、班级、人脸特征向量。课程表课程编号、课程名称、上课时间。考勤记录表记录ID、学号、课程编号、考勤日期、识别时间、考勤状态。系统配置表存储识别阈值、摄像头编号、上课时间等等。有了这四张表日常统计就很好写了。按课程查缺勤名单按学生查学期出勤率按日期导出一整节课的考勤表都是简单 SQL 就能解决的事。很多人在做这类型系统时喜欢把精力全放在人脸识别算法上结果数据库设计一塌糊涂后面统计报表的时候查啥啥慢改啥啥错所以这个部分我建议一开始就用点心。3. 核心模块实现从人脸录入到考勤记录3.1 人脸注册模块先把每个学生的数字身份证存下来考勤系统要能认出学生前提是系统里得先认识这个学生。这个认识的过程就是人脸注册模块要干的事。注册流程我建议做成这样学生坐到摄像头前采集一张正面照片或者是直接调用摄像头抓拍。程序检测照片中的人脸数量必须恰好是 1 张多了一张就提示重新采集。提取这张人脸的 128 维特征向量。把学号、姓名、班级、特征向量一并写入数据库。代码写出来大概是这样的import face_recognition def register_student(student_id, name, image_path): image face_recognition.load_image_file(image_path) face_locations face_recognition.face_locations(image, modelhog) if len(face_locations) ! 1: return {success: False, msg: 照片中必须且只能有一张人脸} encoding face_recognition.face_encodings(image, known_face_locationsface_locations)[0] # 把 encoding 转成 list 存入数据库中的特征字段 feature encoding.tolist() # 这里执行数据库 insert 操作 save_student(student_id, name, feature) return {success: True, msg: 注册成功}有个细节值得提醒注册时最好让学生正对镜头光线均匀不要戴帽子、口罩和大框眼镜。有些系统为了让识别更鲁棒会给每个学生拍好几张不同角度的照片各提取一个特征向量比对时只要有一个向量匹配成功就算识别通过。这个做法是很有效的尤其是在教室这种光线不稳定的环境里多特征样本能明显提升识别率。代价是数据库里存储的特征行数会翻几倍但对 SQLite 来说完全不是问题。3.2 实时多人检测与识别核心代码逻辑拆解这是整个系统的核心环节。摄像头画面里可能同时有几十张脸程序要先定位再逐张识别。核心逻辑用face_recognition写起来非常简洁import cv2 import face_recognition def recognize_frame(frame, known_encodings, known_names, threshold0.45): # 转 RGBface_recognition 用的是 RGB 色彩空间 rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 检测画面中的所有人脸位置 face_locations face_recognition.face_locations(rgb_frame, modelhog) # 提取每个人脸的特征向量 face_encodings face_recognition.face_encodings(rgb_frame, face_locations) recognized [] for encoding in face_encodings: # 与库中所有已知特征计算欧氏距离 distances face_recognition.face_distance(known_encodings, encoding) min_distance min(distances) if min_distance threshold: index distances.argmin() recognized.append((known_names[index], min_distance)) else: recognized.append((Unknown, min_distance)) return face_locations, recognized这段代码就是多人考勤识别的骨架。face_locations拿到的是画面里所有人脸框的坐标face_encodings拿到的是每个人脸的 128 维特征向量两者一一对应。比对时用face_distance计算和所有已注册学生特征之间的距离取最小值如果最小值小于阈值就认定是这个人。这里要重点说明几个参数的影响。modelhog表示使用 HOG 模型做人脸检测这个模型在 CPU 上运行速度比较快适合实时视频流处理。如果换成modelcnn检测精度会更高尤其是小尺寸人脸但速度会慢不少除非你有一块 NVIDIA 的 GPU否则不建议在课堂实时场景里用它。threshold是判定阈值也是整个系统里最需要调的一个参数。这个我后面单独用一节来展开讲因为它的数值直接决定了系统的误报和漏报率。3.3 考勤判定与去重机制识别出了一张脸不等于考勤记录就该落库。如果摄像头一帧一帧地处理同一个学生一节课会被识别到几百次如果不做去重考勤表直接爆炸。所以考勤判定逻辑是这样设计的def check_attendance(student_id, course_id, current_time, lesson_start_time): # 先查这个学生今天这节课有没有打过卡 is_exist query_attendance_record(student_id, course_id, today) if is_exist: return duplicate # 已经记录过跳过 # 比较当前时间和上课时间 delay_minutes (current_time - lesson_start_time).total_seconds() / 60 if current_time lesson_start_time: status normal # 提前到正常出勤 elif delay_minutes 10: status late # 迟到 10 分钟内 else: status normal # 迟到超过 10 分钟也记出勤但标记为迟到 insert_attendance_record(student_id, course_id, today, current_time, status) return status去重的维度是学号 课程 日期三者组合唯一。这样保证一节课一个学生只会产生一条考勤记录即使他在画面里出现又消失再出现也不会重复写入。判定逻辑上我踩过一个比较隐蔽的坑如果你只把上课前识别到当作正常出勤上课后识别到一律当作迟到那就把问题想简单了。一个学生可能第一节上课前没被摄像头拍到但第二节课间他回教室了第二节课又被拍到此时系统如果还按第一节课的时间戳去判断就会误标。所以考勤判定必须绑定课程时间窗口这个概念每节课程有自己的开始时间和结束时间只有落在当前课程窗口内的识别记录才参与该课程的考勤判定。这一点在实际开发中特别重要。4. 多人人脸识别最容易翻车的几个技术细节这一节是我最想写的。因为从源码能跑通到真正能在课堂上稳定用中间隔着的不是算法理论而是这些具体到不能再具体的工程细节。很多人的系统死在实验室里不是算法不行是细节没处理到位。4.1 检测尺度与邻居参数小脸和侧脸问题多人场景下后排学生的脸在 1080p 画面里可能只有 40x40 像素这个尺寸对于 HOG 模型来说已经非常极限了。我实际测试下来face_recognition的默认参数很容易漏掉这种小脸导致后排学生整节课都刷不上记录。解决办法有两个方向。一个是调高检测时的上采样次数。face_recognition.face_locations()里有个number_of_times_to_upsample参数默认是 1把它改成 2等于是先把画面放大一倍再检测小脸就能找出来了。代价是检测时间会明显增加实测大概慢一倍左右但课堂场景下摄像头位置固定画面变化不大这个代价可以接受。另一个方向是如果项目里用的是 OpenCV 的detectMultiScale做检测那就重点关注三个参数scaleFactor、minNeighbors、minSize。scaleFactor越小检测越细致但越慢minNeighbors调小到 3 左右能找回一部分被误删的侧脸但误检也会变多minSize设置成画面尺寸的十分之一小于这个尺寸的人脸直接不给检测能省掉很多无效计算。实测下来一个 40 人左右的教室摄像头放在黑板正上方分辨率调到 1280x720能保证前四排人脸清晰第五排开始要靠上采样才能勉强识别。想让后排也稳定最有效的办法不是调算法而是把摄像头往教室后方挪一点或者用一个视角更窄、能拉近画面的摄像头。4.2 阈值设置相似度多少才算同一个人这是整个项目里最微妙的一个参数。face_distance返回的是两个 128 维特征向量之间的欧氏距离数值越小越可能是同一个人。但小到什么程度算同一个人没有一个放之四海皆准的答案。我给一个可复现的经验值在光线正常的教室里用普通笔记本摄像头阈值设为 0.45 是一个比较稳妥的起点。如果学生之间有长得比较像的或者同一个学生的照片特征和实时画面差异较大可以适当放宽到 0.5如果发现误识别频繁比如把几个人都认成了同一个人那就往下收紧到 0.4 甚至 0.38。我把阈值的效果变化整理成一个表方便对照。阈值表现建议小于 0.35识别极严很多真人识别不出来基本不可用漏报严重0.4 - 0.45稳定识别率高误报少推荐区间从 0.45 开始调0.5 - 0.55识别变宽松部分相似者开始混淆仅在光线复杂或特征差异大时使用大于 0.6基本谁都像同一个人完全不可用误报爆炸我在测试时踩过一个具体的坑第一次跑通系统用的阈值是默认的 0.6结果一个班 40 个人有 12 个人被识别成了同一个同学。因为那个同学的特征向量在库里有 5 张样本照片分布在一个较大的特征空间里其他人的特征离他的特征距离不远阈值一放宽全撞了进去。把阈值收紧到 0.42 之后这个问题基本消失。所以遇到很多人被识别成同一个人的情况先去降阈值。4.3 光线和遮挡摄像头布设与画面预处理人脸识别这个技术对光线是出了名的挑剔。教室里的光照条件比实验室复杂太多了靠窗的位置阳光直射靠门的位置背光投影幕布的光还会在脸上形成色块。这些都是导致识别率剧烈波动的元凶。摄像头布设上我建议放在教室正前方的黑板顶部或者讲台上方镜头正对座位区域保证学生在抬头看讲台时能拍到正脸。要避免放在教室侧面侧面拍到的多是侧脸识别率会大幅下降。画面预处理上有一个简单的优化能明显提高速度把视频帧先缩小再送进识别模块。比如原始帧是 1280x720先缩放到 640x360人脸检测和特征提取的速度能提升好几倍而识别精度的损失在可控范围内。代码很简单import cv2 frame cap.read()[1] small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) # 把缩小后的帧送进 face_recognition 做检测和识别另外要考虑的一个点是遮挡。戴帽子、戴口罩、用手挡脸、低头看手机这些都会导致特征提取不完整。课堂上低头看手机的学生多了去了系统识别不到是正常的不代表系统坏了。我的建议是考勤系统只负责记录摄像头能清楚看到并确认身份的人识别不到的学生课后自动生成一份疑似缺勤名单交给老师在后台人工确认。系统不追求百分百全自动而是用自动化把老师从逐一点名里解放出来把精力集中在确认少量边缘个案上这才是合理的产品定位。5. 数据集与测试流程如何验证系统真实可靠源码拿到手第一步不是直接部署而是先建立一套属于你自己的测试验证流程。我见过不少人把源码跑起来对着自己的脸试了一下识别成功了就说系统没问题结果真上课堂一测翻车翻得很难看。问题就出在测试样本太单一。5.1 自建人脸样本库的方法测试之前要先准备样本库。这里说的样本库分两层一层是已注册学生的特征库另一层是专门用来测试的各种照片库。这俩不是一回事。已注册学生的特征样本我建议每个学生至少拍 3 张照片一张正面、一张微侧脸、一张略微低头或戴普通眼镜的。有条件的话在不同时间、不同光照下各拍一张比如上午十点的教室和下午三点的教室光线差别非常大。样本越多样模型在真实环境里的适应能力越强。测试用的照片库要包含这些类型单人正脸、多人同框、有人低头、有人侧脸、后排小脸、戴帽子、戴眼镜、画面中有其他人物的背景干扰。把这些照片按场景建立测试集逐个跑识别统计结果。5.2 测量识别率与误报率评估人脸识别系统不能只凭感觉说挺好的都认出来了。要量化。我用的评估口径是三个数正确识别率应识别出的学生中被正确识别出来的比例。漏报率应识别出的学生中没被识别出来的比例。误报率被识别成错误身份的人脸数占所有识别结果的比例。举一个我实测过的场景一个 25 人的班级正常坐在教室中摄像头 720p 分辨率放在前方。测试结果是前排 10 人全部正确识别中排 8 人全部正确识别后排 7 人中有 2 人未检出漏报率 8%。误报为 0。把number_of_times_to_upsample2打开之后后排的 2 人也能检出但程序帧率从 12 帧左右降到了 6 帧左右。这个数据想说明的是系统的性能是有边界的不是有了源码就万事大吉。你必须在自己的硬件和教室环境下实测拿到自己的数据然后根据数据去调整参数。这就是调优的过程。5.3 边界场景测试除了正常场景一定要测边界场景。我整理了一份测试清单每个场景都很容易暴露出问题两个人脸长得像的同学同时入镜是否混淆。多人同时入镜时后排小脸是否能检出。画面中有人举着手机手机屏幕里恰好人脸照片系统会不会误判。摄像头位置偏移后侧面人脸是否还能识别。电脑同时开着会议软件占用摄像头时系统能否正常打开摄像头并报出明确错误。最后一个场景尤其值得注意课堂上老师可能会同时开着投屏软件、直播软件如果摄像头被占用程序直接崩溃那课堂体验就很糟糕了。所以摄像头打开失败一定不能是静默异常要给用户弹一个清晰的中文提示告诉他摄像头被占用还是未检测到摄像头设备。6. 交付一个让用户能直接用的源码包最后说交付。这个项目的交付物是源码.zip说明使用者拿到的不是一堆零散的.py文件而是一个结构完整、依赖清晰、能照着 README 跑起来的压缩包。这里面的门道比想象中多。6.1 源码包的目录结构与配置文件一份及格的源码包解压之后应该是这样的结构classroom-attendance/ ├── requirements.txt ├── config.ini ├── README.md ├── main.py ├── ui/ │ ├── __init__.py │ ├── login_window.py │ └── attendance_window.py ├── core/ │ ├── __init__.py │ ├── recognizer.py │ ├── register.py │ └── camera.py ├── database/ │ ├── __init__.py │ ├── db_helper.py │ └── init.sql ├── models/ │ └── (存放 dlib 模型文件) ├── data/ │ └── (考勤记录导出目录) └── logs/ └── (运行日志)config.ini是最关键的配置文件里面至少要包含这几项[camera] camera_id 0 resolution 1280x720 [recognition] model hog threshold 0.45 upsample_times 1 [attendance] late_minutes 10 [database] path data/attendance.db把所有可调参数集中在配置里而不是散落在代码各处好处是使用者不需要懂代码打开配置文件就能微调系统行为。这个设计思路对任何类型的源码交付项目都适用。6.2 依赖管理与模型文件处理Python 项目的依赖管理是最容易让使用者在第一步就放弃的地方。face_recognition这个库底层依赖dlib和opencv在 Windows 上dlib的安装经常会出现编译错误尤其是没有装 Visual Studio Build Tools 的机器。针对这个问题requirements.txt里一定要锁好版本并写清楚 Python 版本要求。我的建议是requirements.txt里明确标注dlib19.24.2这个版本在 Windows 下有预编译 wheel不用现场编译和opencv-python、face_recognition等版本。Python 版本建议用 3.8 或 3.9实测下来这两个版本跟 dlib 的兼容性最稳。Python 3.10 及以上dlib 编译失败的概率会明显上升。模型文件这块face_recognition库的好处是它内置了 HOG 人脸检测模型和 128 维特征提取模型安装库的时候模型文件就自动带上了不需要单独下载对用户来说省心很多。如果你用的是其他需要单独下载prototxt、caffemodel的检测方案一定要在 README 里写清楚模型的下载地址、存放路径最好是写一个脚本自动下载否则用户解压源码包之后发现缺模型运行直接报错体验很差。6.3 使用者常见的解压和运行问题把源码包交付出去之后使用者会遇到什么样的问题写 README 的时候要把它们提前写了。根据我的观察出现频率最高的是这几个。压缩包解压报错提示file is not a zip file。这个基本是下载不完整导致的文件传输中断或者下载工具异常退出都会让 zip 文件损坏。这时候不要双击打开用命令行工具重新下载下载完先查看文件大小是否和页面上描述一致再解压。解压后中文文件名乱码。这个跟 zip 文件的编码格式有关Windows 上常见的问题是压缩包是在其他操作系统下打包的文件名编码不一致。解决方法是用解压软件时选择自动检测编码或者让打包方在压缩时就用 UTF-8 编码。依赖安装失败集中在 dlib 的编译上。Windows 用户建议直接执行pip install dlib19.24.2如果出现编译错误先装 Visual Studio Build Tools 的 C 开发组件或者改用 conda 安装conda install -c conda-forge dlib。macOS 用户则需要先安装 cmake 和 boost。还有一类问题跟摄像头有关。程序运行后摄像头画面全黑或者提示无法打开摄像头。先确认系统自带的相机应用能不能正常出画面再检查有没有其他软件占用了摄像头最后检查一下是不是把摄像头编号写错了外接摄像头可能对应编号 1 而不是 0。我在做系统交付时还有一种体会源码包的 README 一定要包含一份五分钟快速启动指南把从解压到跑起来的每一步都写出来包括安装依赖的命令、启动命令、界面操作路径。使用者如果第一步就走通后面遇到问题会更有耐心去解决这个项目在别人心里的靠谱程度也会高很多。关于这套源码最后再分享一点实际经验做这套系统的时候我还有两个很深的体会想补充。第一个是关于活体检测的。这套系统目前是纯 2D 人脸识别拿着照片放到摄像头前是有可能骗过系统的。如果是课堂教学场景学生坐在座位上被点名作弊成本比较高风险还能接受。但如果以后要把这套东西扩展成考试门禁或者宿舍门禁一定要加上活体检测比如眨眼检测、摇头检测或者直接用带深度信息的 3D 摄像头。这是我从项目一期结束之后复盘时得出的一个重要结论防作弊能力和识别能力是两个维度的问题。第二个是关于隐私数据的处理。人脸特征向量虽然不能直接还原成照片但这仍然是个人信息理论上受到相关隐私法规的保护。我在源码包里加了两个约定一是所有学生数据只保存在本地数据库中不向任何云端服务器上传二是导出考勤报表时只包含学号、姓名、考勤状态不包含人脸特征数据。如果是在学校环境里正式部署建议提前跟教务部门确认数据使用的合规性别等系统上线了再补流程。从我个人的角度看这套Python基于多人人脸识别的课堂考勤系统是一个很有代表性的综合性项目——它有人脸检测、特征提取、特征比对这些 AI 技术点有数据库设计的业务逻辑有 GUI 交互有异常处理还有源码打包交付的工程化细节。把一个班几十号人真正跑通把阈值调稳把去重逻辑做严谨你收获的东西会远超能用这两个字。如果你也想动手试一试先别急着找现成的门禁设备拿这套源码在我们自己的教室里跑一轮亲眼看一看光线、角度、座位距离对人脸识别的影响你对这个领域的理解会扎实很多。本文还有配套的精品资源点击获取
返回列表