杰瑞科技汇

python opencv 人脸识别

目录

  1. 核心概念:人脸检测 vs. 人脸识别
  2. 环境准备
  3. 人脸检测
    • 使用 Haar 级联分类器
    • 使用 DNN 模型(更推荐)
  4. 人脸识别
    • 使用 OpenCV 的 LBPH (Local Binary Patterns Histograms) 人脸识别器
    • 完整代码示例:从检测到识别
  5. 进阶与最佳实践
    • 使用更先进的模型(如 FaceNet, DeepFace)
    • 处理视频流
    • 性能优化

核心概念:人脸检测 vs. 人脸识别

这是两个经常被混淆但完全不同的概念:

python opencv 人脸识别-图1
(图片来源网络,侵删)
  • 人脸检测:目标是找到图像中人脸的位置,它只回答“哪里有人脸?”,不关心这个人是谁,它的输出通常是图像中人脸的边界框坐标。
    • 应用场景:人脸解锁、美颜相机、拍照时自动对焦。
  • 人脸识别:目标是识别出人脸的身份,它回答“这是谁?”,这个过程通常包括人脸检测,然后提取人脸的特征,并与数据库中已知人脸的特征进行比对。
    • 应用场景:手机解锁、门禁系统、身份验证。

流程总结人脸识别 的流程通常是:图像 -> 人脸检测 -> 人脸对齐/裁剪 -> 特征提取 -> 特征比对,我们今天要实现的就是这个完整流程。


环境准备

你需要安装必要的库。

# 安装 OpenCV
pip install opencv-python
# 安装 NumPy (OpenCV 内部依赖)
pip install numpy
# 可选:用于更高级的人脸识别模型
pip install tensorflow
pip install deepface

重要:获取预训练模型文件

OpenCV 自带了一些预训练模型,我们需要下载它们。

python opencv 人脸识别-图2
(图片来源网络,侵删)
  1. 人脸检测模型

    • Haar 级联模型:文件名 haarcascade_frontalface_default.xml,这是一个经典但速度较快、准确率一般的模型。
    • DNN 模型:文件名 opencv_face_detector_uint8.pbopencv_face_detector.pbtxt,这是一个基于 Caffe 模型的现代 DNN,准确率更高,推荐使用。
    • 下载地址:你可以从 OpenCV 的 GitHub 仓库或官方模型库下载,这里提供一个常用的下载链接:

    下载后,将这两个文件(或 Haar 文件)放在你的 Python 脚本同目录下的一个 models 文件夹中,以便代码可以找到它们。

  2. 人脸识别模型

    • OpenCV 提供了三种内置的人脸识别算法:Eigenfaces, Fisherfaces, 和 LBPH
    • 我们将使用 LBPH,因为它对光照变化和姿态变化不那么敏感,效果最好。
    • 训练数据:这些模型需要你自己提供一组已知人物的人脸图片来进行训练,模型会学习这些人的特征。

步骤一:人脸检测

我们先来看如何检测人脸。

python opencv 人脸识别-图3
(图片来源网络,侵删)

方法 A:使用 Haar 级联分类器 (简单)

import cv2
# 加载 Haar 级联分类器
face_cascade = cv2.CascadeClassifier('models/haarcascade_frontalface_default.xml')
# 读取图像
img = cv2.imread('test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图像,检测更快
# 检测人脸
# scaleFactor: 图像缩放比例,用于构建图像金字塔
# minNeighbors: 每个候选矩形应包含的邻近像素点数,值越大,检测越严格,越不容易误检
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
# 在检测到的人脸周围画矩形
for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
# 显示结果
cv2.imshow('Faces detected with Haar', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

方法 B:使用 DNN 模型 (推荐)

这种方法更准确,是现代应用的首选。

import cv2
# 加载模型
# pbtxt 是模型配置文件,pb 是模型权重文件
net = cv2.dnn.readNetFromTensorflow('models/opencv_face_detector_uint8.pb', 'models/opencv_face_detector.pbtxt')
# 读取图像
img = cv2.imread('test.jpg')
(h, w) = img.shape[:2]
# 从输入图像创建一个 "blob" (二进制大对象)
# 300x300 是 DNN 模型期望的输入尺寸
# 1.0 是缩放因子,0.0 是均值减法
blob = cv2.dnn.blobFromImage(img, scalefactor=1.0, size=(300, 300), mean=(104.0, 177.0, 123.0))
# 将 blob 设置为网络的输入
net.setInput(blob)
# 前向传播,获取检测结果
detections = net.forward()
# 遍历检测结果
for i in range(detections.shape[2]):
    confidence = detections[0, 0, i, 2] # 获取检测置信度
    # 设置一个置信度阈值,过滤掉低置信度的检测
    if confidence > 0.7:
        # 计算人脸边界框的坐标
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")
        # 确保边界框在图像范围内
        (startX, startY) = (max(0, startX), max(0, startY))
        (endX, endY) = (min(w - 1, endX), min(h - 1, endY))
        # 画矩形
        cv2.rectangle(img, (startX, startY), (endX, endY), (0, 255, 0), 2)
# 显示结果
cv2.imshow('Faces detected with DNN', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

步骤二:人脸识别 (使用 LBPH)

现在我们来整合检测和识别,我们将创建一个简单的系统,可以识别出图像中的某个人是否是预先注册的 "Alice" 或 "Bob"。

完整代码示例

这个示例需要你创建一个 dataset 文件夹,里面为每个已知人物创建一个子文件夹,并放入他们的人脸照片。

目录结构:

.
├── face_recognition.py
├── models/
│   ├── opencv_face_detector_uint8.pb
│   └── opencv_face_detector.pbtxt
└── dataset/
    ├── person1/
    │   ├── person1_1.jpg
    │   └── person1_2.jpg
    └── person2/
        ├── person2_1.jpg
        └── person2_2.jpg

face_recognition.py 代码:

import cv2
import numpy as np
import os
# --- 1. 准备数据 ---
# 创建人脸识别器
recognizer = cv2.face.LBPHFaceRecognizer_create()
# 准备训练数据
def prepare_training_data(data_folder_path):
    dirs = os.listdir(data_folder_path)
    faces = []
    labels = []
    label_ids = {}
    current_id = 0
    for person_name in dirs:
        person_dir_path = os.path.join(data_folder_path, person_name)
        if not os.path.isdir(person_dir_path):
            continue
        images = os.listdir(person_dir_path)
        for image_name in images:
            if image_name.endswith('.jpg') or image_name.endswith('.png'):
                image_path = os.path.join(person_dir_path, image_name)
                image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
                # 使用 Haar 级联检测器找到人脸
                # 注意:这里假设每张图里只有一张人脸
                face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
                faces_detected = face_cascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5)
                for (x, y, w, h) in faces_detected:
                    face_roi = image[y:y+h, x:x+w]
                    faces.append(face_roi)
                    # 为每个人分配一个唯一的 ID
                    if person_name not in label_ids:
                        label_ids[person_name] = current_id
                        current_id += 1
                    labels.append(label_ids[person_name])
    return faces, labels, label_ids
# --- 2. 训练模型 ---
print("Preparing data...")
faces, labels, label_ids = prepare_training_data("dataset")
print(f"Data prepared. {len(faces)} faces and {len(labels)} labels found.")
print("Training the model...")
# 训练 LBPH 模型
recognizer.train(faces, np.array(labels))
print("Model trained successfully.")
# 保存模型和标签映射,以便下次使用
recognizer.save("trainer.yml")
# 注意:label_ids 需要单独保存,因为 LBPH 模型只保存了ID,不保存名字
np.save("label_ids.npy", label_ids)
print("Model and label IDs saved.")
# --- 3. 进行识别 ---
# 加载已训练的模型和标签
recognizer.read("trainer.yml")
label_ids = np.load("label_ids.npy", allow_pickle=True).item()
# 创建一个反向映射,从ID到名字
id_to_name = {v: k for k, v in label_ids.items()}
# 加载 DNN 人脸检测器(推荐)
net = cv2.dnn.readNetFromTensorflow('models/opencv_face_detector_uint8.pb', 'models/opencv_face_detector.pbtxt')
# 读取一张待识别的图片
test_image_path = "test.jpg"
img = cv2.imread(test_image_path)
(h, w) = img.shape[:2]
# 创建 blob 并进行人脸检测
blob = cv2.dnn.blobFromImage(img, scalefactor=1.0, size=(300, 300), mean=(104.0, 177.0, 123.0))
net.setInput(blob)
detections = net.forward()
# 遍历检测到的人脸
for i in range(detections.shape[2]):
    confidence = detections[0, 0, i, 2]
    if confidence > 0.7:
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")
        # 裁剪人脸区域
        face_roi = img[startY:endY, startX:endX]
        # 将人脸区域转换为灰度图
        gray_roi = cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY)
        # 使用训练好的识别器进行预测
        label_id, confidence = recognizer.predict(gray_roi)
        # 获取预测的名字
        predicted_name = id_to_name.get(label_id, "Unknown")
        # 在图像上显示结果
        text = f"{predicted_name} ({confidence:.2f})"
        cv2.rectangle(img, (startX, startY), (endX, endY), (0, 255, 0), 2)
        cv2.putText(img, text, (startX, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
# 显示最终结果
cv2.imshow('Face Recognition Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

进阶与最佳实践

A. 使用更先进的模型 (FaceNet, DeepFace)

OpenCV 内置的 LBPH 模型虽然简单,但准确率有限,对于要求更高的应用,可以考虑使用深度学习模型。

  • DeepFace:这是一个基于 Python 的强大人脸识别库,封装了 FaceNet, VGG-Face, Google FaceNet, OpenFace 等多种 SOTA 模型,使用起来非常方便。

    pip install deepface
    from deepface import DeepFace
    # 验证两张图片是否为同一个人
    result = DeepFace.verify(img1_path, img2_path)
    print(result) # 返回一个包含 'verified' (布尔值) 和 'distance' 的字典
    # 识别图片中的人是谁
    df = DeepFace.find(img_path = "test.jpg", db_path = "dataset")
    print(df) # 返回一个 DataFrame,包含匹配的图片和人物信息

    DeepFace 是快速原型开发和实现高精度人脸识别的最佳选择。

B. 处理视频流

识别静态图片很简单,但识别视频(如摄像头实时画面)是常见需求,核心思路是循环读取视频帧,对每一帧执行上述的检测和识别逻辑。

import cv2
# ... (加载模型、识别器等代码与上面相同) ...
# 打开摄像头
cap = cv2.VideoCapture(0) # 0 表示默认摄像头
while True:
    # 读取一帧
    ret, frame = cap.read()
    if not ret:
        break
    # ... (将上面的 DNN 检测和识别逻辑放在这里,对 'frame' 进行处理) ...
    # 
    # blob = cv2.dnn.blobFromImage(frame, ...)
    # ... (检测和识别代码) ...
    # cv2.imshow('Video', frame_with_labels)
    # 显示处理后的帧
    cv2.imshow('Video', frame)
    # 按 'q' 键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
# 释放资源
cap.release()
cv2.destroyAllWindows()

C. 性能优化

  • 降低分辨率:对于视频流,可以适当降低输入帧的分辨率,以加快处理速度。
  • 跳帧处理:不一定需要对每一帧都进行人脸识别,可以每隔几帧处理一次。
  • 多线程:可以将人脸检测和特征提取/比对放在不同的线程中,实现流水线处理,提高效率。
  • 使用 GPU:如果你的 OpenCV 是支持 CUDA 的版本,可以将 DNN 模型加载到 GPU 上进行加速。

希望这份详细的指南能帮助你从零开始使用 Python 和 OpenCV 进行人脸识别!

分享:
扫描分享到社交APP
上一篇
下一篇