初识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越大,检测越严格,误检越少但可能漏检

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

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):水平翻转实现镜像效果,让操作更自然

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

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

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

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

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库的ImageFontImageDraw实现中文绘制。核心步骤是:

  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()
相关推荐
byte轻骑兵1 小时前
【BlueZ 】util 模块:通用工具函数,源码中高频复用的基础组件
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
Cicada1281 小时前
分享我一直在用的一套 AI 记忆系统方案
大数据·数据库·人工智能
桃西西呀1 小时前
买房怕买贵?我把真实成交价丢进决策树,看它到底按什么给房子定价
人工智能·机器学习·llm
jimmyleeee1 小时前
大模型安全之十:LLM无界消耗(Unbounded Consumption)
人工智能·安全
泡干脆面就番茄1 小时前
04_EigenFace特征脸PCA人脸识别
python·opencv
m0_462605221 小时前
大模型实战营week6
人工智能
广东帝工智能安防1 小时前
景区水域安全预警方案:BCAS架构在旅游场景的适配与落地
人工智能·安全·三维激光雷达·景区安全·雷视融合
2601_949950631 小时前
一套小程序,解决资料整理、在线刷题和错题复盘
人工智能·小程序·刷题·练习·小程序推荐
渡川见闻1 小时前
2026年工业生产运营软件推荐
人工智能