欢迎光临
我们一直在努力

opencv计算机视觉--LBPH&EigenFace&FisherFace人脸识别

一、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()

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)

recognizer.train(images, np.array(labels))

功能:训练LBPH模型

  • images:训练图像列表(必须为灰度图)

  • labels:对应的标签数组
    内部过程:

  • 对每张图像提取LBP特征

  • 将图像划分为grid_x × grid_y个单元格

  • 为每个单元格计算LBP直方图

  • 连接所有直方图形成特征向量

  • 存储特征向量和标签的映射关系

3. recognizer.predict(src)

label, confidence = recognizer.predict(predict_image)

功能:识别人脸并返回结果

  • 返回值label:识别出的标签,-1表示无法识别

  • 返回值confidence:置信度分数(越低表示匹配度越高)

置信度说明:

  • 0-50:高度匹配

  • 50-80:一般匹配

  • 80:可能不匹配(具体阈值可调整)

LBPH算法特点

优点:
  • 光照不变性:对光照变化不敏感

  • 计算简单:实时性好,适合嵌入式系统

  • 旋转不变性:可通过扩展实现旋转不变性

  • 内存占用小:特征向量维度相对较低

  • 缺点:
  • 对姿态变化敏感

  • 需要人脸对齐预处理

  • 受遮挡影响较大

  • 实际应用

    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个最大特征值对应的特征脸构成投影矩阵

    特征脸可视化

    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()

    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)

    训练过程:

    recognizer.train(images, np.array(labels))

    内部步骤:

  • 将图像转换为列向量(140×120 → 16800维)

  • 计算平均脸(所有训练图像的平均)

  • 计算协方差矩阵的特征值和特征向量

  • 选择前num_components个特征向量(特征脸)

  • 将训练图像投影到特征脸空间

  • 存储投影系数和标签

  • 3 .recognizer.predict(pre_image)

    label, confidence = recognizer.predict(pre_image)

    识别过程:

  • 将测试图像减去平均脸

  • 投影到特征脸空间得到特征向量

  • 与所有训练样本的特征向量比较(欧氏距离)

  • 返回最近邻的标签和距离

  • 返回值:

    • label:识别结果标签,-1表示无法识别

    • confidence:欧氏距离,值越小表示越相似

    3. EigenFace算法特点

    优点:
  • 降维效果好:将高维人脸数据降至几十维

  • 计算相对简单:数学原理清晰

  • 光照处理:通过减去平均脸,部分消除光照影响

  • 存储效率高:只需存储特征脸和投影系数

  • 缺点:
  • 对光照敏感:仍需较好的光照条件

  • 姿态要求严格:需要严格对齐的人脸

  • 表情影响大:表情变化会影响识别效果

  • 背景干扰:需要准确的人脸裁剪

  • EigenFace是经典的人脸识别算法,虽然现在有更先进的深度学习方法,但其原理简单、计算高效的特点使其在特定场景下仍有应用价值。

    4.实际应用

    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()

    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)

    recognizer.train(images, np.array(labels))

    内部训练步骤:

  • 为每个类别计算均值向量(类中心)

  • 计算总体均值向量

  • 计算类内散度矩阵Sw

  • 计算类间散度矩阵Sb

  • 求解广义特征值问题:Sb·w = λ·Sw·w

  • 选择前num_components个特征向量

  • 将数据投影到FisherFace空间

  • 2.3 recognizer.predict(pre_image)

    label, confidence = recognizer.predict(pre_image)

    识别过程:

  • 将测试图像投影到FisherFace空间

  • 计算与各类别中心的距离

  • 使用最近邻分类器

  • 返回最近邻的标签和距离

  • 3. 实际应用

    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

    • 实际应用:可结合多种方法进行集成

    赞(0)
    未经允许不得转载:171主机测评 » opencv计算机视觉--LBPH&EigenFace&FisherFace人脸识别
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址