计算机视觉(opencv)实战——人脸识别:LBPH / Eigen / Fisher 综合使用指南(训练、保存、加载、预测与调优)
收集至少 5~10 张/人的多光照、多表情照片(LBPH 少量也能用)。用级联/关键点检测裁切并对齐人脸,转为灰度并统一尺寸(如 200×200)。做直方图均衡或其他归一化。按目录加载数据,构建。选择识别器(LBPH/Eigen/Fisher),进行train()。使用验证集调参并设定confidence阈值。save()模型与 label 映射。部署时read()模型,并在实时流中检测 -> 预
人脸识别实战: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()
五、性能评估 & 调参建议
-
评价指标:准确率(accuracy)、精确率/召回率、混淆矩阵、ROC(如果做二分类)。
-
LBPH 参数:
-
radius
(半径):LBP 的采样半径。 -
neighbors
:采样点数,越大对噪声更鲁棒但特征更粗。 -
grid_x
、grid_y
:网格划分,越多则特征维度越高。 -
经验:对小数据库,
radius=1~3, neighbors=8, grid 8x8
通常可用。
-
-
Eigen / Fisher 参数:
num_components
(主成分数量)——太大可能过拟合、太小信息不足。通常取min(n_samples-#classes, 50~150)
范围内调整。 -
预处理极重要:
-
对齐(用眼睛位置旋转水平)能极大提升模型效果。
-
使用
cv2.equalizeHist()
做直方图均衡缓解光照影响。 -
统一人脸大小(例如 200x200 或 100x100)。
-
-
数据增强:对训练集做轻微旋转、平移、亮度变化可以提升泛化。
-
阈值设置:用验证集(未用于训练)来设定
confidence
的接受阈值,避免盲目用固定数字。
六、实用技巧与常见问题
-
OpenCV 报错没有
cv2.face
:需安装opencv-contrib-python
(包含 face 模块):pip install opencv-contrib-python
。 -
Fisher 报错样本不足:Fisher 需要类内样本数 ≥ 2(并且类间样本分布合理)。若单人只有一张图,优先用 LBPH。
-
保存格式
.yml
/.xml
:save()
/write()
/read()
/load()
在不同 OpenCV 版本上 API 名称有差——常用save()
/read()
。建议检查你的 OpenCV 版本并测试。 -
跨设备/跨分辨率问题:训练时尽量包含和部署环境相似的光照与相机条件。
-
隐私与伦理:人脸识别涉及隐私与法律合规(例如使用许可、数据加密存储、告知被拍摄者等),生产部署前请确认合规要求。
七、进阶方向(可选)
-
使用 DNN(深度学习)的人脸特征提取(例如 FaceNet、ArcFace),再用简单分类器或度量学习提高准确率。
-
做人脸对齐(基于关键点检测)来提高识别精度。
-
使用多模态(RGB + 红外)或多摄像头融合提升鲁棒性。
-
在线学习:使用
recognizer.update()
增量加入新样本并即时更新模型(注意过拟合/遗忘问题)。
八、完整流程总结(快速模板)
-
收集至少 5~10 张/人的多光照、多表情照片(LBPH 少量也能用)。
-
用级联/关键点检测裁切并对齐人脸,转为灰度并统一尺寸(如 200×200)。
-
做直方图均衡或其他归一化。
-
按目录加载数据,构建
images, labels
。 -
选择识别器(LBPH/Eigen/Fisher),进行
train()
。 -
使用验证集调参并设定
confidence
阈值。 -
save()
模型与 label 映射。部署时read()
模型,并在实时流中检测 -> 预测 -> 阈值判定 -> 输出结果。
更多推荐
所有评论(0)