03_LBPH人脸识别算法原理与实战

OpenCV 之人脸识别:LBPH 算法原理与完整代码实现


摘要 :本文详细讲解 OpenCV 中 LBPH(局部二值模式直方图)人脸识别算法的原理与完整实现。首先介绍 LBP 特征的计算原理(基本算子与圆形扩展),接着说明如何从 LBP 特征图划分单元格、统计直方图并拼接成特征向量,然后梳理训练与识别两个阶段的核心流程。文章给出可直接运行的环境安装命令与完整 Python 代码,并对 LBPHFaceRecognizer_create、train、predict 等关键 API 逐一详解,最后总结提高准确率的实用技巧与 LBPH 的优缺点,适合人脸识别入门读者参考。

前言

前两篇我们学习了人脸检测 ------也就是在图像中找到人脸的位置。从本篇开始,我们进入更高级的人脸识别领域:不仅要找到人脸在哪里,还要判断出「这个人是谁」。

OpenCV 提供了三种经典的人脸识别算法:

  • LBPH(Local Binary Patterns Histograms,局部二值模式直方图)
  • EigenFace(特征脸,基于 PCA)
  • FisherFace(基于 LDA)

三种算法各有特点,其中 LBPH 是最容易理解、对光照变化最不敏感的一种,非常适合作为人脸识别的入门算法。

本篇我们就来详细讲解 LBPH 的原理,并给出完整的训练和识别代码。


一、什么是 LBPH

LBPH 全称 Local Binary Patterns Histograms ,中文叫「局部二值模式直方图」。它的核心思想是:用局部纹理特征来描述人脸。

每个人的脸部纹理都是独一无二的------皱纹分布、毛孔排列、皮肤纹理......这些微观特征组合起来,就是一个人的「面部指纹」。LBPH 就是通过提取这些局部纹理信息,再统计成直方图,作为人脸的特征向量。

LBPH 的优势:

  • 对光照变化不敏感:因为它比较的是相邻像素的相对大小,而不是绝对亮度
  • 原理简单,容易理解:没有复杂的数学推导
  • 单张图片也能训练:不需要大量样本

二、LBP 特征计算原理

2.1 基本 LBP 算子

LBP 的计算非常直观,对每个像素点,我们做以下操作:

  1. 以该像素为中心,取周围 8 个邻居像素(3×3 邻域)
  2. 把周围像素和中心像素比较:
    • 如果周围像素 ≥ 中心像素,标记为 1
    • 如果周围像素 < 中心像素,标记为 0
  3. 从某个位置开始(通常是正上方顺时针),把这 8 个 0/1 值连成一个 8 位二进制数
  4. 把这个二进制数转换成十进制,就是该像素的 LBP 值

举个例子:

复制代码
邻域像素(3×3):
  10   20   30
  40   25   60      中心像素 = 25
  70   80   90

逐个比较(≥25 记为 1,<25 记为 0):
  0    0    1
  1    .    1
  1    1    1

从正上方顺时针排列:0 1 1 1 1 1 1 0
二进制 01111110 = 十进制 126
所以中心像素的 LBP 值 = 126

就这样,一张灰度图经过 LBP 运算后,变成了一张「LBP 特征图」,每个像素的值是 0~255 之间的整数,代表了该位置的局部纹理模式。

2.2 圆形 LBP(扩展版)

基本 LBP 用的是 3×3 的正方形邻域,但后来研究者提出了圆形 LBP------在一个圆上均匀采样多个点,这样可以控制半径和采样点数,灵活性更高。

OpenCV 中 LBPH 使用的就是圆形 LBP:

  • radius:圆形邻域的半径
  • neighbors:圆上采样点的数量,通常是 8 个

半径越大,捕捉的纹理范围越大;采样点越多,描述越精细。但计算量也会随之增加。默认参数通常效果就不错。


三、从 LBP 图到 LBPH 直方图

光有 LBP 特征图还不够------两张相同人脸的图片,人脸位置可能略有不同,直接逐像素比较肯定不准。

怎么办?答案是划分区域 + 统计直方图。

步骤

  1. 把 LBP 特征图划分成若干个单元格(cell)

    • 比如划分为 8×8 = 64 个单元格
    • OpenCV 中用 grid_x 和 grid_y 控制水平和垂直方向的单元格数
  2. 对每个单元格统计 LBP 值的直方图

    • 每个单元格统计一个 256 维的直方图(因为 LBP 值是 0~255)
    • 直方图统计的是「每种 LBP 模式出现了多少次」
  3. 把所有单元格的直方图拼接成一个长向量

    • 8×8 个单元格 × 256 维 = 16384 维特征向量
    • 这个向量就是整张人脸的特征表示

这样做的好处:

  • 具有一定的平移不变性:人脸稍微移动一点,每个单元格内的直方图变化不大
  • 保留了空间信息:不同位置的单元格是分开的,不是整张图一个直方图,所以能区分眼睛在上面、嘴巴在下面的空间结构

四、人脸识别过程

有了 LBPH 特征向量之后,人脸识别怎么进行呢?

训练阶段

  1. 收集已知身份的人脸图像(每人若干张)
  2. 对每张人脸图像计算 LBPH 特征向量
  3. 把特征向量和对应的身份标签存起来,形成「人脸库」

识别阶段

  1. 对待识别的人脸图像计算 LBPH 特征向量
  2. 和人脸库中的每个特征向量计算距离(相似度)
  3. 找到距离最近的那个人脸,其身份就是识别结果
  4. 如果最近距离超过了阈值,说明库中没有这个人,返回「无法识别」

LBPH 通常使用 直方图相交距离 或 欧氏距离 来衡量相似度。距离越小,说明两张脸越相似。


五、环境准备

LBPH 人脸识别器在 opencv-contrib-python 包中,所以除了基础的 opencv-python,还需要安装 contrib 包:

bash 复制代码
pip install opencv-python opencv-contrib-python

注意:两个包的版本号最好保持一致,否则可能出现兼容性问题。


六、完整代码实现

下面是 LBPH 人脸识别的完整代码,包含训练和预测两个步骤:

python 复制代码
import cv2
import numpy as np

# ========== 1. 准备训练数据 ==========
# images 列表存放训练用的人脸灰度图
images = []
# 每人 2 张训练图片,标签分别为 0 和 1
images.append(cv2.imread('wyz1.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('wyz2.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('yy1.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('yy2.png', cv2.IMREAD_GRAYSCALE))

# 标签:与 images 一一对应
# 0 代表 wyz,1 代表 yy
labels = [0, 0, 1, 1]

# 标签到姓名的映射字典,-1 表示无法识别
dic = {0: 'wyz', 1: 'yy', -1: '无法识别'}

# ========== 2. 读取待识别的图像 ==========
predict_image = cv2.imread('wyz.png', cv2.IMREAD_GRAYSCALE)

# ========== 3. 创建 LBPH 人脸识别器 ==========
# cv2.face.LBPHFaceRecognizer_create(radius, neighbors, grid_x, grid_y, threshold)
# 参数说明:
#   radius    - 圆形LBP的半径,默认 1
#   neighbors - 采样点数量,默认 8
#   grid_x    - 水平方向单元格数,默认 8
#   grid_y    - 垂直方向单元格数,默认 8
#   threshold - 识别阈值,距离超过阈值则返回 -1(无法识别)
recognizer = cv2.face.LBPHFaceRecognizer_create(threshold=80)

# ========== 4. 训练识别器 ==========
# recognizer.train(训练图像列表, 标签数组)
# 注意:标签需要转换为 numpy 数组
recognizer.train(images, np.array(labels))

# ========== 5. 进行预测 ==========
# recognizer.predict(待识别图像) 返回 (label, confidence)
#   label      - 预测的标签,-1 表示无法识别
#   confidence - 置信度(距离),越小表示越相似,通常 80 以下认为可靠
label, confidence = recognizer.predict(predict_image)

# ========== 6. 输出结果 ==========
print('这人是:', dic[label])
print('置信度:', confidence)

七、API 详解

7.1 创建识别器

python 复制代码
recognizer = cv2.face.LBPHFaceRecognizer_create(
    radius=None,
    neighbors=None,
    grid_x=None,
    grid_y=None,
    threshold=None
)
参数 默认值 含义
radius 1 圆形 LBP 的半径,值越大捕捉的纹理范围越广
neighbors 8 圆形邻域的采样点数,值越大特征越丰富
grid_x 8 水平方向划分的单元格数
grid_y 8 垂直方向划分的单元格数
threshold DBL_MAX 识别阈值,距离超过此值返回 -1

7.2 训练模型

python 复制代码
recognizer.train(src, labels)
参数 类型 含义
src list 训练图像列表,每张都是灰度图,尺寸可以不同
labels np.array 对应的标签数组,整数类型

注意:和 EigenFace / FisherFace 不同,LBPH 不要求训练图像尺寸一致,因为它统计的是直方图,和图像绝对大小无关。这是 LBPH 的一大优势。

7.3 预测识别

python 复制代码
label, confidence = recognizer.predict(src)
返回值 含义
label 预测的标签(整数),-1 表示无法识别
confidence 置信度评分(距离),越小越相似

关于置信度的说明:

  • LBPH 的置信度本质上是「距离」,值越小表示越相似
  • 经验值:低于 80 通常认为是比较可靠的匹配
  • 高于 80 说明差异较大,可能不是同一个人
  • 如果设置了 threshold 参数,超过阈值会直接返回标签 -1

八、运行与测试

准备数据

你需要准备以下图片(和代码放在同一目录下):

复制代码
wyz1.png  wyz2.png    # 第一个人的 2 张训练图
yy1.png   yy2.png     # 第二个人的 2 张训练图
wyz.png               # 待测试的图片(可以是 wyz 的新照片)

训练图片建议:

  • 都是正面人脸,光线均匀
  • 大小适中,人脸占主要部分
  • 每人至少 2 张,越多识别越准确

运行结果

运行代码后,控制台输出:

复制代码
这人是: wyz
置信度: 71.13878086125617

识别结果图:

置信度 71.14 在 80 以下,说明识别结果比较可靠,模型正确地识别出了测试图片中的人脸身份。


九、提高准确率的技巧

1. 增加训练样本

每人的训练图片越多,识别越准确。建议每人至少 3~5 张,涵盖不同表情、不同角度。

2. 人脸对齐

训练前先用 Haar 级联分类器检测人脸,裁剪出人脸区域再训练。这样可以排除背景干扰,让特征更集中。

3. 直方图均衡化

对训练和测试图片都做直方图均衡化,减少光照差异的影响:

python 复制代码
gray = cv2.equalizeHist(gray)

4. 调整阈值

根据实际场景调整 threshold:

  • 安全要求高的场景(如门禁),阈值设小一点,宁可漏识别也不要误识别
  • 一般场景,阈值 80 左右比较合适

5. 调整 grid 参数

grid_x 和 grid_y 越大,空间分辨率越高,但特征维度也越高,可能过拟合。默认 8×8 通常够用。


十、LBPH 的优缺点

优点 缺点
原理简单,容易理解 识别准确率一般,不如深度学习方法
对光照变化不敏感 对姿态变化(侧脸)较敏感
单张图片也能训练 特征维度较高,识别速度随样本数线性下降
不要求图像尺寸一致 对表情变化、遮挡的鲁棒性一般
训练速度快

小结

本篇详细讲解了 LBPH 人脸识别算法的原理和实现。核心要点:

  1. LBP 算子:比较中心像素和邻域像素,生成二进制编码,描述局部纹理
  2. LBPH 直方图:将图像划分为单元格,每个单元格统计 LBP 直方图,拼接成特征向量
  3. 识别原理:计算待识别图像与库中图像的特征距离,取最近邻作为结果
  4. API 用法 :LBPHFaceRecognizer_create → train → predict 三步走
  5. 参数调优:阈值、grid 大小、训练样本数量都会影响准确率

相关推荐
夏天拐跑了西瓜1 小时前
一文入门LangChain:从框架认知到构建你的第一个AI Agent
python·langchain·conda·agent
魔猴疯猿2 小时前
从0到1用Python开发第一个智能体
人工智能·python·深度学习·神经网络·机器学习
xiaoqi01953 小时前
机器学习因子分析实战:从量化特征到可视化决策
人工智能·python·机器学习
Escalating_xu3 小时前
【Python】基础语法(1):常量、变量、类型、输入输出与运算符
开发语言·python
happylifetree4 小时前
Python14:核心语法-数据存储与运算-字符串定义
python
hhb_6184 小时前
AIRAGDebug:一键定位RAG链路异常
人工智能·python·算法
小静AI工程实验室4 小时前
asyncio.Queue 有界队列实战:背压、task_done、join 与异常收尾
python
小凡geo4 小时前
本地商家 GEO:用脚本一键生成 FAQPage 结构化数据,让 AI 切片更稳
开发语言·人工智能·python·microsoft·搜索引擎·ai
Zhou1411364 小时前
SpringBoot_03_Web开发
前端·spring boot·python
weixin199701080164 小时前
《1688物流API接口边界:logistics.trace.get 与 freight.template.list 的隐藏约束》(附Python源码)
windows·python·list