一、LBPH(Local Binary Patterns Histograms)原理
1. LBP(局部二值模式)基础
LBP是一种用于图像纹理分析的算子,其基本思想是:
-
以每个像素为中心,比较其与周围像素的灰度值
-
将比较结果编码为二进制数,再转换为十进制数
-
公式:LBP(x_c, y_c) = \\sum_{p=0}\^{P-1} s(g_p - g_c) \\times 2\^p
其中 s(x) = \\begin{cases} 1 \& \\text{if } x \\geq 0 \\ 0 \& \\text{else} \\end{cases}
2. LBPH人脸识别流程
原始图像 → 提取LBP特征 → 分块直方图 → 连接直方图 → 形成特征向量 → 分类识别
代码中使用的重要函数详解
1. cv2.face.LBPHFaceRecognizer_create()
python
recognizer = cv2.face.LBPHFaceRecognizer_create(
radius=1, # LBP半径,默认1
neighbors=8, # 采样点数量,默认8
grid_x=8, # 水平方向网格数
grid_y=8, # 垂直方向网格数
threshold=80 # 识别阈值
)
参数说明:
-
radius:半径值,表示从中心像素到周围像素的距离
-
neighbors:采样点数量,通常为8、12、16等
-
grid_x/grid_y:将图像划分的网格数,用于提取局部直方图
-
threshold:置信度阈值,高于此值认为无法识别
2. recognizer.train(images, labels)
python
recognizer.train(images, np.array(labels))
功能:训练LBPH模型
-
images:训练图像列表(必须为灰度图)
-
labels :对应的标签数组
内部过程:-
对每张图像提取LBP特征
-
将图像划分为grid_x × grid_y个单元格
-
为每个单元格计算LBP直方图
-
连接所有直方图形成特征向量
-
存储特征向量和标签的映射关系
-
3. recognizer.predict(src)
python
label, confidence = recognizer.predict(predict_image)
功能:识别人脸并返回结果
-
返回值label:识别出的标签,-1表示无法识别
-
返回值confidence:置信度分数(越低表示匹配度越高)
置信度说明:
-
0-50:高度匹配
-
50-80:一般匹配
-
80:可能不匹配(具体阈值可调整)
LBPH算法特点
优点:
-
光照不变性:对光照变化不敏感
-
计算简单:实时性好,适合嵌入式系统
-
旋转不变性:可通过扩展实现旋转不变性
-
内存占用小:特征向量维度相对较低
缺点:
-
对姿态变化敏感
-
需要人脸对齐预处理
-
受遮挡影响较大
实际应用
python
import cv2
import numpy as np
# 提前训练的人脸照片
images = []
images.append(cv2.imread('qingzi/qz1.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('qingzi/qz2.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('qingzi/qz3.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('qingzi/qz4.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('susu/ss1.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('susu/ss2.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('susu/ss3.jpg', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('susu/ss4.jpg', cv2.IMREAD_GRAYSCALE))
labels = [0, 0, 0, 0, 1, 1, 1, 1]
dic = {0: 'qz', 1: 'ss', -1: '无法识别'}
predict_image = cv2.imread('qingzi/qz5.jpg', cv2.IMREAD_GRAYSCALE) # 待识别人脸
# predict_image = cv2.imread('susu/ss5.jpg', cv2.IMREAD_GRAYSCALE) # 待识别人脸
# 创建一个LBPH的人脸特征识别器
# 函数说明:
# radius: 可选参数,圆度与半径的平方根,建议使用默认值
# neighbors: 可选参数,圆度与半径的平方根,建议使用默认值
# grid_x: 可选参数,水平方向上的像素数,默认值为8,即将LBP特征图在水平方向上划分为8个单元
# grid_y: 可选参数,垂直方向上的像素数,默认值为8,建议使用默认值
# threshold: 可选参数,人脸识别时使用的阈值,建议使用默认值
recognizer = cv2.face.LBPHFaceRecognizer_create(threshold=80) # 修正:facce应为face
# 函数train用给定的数据和相关标签训练生成的实例模型
# 语法格式:None = 识别对象.train(src, labels)
# 参数说明:
# src: 训练图像,用来学习的人脸图像
# labels: 标签,人脸图像对应的标签
recognizer.train(images, np.array(labels))
# 函数predict()对一个待识别的人脸图像进行判断,寻找与当前图像距离最近的人脸图像
# 参数与返回值的含义如下:
# src: 需要识别的原始图像
# label: 返回的识别结果标签,返回-1表示无法识别当前人脸
# confidence: 返回的置信度评分,用来衡量识别结果与原有模型之间的差异
# 评价标准是不同距离,但若是高于80,则认为识别结果与原有模型差距大
label, confidence = recognizer.predict(predict_image)
print('这人是:', dic[label]) # 修正:应为dic[label]而不是字符串
print('置信度:', confidence) # 修正:应为confidence变量而不是字符串

二、EigenFace
1.EigenFace(特征脸)原理
核心思想
EigenFace是基于**主成分分析(PCA)**的人脸识别方法,核心思想是将人脸图像从高维像素空间转换到低维特征空间。
数学原理
1. 数据准备:将所有训练人脸图像拉直为列向量
X = [x₁, x₂, ..., xₙ] ∈ ℝ^(m×n)
2. 计算平均脸:
ψ = (1/n) Σ xᵢ
3. 计算差值矩阵:
A = [x₁-ψ, x₂-ψ, ..., xₙ-ψ]
4. 计算协方差矩阵:
C = A·Aᵀ ∈ ℝ^(m×m) # 维度极大,直接计算困难
5. 使用SVD求解特征向量(特征脸):
Aᵀ·A·vᵢ = λᵢ·vᵢ # 先计算小矩阵的特征向量
uᵢ = A·vᵢ # 再得到特征脸
6. 选取前k个最大特征值对应的特征脸构成投影矩阵
特征脸可视化
python
import matplotlib.pyplot as plt
# 获取特征脸
mean_face = recognizer.getMean() # 平均脸
eigenfaces = recognizer.getEigenVectors() # 特征脸
# 显示平均脸
plt.subplot(1, 5, 1)
plt.title("Mean Face")
plt.imshow(mean_face.reshape(140, 120), cmap='gray')
# 显示前4个特征脸
for i in range(4):
plt.subplot(1, 5, i+2)
plt.title(f"Eigenface {i+1}")
plt.imshow(eigenfaces[:, i].reshape(140, 120), cmap='gray')
plt.show()
2、 代码中使用的函数详解
1 .cv2.face.EigenFaceRecognizer_create()
python
recognizer = cv2.face.EigenFaceRecognizer_create(
num_components=0, # PCA保留的主成分数量
threshold=7000 # 识别阈值
)
参数说明:
-
num_components:
-
0或None:保留所有特征向量(默认)
-
N:保留前N个最大的特征向量
-
建议值:通常取80-150,保留总方差的85%-95%
-
-
threshold:
-
识别阈值,高于此值认为无法识别
-
EigenFace的confidence范围:0~20000
-
<5000:可靠匹配
-
5000-7000:可能匹配
-
7000:可能不匹配
-
2.recognizer.train(images, labels)
训练过程:
python
recognizer.train(images, np.array(labels))
内部步骤:
-
将图像转换为列向量(140×120 → 16800维)
-
计算平均脸(所有训练图像的平均)
-
计算协方差矩阵的特征值和特征向量
-
选择前num_components个特征向量(特征脸)
-
将训练图像投影到特征脸空间
-
存储投影系数和标签
3 .recognizer.predict(pre_image)
python
label, confidence = recognizer.predict(pre_image)
识别过程:
-
将测试图像减去平均脸
-
投影到特征脸空间得到特征向量
-
与所有训练样本的特征向量比较(欧氏距离)
-
返回最近邻的标签和距离
返回值:
-
label:识别结果标签,-1表示无法识别
-
confidence:欧氏距离,值越小表示越相似
3. EigenFace算法特点
优点:
-
降维效果好:将高维人脸数据降至几十维
-
计算相对简单:数学原理清晰
-
光照处理:通过减去平均脸,部分消除光照影响
-
存储效率高:只需存储特征脸和投影系数
缺点:
-
对光照敏感:仍需较好的光照条件
-
姿态要求严格:需要严格对齐的人脸
-
表情影响大:表情变化会影响识别效果
-
背景干扰:需要准确的人脸裁剪
EigenFace是经典的人脸识别算法,虽然现在有更先进的深度学习方法,但其原理简单、计算高效的特点使其在特定场景下仍有应用价值。
4.实际应用
python
import cv2
import numpy as np
images = [] # 读取训练图像,注意:图片大小需要一致
# 读取并调整图像大小
a = cv2.imread('../qingzi/qz1.jpg', 0) # flags=0 表示以灰度模式读取
a = cv2.resize(a, (120, 140))
b = cv2.imread('../qingzi/qz2.jpg', 0)
b = cv2.resize(b, (120, 140))
c = cv2.imread('../qingzi/qz3.jpg', 0)
c = cv2.resize(c, (120, 140))
d = cv2.imread('../susu/ss1.jpg', 0)
d = cv2.resize(d, (120, 140))
e = cv2.imread('../susu/ss2.jpg', 0)
e = cv2.resize(e, (120, 140))
f = cv2.imread('../susu/ss3.jpg', 0)
f = cv2.resize(f, (120, 140))
images.append(a)
images.append(b)
images.append(c)
images.append(d)
images.append(e)
images.append(f)
labels = [0, 0, 0, 1, 1, 1]
# 读取并调整待预测图像大小
# pre_image = cv2.imread('susu/ss4.jpg', 0)
pre_image = cv2.imread('../qingzi/qz4.jpg', 0)
pre_image = cv2.resize(pre_image, (120, 140))
# 创建Eigenfaces人脸识别器
# 作用: 创建一个EigenFace的人脸特征识别器
# num_components: 在PCA中要保留的分量个数。该参数值通常要根据输入数据来具体确定
# 一般程序中,取80即可(降维成多少个特征)
# threshold: 进行人脸识别所采用的阈值
recognizer = cv2.face.EigenFaceRecognizer_create(threshold=7000) # threshold=5000或4000
# 使用训练数据(images和labels)来训练识别器
# 函数FaceRecognizer.train()用给定的数据和相关标签训练生成的实例模型
# 参数的含义如下:
# src: 训练图像,用来学习的人脸图像
# labels: 标签,人脸图像对应的标签
recognizer.train(images, np.array(labels))
# 对预测图像(pre_image)进行人脸识别预测
# confidence: 大小介于0到20000,只要低于5000都被认为是可靠的结果
label, confidence = recognizer.predict(pre_image)
dic = {0: 'qz', 1: 'susu', -1: '无法识别'}
print('这是:', dic[label])
print('置信度为:', confidence)
# 在原图上标注识别结果并显示
# original_img = cv2.imread('susu/ss4.jpg').copy()
original_img = cv2.imread('../qingzi/qz4.jpg').copy()
original_img=cv2.resize(original_img,dsize=None,fx=0.5,fy=0.5)
# 在图像上添加文本
result_text = dic[label]
cv2.putText(original_img, result_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
cv2.imshow('susu', original_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、FisherFace
1. FisherFace(费舍尔脸)原理
核心思想
FisherFace是基于线性判别分析(LDA) 的人脸识别方法,与EigenFace的PCA不同,LDA是有监督的降维方法,旨在最大化类间距离同时最小化类内距离。
数学原理(Fisher线性判别)
目标:找到最佳投影方向w,使得投影后的数据:
1. 类间散度(between-class scatter)最大
2. 类内散度(within-class scatter)最小
定义:
- 类内散度矩阵:Sw = Σ Σ (x - μᵢ)(x - μᵢ)ᵀ
- 类间散度矩阵:Sb = Σ nᵢ(μᵢ - μ)(μᵢ - μ)ᵀ
- 总体散度矩阵:St = Sw + Sb
优化目标:J(w) = (wᵀSb w) / (wᵀSw w) → 最大化
求解:通过广义特征值分解 Sb·w = λ·Sw·w
取前(C-1)个最大特征值对应的特征向量(C为类别数)
FisherFace vs EigenFace
| 特征 | EigenFace (PCA) | FisherFace (LDA) |
|---|---|---|
| 监督性 | 无监督 | 有监督 |
| 目标 | 最大方差方向 | 最大类间区分度 |
| 特征脸数量 | 可任意选择 | 最多C-1个(C为类别数) |
| 适用场景 | 数据降维、可视化 | 分类任务 |
| 小样本问题 | 相对较好 | 容易过拟合 |
2. 代码中使用的函数详解
2.1 cv2.face.FisherFaceRecognizer_create()
python
recognizer = cv2.face.FisherFaceRecognizer_create(
num_components=0, # LDA保留的特征数量
threshold=2600 # 识别阈值
)
参数说明:
-
num_components:
-
0或None:保留C-1个特征向量(C为类别数)
-
N:保留前N个特征向量(N ≤ C-1)
-
例如:2个人 → 最多1个特征向量
-
例如:10个人 → 最多9个特征向量
-
-
threshold:
-
识别阈值,高于此值认为无法识别
-
FisherFace的confidence范围:0~10000+
-
<2000:高度可靠
-
2000-2600:一般可靠
-
2600:可能不可靠
-
2.2 recognizer.train(images, labels)
python
recognizer.train(images, np.array(labels))
内部训练步骤:
-
为每个类别计算均值向量(类中心)
-
计算总体均值向量
-
计算类内散度矩阵Sw
-
计算类间散度矩阵Sb
-
求解广义特征值问题:Sb·w = λ·Sw·w
-
选择前num_components个特征向量
-
将数据投影到FisherFace空间
2.3 recognizer.predict(pre_image)
python
label, confidence = recognizer.predict(pre_image)
识别过程:
-
将测试图像投影到FisherFace空间
-
计算与各类别中心的距离
-
使用最近邻分类器
-
返回最近邻的标签和距离
3. 实际应用
python
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# 1个用法
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
"""向图片中添加中文"""
if isinstance(img, np.ndarray): # 判断是否OpenCV图片类型
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # 实现array到image的转换
draw = ImageDraw.Draw(img) # 在img图片上创建一个绘图的对象
# 字体的格式
fontStyle = ImageFont.truetype('simsun.ttc', textSize, encoding="utf-8")
draw.text(position, text, textColor, font=fontStyle) # 绘制文本
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR) # 转换回OpenCV格式
# 4个用法
def image_re(image):
a = cv2.imread(image, 0)
a = cv2.resize(a, (120, 180))
images.append(a)
images = []
image_re('../qingzi/qz1.jpg')
image_re('../qingzi/qz2.jpg')
image_re('../qingzi/qz3.jpg')
image_re('../susu/ss1.jpg')
image_re('../susu/ss2.jpg')
image_re('../susu/ss3.jpg')
Labels = [0, 0, 0, 1, 1, 1]
pre_image = cv2.imread('../susu/ss4.jpg', 0) # 读取待识别图像
# pre_image = cv2.imread('qingzi/qz5.jpg', 0) # 读取待识别图像
pre_image = cv2.resize(pre_image, (120, 180))
# 创建一个FisherFace的人脸特征识别器
recognizer = cv2.face.FisherFaceRecognizer_create(threshold=2600)
# 训练模型
recognizer.train(images, np.array(Labels))
# 预测
label, confidence = recognizer.predict(pre_image)
dic = {0: '晴子', 1: '苏苏', -1: '无法识别'}
print('这人是:', dic[label])
print('置信度为:', confidence)
# 在图片上添加中文标签
image = cv2AddChineseText(cv2.imread('../susu/ss4.jpg').copy(), dic[label], (30, 10), textColor=(255, 0, 0))
# image = cv2AddChineseText(cv2.imread('qingzi/qz5.jpg').copy(), dic[label], (30, 10), textColor=(255, 0, 0))
image=cv2.resize(image,dsize=None,fx=0.6,fy=0.6)
cv2.imshow('susu', image)
# cv2.imshow('qingzi', image)
cv2.waitKey(0)

4. FisherFace的适用场景和限制
适用场景:
-
类别数较少(通常<50)
-
每个类别样本充足
-
需要强判别性的分类任务
-
数据维度高但样本相对有限
限制和解决方案:
-
小样本问题(SSS问题):
-
问题:当样本数小于特征维度时,Sw矩阵奇异
-
解决:PCA + LDA(两步法),或使用正则化
-
-
最多C-1个特征:
python
# 两步法:先PCA降维,再LDA # 1. 使用PCA降维到适当维度 # 2. 再使用FisherFace进行分类 -
类别不平衡:
python
# 对少数类样本进行数据增强 # 或使用加权LDA
5. 三种人脸识别算法对比
| 算法 | 原理 | 特征数 | 监督性 | 优点 | 缺点 |
|---|---|---|---|---|---|
| EigenFace | PCA | 任意 | 无监督 | 计算简单,降维效果好 | 判别性不强 |
| FisherFace | LDA | ≤C-1 | 有监督 | 判别性强,分类效果好 | 小样本问题 |
| LBPH | 局部二值模式 | 固定 | 有监督 | 光照鲁棒性好 | 对姿态敏感 |
推荐选择策略:
-
样本充足且需要强判别性:FisherFace
-
样本较少或需要降维:EigenFace
-
光照变化大:LBPH
-
实际应用:可结合多种方法进行集成