拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于OpenCV+dlib+LBPH的人脸识别系统实战:从环境配置到PyQt5打包

简介一份面向人脸识别学习与课堂考勤场景的Python实战项目以OpenCV实现Haar级联人脸检测借助dlib完成人脸的实时跟踪与关键点定位并应用LBPH局部二值模式直方图算法进行身份特征提取与匹配最终通过PyQt5构建可视化操作界面。系统内置登录验证、学生信息管理、考勤记录、数据导出等模块配合MySQL持久化存储形成从人脸录入、识别到点名统计的完整闭环。压缩包内共49个文件涵盖Python源码、多个PyQt5界面文件、Haar级联XML模型、dlib人脸关键点模型、图标与提示音效、依赖库安装包、配置说明及测试数据等整体体积约108.74MB目录按功能模块整理便于快速定位和二次开发。同时提供考勤CSV样例与依赖清单帮助使用者理解人脸检测、特征提取、数据库存储等关键流程。目前已有157人学习下载适合人脸识别初学者、毕业设计选题者及需要构建简易考勤系统的开发者参考与复现。1. 为什么人脸识别系统选了opencvdlibLBPH这条技术栈人脸识别系统不一定需要GPU和深度学习框架。在很多考勤和门禁项目里opencvdlibLBPH这套组合在纯CPU环境下就能把识别速度做到实时训练几十人规模的人脸库用不了五秒。它的核心思路是先用dlib检测人脸并给出68个关键点把歪着的脸对齐成统一尺寸再让OpenCV的LBPH识别器在局部二值模式直方图上做匹配。PyQt5则把这套流程封装成带注册、训练、识别界面的成品适合毕业设计、企业内部工具也适合刚入门CV的开发者拿来做工程化练习。下面把从环境配置到打包发布的技术路径完整走一遍每个环节的坑都会提到。2. 环境准备opencv、dlib与pyqt5的安装联动很多项目不是卡在算法上而是卡在环境配置。opencv-python装完之后导入cv2正常一调cv2.face就报AttributeErrordlib安装卡在编译阶段PyQt5装好又提示找不到Qt platform plugin。这些问题几乎每个项目都会遇到而且都和依赖顺序、扩展包选择有关。2.1 为什么标准opencv-python包里找不到LBPH识别器OpenCV在pip上主要以两个包分发opencv-python是核心包只包含基础模块opencv-contrib-python是扩展包包含了人脸识别、文本检测等额外模块。LBPH识别器所在的face模块被放在contrib里所以只安装opencv-python时import cv2不报错但cv2.face不存在直到运行cv2.face.LBPHFaceRecognizer_create()才报AttributeError。这种滞后报错很容易让人误判问题源头。解决方法是直接安装opencv-contrib-python并且不要和opencv-python混装。推荐在独立虚拟环境里操作避免系统Python旧包干扰python -m venv face_env # Windows 激活 face_env\Scripts\activate # Linux / macOS 激活 source face_env/bin/activate pip install --upgrade pip setuptools wheel pip install opencv-contrib-python pip install cmake dlib pip install pyqt5 numpy命令顺序是有原因的opencv-contrib-python会顺带安装好numpydlib需要cmake参与源码编译所以cmake必须先装最后装pyqt5避免它链接不到刚装好的numpy。numpy建议锁在1.26系列numpy 2.x和较旧版本的dlib在运行时可能报dlib.capi模块找不到。提示opencv-python和opencv-contrib-python不要同时安装二者都提供同名cv2目录混装后会形成动态库覆盖模块时有时无。表三个核心依赖的安装错误对照依赖包Windows常见报错Linux常见报错处理方式opencv-contrib-pythonpip下载速度慢ImportError: libGL.so.1换国内镜像安装libgl1dliberror C2653 / LNK1104找不到cmake先装VS Build Tools和cmakepyqt5找不到Qt platform plugin依赖libxcb相关库配置platforms目录安装libxcb-xinerama0这些错误里只有小部分来自网速或镜像问题大部分来自依赖顺序和编译组件缺失。对照表格处理比反复重装有效得多。2.2 dlib在Windows/Linux下的编译安装步骤dlib在pip上默认走源码构建所以编译工具链决定成败。Windows下需要Visual Studio的C生成工具和CMakeLinux下需要build-essential、libx11-dev和libopenblas-dev。# Windows pip install cmake pip install dlib # Linux sudo apt-get install build-essential libx11-dev libopenblas-dev pip install cmake dlib编译日志是排错的关键。出现“Unable to find cmake”说明cmake没有成功进入PATH先执行cmake --version验证出现“error C2653”说明C工具链版本和Python架构不匹配出现“fatal error C1083”多半是Windows SDK组件没装全。Linux下如果缺libopenblas编译可能通过但在后期训练做矩阵运算时会崩溃这一点比编译失败更难排查。2.3 pyqt5安装与OpenCV的GUI窗口冲突pyqt5的安装本身只有一条命令pip install pyqt5但运行时会遇到两类典型问题。第一类是PyQt5的Qt基础库版本冲突某些环境下pip会将PyQt5-qt5解析成registryhttps这样的本地注册路径随后报DistributionNotFound。此时统一pip源清空缓存重装即可。第二类是当界面同时使用cv2.imshow和QMainWindow时HighGUI窗口会抢焦点甚至导致退出卡死。Windows下修复Qt平台插件路径set QT_QPA_PLATFORM_PLUGIN_PATHface_env\Lib\site-packages\PyQt5\Qt5\plugins\platforms如果项目本身以PyQt5为主界面建议直接放弃cv2.imshow调试窗口所有画面统一用QLabel刷新。在cmd里安装pyqt5时先激活face_env再执行pip install pyqt5用PyCharm时把项目解释器指向face_env的python.exe即可没有额外配置。这样能避免许多边界问题。3. 用dlib做人脸检测与关键点对齐补齐LBPH识别的成败前提在纯CPU、轻量部署的需求下dlib比OpenCV的Haar级联和DNN检测器更适合。Haar级联对光照和侧脸太敏感DNN检测器即使使用精简模型也要多下载几十MB权重。dlib的HOG加线性SVM检测器只有几百KB检测一张脸在普通笔记本上大约30到60ms已经足够支撑实时识别流程。3.1 HOG检测器跑通第一张人脸的流程首先从静态图片开始把检测结果和68个关键点一起绘制出来。这里需要dlib预训练的关键点模型文件shape_predictor_68_face_landmarks.dat。import cv2 import dlib detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) img cv2.imread(sample.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rects detector(gray, 1) # 1表示做一次金字塔上采样 for rect in rects: x, y, w, h rect.left(), rect.top(), rect.width(), rect.height() cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2) shape predictor(gray, rect) for i in range(68): pt (shape.part(i).x, shape.part(i).y) cv2.circle(img, pt, 1, (0, 0, 255), -1) cv2.imshow(result, img) cv2.waitKey(0) cv2.destroyAllWindows()detector的第二个参数是upsample值指把图像放大几倍再检测值越大越容易找到小脸但耗时成倍增加。720P画面取1已经够用人脸只有几十像素宽时调到2超过2后CPU算力会成为瓶颈不如直接提高摄像头采集分辨率。关键点索引36和45分别是左眼外眼角和右眼外眼角接下来对齐要用这两个点。3.2 用68关键点把脸摆正仿射变换与尺寸归一化LBPH统计的是局部像素关系对几何变化没有自适应能力。左半边脸训练、右半边脸识别直方图分布会明显偏移。所以在送进识别器之前必须把脸旋转到水平方向裁出统一大小的人脸区域。import math import cv2 def align_face(gray, rect, shape, output_size112): left_eye shape.part(36) right_eye shape.part(45) dy right_eye.y - left_eye.y dx right_eye.x - left_eye.x angle math.degrees(math.atan2(dy, dx)) eyes_center ((left_eye.x right_eye.x) / 2, (left_eye.y right_eye.y) / 2) rot_mat cv2.getRotationMatrix2D(eyes_center, angle, scale1.0) rotated cv2.warpAffine(gray, rot_mat, (gray.shape[1], gray.shape[0])) x1 max(0, int(eyes_center[0] - rect.width() * 0.6)) y1 max(0, int(eyes_center[1] - rect.height() * 0.5)) x2 min(rotated.shape[1], int(eyes_center[0] rect.width() * 0.6)) y2 min(rotated.shape[0], int(eyes_center[1] rect.height() * 0.7)) face rotated[y1:y2, x1:x2] face cv2.resize(face, (output_size, output_size)) face cv2.equalizeHist(face) # 直方图均衡化降低光照影响 return face两点说明旋转角度由两眼坐标差算出正值代表顺时针warpAffine会做双线性插值重采样裁剪范围不是直接使用检测矩形而是以眼睛中心外扩宽度取矩形宽的1.2倍、高度取矩形高的1.2倍并向下多留一点这样额头和下巴都能保住LBPH的纹理特征不会因为裁得过紧而损失。统一输出到112x112是LBPH比较合适的尺寸分辨率太低丢失纹理太高则直方图噪声变大。训练和推理必须使用同一条对齐链路任何一端变动都会影响最终准确率。3.3 对齐质量对LBPH识别准确率影响的对比在相同训练数据和阈值条件下对齐前后差异十分明显。几类常见情况的经验数据如下置信度越低表示匹配越可靠表对齐前后置信度对比测试情形未对齐置信度对齐后置信度说明轻微侧脸9673角度偏差被旋转修正戴眼镜11881关键点稳定眼镜干扰减少亮度偏低13492均衡化后纹理关系更稳定从数据可以看到对齐加直方图均衡化能让置信度稳定下降三成左右。很多LBPH项目被说准确率低真正原因不是算法弱而是没有做几何归一化。这一步看似简单却直接决定模型能不能用。4. 训练LBPH人脸识别模型从样本采集到置信度调优LBPH在OpenCV里有现成实现不需要手动构造二进制编码。但参数不调默认值直接上生产环境效果不会太好。而从样本采集到阈值设定每个环节都影响最终识别率。4.1 LBPH核心参数解释radius、neighbors与grid划分LBPH把灰度图划分成若干小网格在每个网格内用局部二值模式比较中心像素和邻域像素的灰度关系小于中心像素记0大于记1再把这个二进制串转成十进制数统计成直方图。所有网格的直方图拼起来就是一张人脸的特征向量。识别时计算两张特征向量的欧氏距离输出距离作为置信度。表LBPH参数与经验取值参数含义经验取值范围radius邻域采样半径单位为像素1到2数值太大会丢失细节neighbors邻域采样点数固定8这是圆形邻域标准取法grid_x / grid_y水平和垂直方向网格数8x8常用低分辨率降到4x4threshold预测时的置信度阈值独立调节不参与训练半径取1能捕捉眼镜、胡须这类细节纹理取2对噪声更鲁棒但识别精度会下降。网格数要跟着输入尺寸走如果图像缩到64x64grid还取8会导致单个网格太小直方图不稳定。4.2 用Python代码完成样本采集与模型训练样本采集是数据基础。每个人建议采集25到40张图覆盖正脸、左右偏转15度、上下俯仰和室内常见光照变化。采集时只在单人脸画面下保存避免把背景裁进样本。import cv2 import dlib import os save_root faces/zhangsan os.makedirs(save_root, exist_okTrue) cap cv2.VideoCapture(0) detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) count 0 while count 30: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) rects detector(gray, 1) if len(rects) 1: # 单人脸才保存 rect rects[0] shape predictor(gray, rect) face align_face(gray, rect, shape) # 复用3.2节对齐函数 cv2.imwrite(os.path.join(save_root, f{count:03d}.jpg), face) count 1 cv2.imshow(collect, frame) key cv2.waitKey(1) 0xFF if key ord(q): break cap.release() cv2.destroyAllWindows()这个函数依赖第3.2节的align_face函数预处理链路和之后推理时保持一致。count每保存一张才加1避免连续帧产生大量相似样本。30张左右的数据对LBPH来说方差是可接受的少于15张时直方图统计意义会明显下降。训练并保存模型import cv2 import numpy as np from pathlib import Path face_root Path(faces) images, labels [], [] label_to_name {} for label_id, person_dir in enumerate(sorted(face_root.iterdir())): if not person_dir.is_dir(): continue label_to_name[label_id] person_dir.name for img_file in person_dir.glob(*.jpg): img cv2.imread(str(img_file), cv2.IMREAD_GRAYSCALE) images.append(img) labels.append(label_id) recognizer cv2.face.LBPHFaceRecognizer_create( radius1, neighbors8, grid_x8, grid_y8) recognizer.train(images, np.array(labels)) recognizer.save(lbph_model.yml) print(训练完成标签映射:, label_to_name)代码要点标签从0开始连续分配中间不能跳号否则predict返回的label无法对应人名LBPH训练必须传灰度图彩色图会报size mismatch模型文件本身只保存特征不保存人名label_to_name需要单独持久化可以用numpy的save或json。4.3 置信度阈值怎么定识别、拒绝和误判的平衡predict返回两个值label是身份编号confidence是直方图欧氏距离越小越相似。真实项目不能只看label必须用阈值把陌生人挡在系统外label_id, confidence recognizer.predict(face) if confidence 80: name label_to_name.get(label_id, unknown) else: name unknown阈值和业务场景强相关。不同取值会改变误识和拒识的比例表阈值在不同场景下的取舍阈值误识表现拒识表现适用场景50陌生人几乎不会通过本人偶尔被拒严格门禁80有一定误识风险绝大多数用户通过考勤打卡120陌生人也可能放行极少误拒黑名单辅助比对建议先离线跑一遍所有样本把同一人置信度分布和不同人置信度分布画出来取两类分布中位点作为初始阈值。上线后记录拒识日志再微调10到20个数值。不要把阈值写死在代码参数里做成配置文件或界面输入框现场调整会方便很多。5. PyQt5界面整合摄像头实时识别与打包发布检测、对齐、训练都跑通之后剩下的工作是把流程封装进PyQt5界面。需要解决两个问题摄像头画面如何稳定刷新以及打包成独立可执行文件后依赖还能不能完整。5.1 用QTimer驱动摄像头刷新避免QThread多线程复杂度在摄像头识别场景里用QThread反而会增加复杂度。摄像头读取本身是阻塞的每次读取30ms左右QTimer每30ms触发一次process_frame即可。如果某次处理超过30msQTimer会自动跳过下一次触发不会产生队列堆积。from PyQt5.QtCore import QTimer, Qt from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QMainWindow class MainWindow(QMainWindow): def __init__(self): super().__init__() self.cap cv2.VideoCapture(0) self.timer QTimer() self.timer.timeout.connect(self.process_frame) self.timer.start(30) def process_frame(self): ret, frame self.cap.read() if not ret: return gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) rects detector(gray, 1) for rect in rects: shape predictor(gray, rect) face align_face(gray, rect, shape) label_id, conf recognizer.predict(face) name label_to_name.get(label_id, ?) if conf 80 else unknown cv2.rectangle(frame, (rect.left(), rect.top()), (rect.right(), rect.bottom()), (0, 255, 0), 2) cv2.putText(frame, name, (rect.left(), rect.top() - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qimg) .scaled(self.label.size(), Qt.KeepAspectRatio))cv2默认输出BGR显示到Qt界面必须先转RGBQImage构造函数里bytes_per_line必须传ch * w否则画面会错位。scaled加上Qt.KeepAspectRatio保证人脸不变形。窗口关闭时记得执行self.cap.release()否则摄像头资源会被占用到进程结束。5.2 从源码到exepyinstaller打包opencvdlibpyqt5的依赖收集打包是交付的最后一步也是最容易遗漏依赖的地方。dlib动态库、cv2子模块和PyQt5插件目录都不会被自动收集。推荐用PyInstaller并显式指定pyinstaller -w -F main.py \ --collect-all dlib \ --collect-all cv2 \ --add-data shape_predictor_68_face_landmarks.dat;. \ --add-data lbph_model.yml;.-w表示不显示控制台窗口-F打成单个exe--collect-all把dlib和cv2的完整子模块收进来。模型文件用--add-data加进包内运行时需要用PyInstaller的路径机制找到它们import sys, os base_path getattr(sys, _MEIPASS, os.path.abspath(.)) model_path os.path.join(base_path, lbph_model.yml)打包后如果在某些机器上报Qt platform plugin错误最省事的做法是在exe同级目录放一个platforms文件夹也就是从PyQt5的plugins目录直接拷贝。上线机器没有摄像头驱动时VideoCapture(0)会返回False界面应弹一个警告框再退出而不是在process_frame里静默return导致界面卡死。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门