初识OpenCV——人脸检测与识别实战

一、引言:让计算机看懂人脸

在之前的博客中,我们学习了OpenCV的图像处理、特征检测、目标提取等技术。今天,我们将进入一个更加贴近生活的应用领域------人脸检测与识别。

人脸检测与识别是计算机视觉中最经典、应用最广泛的技术之一。它已经深入到我们日常生活的方方面面:

手机解锁:Face ID让你看一眼屏幕就能解锁手机

门禁系统:刷脸进出办公楼和小区

相机美颜:自动检测人脸并应用美颜滤镜

安防监控:在人群中自动识别特定人员

表情分析:检测微笑、愤怒等情绪状态

这些看似神奇的功能,其实都离不开两大核心技术:人脸检测 (找到人脸在哪里)和人脸识别(确认这是谁的脸)。

本篇博客将结合6个完整的代码示例,从静态图片检测到视频实时检测,从LBPH到EigenFaces再到FisherFace,带领大家系统掌握OpenCV中的人脸检测与识别技术。

二、人脸检测------Haar级联分类器

2.1 什么是Haar级联分类器

Haar级联分类器是一种基于机器学习的对象检测方法,由Paul Viola和Michael Jones在2001年提出。它通过使用Haar-like特征和级联分类器来实现快速的人脸检测。

Haar-like特征是一种简单的矩形特征,通过计算图像中相邻矩形区域的像素和之差来描述图像的局部特征。例如,眼睛区域比脸颊区域暗,鼻梁区域比眼睛区域亮,这些明暗对比关系可以通过Haar特征来捕捉。

级联分类器的核心思想是"层层筛选":将多个弱分类器串联起来,前面的分类器快速排除大量非人脸区域,后面的分类器对疑似人脸区域进行更精细的判断。这种方式可以大幅提高检测速度。

OpenCV已经预训练好了多种Haar级联分类器,存放在cv2/data/目录下:

分类器文件 检测目标
haarcascade_frontalface_default.xml 正面人脸
haarcascade_frontalface_alt.xml 正面人脸(替代版)
haarcascade_smile.xml 微笑
haarcascade_eye.xml 眼睛
haarcascade_fullbody.xml 全身

2.2 静态图像人脸检测

让我们先从一个简单的例子开始------检测一张静态图片中的人脸:

python 复制代码
import cv2

# 读取图像并转为灰度图
image = cv2.imread('people.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 加载人脸分类器
faceCascade = cv2.CascadeClassifier(
    "haarcascade_frontalface_default.xml"
)

# 检测人脸
faces = faceCascade.detectMultiScale(
    gray, 
    scaleFactor=1.05,
    minNeighbors=9, 
    minSize=(8, 8)
)

print("发现{}张人脸!".format(len(faces)))
print("其位置分别是:", faces)

# 标注人脸
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imshow("result", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

detectMultiScale()参数详解:

参数 含义 说明
image 待检测图像 通常为灰度图像
scaleFactor 缩放比例 前后两次扫描中搜索窗口的缩放比例,默认1.1
minNeighbors 最小相邻矩形数 构成检测目标所需的最小相邻矩形个数,默认3
flags 标志位 通常省略
minSize 最小尺寸 小于该尺寸的目标将被忽略
maxSize 最大尺寸 大于该尺寸的目标将被忽略

返回值 :faces是一个矩形框向量组,包含每个检测到的人脸的(x, y, w, h)信息。

参数调优建议:

  • scaleFactor越小,检测越细致,但速度越慢

  • minNeighbors越大,检测越严格,误检越少但可能漏检

  • 根据图像大小调整minSize和maxSize可以显著提升检测效率

2.3 视频实时人脸检测与微笑检测

将人脸检测应用到视频流中,就可以实现实时检测。我们还可以在人脸区域内进一步检测微笑:

python 复制代码
import cv2

# 加载分类器
faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
smile = cv2.CascadeClassifier("haarcascade_smile.xml")

# 打开摄像头或视频文件
cap = cv2.VideoCapture('xiao.mp4')

while True:
    ret, image = cap.read()
    if ret is None:
        break
    
    image = cv2.resize(image, (500, 500))
    image = cv2.flip(image, flipCode=1)  # 水平翻转(镜像)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 人脸检测
    faces = faceCascade.detectMultiScale(
        gray, scaleFactor=1.1, minNeighbors=15, minSize=(5, 5)
    )

    for (x, y, w, h) in faces:
        # 绘制人脸矩形框
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        
        # 提取人脸区域
        roi_gray_face = gray[y:y + h, x:x + w]
        
        # 在人脸区域内检测微笑
        smiles = smile.detectMultiScale(
            roi_gray_face, scaleFactor=1.5, minNeighbors=10, minSize=(50, 50)
        )
        
        for (sx, sy, sw, sh) in smiles:
            # 绘制微笑区域
            a = x + sx
            b = y + sy
            cv2.rectangle(image, (a, b), (a + sw, b + sh), (255, 0, 0), 2)
            # 显示文字"smile"
            cv2.putText(image, "smile", (x, y), 
                       cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 255, 255), 2)

    cv2.imshow("detect", image)
    if cv2.waitKey(30) == 27:  # ESC键退出
        break

cap.release()
cv2.destroyAllWindows()

代码要点:

  • cv2.flip(image, flipCode=1):水平翻转实现镜像效果,让操作更自然

  • 微笑检测仅在人脸区域内进行,这样可以大幅减少误检和计算量

  • 微笑分类器的scaleFactor和minNeighbors需要比人脸检测更严格,因为微笑特征更局部

三、人脸识别------三种经典算法

人脸检测解决了"人脸在哪里"的问题,而人脸识别要解决的是"这是谁的脸"。

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

算法 核心思想 特点
LBPH 局部二值模式直方图 对光照变化鲁棒,适合小样本
EigenFaces 主成分分析(PCA) 降维提取主要特征,速度快
FisherFace 线性判别分析(LDA) 最大化类间差异,识别率高

3.1 LBPH算法------局部二值模式直方图

LBPH(Local Binary Patterns Histograms) 是一种基于局部纹理特征的识别方法。

核心原理:

  1. 对图像中每个像素,将其与周围邻居比较,大于等于中心像素标记为1,小于标记为0

  2. 将二进制序列转换为十进制数,得到该像素的LBP值

  3. 将图像划分为多个网格(如8×8),统计每个网格的LBP直方图

  4. 将所有网格的直方图拼接成特征向量,用于识别

LBPH的优势:

  • 对光照变化不敏感

  • 计算简单,适合实时应用

  • 可以处理小样本训练

下面的代码展示了LBPH的完整使用流程:

python 复制代码
import cv2
import numpy as np

# 准备训练数据
images = []
images.append(cv2.imread('hg1.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('hg2.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('yz1.png', cv2.IMREAD_GRAYSCALE))
images.append(cv2.imread('yz2.png', cv2.IMREAD_GRAYSCALE))

labels = [0, 0, 1, 1]
dic = {0: 'hg', 1: 'yz', -1: '无法识别'}

# 待识别人脸
predict_image = cv2.imread('yz3.png', cv2.IMREAD_GRAYSCALE)

# 创建LBPH识别器
recognizer = cv2.face.LBPHFaceRecognizer.create(threshold=80)

# 训练模型
recognizer.train(images, np.array(labels))

# 预测
label, confidence = recognizer.predict(predict_image)
print('这是:', dic[label])
print('置信度:', confidence)

参数说明:

  • radius:圆形局部二进制模式的半径,建议默认

  • neighbors:采样点数,建议默认

  • grid_x, grid_y:水平/垂直方向的单元格数,默认8×8

  • threshold:识别阈值,低于该值认为匹配可靠

返回值:

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

  • confidence:置信度,越小表示匹配度越高

3.2 EigenFaces算法------特征脸

EigenFaces 基于主成分分析(PCA) ,将人脸图像从高维空间投影到低维特征空间,提取最具代表性的"特征脸"。

核心思想:

  1. 将所有训练人脸图像拉成向量,计算平均脸

  2. 每张人脸减去平均脸,得到差异图像

  3. 通过PCA找到差异图像的主要成分(特征脸)

  4. 将每张人脸表示为特征脸的线性组合

  5. 识别时,将待识别人脸投影到特征脸空间,与训练样本比较距离

下面展示了EigenFaces的使用:

python 复制代码
import cv2
import numpy as np

# 准备训练数据(注意:图片大小必须一致)
images = []
a = cv2.imread('hg1.png', 0)
a = cv2.resize(a, (200, 200))
b = cv2.imread('hg2.png', 0)
b = cv2.resize(b, (200, 200))
d = cv2.imread('yz1.png', 0)
d = cv2.resize(d, (200, 200))
e = cv2.imread('yz2.png', 0)
e = cv2.resize(e, (200, 200))
images = [a, b, d, e]

labels = [0, 0, 1, 1]

# 待识别图像
image = cv2.imread('yz3.png')
pre_image = cv2.resize(image, (200, 200))
pre_image = cv2.cvtColor(pre_image, cv2.COLOR_BGR2GRAY)

# 创建EigenFaces识别器
recognizer = cv2.face.EigenFaceRecognizer.create(threshold=5000)

# 训练
recognizer.train(images, np.array(labels))

# 预测
label, confidence = recognizer.predict(pre_image)
dic = {0: 'yz', 1: 'hg', -1: '-1'}
print('这人是:', dic[label])
print('置信度为:', confidence)

注意事项:

  • 所有训练图片和待识别图片必须调整为相同尺寸(如200×200)

  • threshold需要根据实际情况调整,太大容易误识别,太小容易拒识

  • EigenFaces对光照和角度变化较为敏感

3.3 FisherFace算法------线性判别分析

FisherFace 基于线性判别分析(LDA) ,与EigenFaces类似,但它不仅考虑数据的主要变化方向,还最大化类间差异、最小化类内差异。

核心思想:

  • EigenFaces只关注"哪些方向变化最大"

  • FisherFace关注"哪些方向最能区分类别"

因此,FisherFace通常比EigenFaces有更高的识别率,尤其适合类别区分明显的场景。

下面展示了FisherFace的使用,并实现了中文标注:

python 复制代码
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont

def cv2add_chinese_text(img, text, position, textcolor=(0, 255, 0), textsize=30):
    """向图片中添加中文"""
    if isinstance(img, np.ndarray):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(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)

def image_re(image):
    a = cv2.imread(image, 0)
    a = cv2.resize(a, (200, 200))
    images.append(a)

# 准备训练数据
images = []
image_re('hg1.png')
image_re('hg2.png')
image_re('yz1.png')
image_re('yz2.png')

labels = [0, 0, 1, 1]

# 待识别图像
image = cv2.imread('hg3.png')
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
pre_image = cv2.resize(grey, (200, 200))

# 创建FisherFace识别器
recognizer = cv2.face.FisherFaceRecognizer.create(threshold=5000)

# 训练
recognizer.train(images, np.array(labels))

# 预测
label, confidence = recognizer.predict(pre_image)
dic = {0: '胡歌', 1: '彦祖', -1: '无法识别'}
print('这是:', dic[label])
print('置信度为:', confidence)

# 输出中文
image = cv2add_chinese_text(image.copy(), dic[label], (30, 10), (255, 0, 0))
cv2.imshow('xx', image)
cv2.waitKey(0)

中文标注的技巧 :OpenCV自带的cv2.putText()不支持中文,我们借助PIL库的ImageFont和ImageDraw实现中文绘制。核心步骤是:

  1. 将OpenCV图像转为PIL图像

  2. 使用PIL绘制中文文本

  3. 再转回OpenCV格式

四、三种算法对比与选择建议

对比维度 LBPH EigenFaces FisherFace
核心原理 局部二值模式 主成分分析(PCA) 线性判别分析(LDA)
光照鲁棒性 强 弱 中
训练速度 快 中 中
识别速度 快 快 快
小样本表现 好 一般 好
是否需要统一尺寸 否 是 是
适用场景 实时检测、光照变化大 简单场景 类别区分明显的场景

选择建议:

  • LBPH:最推荐,对光照不敏感,使用最灵活

  • EigenFaces:适合光照稳定的简单场景

  • FisherFace:适合需要高识别率的场景

五、综合实战:完整的人脸识别系统

将人脸检测和人脸识别结合,可以构建一个完整的实时人脸识别系统:

python 复制代码
import cv2
import numpy as np

# 加载人脸检测器
faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

# 准备训练数据
images = []
labels = []
dic = {0: 'Person A', 1: 'Person B'}

# 假设已经准备好了训练图像
# images.append(...)
# labels.append(...)

# 创建LBPH识别器
recognizer = cv2.face.LBPHFaceRecognizer.create(threshold=80)
# recognizer.train(images, np.array(labels))

# 打开摄像头
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 人脸检测
    faces = faceCascade.detectMultiScale(gray, 1.1, 5)
    
    for (x, y, w, h) in faces:
        # 绘制人脸框
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
        
        # 提取人脸区域并调整大小
        roi = gray[y:y + h, x:x + w]
        roi = cv2.resize(roi, (200, 200))
        
        # 人脸识别
        # label, confidence = recognizer.predict(roi)
        # name = dic.get(label, '未知')
        # cv2.putText(frame, name, (x, y - 10), 
        #            cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
    
    cv2.imshow('Face Recognition', frame)
    if cv2.waitKey(1) == 27:
        break

cap.release()
cv2.destroyAllWindows()

六、总结

本篇博客系统学习了OpenCV中人脸检测与识别的核心技术:

技术 核心方法 关键函数
人脸检测 Haar级联分类器 detectMultiScale()
微笑检测 Haar级联分类器(smile) detectMultiScale()
LBPH识别 局部二值模式直方图 LBPHFaceRecognizer.create()
EigenFaces识别 主成分分析 EigenFaceRecognizer.create()
FisherFace识别 线性判别分析 FisherFaceRecognizer.create()
中文标注 PIL文字绘制 ImageFont.truetype()
相关推荐
lzhdim6 小时前
AI的分类
人工智能·算法·机器学习·分类·数据挖掘
程序员海军8 小时前
用了两年多 GPT,我最近开始换模型了,还折腾了个求职避坑 Agent
前端·人工智能·后端
AIsoft_86898 小时前
人才评估中的关键信息如何沉淀?
人工智能·iphone
紧跟先前的步伐8 小时前
Go AI Agent 实战 06|JSON 结构化输出与 Go 后端校验
人工智能·ai·golang
拜小白8 小时前
【论文解读】DMH-Net:用深度曼哈顿霍夫变换从全景立方图估计3D房间布局
深度学习·计算机视觉·全景图
vibecoding7710 小时前
Codex 分层用模型完整教程:让 GPT-6.1 Sol 拍板、GPT-6 Luna 跑侦察
人工智能·gpt
新知图书10 小时前
1.3 启动第一个机器人
人工智能·机器人
明志数科10 小时前
自主探索范式下机器人示教数据的工程定位:少量示范为何对质量更敏感
人工智能·机器学习·机器人
Maynor99611 小时前
WorkBuddy 一键接通 gpt-6.1-sol:MomoAI / TryAllAPI 把最新模型接到桌面
人工智能·gpt·ai·大模型·编程工具
火山引擎开发者社区11 小时前
问得出、搜得准、信得过:ContextSearch 重构销售检索体验
人工智能