基于Python+OpenCV的人脸识别考勤系统:LBPH算法与完整源码复现
简介泛微OA e-cology 8 文档WebService接口资料面向需要基于该版本进行二次开发、文档模块集成的实施与开发人员。资源主要内容为最新文档接口说明书包括DocService接口在services.xml中的部署配置、重启及WSDL地址验证方式login、createDoc、updateDoc、deleteDoc、getDoc、getDocCount、getList等方法的参数与返回值说明并详细列出文档对象DocInfo的属性字段覆盖文档ID、类型、标题、编号、目录、部门、语言及创建、修改、批准、归档等状态信息便于开发者直接对照编写调用代码。包体为1个docx文件约330KB结构紧凑适合作为接口联调或培训的速查手册。已有6813人学习是泛微OA系统集成中较实用的参考文档。1. 人脸识别考勤系统源码、数据库与使用说明的完整复现做过课程设计的人都有体会人脸识别考勤系统这类题目看着不难真动手才发现坑全在后面OpenCV 版本对不上、LBPH 模型训练出来识别率低、数据库连不上、UI 界面跑不起来。这套基于 Python OpenCV 的人脸识别考勤系统源码包正好把这些环节串成了一条完整的链路——从人脸采集、模型训练到实时识别打卡、考勤记录查询每个模块都有对应代码数据库脚本和配套文档也齐了。适合正做人脸识别相关课程设计的学生也适合刚接触 OpenCV 想找一个完整项目练手的开发者。本文直接拆解这套资源的结构、核心算法和复现步骤把环境配置和常见报错一并讲清照着做就能跑通。2. 系统架构与 LBPH 识别原理先搞清楚它为什么能用2.1 整体模块划分采集、训练、识别、记录四条线这套考勤系统的代码组织思路很清晰四大功能模块对应四条调用链理解了这个结构后面改代码就有方向感了。人脸采集模块负责调用摄像头捕获人脸图像并存储到本地数据集目录同时记录对应的用户 ID 和姓名信息数据集训练模块读取采集到的人脸样本提取特征后生成训练模型文件实时识别模块加载训练好的模型对摄像头画面中的人脸进行匹配返回预测的 ID 和置信度考勤记录模块在识别成功后把时间戳写入数据库并提供查询接口。四个模块之间有明确的先后依赖关系必须先采集足够数量的样本再训练生成模型然后才能启动识别。如果把顺序搞反识别模块会因为找不到模型文件直接报错。这四条线在源码里分别对应不同的函数和文件操作入口都集中在主控界面里用按钮触发对应的功能调用。2.2 LBPH 算法为什么课程设计选它而不是深度学习LBPHLocal Binary Pattern Histogram局部二值模式直方图是 OpenCV 内置的人脸识别算法之一和 EigenFaces、FisherFaces 并列。它的核心思想是把人脸图像分成若干小块在每个小块内计算每个像素的局部二值模式特征生成直方图序列再通过比较直方图之间的相似度来判断人脸属于哪个已知样本。LBPH 的计算过程分三步。第一步是对每个像素计算 LBP 值方法是以该像素为中心、半径为 1 的邻域内比较 8 个相邻像素与中心像素的灰度值大小大于记 1、小于记 0得到一个 8 位二进制数转成十进制就是这个像素的 LBP 值。第二步是分块统计直方图把图像切成若干个小区域每个区域统计 LBP 值的分布情况。第三步是特征拼接与匹配把所有块的直方图拼接成一个特征向量识别时计算当前人脸特征向量与已训练特征向量的距离距离最小的那个类别就是识别结果。你可能会问现在深度学习人脸识别这么成熟为什么要选 LBPH两个原因。一是课程设计的场景要求你能讲清楚原理LBPH 的特征提取过程和匹配逻辑可以手绘流程图讲明白而卷积神经网络的结构和训练过程很难在答辩时用几句话说清。二是资源约束LBPH 不需要 GPU不需要安装 TensorFlow 或 PyTorch一台普通笔记本跑得飞快数据量几百张图片训练时间也就几十秒。这套资源的选型是符合它定位的你如果硬要换成深度学习方案环境配置和代码改动量不是一天能搞定的。2.3 核心代码走读从加载数据集到生成模型训练模块的代码是整个系统最核心的部分它把采集到的人脸图像和标签喂给 LBPH 算法生成一个可供识别模块调用的模型文件。源码里的核心逻辑如下import cv2 import os from PIL import Image import numpy as np def train_model(data_path, model_save_path): # 获取数据集目录下的所有子目录每个子目录代表一个人 person_dirs os.listdir(data_path) faces [] labels [] for person_dir in person_dirs: # 目录名格式为 ID_姓名比如 1_张三 label int(person_dir.split(_)[0]) person_path os.path.join(data_path, person_dir) # 遍历该人员目录下的所有图片 for img_name in os.listdir(person_path): img_path os.path.join(person_path, img_name) # 转换为灰度图并读取为数组 gray_img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) if gray_img is None: continue # 缩放到统一尺寸保证特征维度一致 resized_img cv2.resize(gray_img, (200, 200)) faces.append(resized_img) labels.append(label) # 创建 LBPH 识别器并训练 recognizer cv2.face.LBPHFaceRecognizer_create() recognizer.train(faces, np.array(labels)) recognizer.save(model_save_path) print(模型训练完成共处理 {} 张图片.format(len(faces)))这段代码的逻辑不复杂但有两个细节容易踩坑。第一是目录命名规范训练脚本会按ID_姓名的格式解析用户身份ID 必须是整数如果命名格式不对int()转换会直接抛异常。第二是图片统一下采样到 200x200这个尺寸不是随便定的——LBPH 对输入尺寸没有硬性要求但同一批训练样本尺寸必须一致否则特征向量维度对不上训练时会报错。用 200x200 是一个折中值既保留足够的人脸细节又不会让计算量过大。识别模块加载模型后对摄像头捕获的每一帧做同样的预处理然后调用predict方法得到预测结果和置信度。置信度是一个浮点数数值越低表示匹配度越高一般在 50 到 80 之间可以认为是同一人超过 80 就要怀疑是陌生人。这个阈值在源码里是可调的我习惯把它设置成 100低于 100 才认为是已注册人员这样能减少误识别。3. 数据库与考勤记录MySQL 脚本和写入逻辑的配合3.1 数据库脚本的内容与初始化步骤这套资源里附带了一个 SQL 脚本作用是创建考勤系统所需的数据库和表结构。核心是两张表一张用户信息表用来存储系统里注册的人员信息一张考勤记录表用来存储每次识别成功后的打卡记录。用户信息表的字段设计比较精简包含用户 ID、姓名、注册时间等基础信息。考勤记录表则是按每次打卡一行来记录包括打卡用户 ID、打卡时间、识别置信度等字段。这种设计符合课程设计的定位——不需要复杂的部门表和排班表够用就好。初始化数据库的步骤在 Windows 环境下一般是这样的先启动 MySQL 服务然后用命令行或图形化工具执行 SQL 脚本。命令行的方式如下mysql -u root -p attendance.sql执行时系统会提示输入 root 用户的密码输入后脚本自动运行创建数据库和表结构。如果 MySQL 的 bin 目录没有加入系统 PATH 环境变量需要先切换到 bin 目录再执行或者使用 mysql 客户端的完整路径。执行完成后可以用show tables;命令验证表是否创建成功。3.2 Python 连接 MySQL配置项和依赖包考勤记录模块通过 pymysql 库连接数据库连接参数集中在代码开头的一个配置区里。常见的配置项包括数据库地址、端口、用户名、密码和数据库名。这套源码里的默认配置是本地连接如果你的 MySQL 设置了不同密码或者数据库名不是脚本里创建的需要手动修改这个配置区。import pymysql db_config { host: localhost, port: 3306, user: root, password: 123456, database: attendance_db, charset: utf8mb4 } def insert_attendance_record(user_id, confidence): # 建立数据库连接 conn pymysql.connect(**db_config) cursor conn.cursor() # 插入考勤记录注意使用参数化查询防止注入 sql INSERT INTO attendance (user_id, check_time, confidence) VALUES (%s, NOW(), %s) cursor.execute(sql, (user_id, confidence)) conn.commit() cursor.close() conn.close()这里必须提醒一个常见问题pymysql不是 Python 标准库需要单独安装。很多人在这个环节翻车因为程序跑起来才报ModuleNotFoundError: No module named pymysql。解决方法是先确认 pip 可用然后执行pip install pymysql如果是 Python 3 环境可能需要用pip3命令。另外MySQL 8.0 以上版本默认的认证插件是caching_sha2_password旧版本的 pymysql 可能连不上升级到最新版 pymysql 就能解决。数据库表结构设计有一个值得注意的点考勤记录的user_id字段应该和用户信息表的主键对应形成一个简单的关联关系。查询某个人的考勤记录时通过 JOIN 或者 WHERE 条件按 user_id 过滤即可。这套资源的 SQL 脚本里已经把这个关系建好了不需要额外修改。3.3 考勤记录写入的时机与异常处理考勤写入不是每识别一帧都执行那样会导致数据库被刷爆。源码里的做法是当识别出的用户 ID 与上一秒识别结果一致时不重复写入只有识别结果发生变化或者距离上次写入超过一定时间间隔才执行插入。这种去重机制在实际使用中很重要。import time last_record_time 0 last_user_id -1 def process_recognition(user_id, confidence): global last_record_time, last_user_id current_time time.time() # 同一用户 5 秒内不重复打卡 if user_id last_user_id and (current_time - last_record_time) 5: return # 识别置信度超过阈值则视为无效识别不写入 if confidence 100: return insert_attendance_record(user_id, confidence) last_record_time current_time last_user_id user_id这个逻辑有两个参数值得关注时间间隔 5 秒和置信度阈值 100。5 秒的去重间隔适合考勤场景——一个人站在摄像头前不会连续刷出几千条记录如果你希望更频繁地记录可以把这个值改小但不建议小于 2 秒。置信度阈值 100 是经验值不同环境下可能需要微调这个在避坑章节会专门展开。异常处理方面源码里的insert_attendance_record没有做 try-except 包裹如果数据库没启动或者连接超时程序会在写入时崩溃。我一般会建议加上异常处理至少在写入失败时打印错误信息而不是直接挂掉整个识别线程。4. 完整复现步骤从环境准备到跑通一次识别打卡4.1 环境清单与版本建议复现这个项目的环境组合比较固定版本选对能少踩一半坑。Python 版本建议用 3.8 或 3.9OpenCV 用 4.5 左右的版本配合 opencv-contrib-python 包。为什么强调 contrib 包因为 LBPH 识别器在 OpenCV 主包里已经内置了但如果你未来想用其他扩展功能contrib 包更全而且和主包的安装方式略有区别。安装命令一般是这样pip install opencv-python4.5.5.64 pip install opencv-contrib-python4.5.5.64 pip install pymysql pip install pillow版本号锁定到具体小版本是因为 OpenCV 的高版本在部分接口上有调整比如cv2.face.LBPHFaceRecognizer_create()这个函数的命名在旧版本里是cv2.face.createLBPHFaceRecognizer()如果你装的是最新版 OpenCV直接按源码里的写法通常没问题但如果装的是老版本就要改成旧接口名。为了避免不必要的兼容性问题建议直接按上面的版本号安装。4.2 人脸采集的操作要点与样本数量控制人脸采集是整个流程里最容易被忽视的环节采集质量直接决定模型训练效果。源码里的采集模块会打开摄像头在画面中检测人脸并截取保存每按一次空格保存一张。操作上要注意的是采集时人应该正对摄像头光线均匀不要有大幅度的头部转动。采集数量怎么控制我实践下来的经验是每人 50 到 100 张比较合适太少模型容易欠拟合太多则增加训练时间且可能引入重复样本。采集过程中可以轻微变换角度和表情——比如稍微低头、抬头、左右转脸这能增加样本多样性提高模型的泛化能力。这里有个心理预期要提前建立LBPH 对姿态和光照变化比较敏感即使采集了 100 张识别时你只要侧脸超过一定角度大概率还是认不出来。这不是代码 bug是算法本身的能力边界。采集后的图片存储路径也有讲究源码里要求放到data/ID_姓名这样的目录结构下例如data/1_zhangsan。不要改这个结构因为训练模块依赖于目录名解析用户 ID 和姓名。4.3 训练模型并启动实时识别训练前建议删掉数据集目录里的无关文件只保留采集的人脸图片。运行训练脚本后控制台会输出处理了多少张图片并在指定路径生成训练模型文件。模型文件通常以.yml或.xml后缀命名内容是 LBPH 算法算出的直方图特征数据。启动识别模块后程序会打开摄像头画面在画面中框出检测到的人脸并显示识别出的 ID 和置信度。如果置信度在阈值内系统自动写入考勤记录同时界面上会给出打卡成功的提示。整个流程从启动到看到效果只需要几分钟这也是这套资源对新手友好的原因——不用配置深度学习环境不用下载预训练权重代码跑起来就能看到反馈。import cv2 # 加载已训练好的模型 recognizer cv2.face.LBPHFaceRecognizer_create() recognizer.read(trainer/trainer.yml) # 加载人脸检测器 face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml) # 打开摄像头 cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, scaleFactor1.2, minNeighbors5) for (x, y, w, h) in faces: # 裁剪人脸区域并预处理 roi_gray gray[y:yh, x:xw] roi_resized cv2.resize(roi_gray, (200, 200)) # 预测 ID 和置信度 pred_id, confidence recognizer.predict(roi_resized) # 置信度越低表示匹配度越高 if confidence 100: cv2.rectangle(frame, (x, y), (xw, yh), (0, 255, 0), 2) cv2.putText(frame, fUser: {pred_id} conf: {confidence:.2f}, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) else: cv2.rectangle(frame, (x, y), (xw, yh), (0, 0, 255), 2) cv2.putText(frame, Unknown, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) cv2.imshow(Attendance System, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码里的scaleFactor1.2和minNeighbors5是人脸检测器的参数可以理解为检测灵敏度与准确率的权衡scaleFactor越小检测越精细但速度越慢minNeighbors越大误检越少但也可能漏检。课程设计场景下保持默认值即可除非你在走廊摄像头那种多人远距离场景下使用才需要调整这些参数。4.4 双摄像头环境下的设备选择问题如果你的笔记本有前置摄像头和外接摄像头cv2.VideoCapture(0)不一定能打开你期望的那个摄像头。Windows 下可以通过修改参数来切换0是默认第一个摄像头1是第二个。如果都打不开检查一下摄像头是否被其他软件占用比如微信视频通话正在使用摄像头时OpenCV 是抢不到的。这个问题的排查方法比较直接在命令行里跑一个两行的 Python 脚本尝试逐个打开摄像头索引并读取画面哪个能正常输出帧就用哪个索引。不要上来就怀疑代码有问题摄像头占用在 Windows 上是非常常见的情况。5. 避坑指南环境配置、识别效果和数据库连接的高频翻车点5.1 OpenCV 安装后 import 报错现象执行import cv2直接报错提示找不到模块或者提示 DLL 加载失败。 原因最常见的是安装的版本不匹配比如在 Python 3.10 以上的环境安装了过旧的 OpenCV 轮子或者是安装了多个 Python 版本pip 装到了一个版本运行时用的却是另一个。 解决先确认当前 Python 版本和 OpenCV 版本执行python --version和pip show opencv-python。如果是版本问题按 4.1 节的版本组合重装如果是多 Python 环境问题用where python查看默认解释器路径确保 pip 和 python 属于同一个环境。5.2 LBPH 识别率低换了光线就认不出现象采集时识别率很高换个环境或者背对窗户时同一个人的识别置信度直逼 200直接判成陌生人。 原因LBPH 算法对光照变化极敏感训练样本和识别场景的光照条件差异过大时直方图特征漂移严重。这属于算法的固有局限不是调参能根治的。 解决采集训练样本时尽量覆盖不同的光照条件——分别在开灯、关灯、拉窗帘等场景下各采集一部分让模型见过更多光线下的人脸。如果项目允许可以在识别场景中加入补光让摄像头前的人脸亮度保持稳定。另外一个技巧是训练时多保留一些模糊边缘的样本让模型对噪点不那么敏感。5.3 pymysql 连接 MySQL 8.0 报错 Authentication plugin现象连接数据库时报Authentication plugin caching_sha2_password cannot be loaded或者类似的加密规则错误。 原因MySQL 8.0 默认的用户认证插件是caching_sha2_password而旧版的 pymysql 只支持mysql_native_password。 解决升级 pymysql 到最新版命令是pip install --upgrade pymysql。如果还不行可以登录 MySQL 修改用户的认证插件执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;然后刷新权限。两种方法选第一种更安全毕竟改认证插件会影响数据库的整体策略。5.4 摄像头打开失败或画面卡住不动现象程序启动后窗口弹出来了但画面全黑或者运行几秒后就卡死。 原因一是摄像头索引不对打开了一个不存在的设备而代码没有做空帧判断二是 OpenCV 的视频后端与摄像头驱动不兼容常见于 USB 摄像头。 解决给cap.read()的返回值加一个判断如果ret为False就继续下一帧而不是直接 break配合打印日志排查如果确认索引没问题但画面黑屏尝试改cv2.CAP_DSHOW后端cap cv2.VideoCapture(0, cv2.CAP_DSHOW)在 Windows 下经常能解决问题。5.5 训练时报错 Expected Ptr cv::face::FaceRecognizer for argument self现象调用recognizer.train()时报这个错字面意思很绕看不出来是什么问题。 原因cv2.face子模块没有被正确加载。虽然安装了 opencv-python但人脸识别模块在opencv-contrib-python包里单独装主包是不包含cv2.face的。 解决检查python -c import cv2; print(cv2.__version__); print(dir(cv2.face))如果报AttributeError: module cv2 has no attribute face说明没有安装 contrib 包。执行 4.1 节里的命令安装后重新打开 Python 验证。这个坑特别隐蔽因为它不会在import cv2时报错而是在调用识别器时才开始炸。6. 验证识别效果的技巧置信度阈值、测试集和界面联动跑通了基础流程只是第一步怎么确认系统真的可靠我习惯做三件事来验证一是统计不同置信度区间下的识别表现二是用陌生面孔做负样本测试三是看考勤记录表和界面显示是否严格一致。置信度阈值调整有规律可循。把阈值从默认的 100 调低到 80已注册用户的识别率会下降但陌生人被误判为已注册用户的概率也下降调高到 120已注册用户基本都能认出来但陌生人混进来被当成某个已知用户的风险增加。课程设计里通常没有真正意义上的陌生人所有测试者都是已注册用户所以阈值稍微放宽问题不大但如果你要演示防陌生人的功能阈值要从严。负样本测试的操作方式是找几个没参与采集的人站在摄像头前观察系统是否将其判为已注册用户。如果连续出现陌生人被判为已注册用户的情况就需要提高阈值或者增加训练样本量。我一般会跑三轮测试单人正脸、单人侧脸、多人同框分别记录识别正确率。单人正脸的识别率应该接近 100%侧脸识别率会明显下降这个衰退幅度就是你要拿去写在报告里分析的实验数据。界面和数据库的联动验证也不难。打卡成功后用 MySQL 查询命令SELECT * FROM attendance ORDER BY check_time DESC LIMIT 5;查看最新的几条记录确认写入时间和显示结果吻合。如果记录里出现大量同一用户 5 秒内的重复记录说明去重逻辑没生效回去检查全局变量有没有被重新赋值。关于系统边界有两点要清晰。一是这套系统是单机版的识别和考勤都在同一台机器上跑不能直接搬到多人同时刷脸的部署场景二是 LBPH 的模型文件是训练数据集的压缩表示换了一批人就要重新采集训练不能指望旧模型直接识别新面孔。这两点写进课程设计文档里答辩时反而显得你对技术边界理解到位。这套资源能帮你把逻辑完整跑通但原理和局限还是得自己想清楚——从那以后我每次拿到 OpenCV 项目都会先跑一遍负样本测试再谈效果这个习惯省了不少回头改代码的时间希望帮到你。本文还有配套的精品资源点击获取