目录
一、前置知识:环境搭建与核心概念
1.1 环境配置(关键版本)
1.2 人脸识别核心流程
二、算法原理与代码实战
2.1 LBPH 算法:抗光照 / 旋转,最实用的传统算法
2.2 EigenFaces 算法:基于 PCA 降维,适合小数据集
2.3 FisherFaces 算法:基于 LDA,类间区分度更高
三、三种算法对比与选型建议
四、常见问题与解决方案
五、扩展:从单张预测到实时摄像头识别
在计算机视觉领域,人脸识别是最经典且实用的技术之一。OpenCV 提供了 LBPH、EigenFaces、FisherFaces 三种成熟的人脸识别算法,无需复杂的深度学习框架,仅用传统机器学习就能实现高效人脸匹配。
一、前置知识:环境搭建与核心概念
在开始代码实战前,需先完成环境配置,并理解人脸识别的基本流程。
1.1 环境配置(关键版本)
OpenCV 的 face
模块在高版本中可能存在兼容性问题,推荐使用以下稳定版本:
# 安装核心库
pip install opencv-python==3.4.18.65
# 安装包含face模块的扩展库
pip install opencv-contrib-python==3.4.18.65
# 安装图像处理辅助库(用于中文显示)
pip install pillow numpy
1.2 人脸识别核心流程
所有算法的核心逻辑一致,分为 4 步:
- 数据准备:收集同一人多张人脸图像(用于训练)和待识别人脸图像;
- 模型训练:用训练集图像和对应标签(如 “0 - 李云龙”“1 - 赵刚”)训练识别器;
- 预测匹配:将待识别人脸输入模型,计算匹配置信度;
- 结果输出:根据置信度判断是否识别成功,输出对应人物名称。
二、算法原理与代码实战
下面分别讲解 LBPH、EigenFaces、FisherFaces 三种算法的原理、完整代码及关键参数说明,所有代码均可直接运行(需替换自己的人脸图像路径)。
2.1 LBPH 算法:抗光照 / 旋转,最实用的传统算法
原理核心
LBPH(局部二值模式直方图)通过 “像素对比编码 + 分区域统计” 实现鲁棒识别:
- 像素编码:以每个像素为中心,对比周围 8 个像素灰度值,大于中心记 “1”,否则记 “0”,生成 8 位二进制码(如
00110101
); - 分块统计:将编码后的图像分成 8×8 网格,统计每个网格的直方图,最终拼接成全局特征;
- 匹配逻辑:通过对比两张人脸的特征直方图差异(置信度)判断是否为同一人,置信度<80 为可靠匹配。
完整代码(带详细注释)
import cv2
import numpy as np
# -------------------------- 1. 准备训练数据 --------------------------
# 存储训练图像(灰度图,减少计算量)
train_images = []
# 读取同一人多张图像(替换为你的图像路径)
train_images.append(cv2.imread('lyl1.png', cv2.IMREAD_GRAYSCALE)) # 李云龙1
train_images.append(cv2.imread('lyl2.png', cv2.IMREAD_GRAYSCALE)) # 李云龙2
train_images.append(cv2.imread('zg1.png', cv2.IMREAD_GRAYSCALE)) # 赵刚1
train_images.append(cv2.imread('zg2.png', cv2.IMREAD_GRAYSCALE)) # 赵刚2
# 训练标签:与图像顺序对应(0=李云龙,1=赵刚)
train_labels = [0, 0, 1, 1]
# 标签映射字典:将数字标签转为人名,-1表示无法识别
label_dict = {0: '李云龙', 1: '赵刚', -1: '无法识别'}
# 待识别人脸(同样转为灰度图)
predict_image = cv2.imread('lyl_test.png', cv2.IMREAD_GRAYSCALE)
# -------------------------- 2. 创建并训练LBPH识别器 --------------------------
# threshold=80:置信度阈值,超过80则判定为“无法识别”
recognizer = cv2.face.LBPHFaceRecognizer_create(threshold=80)
# 训练模型:输入训练图像和标签(标签需转为numpy数组)
recognizer.train(train_images, np.array(train_labels))
# -------------------------- 3. 预测待识别人脸 --------------------------
# 返回两个结果:label=预测标签,confidence=置信度(越小匹配度越高)
predict_label, confidence = recognizer.predict(predict_image)
# -------------------------- 4. 输出结果 --------------------------
print(f'识别结果:{label_dict[predict_label]}')
print(f'置信度(越小越准):{confidence:.2f}')
# (可选)在原图上绘制识别结果(需先将灰度图转为彩色图)
predict_image_color = cv2.cvtColor(predict_image, cv2.COLOR_GRAY2BGR)
# 绘制文字:参数依次为“图像、文字、位置、字体、字号、颜色、线宽”
cv2.putText(predict_image_color, label_dict[predict_label],
(10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1, (0, 0, 255), 2)
# 显示结果窗口
cv2.imshow('LBPH人脸识别结果', predict_image_color)
# 等待按键关闭窗口(0表示无限等待)
cv2.waitKey(0)
# 释放窗口资源
cv2.destroyAllWindows()
2.2 EigenFaces 算法:基于 PCA 降维,适合小数据集
原理核心
EigenFaces(特征脸)基于 PCA(主成分分析)降维,保留人脸最关键的 “特征脸” 信息:
- 数据降维:将所有训练人脸图像(如 120×180 像素 = 21600 维)投影到低维空间(如 80 维),保留 90% 以上的关键信息;
- 特征脸生成:降维后得到的主成分就是 “特征脸”,普通人脸可看作特征脸的线性组合;
- 匹配逻辑:计算待识别人脸与训练集在低维空间的距离,置信度<5000 为可靠匹配(阈值比 LBPH 大,因维度差异)。
关键注意点
EigenFaces 对图像尺寸敏感,所有训练图和待识别图必须统一尺寸(如 120×180),否则会报错。
完整代码(带详细注释)
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont # 用于显示中文(OpenCV默认不支持中文)
# -------------------------- 1. 准备训练数据(统一尺寸) --------------------------
train_images = []
# 读取图像→转为灰度图→缩放为120×180(必须统一尺寸)
def preprocess_image(img_path):
img = cv2.imread(img_path, 0) # 0=灰度模式
img_resized = cv2.resize(img, (120, 180)) # 统一尺寸:宽120,高180
return img_resized
# 加载训练图像(替换为你的路径)
train_images.append(preprocess_image('lyl1.png')) # 李云龙1
train_images.append(preprocess_image('lyl2.png')) # 李云龙2
train_images.append(preprocess_image('zg1.png')) # 赵刚1
train_images.append(preprocess_image('zg2.png')) # 赵刚2
# 训练标签和映射字典
train_labels = [0, 0, 1, 1]
label_dict = {0: '李云龙', 1: '赵刚', -1: '无法识别'}
# 预处理待识别人脸(同样统一尺寸)
predict_image = preprocess_image('zg_test.png')
# -------------------------- 2. 创建并训练EigenFaces识别器 --------------------------
# num_components=80:保留80个主成分(特征脸),threshold=7000:置信度阈值
recognizer = cv2.face.EigenFaceRecognizer_create(num_components=80, threshold=7000)
recognizer.train(train_images, np.array(train_labels))
# -------------------------- 3. 预测与结果输出 --------------------------
predict_label, confidence = recognizer.predict(predict_image)
print(f'识别结果:{label_dict[predict_label]}')
print(f'置信度(<5000可靠):{confidence:.2f}')
# (可选)显示带中文的结果图(用PIL处理中文)
# 1. 将OpenCV图像转为PIL图像(需先转灰度为彩色)
predict_image_bgr = cv2.cvtColor(predict_image, cv2.COLOR_GRAY2BGR)
predict_image_rgb = cv2.cvtColor(predict_image_bgr, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(predict_image_rgb)
# 2. 绘制中文(需指定中文字体路径,Windows默认路径如下)
draw = ImageDraw.Draw(pil_img)
try:
# 加载Windows自带黑体(路径根据系统调整)
font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 24)
except IOError:
# 若找不到中文字体,用英文字体兜底
font = ImageFont.truetype("arial.ttf", 24)
print("警告:未找到中文字体,将显示英文")
# 绘制中文文字:位置(10,30),红色(255,0,0)
draw.text((10, 30), label_dict[predict_label], font=font, fill=(255, 0, 0))
# 3. 转回OpenCV格式并显示
result_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
cv2.imshow('EigenFaces人脸识别结果', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.3 FisherFaces 算法:基于 LDA,类间区分度更高
原理核心
FisherFaces 基于 LDA(线性判别分析),相比 EigenFaces 更关注 “类间差异”:
- 优化目标:降维时不仅保留信息,还最大化不同类别(如李云龙 vs 赵刚)的距离,最小化同类内部的距离;
- 适用场景:当训练集中不同人的人脸差异较小时,FisherFaces 识别准确率更高;
- 匹配逻辑:置信度判断与 EigenFaces 类似,阈值建议设为 5000,低于则匹配成功。
完整代码(带详细注释)
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# -------------------------- 1. 工具函数:预处理+中文绘制 --------------------------
# 统一图像尺寸并转为灰度图
def preprocess_image(img_path, size=(120, 180)):
img = cv2.imread(img_path, 0)
return cv2.resize(img, size)
# 在图像上绘制中文
def add_chinese_text(img, text, pos=(10, 30), color=(255, 0, 0), font_size=24):
# OpenCV→PIL
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(img_rgb)
# 绘制中文
draw = ImageDraw.Draw(pil_img)
try:
font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", font_size)
except IOError:
font = ImageFont.truetype("arial.ttf", font_size)
draw.text(pos, text, font=font, fill=color)
# PIL→OpenCV
return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
# -------------------------- 2. 数据准备 --------------------------
train_images = [
preprocess_image('lyl1.png'),
preprocess_image('lyl2.png'),
preprocess_image('zg1.png'),
preprocess_image('zg2.png')
]
train_labels = [0, 0, 1, 1]
label_dict = {0: '李云龙', 1: '赵刚', -1: '无法识别'}
predict_image = preprocess_image('lyl_test2.png')
# -------------------------- 3. 训练与预测 --------------------------
# threshold=5000:FisherFaces阈值建议设为5000
recognizer = cv2.face.FisherFaceRecognizer_create(threshold=5000)
recognizer.train(train_images, np.array(train_labels))
predict_label, confidence = recognizer.predict(predict_image)
# -------------------------- 4. 结果展示 --------------------------
print(f'识别结果:{label_dict[predict_label]}')
print(f'置信度(<5000可靠):{confidence:.2f}')
# 显示带中文的结果图
predict_image_bgr = cv2.cvtColor(predict_image, cv2.COLOR_GRAY2BGR)
result_img = add_chinese_text(predict_image_bgr, label_dict[predict_label])
cv2.imshow('FisherFaces人脸识别结果', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
三、三种算法对比与选型建议
算法 | 核心思想 | 优势 | 劣势 | 适用场景 |
---|---|---|---|---|
LBPH | 局部二值模式 | 抗光照、旋转、缩放,无需统一尺寸 | 对遮挡敏感 | 实时监控、门禁识别 |
EigenFaces | PCA 降维 | 计算快,适合小数据集 | 对光照敏感,需统一尺寸 | 静态人脸库匹配 |
FisherFaces | LDA 类间优化 | 类间区分度高,准确率高 | 需统一尺寸,对噪声敏感 | 多人脸小样本精准识别 |
选型口诀:
- 实时场景选 LBPH(抗干扰强);
- 小样本静态匹配选 EigenFaces(快);
- 多人精准区分选 FisherFaces(准)。
四、常见问题与解决方案
1.报错 “face 模块不存在”:原因:安装的 opencv-python 不含 face 模块。解决:卸载后重新安装 opencv-contrib-python==3.4.18.65
。
2.中文显示乱码:原因:OpenCV 不支持中文字体。解决:用 PIL 库绘制中文,参考代码中的 add_chinese_text
函数。
3.识别准确率低:原因:训练样本少、图像光照差异大、未统一尺寸(Eigen/Fisher)。解决:
- 每人至少提供 2-5 张不同角度 / 光照的图像;
- 对图像进行灰度归一化(如
cv2.normalize
); - Eigen/Fisher 算法严格统一图像尺寸。
五、扩展:从单张预测到实时摄像头识别
如果想实现实时摄像头人脸识别,只需在上述代码基础上添加 “摄像头读取” 循环,核心逻辑如下:
# 加载人脸检测器(OpenCV自带Haar级联分类器)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# 打开摄像头(0表示默认摄像头)
cap = cv2.VideoCapture(0)
while True:
# 读取一帧图像
ret, frame = cap.read()
if not ret:
break # 摄像头读取失败则退出
# 检测人脸(灰度图检测更高效)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
# 对每个检测到的人脸进行识别
for (x, y, w, h) in faces:
# 裁剪人脸区域并预处理
face_roi = gray[y:y+h, x:x+w]
face_roi = cv2.resize(face_roi, (120, 180)) # 统一尺寸(Eigen/Fisher需加)
# 预测
predict_label, confidence = recognizer.predict(face_roi)
result_text = f'{label_dict[predict_label]}({confidence:.0f})'
# 绘制人脸框和结果
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绿色框
frame = add_chinese_text(frame, result_text, (x, y-30)) # 文字在框上方
# 显示实时画面
cv2.imshow('实时人脸识别', frame)
# 按ESC键退出(27是ESC的ASCII码)
if cv2.waitKey(1) == 27:
break
# 释放资源
cap.release()
cv2.destroyAllWindows()