1. 项目概述

这个Python人脸识别项目采用了LBPH(Local Binary Patterns Histograms)算法,是一个非常适合初学者入门的计算机视觉实践项目。我在实际开发中发现,虽然LBPH的识别精度可能不如深度学习模型,但它对硬件要求低、实现简单,特别适合在个人电脑上快速搭建原型。

项目核心流程分为三个关键阶段:

  1. 人脸数据采集 - 通过摄像头实时捕获人脸图像并建立数据库
  2. 模型训练 - 使用LBPH算法学习人脸特征
  3. 实时识别 - 将摄像头画面与数据库进行匹配识别

提示:整个项目可以在Windows/Linux/Mac上运行,推荐使用PyCharm作为开发环境。关键依赖包括OpenCV、PyQt5等库,具体版本兼容性需要注意。

2. 环境配置与依赖安装

2.1 基础环境准备

首先需要确保Python环境(建议3.7+)已正确安装。我推荐使用conda创建虚拟环境,避免包冲突:

conda create -n face_rec python=3.8
conda activate face_rec

2.2 关键库安装

项目依赖的核心库包括:

pip install opencv-contrib-python==4.5.5.64
pip install PyQt5==5.15.7
pip install pillow==9.0.1
pip install numpy==1.21.5

特别注意:

  • opencv-contrib-python必须包含contrib模块,因为LBPH识别器在该模块中
  • PyQt5版本不宜过高,避免兼容性问题
  • 如果遇到GUI相关错误,可能需要额外安装Qt运行时库

3. 人脸数据采集实现

3.1 采集脚本详解

数据采集是整个项目的基础,质量直接决定最终识别效果。核心代码如下:

import cv2
import os

def collect_data(user_id):
    # 初始化摄像头
    cam = cv2.VideoCapture(0)
    # 加载Haar级联分类器
    detector = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
    
    # 创建数据集目录
    if not os.path.exists('dataset'):
        os.makedirs('dataset')

    count = 0
    while True:
        ret, img = cam.read()
        if not ret:
            print("摄像头读取失败")
            break
            
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 检测人脸,参数需要根据实际情况调整
        faces = detector.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
        
        for (x,y,w,h) in faces:
            cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)
            count += 1
            # 保存人脸区域灰度图
            face_roi = gray[y:y+h, x:x+w]
            # 统一调整为200x200尺寸
            face_roi = cv2.resize(face_roi, (200, 200))
            cv2.imwrite(f"dataset/User.{user_id}.{count}.jpg", face_roi)
        
        cv2.imshow('Collecting', img)
        # ESC键退出或采集100张后停止
        if cv2.waitKey(100) == 27 or count >= 100:
            break
    
    cam.release()
    cv2.destroyAllWindows()

3.2 数据采集最佳实践

根据我的项目经验,高质量数据采集需要注意:

  1. 光照条件 :避免强背光和极端光照,均匀自然光最佳
  2. 面部角度 :采集时应包含正面、左右侧脸(约15-30度)
  3. 表情变化 :采集不同表情(自然、微笑等)
  4. 距离控制 :保持人脸占画面60-70%的比例
  5. 数据量 :每人至少50-100张样本

注意:user_id必须为纯数字,这是LBPH识别器的要求。如果使用字符串ID,后续训练会报错。

4. LBPH模型训练

4.1 训练流程实现

训练脚本主要完成以下工作:

  1. 加载数据集中的图像
  2. 提取人脸和对应ID
  3. 训练LBPH识别器
  4. 保存训练模型
import cv2
import numpy as np
from PIL import Image
import os

def train_model():
    # 创建LBPH识别器
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    # 加载人脸检测器
    detector = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
    
    def get_images_labels(dataset_path):
        face_samples = []
        ids = []
        
        for filename in os.listdir(dataset_path):
            if filename.startswith('User.'):
                path = os.path.join(dataset_path, filename)
                # 使用PIL读取并转换灰度
                pil_img = Image.open(path).convert('L')
                img_numpy = np.array(pil_img, 'uint8')
                # 从文件名解析用户ID
                user_id = int(filename.split('.')[1])
                
                # 检测人脸(确保数据质量)
                faces = detector.detectMultiScale(img_numpy)
                for (x,y,w,h) in faces:
                    face_samples.append(img_numpy[y:y+h,x:x+w])
                    ids.append(user_id)
        
        return face_samples, np.array(ids)
    
    print("正在训练模型...")
    faces, ids = get_images_labels("dataset")
    recognizer.train(faces, ids)
    # 保存训练结果
    recognizer.write("trainer.yml")
    print(f"训练完成,共处理了 {len(np.unique(ids))} 个人的 {len(faces)} 张人脸图像")

4.2 LBPH算法原理

LBPH(局部二值模式直方图)算法工作流程:

  1. 图像分块 :将人脸图像划分为多个小区域(默认8x8)
  2. LBP特征提取 :对每个像素,比较其与周围像素的灰度值
    • 周围像素值≥中心则标记1,否则0
    • 形成8位二进制数(0-255)
  3. 直方图统计 :计算每个区域的LBP直方图
  4. 特征拼接 :将所有区域的直方图连接成最终特征向量
  5. 分类识别 :使用k-NN算法比较输入图像与数据库中的特征向量

关键参数说明:

  • radius :LBP半径(默认1)
  • neighbors :采样点数量(默认8)
  • grid_x / grid_y :图像划分网格数(默认8x8)
  • threshold :识别置信度阈值

5. 实时识别系统实现

5.1 PyQt5界面开发

使用PyQt5创建带实时视频流的人脸识别界面:

from PyQt5 import QtCore, QtGui, QtWidgets
import cv2
import sys

class FaceRecognitionApp(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("人脸识别系统")
        self.setGeometry(100, 100, 800, 600)
        
        # 中央窗口
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        
        # 视频显示标签
        self.video_label = QtWidgets.QLabel()
        self.video_label.setAlignment(QtCore.Qt.AlignCenter)
        
        # 信息显示
        self.info_label = QtWidgets.QLabel("准备开始识别...")
        self.info_label.setAlignment(QtCore.Qt.AlignCenter)
        
        # 布局
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.video_label)
        layout.addWidget(self.info_label)
        self.central_widget.setLayout(layout)
        
        # 初始化识别线程
        self.recognition_thread = RecognitionThread()
        self.recognition_thread.change_pixmap_signal.connect(self.update_image)
        self.recognition_thread.recognition_result.connect(self.update_info)
        
    def start_recognition(self):
        self.recognition_thread.start()
        
    def update_image(self, qt_img):
        self.video_label.setPixmap(QtGui.QPixmap.fromImage(qt_img))
        
    def update_info(self, user_id, confidence):
        self.info_label.setText(f"识别结果: ID {user_id} (置信度: {confidence:.2f})")

class RecognitionThread(QtCore.QThread):
    change_pixmap_signal = QtCore.pyqtSignal(QtGui.QImage)
    recognition_result = QtCore.pyqtSignal(int, float)
    
    def run(self):
        # 初始化摄像头
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            print("无法打开摄像头")
            return
            
        # 加载训练好的模型
        recognizer = cv2.face.LBPHFaceRecognizer_create()
        recognizer.read('trainer.yml')
        
        # 加载人脸检测器
        detector = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        
        while True:
            ret, frame = cap.read()
            if not ret:
                continue
                
            # 转换为灰度图像
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # 检测人脸
            faces = detector.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
            
            for (x, y, w, h) in faces:
                # 识别处理
                id_, confidence = recognizer.predict(gray[y:y+h, x:x+w])
                # 绘制矩形框和文字
                cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)
                text = f"ID:{id_} Conf:{confidence:.2f}"
                cv2.putText(frame, text, (x+5,y-5), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
                
                # 发送识别结果
                self.recognition_result.emit(id_, confidence)
            
            # 转换图像格式用于显示
            rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            qt_img = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
            self.change_pixmap_signal.emit(qt_img)
            
            # 降低CPU占用
            QtCore.QThread.msleep(30)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = FaceRecognitionApp()
    window.show()
    window.start_recognition()
    sys.exit(app.exec_())

5.2 识别结果优化

LBPH的predict方法返回两个值:

  • 识别到的用户ID
  • 置信度(值越小表示匹配度越高)

为提高识别准确率,可以添加置信度阈值:

# 在识别循环中添加阈值判断
confidence_threshold = 70  # 经验值,可根据实际情况调整

id_, confidence = recognizer.predict(gray[y:y+h, x:x+w])
if confidence < confidence_threshold:
    display_id = id_
else:
    display_id = "Unknown"  # 未知人员

6. 性能优化与问题排查

6.1 提高识别准确率

根据我的项目经验,以下方法可以显著提升LBPH的识别效果:

  1. 数据增强

    • 对训练图像添加随机噪声
    • 使用直方图均衡化增强对比度
    • 应用轻微旋转(±5度)和缩放(±10%)
  2. 参数调优

    # 创建识别器时调整参数
    recognizer = cv2.face.LBPHFaceRecognizer_create(
        radius=2,
        neighbors=16,
        grid_x=8,
        grid_y=8,
        threshold=80.0
    )
    
  3. 预处理改进

    • 人脸对齐(使用dlib检测68个关键点)
    • 光照归一化处理
    • 去除背景干扰

6.2 常见问题解决

  1. 摄像头无法打开

    • 检查摄像头是否被其他程序占用
    • 在Linux系统可能需要权限设置: sudo chmod 777 /dev/video0
  2. 训练时报错

    • 确保数据集路径正确
    • 检查图像命名格式是否为 User.[id].[序号].jpg
    • 确认所有图像都能正常读取
  3. 识别结果不稳定

    • 增加训练样本数量
    • 调整 scaleFactor minNeighbors 参数
    • 检查光照条件是否与训练时相似
  4. GUI界面卡顿

    • 降低视频帧率
    • 缩小显示分辨率
    • 在单独的线程中进行图像处理

7. 项目扩展方向

这个基础项目可以进一步扩展:

  1. 多模态识别

    • 结合人脸和语音识别
    • 添加活体检测功能
  2. 数据库集成

    • 将人脸数据存储在SQLite/MySQL中
    • 关联更多用户信息(姓名、权限等)
  3. 网络功能

    • 实现远程视频流识别
    • 开发REST API接口
  4. 性能提升

    • 使用多线程处理视频流
    • 采用更高效的算法(如EigenFace/FisherFace)
  5. 应用场景扩展

    • 考勤系统
    • 智能门禁
    • 个性化推荐系统

在实际部署时,建议考虑使用C++重写核心算法部分以提高性能,Python更适合作为原型开发工具。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐