ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Django+OpenCV+dlib构建人脸识别考勤系统

Django+OpenCV+dlib构建人脸识别考勤系统 简介基于PythonDjangoOpenCV与dlib的员工人脸识别考勤系统是一份面向计算机相关专业毕业设计及课程设计的完整项目包。内容涵盖系统源码、人脸数据集、详细设计文档代码已经过测试运行成功可直接部署也可在此基础上修改扩展适合有Python基础的学生、教师及开发人员用于毕设、课设或项目初期演示。压缩包共包含659个文件以501个Python脚本为核心辅以模型权重pth、配置文件xml/cfg、数据库dat、依赖说明txt及详细文档docx等整体大小约196MB目录组织清晰便于按模块研读。资源内附带的数据集与文档能帮助读者快速理解人脸检测、特征提取和考勤记录的核心流程掌握Django后端与OpenCV、dlib算法集成的实际工程方法。已有251人学习下载对于希望高效完成人脸识别考勤系统开发或进行相关课题研究的读者具有较高的参考价值。1. 基于PythonDjangoOpenCVdlib的人脸识别考勤系统先想清楚它在解决什么指纹机要排队卡片能代打这些旧问题正好是这套系统的切入点。基于PythonDjangoOpenCVdlib构建的员工人脸识别考勤系统把摄像头变成打卡终端员工走到镜头前系统自动检测人脸、提取特征、和底库里的人脸编码比对命中后写入一条考勤记录。标题里的dilb即机器学习库dlib的常见笔误下文统一按dlib处理。对做毕业设计的同学来说这个题目的价值在于一次覆盖机器视觉、Web后端和数据库三条线OpenCV负责图像采集与预处理dlib负责检测和对齐并输出128维人脸特征向量Django负责员工、特征、考勤记录三类数据模型和接口三者在一个工程里协作。对从业者而言这套结构稍加改造就能迁移到访客登记、门禁联动或会议签到场景识别链路和Web骨架都是现成的。先说清它的边界这套方案不等于商用门禁机光线剧烈变化、侧脸超过30度、口罩遮挡时识别率会明显下降。提前知道这些后面调阈值和设计补卡策略时取舍就有依据了。2. dlib人脸特征提取与OpenCV图像处理考勤识别核心库的选型与协作2.1 为什么选dlibOpenCV而不是纯OpenCV或深度学习框架OpenCV自带Haar级联和LBP级联检测器能快速框出人脸区域但它只负责“找到脸”不负责“认出是谁”。传统级联检测在侧脸、暗光、遮挡条件下漏检率明显直接拿它做考勤会频繁触发人工补卡。dlib在这条链路里补上了关键一环它提供HOGSVM检测器和CNN检测器配合shape_predictor_68_face_landmarks.dat把人脸对齐到标准姿态再用dlib_face_recognition_resnet_model_v1.dat输出128维特征向量。比对时算欧氏距离距离小说明同一人距离大说明不同人。dlib属于经典机器学习与深度学习混合的方案检测部分负责定位特征提取部分负责表征。相比从零训练一个分类器直接用dlib自带预训练模型省掉了训练数据和GPU成本CPU上单帧处理大约在几十到几百毫秒量级满足考勤这种低并发场景。这也是大量人脸识别毕业设计把它当作主力库的原因——它把工程难点从“怎么训模型”转移到了“怎么把模型用好、怎么组织底库数据”。2.2 OpenCV在摄像头帧到打卡画面之间的四个职责人脸识别不等于“算法识别那一帧”它依赖OpenCV做大量前置和收尾工作。常见做法是四个职责第一用VideoCapture读取USB摄像头帧或读取一段视频文件作为演示数据源第二图像预处理包括BGR转RGB、直方图均衡化、缩放第三在画面上画出检测框和员工姓名第四把识别结果叠加到画面上给出“打卡成功”的视觉反馈。摄像头调用是很多人第一次跑通时卡住的地方第4章会单独给最小可用代码。2.3 dlib三个模型的调用顺序与特征维度约定dlib的模型文件按职责分成检测、对齐、编码三层调用顺序不可颠倒模型文件作用输入输出mmod_human_face_detector.dat可选CNN人脸检测图像 → 人脸框shape_predictor_68_face_landmarks.dat关键点定位人脸框 → 68个关键点dlib_face_recognition_resnet_model_v1.dat特征编码对齐后人脸 → 128维向量HOG检测器dlib.get_frontal_face_detector()不需要额外模型文件直接可用CNN检测器需要下载mmod模型准确率更高但速度慢一到两倍。考勤场景里人脸通常正对镜头HOG检测器是性价比最高的起点如果现场角度偏、光线杂再切换CNN检测器。编码模型的输出固定是128维浮点向量底库存什么维度比对时就必须是什么维度混用会直接报维度错误或产生无意义距离。2.4 OpenCV与dlib安装教程版本约束和编译踩坑这一步最常翻车。dlib在Windows上需要Visual Studio Build Tools在Linux上需要cmake和g。建议Python版本锁在3.8到3.10之间dlib官方wheel对3.11以上版本的支持依赖编译没有预编译包时安装时间可能长达十分钟。OpenCV用pip安装即可注意不要和opencv-contrib-python同时装否则会出现模块互相覆盖。# Python 3.83.10 虚拟环境内执行 pip install opencv-python4.8.1.78 pip install cmake pip install dlib19.24.2 pip install Django4.2,5.0 pip install numpy参数说明opencv-python4.8.1.78是带主模块的常规发行版摄像头读取、图像处理、图像绘制都在其中dlib19.24.2是Windows和Linux都有预编译wheel的常用版本19.22之前对Python 3.10适配不佳。版本号以实际安装结果为准遇到编译失败优先检查C工具链是否齐全。Django 4.2 LTS要求Python 3.8以上和前面的Python版本约束正好对齐。提示安装dlib前先执行pip install cmakeWindows用户确认已安装Visual Studio C生成工具否则pip install dlib大概率在编译阶段报错。3. Django后端与考勤数据模型从人脸向量到打卡记录3.1 django创建app与项目结构划分dlib和OpenCV只负责“识别出是谁”考勤业务本身要用Django来承载。常见做法是先创建项目和两个应用再把数据模型、接口和后台管理逐层补齐。django-admin startproject face_attendance cd face_attendance python manage.py startapp attendance python manage.py startapp api参数说明attendance应用放核心模型和识别逻辑api应用放REST接口。Django默认的admin站点可以直接当作员工信息管理后台使用界面需要美化时再引入django-admin-interface或自定义admin模板毕业设计里默认admin已经够用。随后在settings.py的INSTALLED_APPS里追加这两个应用执行python manage.py migrate初始化数据库表并在settings.py中配置MEDIA_ROOT和MEDIA_URLImageField上传的照片才会落到磁盘、在admin里可预览。3.2 员工表、人脸特征表、考勤记录表的最小设计考勤系统最少需要三张表员工信息、人脸特征、考勤记录。人脸特征单独成表而不是塞进员工表是因为一个人可能注册多张人脸正脸、侧脸、戴眼镜比对时逐条计算距离取最小值。Django自带的auth_user表可以复用为登录账号表但员工和账号不强制关联毕业设计里可以独立建模。# attendance/models.py from django.db import models class Employee(models.Model): emp_no models.CharField(max_length20, uniqueTrue, verbose_name工号) name models.CharField(max_length50, verbose_name姓名) department models.CharField(max_length100, blankTrue, verbose_name部门) photo models.ImageField(upload_tofaces/, blankTrue, verbose_name照片) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.emp_no} {self.name} class FaceEncoding(models.Model): employee models.ForeignKey(Employee, on_deletemodels.CASCADE, related_nameencodings) encoding models.JSONField(verbose_name128维特征向量) image_path models.CharField(max_length255, blankTrue) class AttendanceRecord(models.Model): employee models.ForeignKey(Employee, on_deletemodels.CASCADE, related_namerecords) check_time models.DateTimeField(auto_now_addTrue, verbose_name打卡时间) source_image models.ImageField(upload_tocaptures/, blankTrue, verbose_name抓拍图) confidence models.FloatField(default0.0, verbose_name比对距离得分)逻辑说明encoding字段用JSONField存dlib输出的128维浮点列表SQLite和MySQL都能直接读写省去单独建二进制特征字段的麻烦confidence存欧氏距离数值越小代表越相似。考勤记录不设“上班/下班”类型一天内多条记录由查询逻辑推导既能避免漏刷状态也让数据模型保持简单。删除员工时on_deletemodels.CASCADE会级联删除该员工的人脸特征和考勤记录这正是Django查询与删除对象的标准行为。3.3 模型加载策略识别服务和Django视图共用模型如果识别逻辑写在Django视图里每次HTTP请求都加载一次三个dlib模型模型加载耗时以秒计打卡接口会被拖垮。常见做法是Django启动后把模型加载到模块级变量或者用一个独立线程跑摄像头轮询识别命中后直接写库。单机演示用模块级变量最省事摄像头常驻方案用独立线程配合队列传递帧数据。# recognition/model_loader.py import dlib _detector None _predictor None _encoder None def get_models(): global _detector, _predictor, _encoder if _detector is None: _detector dlib.get_frontal_face_detector() _predictor dlib.shape_predictor( models/shape_predictor_68_face_landmarks.dat) _encoder dlib.face_recognition_model_v1( models/dlib_face_recognition_resnet_model_v1.dat) return _detector, _predictor, _encoder逻辑说明get_models用惰性加载保证第一次调用时才加载模型之后所有请求复用同一份模型对象。Django开发服务器默认单进程多线程模块级变量在线程间共享是安全的dlib的检测和编码调用不会污染内部状态。部署时如果用gunicorn多worker每个worker会各自持有一份模型内存占用要按worker数估算这是从开发环境上生产环境最容易忽略的一点。3.4 打卡接口与人脸识别比对查询的代码实现视图层只需要两个核心功能POST上传一帧人脸图像返回识别结果GET查询某员工某天的打卡记录。上传的图像先交给识别模块算特征再和FaceEncoding表里全部向量比对取最小距离小于阈值视为命中并写入AttendanceRecord返回姓名和打卡时间没有检出人脸或多张人脸时返回对应错误码。接口方法入参返回/api/check_inPOSTimage文件员工姓名、工号、打卡时间、距离/api/records/emp_noGET路径参数当日打卡记录列表# api/views.py import cv2 import numpy as np from django.http import JsonResponse from django.views.decorators.http import require_POST from attendance.models import Employee, FaceEncoding, AttendanceRecord from recognition.model_loader import get_models def __read_image(image_file): data np.frombuffer(image_file.read(), np.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR) return cv2.cvtColor(img, cv2.COLOR_BGR2RGB) require_POST def check_in(request): image_file request.FILES.get(image) if not image_file: return JsonResponse({code: 400, msg: 缺少image字段}, status400) detector, predictor, encoder get_models() img __read_image(image_file) faces detector(img, 1) if len(faces) 0: return JsonResponse({code: 404, msg: 未检测到人脸}) if len(faces) 1: return JsonResponse({code: 400, msg: 检测到多张人脸}) shape predictor(img, faces[0]) vec np.array(encoder.compute_face_descriptor(img, shape)) best_emp, best_dist None, 1e9 for enc in FaceEncoding.objects.all().select_related(employee): dist np.linalg.norm(vec - np.array(enc.encoding)) if dist best_dist: best_dist, best_emp dist, enc.employee if best_emp is None or best_dist 0.5: return JsonResponse({code: 404, msg: f无人匹配最小距离{best_dist:.4f}}) record AttendanceRecord.objects.create(employeebest_emp, confidencebest_dist) return JsonResponse({code: 0, msg: ok, name: best_emp.name, emp_no: best_emp.emp_no, time: record.check_time.strftime(%Y-%m-%d %H:%M:%S), distance: round(best_dist, 4)})逻辑说明detector(img, 1)中的1是upsample_num_times值越大越容易检出小脸但速度越慢打卡场景摄像头离人1米以内用1即可。np.linalg.norm计算两组128维向量的欧氏距离0.5是常用比对阈值第5章专门讲怎么调。检测到多张人脸直接拒绝避免误打卡。select_related提前联表加载employee避免逐条回表查询这是Django查询优化里性价比最高的一招。正式工程中检测、编码、比对三段逻辑应抽成一个match_face纯函数供这里的视图和第4章的摄像头循环共用识别规则只维护一份。4. 人脸识别考勤主流程注册、摄像头循环、比对、排错4.1 员工注册把一张照片变成底库里的128维向量注册和打卡共用同一套“检测→对齐→编码”链路区别是注册时把向量写入FaceEncoding表打卡时拿向量去比对。注册端可以接受上传照片也可以现场用摄像头抓拍。抓拍注册更贴近真实系统但光线差时出来的向量质量差会直接拉低后续整体识别率。# recognition/register.py import cv2 import numpy as np from attendance.models import Employee, FaceEncoding from recognition.model_loader import get_models def register_face(employee_id, image_path): detector, predictor, encoder get_models() img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) faces detector(img, 1) if len(faces) ! 1: raise ValueError(f需要且仅需要一张人脸当前检测到{len(faces)}张) shape predictor(img, faces[0]) vec encoder.compute_face_descriptor(img, shape, num_jitters10) emp Employee.objects.get(idemployee_id) FaceEncoding.objects.create(employeeemp, encodinglist(vec), image_pathimage_path)参数说明compute_face_descriptor的第三个参数num_jitters表示对同一张脸做多次带微小抖动的采样取平均默认是0只算一次注册时设为10可以把光照和像素抖动带来的噪声平滑掉代价是耗时增加十倍左右。注册质量直接决定阈值能不能设严这一步值得多等几秒。写库前把numpy数组转成listJSONField才能正确序列化。4.2 OpenCV调用相机的实时打卡循环实际部署时考勤机就是一台接了USB摄像头的电脑Django后台跑一个常驻线程循环读帧。OpenCV的VideoCapture读帧有三个常见坑摄像头索引不对、read返回False时不重连、退出时没有release导致下次开不了设备。最小实现如下# recognition/camera_loop.py import cv2 from recognition.matcher import match_face def run_camera(device0): cap cv2.VideoCapture(device) if not cap.isOpened(): raise RuntimeError(f无法打开摄像头 {device}) while True: ok, frame cap.read() if not ok: cap.release() cap cv2.VideoCapture(device) # 断线重连 continue result match_face(frame) if result[matched]: cv2.putText(frame, f{result[name]} 打卡成功, (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明断线重连是必须的——USB摄像头长时间运行后偶发丢帧read返回False时如果继续循环会空转重新release再open可以让设备复位。match_face就是从3.4视图里抽出来的纯函数输入BGR帧内部完成检测、编码、比对并返回dict这样摄像头循环和HTTP接口共用同一套识别逻辑不写两遍。cv2.putText画的是绿色文字只作视觉反馈不依赖它做任何业务判断。4.3 比对策略全表扫描还是先筛后比FaceEncoding表数据量小时直接在Python里全量计算欧氏距离完全够用。100个员工每人注册2张脸也就是200次128维向量减法耗时远小于人脸检测本身。但数据量上万时就要考虑先粗筛再精比比如先按部门过滤待比对员工或用scikit-learn的KDTree做近似最近邻检索精度损失不大速度提升显著。毕业设计做到全表扫描这层就合格了知道KDTree这条升级路径即可。4.4 人脸识别考勤系统的常见报错排查三个库的报错各有规律按下面表格的顺序排查能省下大量时间报错特征常见原因排查方向cv2.error提示通道数不匹配BGR/RGB混用检查cvtColor调用位置dlib RuntimeError deserializing失败模型文件损坏或下到了错误页file命令检查文件格式VideoCapture.isOpened()为False设备索引错、被其他程序占用换device参数或关掉占用进程Django模板块加载超时模型重复初始化确认走get_models惰性加载# Linux环境验证模型文件完整性 file models/dlib_face_recognition_resnet_model_v1.dat # 输出应为 data而不是 HTML document 或 ASCII text注意模型文件下载源五花八门最简单可靠的校验方式就是看file命令输出和文件大小是否在几十MB量级。模型文件损坏时重跑程序报错是好事至少比静默算出垃圾距离好。5. 识别准确率调优与数据集验证的3个技巧5.1 欧氏距离阈值怎么定最稳0.5阈值不是拍脑袋。注册完成后用一批不在注册集里的照片跑一遍统计“本人比对距离”和“他人比对距离”两个分布。同一人的距离通常在0.2到0.4之间不同人通常大于0.55到0.6取两个分布的中点是第一版阈值之后拿真实打卡记录回放微调。阈值每降0.05误识别概率显著下降但漏识别概率上升最终值取决于你更接受“误打卡”还是“让人补卡”。# 阈值统计脚本片段 import numpy as np dists [d for _, d in same_person_pairs] # 本人比对距离 others [d for _, d in different_person_pairs] # 他人比对距离 print(same mean, np.mean(dists), std, np.std(dists)) print(other min, np.min(others)) # 阈值取 (np.mean(dists) np.min(others)) / 2 附近5.2 注册数据集不用贪大要覆盖变化每个员工注册2到3张就够但这几张必须覆盖正面平视、上下左右各15度偏转、戴眼镜与否或换眼镜后的样子。角度变化比数量更重要因为dlib对齐后的128维向量对光照做了一定归一化对姿态的容忍度却有限。如果考勤点背光注册时就用背光环境拍如果员工会戴口罩注册集里加一张戴口罩的照片但dlib对口罩遮挡的鲁棒性有限更稳的做法是保留打卡后的补卡通道而不是硬撑识别。5.3 用录像回放验证识别效果不要只在实时摄像头前反复试拿一段录像当测试集能稳定重现问题。把录像抽帧保存用打卡接口逐个POST统计通过率、误识别率和平均耗时。平均耗时能反映机器性能目标帧率10fps时单帧处理应在100毫秒内超了就降低upsample_num_times的值或把比对前的人脸区域裁剪后统一缩放到固定尺寸。最终核对当天考勤记录与真实员工进出日志是否一致一致再交付不一致就回到5.1重新调阈值。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进