一、前言
人脸识别是计算机视觉领域的经典应用,在考勤、门禁、身份验证等场景中被广泛使用。OpenCV 作为计算机视觉领域的开源利器,提供了完整的人脸识别解决方案,从人脸检测 定位人脸区域,到三种经典人脸识别算法(LBPH、EigenFaces、FisherFaces)实现身份识别。
二、核心原理:人脸检测 vs 人脸识别
很多新手会混淆这两个概念,先明确核心区别:
- 人脸检测 :判断图像中是否有人脸,并定位人脸的位置(框出人脸区域),是人脸识别的前置步骤。
- 人脸识别 :在检测到人脸后,判断这张脸是谁,属于身份识别任务。
2.1 人脸检测:Haar 级联分类器原理
OpenCV 中最经典的人脸检测方案是Haar 级联分类器,核心原理如下:
- Haar 特征提取:通过边缘、线、中心环绕等矩形特征,计算白色区域与黑色区域的像素差,反映图像的灰度变化(比如人脸的眼睛区域比脸颊暗,符合 Haar 特征的灰度差特性)。
- 级联分类器结构:将多个简单弱分类器按顺序级联,前序分类器快速过滤大量负样本(非人脸),仅通过所有分类器的区域才判定为人脸,大幅提升检测效率。
- OpenCV 实现:OpenCV 提供了预训练好的 Haar 级联分类器 XML 文件,直接加载即可实现人脸检测,无需从零训练。
2.2 人脸识别:OpenCV 三大经典算法
OpenCV 提供了三种成熟的人脸识别算法,核心原理对比如下:
|-----------------------|-----------------------------------|--------------------------|-------------------|
| 算法 | 核心原理 | 核心优势 | 适用场景 |
| LBPH(局部二值模式直方图) | 基于 LBP 纹理特征,分区域统计直方图,对比直方图相似度识别 | 抗光照、缩放、旋转、平移,对图像尺寸无强制要求 | 通用场景,新手首选,实战效果最稳定 |
| EigenFaces(特征脸) | 基于 PCA 主成分分析,将人脸图像降维为特征向量,对比特征相似度 | 计算简单,适合小样本训练 | 光照均匀、姿态固定的场景 |
| FisherFaces(Fisher 脸) | 基于 LDA 线性判别分析,最大化类间差异、最小化类内差异 | 分类效果优于 EigenFaces,抗干扰性更强 | 多类别人脸识别,对精度要求高的场景 |
三、项目实战:三种算法完整实现
3.1 算法 1:LBPH 人脸识别
LBPH 是三种算法中最稳定、最适合实战的方案,核心步骤:
- 加载训练集,统一灰度读取
- 创建 LBPH 识别器,设置阈值(默认 80,置信度 < 80 判定为匹配)
- 训练模型,对测试集进行预测
- 输出识别结果与置信度
完整代码
python
import cv2
import numpy as np
# ===================== 1. 加载训练样本 =====================
images = []
# 灰度读取训练人脸图片
images.append(cv2.imread('hg01.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('hg02.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('pyy01.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('pyy02.jpg', cv2.IMREAD_GRAYSCALE))
# 标签:0=胡歌,1=彭于晏
labels = [0, 0, 1, 1]
# 结果映射字典
name_dict = {0: '胡歌', 1: '彭于晏', -1: '无法识别'}
# ===================== 2. 加载待识别图片 =====================
predict_image = cv2.imread('hg.jpg', cv2.IMREAD_GRAYSCALE)
# ===================== 3. 创建LBPH识别器并训练 =====================
# threshold=80:置信度阈值,>80判定为无法识别
recognizer = cv2.face.LBPHFaceRecognizer_create(threshold=80)
# 训练模型
recognizer.train(images, np.array(labels))
# ===================== 4. 预测识别 =====================
label, confidence = recognizer.predict(predict_image)
# ===================== 5. 输出结果 =====================
print('识别结果:', name_dict[label])
print('置信度(越小越匹配):', round(confidence, 2))
# ===================== 6. 图片标注(可选) =====================
result_img = cv2.imread('hg.jpg').copy()
cv2.putText(
result_img,
f"{name_dict[label]} (置信度: {round(confidence, 1)})",
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.9,
(0, 0, 255),
2
)
cv2.imshow('LBPH人脸识别结果', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码说明
- LBPH 算法无需统一训练集图片尺寸,这是它相比另外两种算法的最大优势
- 阈值
threshold=80为经验值,可根据实际效果微调:阈值越低,识别越严格,越容易出现 "无法识别";阈值越高,越容易误识别 - 置信度
confidence越小,代表匹配度越高,LBPH 中通常 < 80 为有效识别
3.2 算法 2:EigenFaces 人脸识别
EigenFaces 基于 PCA 降维,核心要求训练集与测试集图片尺寸必须完全一致,否则会报错。
完整代码
python
import cv2
import numpy as np
# ===================== 1. 加载训练样本(必须统一尺寸) =====================
images = []
# 统一缩放到120x180尺寸
a = cv2.imread('hg01.jpg', 0)
a = cv2.resize(a, (120, 180))
b = cv2.imread('hg02.jpg', 0)
b = cv2.resize(b, (120, 180))
c = cv2.imread('pyy01.jpg', 0)
c = cv2.resize(c, (120, 180))
d = cv2.imread('pyy02.jpg', 0)
d = cv2.resize(d, (120, 180))
images.append(a)
images.append(b)
images.append(c)
images.append(d)
# 标签:0=胡歌,1=彭于晏
labels = [0, 0, 1, 1]
name_dict = {0: '胡歌', 1: '彭于晏', -1: '无法识别'}
# ===================== 2. 加载待识别图片(必须和训练集尺寸一致) =====================
pre_image = cv2.imread('hg.jpg', 0)
pre_image = cv2.resize(pre_image, (120, 180))
# ===================== 3. 创建EigenFaces识别器并训练 =====================
# threshold=5000:置信度阈值,<5000判定为匹配
recognizer = cv2.face.EigenFaceRecognizer_create(threshold=5000)
recognizer.train(images, np.array(labels))
# ===================== 4. 预测识别 =====================
label, confidence = recognizer.predict(pre_image)
# ===================== 5. 输出结果 =====================
print('识别结果:', name_dict[label])
print('置信度(越小越匹配):', round(confidence, 2))
# ===================== 6. 图片标注 =====================
result_img = cv2.imread('hg.jpg').copy()
cv2.putText(
result_img,
name_dict[label],
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.9,
(0, 0, 255),
2
)
cv2.imshow('EigenFaces人脸识别结果', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码说明
- EigenFaces 对光照、姿态敏感,仅适合训练集与测试集环境一致的场景
- 必须严格统一所有图片尺寸,否则
train()方法会直接报错 - 阈值
threshold=5000为经验值,EigenFaces 置信度范围通常为 0~20000,<5000 为有效识别
3.3 算法 3:FisherFaces 人脸识别
FisherFaces 基于 LDA 线性判别分析,分类效果优于 EigenFaces,同时解决了 PCA 丢失关键分类信息的问题。本版本新增中文标注功能 ,解决 OpenCVputText无法显示中文的问题。
完整代码
python
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# ===================== 工具函数:OpenCV添加中文 =====================
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
"""
向OpenCV图像中添加中文文本
:param img: 输入图像
:param text: 要添加的中文文本
:param position: 文本位置 (x, y)
:param textColor: 文本颜色 (B, G, R)
:param textSize: 文本大小
:return: 添加中文后的图像
"""
if isinstance(img, np.ndarray):
# OpenCV格式转PIL格式
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
# 加载宋体字体(需确保simsun.ttc文件在当前目录)
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
# 绘制文本
draw.text(position, text, textColor, font=fontStyle)
# PIL格式转回OpenCV格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# ===================== 工具函数:统一尺寸加载图像 =====================
def load_resize_image(image_path, size=(120, 180)):
img = cv2.imread(image_path, flags=0)
return cv2.resize(img, dsize=size)
# ===================== 1. 加载训练样本 =====================
images = []
images.append(load_resize_image('hg01.jpg'))
images.append(load_resize_image('hg02.jpg'))
images.append(load_resize_image('pyy01.jpg'))
images.append(load_resize_image('pyy02.jpg'))
# 标签:0=胡歌,1=彭于晏
labels = [0, 0, 1, 1]
name_dict = {0: '胡歌', 1: '彭于晏', -1: '无法识别'}
# ===================== 2. 加载待识别图片 =====================
pre_image = load_resize_image('hg.jpg')
# ===================== 3. 创建FisherFaces识别器并训练 =====================
recognizer = cv2.face.FisherFaceRecognizer_create(threshold=5000)
recognizer.train(images, np.array(labels))
# ===================== 4. 预测识别 =====================
label, confidence = recognizer.predict(pre_image)
# ===================== 5. 输出结果 =====================
print('识别结果:', name_dict[label])
print('置信度(越小越匹配):', round(confidence, 2))
# ===================== 6. 中文标注并显示 =====================
# 读取原图,添加中文标注
original_img = cv2.imread('hg.jpg').copy()
result_img = cv2AddChineseText(
original_img,
f"{name_dict[label]} (置信度: {round(confidence, 1)})",
position=(30, 10),
textColor=(255, 0, 0), # 红色
textSize=30
)
cv2.imshow('FisherFaces人脸识别结果(中文标注)', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码说明
- FisherFaces 同样要求统一图片尺寸,分类精度高于 EigenFaces,适合多类别识别
- 中文标注函数通过 PIL 实现,解决了 OpenCV 原生不支持中文的痛点,需提前准备
simsun.ttc字体文件 - 封装了图像加载函数,简化代码结构,提升可维护性
四、三种算法对比与选型建议
4.1 核心差异对比
|------|----------------|---------------|------------------|
| 特性 | LBPH | EigenFaces | FisherFaces |
| 核心原理 | LBP 纹理特征 + 直方图 | PCA 主成分分析 | LDA 线性判别分析 |
| 尺寸要求 | 无需统一 | 必须统一 | 必须统一 |
| 抗干扰性 | 强(抗光照、旋转、缩放) | 弱(对光照 / 姿态敏感) | 中(优于 EigenFaces) |
| 训练速度 | 快 | 快 | 中等 |
| 识别精度 | 高 | 低 | 高 |
| 适用场景 | 通用场景 | 实验室固定环境 | 多类别高精度识别 |