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 的计算非常直观,对每个像素点,我们做以下操作:
- 以该像素为中心,取周围 8 个邻居像素(3×3 邻域)
- 把周围像素和中心像素比较:
- 如果周围像素 ≥ 中心像素,标记为 1
- 如果周围像素 < 中心像素,标记为 0
- 从某个位置开始(通常是正上方顺时针),把这 8 个 0/1 值连成一个 8 位二进制数
- 把这个二进制数转换成十进制,就是该像素的 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 特征图还不够------两张相同人脸的图片,人脸位置可能略有不同,直接逐像素比较肯定不准。
怎么办?答案是划分区域 + 统计直方图。
步骤

-
把 LBP 特征图划分成若干个单元格(cell)
- 比如划分为 8×8 = 64 个单元格
- OpenCV 中用
grid_x和grid_y控制水平和垂直方向的单元格数
-
对每个单元格统计 LBP 值的直方图
- 每个单元格统计一个 256 维的直方图(因为 LBP 值是 0~255)
- 直方图统计的是「每种 LBP 模式出现了多少次」
-
把所有单元格的直方图拼接成一个长向量
- 8×8 个单元格 × 256 维 = 16384 维特征向量
- 这个向量就是整张人脸的特征表示
这样做的好处:
- 具有一定的平移不变性:人脸稍微移动一点,每个单元格内的直方图变化不大
- 保留了空间信息:不同位置的单元格是分开的,不是整张图一个直方图,所以能区分眼睛在上面、嘴巴在下面的空间结构
四、人脸识别过程
有了 LBPH 特征向量之后,人脸识别怎么进行呢?
训练阶段
- 收集已知身份的人脸图像(每人若干张)
- 对每张人脸图像计算 LBPH 特征向量
- 把特征向量和对应的身份标签存起来,形成「人脸库」
识别阶段
- 对待识别的人脸图像计算 LBPH 特征向量
- 和人脸库中的每个特征向量计算距离(相似度)
- 找到距离最近的那个人脸,其身份就是识别结果
- 如果最近距离超过了阈值,说明库中没有这个人,返回「无法识别」
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 人脸识别算法的原理和实现。核心要点:
- LBP 算子:比较中心像素和邻域像素,生成二进制编码,描述局部纹理
- LBPH 直方图:将图像划分为单元格,每个单元格统计 LBP 直方图,拼接成特征向量
- 识别原理:计算待识别图像与库中图像的特征距离,取最近邻作为结果
- API 用法 :
LBPHFaceRecognizer_create→train→predict三步走 - 参数调优:阈值、grid 大小、训练样本数量都会影响准确率