人脸识别实战:LBPH / Eigen / Fisher 综合使用指南(训练、保存、加载、预测与调优)


一、三种识别器简介(直观对比)

  • LBPH(局部二值模式直方图)cv2.face.LBPHFaceRecognizer_create()
    原理:基于局部纹理特征(LBP),对每个小网格计算直方图,拼接成特征向量。
    优点:对光照、表情、遮挡比较鲁棒,适合小样本;参数少,速度快。
    缺点:对姿态极端变换仍有限制。

  • EigenFaces(PCA)cv2.face.EigenFaceRecognizer_create()
    原理:主成分分析(PCA),找出训练集的主成分(特征脸),用低维投影进行比较。
    优点:理论基础清晰,能压缩信息、速度快。
    缺点:对光照敏感,需要较好归一化,样本间差异小可能效果差。

  • FisherFaces(LDA)cv2.face.FisherFaceRecognizer_create()
    原理:线性判别分析(LDA),在投影时最大化类间差异、最小化类内差异。
    优点:比 Eigen 更适合类别判别,光照容忍性优于Eigen。
    缺点:需要每个人至少 2 张样本(样本分布要求更高)。


二、数据准备(文件结构与读取)

推荐数据集目录结构:

dataset/
  person1/
    img1.jpg
    img2.jpg
  person2/
    img1.jpg
    ...

训练前需将每张图裁切为人脸区域(或使用人脸检测实时裁切),统一大小(例如 200x200),并转换为灰度。

下面给出一个读取与预处理的通用函数:

import cv2
import os
import numpy as np

def read_dataset(dataset_path, face_size=(200,200), cascade_path=None, do_equalize=True):
    images, labels, label_names = [], [], {}
    current_label = 0
    face_cascade = None
    if cascade_path:
        face_cascade = cv2.CascadeClassifier(cascade_path)

    for person_name in sorted(os.listdir(dataset_path)):
        person_dir = os.path.join(dataset_path, person_name)
        if not os.path.isdir(person_dir):
            continue
        label_names[current_label] = person_name
        for fname in os.listdir(person_dir):
            fpath = os.path.join(person_dir, fname)
            img = cv2.imread(fpath)
            if img is None:
                continue
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            # 如果提供了人脸检测器,优先裁切人脸,否则使用整图缩放
            if face_cascade is not None:
                faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
                if len(faces) == 0:
                    continue
                x,y,w,h = faces[0]
                gray = gray[y:y+h, x:x+w]
            gray = cv2.resize(gray, face_size)
            if do_equalize:
                gray = cv2.equalizeHist(gray)
            images.append(gray)
            labels.append(current_label)
        current_label += 1
    return images, np.array(labels), label_names

三、训练模型(示例:LBPH / Eigen / Fisher)

注意:使用这些类需要安装 opencv-contrib-python(pip install opencv-contrib-python

import cv2
import numpy as np
import pickle

dataset = "dataset"           # 修改为你的路径
cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"

images, labels, label_names = read_dataset(dataset, face_size=(200,200), cascade_path=cascade_path)

# LBPH 创建与训练(可调参数)
lbph = cv2.face.LBPHFaceRecognizer_create(radius=2, neighbors=8, grid_x=8, grid_y=8)
lbph.train(images, labels)
lbph.save("model_lbph.yml")   # 存盘,兼容大多数 OpenCV 版本
# 或者 lbph.write("model_lbph.xml")

# Eigen 创建与训练(可指定 num_components)
eigen = cv2.face.EigenFaceRecognizer_create(num_components=80)
eigen.train(images, labels)
eigen.save("model_eigen.yml")

# Fisher 创建与训练
fisher = cv2.face.FisherFaceRecognizer_create()
fisher.train(images, labels)
fisher.save("model_fisher.yml")

# 保存 label map(用于将预测 id 转回名字)
with open("labels.pkl", "wb") as f:
    pickle.dump(label_names, f)

提示

  • Eigen/Fisher 对输入要求更严格(需要多样本且灰度一致、尺寸一致)。

  • LBPH 对单人少样本更友好。

  • train() 接受列表或 numpy 数组;labels 必须是一维整型数组。

  • 可以用 recognizer.update(new_images, new_labels) 在线增量训练(LBPH 支持)。


四、加载模型与预测(单张人脸 & 实时摄像头)

预测步骤:检测 -> 裁切 -> 预处理 -> predict()predict() 返回 (label, confidence)。对于 LBPH,confidence 越 代表越相似(OpenCV 不同版本数值含义略有差异,需经验阈值);Eigen/Fisher 的 confidence 含义也类似(一般越小越好)。

示例:实时摄像头识别

import cv2
import pickle

# 加载模型与标签
lbph = cv2.face.LBPHFaceRecognizer_create()
lbph.read("model_lbph.yml")
with open("labels.pkl", "rb") as f:
    label_names = pickle.load(f)

cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(cascade_path)

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, scaleFactor=1.2, minNeighbors=5)
    for (x,y,w,h) in faces:
        face_img = gray[y:y+h, x:x+w]
        face_img = cv2.resize(face_img, (200,200))
        face_img = cv2.equalizeHist(face_img)
        label, confidence = lbph.predict(face_img)
        name = label_names.get(label, "Unknown")
        # 根据经验阈值判断是否为已知人(LBPH的阈值可能在50~100之间,视训练情况而定)
        if confidence < 80:
            text = f"{name} ({confidence:.1f})"
        else:
            text = f"Unknown ({confidence:.1f})"
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2)
        cv2.putText(frame, text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
    cv2.imshow("Recognition", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

五、性能评估 & 调参建议

  1. 评价指标:准确率(accuracy)、精确率/召回率、混淆矩阵、ROC(如果做二分类)。

  2. LBPH 参数

    • radius(半径):LBP 的采样半径。

    • neighbors:采样点数,越大对噪声更鲁棒但特征更粗。

    • grid_xgrid_y:网格划分,越多则特征维度越高。

    • 经验:对小数据库,radius=1~3, neighbors=8, grid 8x8 通常可用。

  3. Eigen / Fisher 参数num_components(主成分数量)——太大可能过拟合、太小信息不足。通常取 min(n_samples-#classes, 50~150) 范围内调整。

  4. 预处理极重要

    • 对齐(用眼睛位置旋转水平)能极大提升模型效果。

    • 使用 cv2.equalizeHist() 做直方图均衡缓解光照影响。

    • 统一人脸大小(例如 200x200 或 100x100)。

  5. 数据增强:对训练集做轻微旋转、平移、亮度变化可以提升泛化。

  6. 阈值设置:用验证集(未用于训练)来设定 confidence 的接受阈值,避免盲目用固定数字。


六、实用技巧与常见问题

  • OpenCV 报错没有 cv2.face:需安装 opencv-contrib-python(包含 face 模块):pip install opencv-contrib-python

  • Fisher 报错样本不足:Fisher 需要类内样本数 ≥ 2(并且类间样本分布合理)。若单人只有一张图,优先用 LBPH。

  • 保存格式 .yml / .xmlsave() / write() / read() / load() 在不同 OpenCV 版本上 API 名称有差——常用 save() / read()。建议检查你的 OpenCV 版本并测试。

  • 跨设备/跨分辨率问题:训练时尽量包含和部署环境相似的光照与相机条件。

  • 隐私与伦理:人脸识别涉及隐私与法律合规(例如使用许可、数据加密存储、告知被拍摄者等),生产部署前请确认合规要求。


七、进阶方向(可选)

  • 使用 DNN(深度学习)的人脸特征提取(例如 FaceNet、ArcFace),再用简单分类器或度量学习提高准确率。

  • 做人脸对齐(基于关键点检测)来提高识别精度。

  • 使用多模态(RGB + 红外)或多摄像头融合提升鲁棒性。

  • 在线学习:使用 recognizer.update() 增量加入新样本并即时更新模型(注意过拟合/遗忘问题)。


八、完整流程总结(快速模板)

  1. 收集至少 5~10 张/人的多光照、多表情照片(LBPH 少量也能用)。

  2. 用级联/关键点检测裁切并对齐人脸,转为灰度并统一尺寸(如 200×200)。

  3. 做直方图均衡或其他归一化。

  4. 按目录加载数据,构建 images, labels

  5. 选择识别器(LBPH/Eigen/Fisher),进行 train()

  6. 使用验证集调参并设定 confidence 阈值。

  7. save() 模型与 label 映射。部署时 read() 模型,并在实时流中检测 -> 预测 -> 阈值判定 -> 输出结果。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐