ARTICLE DETAIL

资讯详情

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

深度学习人脸识别考勤系统实战:特征比对与打卡逻辑解析

深度学习人脸识别考勤系统实战:特征比对与打卡逻辑解析 简介这是一款基于深度学习的人脸识别考勤系统完整毕业设计项目适合计算机相关专业本科生作为毕业设计、课程设计或期末大作业参考也适合希望掌握人脸识别实战开发的学习者。项目经导师指导并调试通过可直接运行覆盖数据预处理、模型训练、评估与预测等完整流程。压缩包内共48个文件以Python源码为主17个py脚本并包含预训练h5模型权重、jpg/png图片数据、xml配置、txt标注文件、使用说明及毕业设计手册docx文档整体约13.4MB目录结构清晰便于按模块学习。目前已有532人学习下载。通过该项目读者可深入理解基于深度学习的人脸识别原理、Facenet与MobileNet等网络结构的应用以及从数据集构建到考勤系统落地的完整工程实现。1. 从毕业设计到能打卡深度学习人脸识别考勤系统该怎么用人脸识别考勤听起来像个产品级项目实际上这份 Python 深度学习源码更像一个“能跑出完整闭环的毕业设计”。核心链路很短摄像头取帧、检测人脸、提取特征向量、与注册库比对、命中后写入考勤记录。真正让新手卡住的不在深度学习而在特征库怎么建、打卡逻辑怎么防重复、遇到光线和中文路径怎么处理。这套资源适合三类人准备交毕业设计或课程设计的学生想快速搭一个教室门口人脸打卡 demo 的开发者以及想搞清楚“深度学习识别后面到底怎么串起来”的初学者。你不需要从头训练任何模型重点是用好封装好的检测和识别管线然后把数据换成你自己的把参数调稳。2. 系统拆解从摄像头帧到考勤记录四步别走偏2.1 为什么这套系统更看重“特征编码”而不是训练一个完整模型先给新手拨开一层误解不是说“深度学习人脸识别”就要拿几十万张照片去训练一个模型。毕业设计这种场景时间有限、数据有限绝大多数源码采用的是“预训练模型 特征比对”的思路。人脸识别的完整链路其实是两段第一段检测框出图中有没有人脸第二段识别把框出来的人脸转成一个向量再跟注册库里的人脸向量做距离比较。这个思路和传统“训练分类器”差别很大。分类器只能回答“这人是张三还是李四”如果班级里来了新人必须重训模型特征比对则把问题变成“这个向量像谁”新增一个人只需要往库里加一张特征编码不需要动网络权重。所以你会看到源码里多半有一个register.py或build_database.py之类脚本它的作用就是把准备好的照片过一遍预训练模型然后保存成特征文件常见的是 pkl 或 npz 格式。选型理由也很直接。如果源码用的是face_recognition库内部就是 dlib 的 ResNet 模型在 CPU 上也能跑很适合毕业设计答辩现场演示如果用的是 MTCNN 加 FaceNet检测更稳侧脸和遮挡稍微好一点但模型加载慢需要更大内存。你先看压缩包里有没有“weights”或者“models”目录就能判断是哪一条路后续调参重心也不同。这套系统的价值在于它把“预处理、特征提取、比对、记录”整个链条都串好了你要做的不是重写算法而是理解每个环节的输入输出边界。2.2 注册与建库把人脸变成一组可比较的数字拿到资源之后第一步要处理的不是识别而是“建库”。常见做法是准备一个face_db目录里面每个子目录以学生姓名命名放若干张该学生在不同角度、不同光线下的照片。然后写一个建库脚本遍历这些照片用检测器找到人脸编码成 128 维向量最后存到一个干净的特征库里。这里“干净”指剔除掉检测失败、多人脸、模糊照片否则后续识别会出现莫名其妙的误匹配。以下是一个简化版建库脚本思路和你拿到的源码基本一致import os import pickle import face_recognition face_db_path face_db # 每个子文件夹名就是姓名 feature_file face_features.pkl db {} for name in os.listdir(face_db_path): person_dir os.path.join(face_db_path, name) if not os.path.isdir(person_dir): continue encodings [] for img_name in os.listdir(person_dir): img_path os.path.join(person_dir, img_name) # load_image_file 内部已经处理好RGB顺序 image face_recognition.load_image_file(img_path) # 用hog模型检测若漏检可换成cnn boxes face_recognition.face_locations(image, modelhog) if len(boxes) ! 1: print(f{img_path} 检测到 {len(boxes)} 张人脸跳过) continue vec face_recognition.face_encodings(image, known_face_locationsboxes)[0] encodings.append(vec) if encodings: db[name] encodings print(f{name} 入库 {len(encodings)} 张) with open(feature_file, wb) as f: pickle.dump(db, f) print(特征库已保存到, feature_file)这段代码的关键点在于每个学生可以不止一张照片所以数据库里存的是“一个名字对应一组向量”而不是单个向量。后续做比对时可以取平均也可以在比对时逐条计算取最小值显然后者更能容忍表情和光照差异。参数说明里face_db_path指向你的人脸数据目录feature_file是输出路径。pickle 序列化后的文件很小几十个人的特征库一般不到 1MB拷到答辩机器上也能直接加载。如果你发现有些照片总被跳过是因为modelhog对侧脸和暗光不够敏感可以改成modelcnn但需要 TensorFlow 环境支持同时每张图会慢不少。2.3 考勤主流程识别、打卡、落库串成闭环建库完成之后考勤脚本要做的就是循环读摄像头帧对每一帧做人脸检测和编码再与特征库比对。比对标准很简单计算当前人脸向量与库里每个向量的欧氏距离最小距离低于阈值就认为匹配成功否则提示“未识别”。真正生产化的逻辑还要处理重复打卡、时间窗口、批量导入但先把主干跑通最重要。下面这段是考勤主流程的核心片段import cv2 import pickle import datetime import sqlite3 import face_recognition with open(face_features.pkl, rb) as f: known_db pickle.load(f) conn sqlite3.connect(attendance.db) cur conn.cursor() cur.execute(CREATE TABLE IF NOT EXISTS attendance (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, check_time TEXT)) conn.commit() cap cv2.VideoCapture(0) tolerance 0.45 # 越小越严格0.4~0.5 之间调 while True: ok, frame cap.read() if not ok: break small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) rgb_frame cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB) boxes face_recognition.face_locations(rgb_frame, modelhog) encodings face_recognition.face_encodings(rgb_frame, boxes) for encoding, box in zip(encodings, boxes): best_name None best_dist 1.0 for name, vec_list in known_db.items(): distances face_recognition.face_distance(vec_list, encoding) min_dist min(distances) if min_dist best_dist: best_dist min_dist best_name name # 只有距离低于阈值才确认打卡 if best_name and best_dist tolerance: now datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) cur.execute(INSERT INTO attendance (name, check_time) VALUES (?, ?), (best_name, now)) conn.commit() print(f{best_name} 打卡距离{best_dist:.3f}时间{now}) else: print(f未识别最近距离{best_dist:.3f}) cap.release() conn.close()这段代码演示了两个容易忽略的点。第一摄像头的原始帧一般比较大先用 0.5 倍缩小再人脸检测速度能快不少代价是远处小脸可能框不到实际使用时根据摄像头分辨率调整系数。第二打卡之后我没有立刻 break因为循环本身可以在现场连续运行但如果不加“一天只打一次”的约束同一张脸会不停往表里插数据。真正要交作业的版本你需要在这里补一个时间窗口判断先查这个人今天有没有记录有就直接跳过或者只在规定时段内允许打卡。这个逻辑看着简单却是答辩提问时最容易暴露短板的位置。3. 参数与调优容差、检测模型和打卡时间缺一不可3.1 容差/阈值先理解“误识”与“拒识”的此消彼长这个系统里最影响使用体验的数字是tolerance。face_recognition库的默认值是0.6这个值表示允许的欧氏距离上限物理含义是两张人脸特征向量在 128 维空间里的距离越小表示越像。实际调参时不能只看“能不能认出自己”要看两个极端。tolerance调得太大比如0.6容易出现“谁都像谁”张三刷脸可能把李四的考勤打了这在答辩演示时非常尴尬。tolerance调得太小比如0.3又会把同一个人的不同表情、眼镜、光线变化全部拒之门外导致站在摄像头前半天识别不了。我一般会从0.5开始然后刻意用班级里最像的两个人做测试。如果这两个人被互相识别就把tolerance下调0.02再测如果同一个人换角度就识别失败再上调。通常0.45左右是一个不错的起点但不要盲从因为每台机器的照片质量和摄像头清晰度不同最佳值可能差0.05。调参时要一边看控制台打印的距离值一边调整先观察同一个人在不同光照下的距离波动范围再把阈值设在这个范围的下沿附近。3.2 检测模型HOG、CNN 与场景匹配检测模型的选择直接影响识别率。face_recognition提供了两个预置模型hog和cnn。hog在 CPU 上跑得很快帧率能到几十但对于低头、侧脸、戴帽子这些情况容易漏检cnn使用的是深度学习检测器抗遮挡能力强但加载模型需占用几个 GB 内存没有 GPU 时速度很慢现场演示如果摄像头连续抽帧会卡得不像样。很多基于深度学习的人脸识别考勤系统会默认用cnn但这反而成了演示翻车的第一原因帧率太低人还没站定框就跳走了。这里的建议是分场景选择如果是教室门口学生正常看镜头用hog就够了漏检率可以接受如果是实验室门口光线复杂建议改用cnn同时把摄像头帧率降到更低或者让用户按一下“开始签到”再检测避免持续抽帧。源码里通常会留一个参数切换类似modelcnn你把这一行改掉就能生效。注意如果源码用的是 MTCNN那么检测器是独立封装的切换模型就不是改一个字符串的事而是改整个检测管线。还要注意输入分辨率。有的源码会把摄像头帧缩到 1/4 来跑速度上去了但小脸彻底看不见。我的习惯是保持宽高比缩到 640再往下缩到 480 就很容易漏检。这个值没有固定标准跟你装摄像头的位置、学生离镜头距离直接相关需要在现场试。3.3 打卡逻辑与时间窗口别只判断“是不是这个人”很多毕业设计翻车不在人脸识别而在考勤逻辑。最典型的问题是学生在一节课里反复进出记录表会被刷屏或者晚上演示的时候随便一张打印照片也能通过。所以一个能答辩的考勤系统至少要处理三件事。一天只记一次在插入打卡记录前先查当天是否已有该学生记录已有则跳过或更新为最后一次时间。考勤时间窗口只在规定的时间段内允许打卡比如早 8:00 到 8:30超出时间记录为迟到或缺勤这个判断可以放在写入数据库之前也可以在导出报表时处理。活体检测纯照片可能骗过系统如果源码没有活体检测答辩前至少要准备一个“眨眼”演示话术更稳妥的做法是加一个简单的眨眼检测。其中“一天只记一次”的代码很好加在往attendance表插入前先执行一条查询today datetime.date.today().isoformat() cur.execute( SELECT COUNT(*) FROM attendance WHERE name? AND date(check_time)?, (best_name, today) ) already cur.fetchone()[0] if already 0 and start_time now_time end_time: cur.execute(INSERT INTO attendance (name, check_time) VALUES (?, ?), (best_name, now)) conn.commit()这里的start_time和end_time建议设置成配置项写在脚本头部方便答辩时解释。date(check_time)依赖 SQLite 的日期函数所以写入的check_time要保持YYYY-MM-DD HH:MM:SS格式。查询里的COUNT(*)只判断“有没有”不考虑迟到和早退需要更精细的状态判断时可以再加一个status字段把正常 / 迟到 / 缺勤三个状态落到数据库里这样导出的报表才像正经系统。4. 避坑手册五个白天正常、晚上翻车的真实案例4.1 摄像头、光线与多脸误检先说一个我踩过的坑。白天在办公室测试人脸识别距离基本稳定在0.35左右识别率接近百分之百到了傍晚开灯再测同一张脸的距离直接飙到0.6以上频繁提示未识别。现象就是“白天正常、晚上翻车”原因是摄像头自动白平衡和曝光把脸照得偏色或过曝特征向量整体偏移了。解决方法是固定摄像头曝光和色温参数或者在建库时就把注册照片和现场识别放在同一光照条件下拍不要拿白天拍的照片去匹配晚上的现场光这属于自己给自己挖坑。第二个坑是两个人离得近检测框漂移。现象是两个人同框时系统有时框到两张脸中间把背景混进特征编码结果两个人各打一次卡或者完全识别不出。原因是hog检测器对近距离脸部的边界不够敏感置信度低时框不完整。解决方法是调整考勤现场的通道位置让队伍一个一个过同时把tolerance适当调高一点让“框偏了”的判定不至于直接变成“未识别”。如果你不改现场只在代码层面调参治标不治本。第三个坑是照片骗过系统。现象是举着手机屏幕也能成功打卡答辩时评委一眼看穿印象分直接崩。原因是特征比对只看静态脸不会判断你是不是活人。解决思路分两层第一层如果源码本身没有活体检测演示前准备一个“真人动一动”的交互比如眨眼一次再记录第二层时间充裕的话加一个基于人脸关键点的眨眼检测眼睛纵横比连续几帧低于阈值才算活体。哪怕只是半成品也表明你考虑过这个漏洞。4.2 中文路径、编码与库文件版本坑第四个坑和编码有关尤其是 Windows 上跑这类 Python 考勤系统。现象是建库脚本遍历“张三”文件夹时突然报UnicodeDecodeError或者明明能看到中文文件名程序就是没法打开。原因是 Windows 默认使用 GBK 解码文件系统路径而源码里写死了某种编码方式两种编码一碰就崩。解决方法是不要硬碰编码把face_db下所有子目录改成拼音或学号比如zhangsan_2024001识别时不展示中文就用拼音显示如果在考勤报表里必须要中文可以另外维护一个“学号到姓名”的映射表这样既避开编码问题也方便做数据库关联。第五个坑特别容易被忽略换电脑之后特征库失效。现象是在实验室机器上建好face_features.pkl拷到答辩笔记本上跑原来能识别的人全都不认识。原因是 pickle 版本兼容性或者两台机器上装的 dlib / face_recognition 版本不一致导致同一个网络提取出的特征分布有差异这属于环境层面的玄学和算法本身没关系。解决方法是在答辩前用目标机器重新跑一遍建库脚本如果时间不够至少保证两台机器的依赖版本完全一致不要上了答辩台还顺手升级库版本。这个坑我见过好几个人踩都是拿着旧特征文件直接换机器临时重装环境又来不及最后只能现场录照片重新建库。5. 从“能跑”到“好用”特征库重训、多角度采集与报表导出5.1 换人重训与多角度底库别把全班压在一张照片上毕业设计交上去之前通常要把示例数据换成自己班级的真实数据。操作分三步清空face_db目录放入新的人脸照片删掉旧的face_features.pkl重新运行建库脚本清空attendance.db里的打卡表或者直接删文件。这里有个容易忽略的点如果学生照片是手机拍的背景很杂最好先用脚本批量裁剪出人脸区域再入库。我一般会写一个预处理脚本用face_recognition.face_locations检测人脸把框出来的区域单独存成小图再喂给建库脚本这样背景干扰会小很多。建库的底图数量也很关键。一个人只放一张精修图虽然入库时快但换发型、换眼镜就会翻车。我的做法是每个人至少三张一张正脸光线均匀、一张手机前置摄像头自拍、一张稍微侧脸带点自然光。这三张分别做编码存进特征库比对时取最小距离容错空间会大很多。如果你手里的资源压缩包里已经带有批量采集脚本用起来会更省事没有的话自己写 20 行 Python 也就够了。5.2 报表导出与每日核对习惯考勤数据落库后报表导出是容易被轻视的一步。用 Python 写 CSV 时Excel 直接打开中文列名经常乱码原因是编码应该用utf-8-sig而不是utf-8。更省心的做法是用openpyxl直接写 xlsx一步到位。导出后还要把每日签到记录和数据库里的条数核对一遍能提前发现重复写入、漏写等问题。我自己有个习惯每跑完一次考勤不看屏幕上的“打卡成功”去看attendance.db里实际插了几条确认数量对了再关程序。从那以后我每次拿到这类考勤资源都不会先急着开摄像头而是单独拎出建库脚本用三张自己的照片做冒烟测试确认特征库没问题再跑主流程。这套方法让我换数据、换机器、临时演示都没再翻过大车。希望这些踩坑经验能帮你在答辩前少走弯路把精力留在真正要展示的识别逻辑上。本文还有配套的精品资源点击获取
返回列表