跳至主要内容

使用 Python 与 OpenCV 的人脸检测

了解如何在 Python 中使用 OpenCV 库进行目标检测,并学习将其应用于人脸检测等任务。
· 8分钟 阅读

使用 AI 探索

OpenCV 是一个计算机视觉库,支持 Python、C++ 和 Java 等编程语言。该项目最初由英特尔于 1999 年创建,随后开源并向公众发布。

OpenCV 使开发者和非数学背景的人员无需从零开始编写代码,就能轻松构建计算机视觉应用。该库包含 2,500 多个算法,支持人脸识别、目标检测等任务。

Google、Microsoft、IBM 和 Intel 等成熟组织的开发者与数据从业者都在广泛使用 OpenCV 库,目前该库可免费用于商业用途。

在本文中,我们将使用 OpenCV 在 Python 中执行人脸检测。

学完本教程,您将能够:

  • 使用 Python 中的 OpenCV 检测图像中的人脸
  • 在来自网络摄像头的实时视频流中执行人脸检测

什么是人脸检测?

人脸检测是指在图像或视频中识别出人的脸。这是通过分析视觉输入,以判断是否存在人的面部特征来完成的。

由于人脸的多样性较高,人脸检测模型通常需要在大量输入数据上进行训练才能达到较高准确度。训练数据集必须充分覆盖不同背景、性别与文化的人群。

此外,还需要提供包含不同光照、角度与朝向的众多训练样本,才能在真实场景中做出正确预测。

这些细节使得人脸检测并非易事,往往需要耗费大量训练时间和数以百万计的数据样本。

所幸,OpenCV 提供了预训练的人脸检测模型,这意味着我们无需从头训练算法。更具体地说,该库采用一种称为 Haar 级联的机器学习方法来识别视觉数据中的目标。

OpenCV 人脸检测教程

本节我们将学习如何使用 OpenCV 与 Python,应用一种常见的人脸检测方法——Haar 级联(Haar Cascade)。

在线运行和编辑此教程中的代码

运行代码

Haar 级联分类器简介

该方法最早由 Paul Viola 与 Michael Jones 在论文 Rapid Object Detection Using a Boosted Cascade of Simple Features 中提出。

其核心思想是使用一系列级联的分类器来检测图像中的不同特征,然后将这些分类器组合成一个强分类器,以准确区分包含人脸与不包含人脸的样本。

OpenCV 内置的 Haar 级联分类器已经基于大型人脸数据集完成训练,因此无需再做额外训练。我们只需从库中加载该分类器,并将其用于输入图像的人脸检测。

为 Python 安装 OpenCV

要安装 OpenCV 库,只需打开命令提示符或终端窗口并运行以下命令:

pip install opencv-python

该命令仅在您的设备已安装 pip 的情况下有效。若想进一步了解 pip 包管理器,您可以阅读我们的 PIP Python 教程。

在图像中使用 OpenCV 进行人脸检测

我们将基于 Unsplash 上的一张照片构建一个检测器来识别人脸。请先将图片保存到您的工作目录,并在编码前将其重命名为 input_image。

步骤 1:导入 OpenCV 包

现在,让我们导入 OpenCV,并通过以下代码输入图像路径:

import cv2

imagePath = 'input_image.jpg'

步骤 2:读取图像

接着,我们使用 OpenCV 的 imread() 函数读取图像:

img = cv2.imread(imagePath)

这将从指定的文件路径加载图像,并以 Numpy 数组的形式返回。

让我们打印该数组的维度:

img.shape
(4000, 2667, 3)

可以看到这是一个三维数组。数组的值分别表示图片的高度、宽度和通道数。由于这是彩色图像,使用了三个通道来表示——蓝、绿、红(BGR)。

请注意,尽管表示图像的常规顺序是 RGB(红、绿、蓝),但 OpenCV 使用相反的布局(蓝、绿、红)。

步骤 3:将图像转换为灰度

为提升计算效率,我们需要先将图像转换为灰度,再进行人脸检测:

gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

现在我们来查看该灰度图像的维度:

gray_image.shape
(4000, 2667)

注意该数组只有两个值,因为灰度图像已不再包含第三个颜色通道。

步骤 4:加载分类器

让我们加载 OpenCV 内置的预训练 Haar 级联分类器:

face_classifier = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

这里我们使用了名为 haarcascade_frontalface_default.xml 的文件。该分类器专门用于检测正面人脸。

OpenCV 还提供了其他预训练模型,用于检测图像中的不同目标——例如人的眼睛、笑容、上半身,甚至车辆的车牌。您可以通过查看该库的 GitHub 仓库了解更多内置分类器。

步骤 5:执行人脸检测

现在,我们可以使用刚加载的分类器对灰度图像执行人脸检测:

face = face_classifier.detectMultiScale(
    gray_image, scaleFactor=1.1, minNeighbors=5, minSize=(40, 40)
)

让我们拆解上述代码中涉及的方法与参数:

  1. detectMultiScale():

detectMultiScale() 方法用于识别输入图像中不同尺寸的人脸。

  1. grey_image:

该方法的第一个参数是 grey_image,即我们先前创建的灰度图像。

  1. scaleFactor:

该参数用于按比例缩小输入图像的尺寸,从而便于算法检测较大的脸部。在此我们指定了 1.1 的缩放因子,表示将图像尺寸缩小 10%。

  1. minNeighbors:

级联分类器通过在图像中应用滑动窗口来检测人脸。您可以将这些窗口理解为矩形。

初始阶段,分类器会捕获大量误检(false positives)。通过 minNeighbors 参数可以过滤这些误检,该参数指定一个对象要被视为有效检测,所需的邻近矩形数量。

简而言之,将该参数设置为 0 或 1 等较小值会导致较多误检,而设置为较大数值则可能错失许多真正的正样本(true positives)。

关键在于寻求权衡:既能剔除误检,又能尽可能准确地识别真阳性。

  1. minSize:

最后,minSize 参数用于设置待检测对象的最小尺寸。小于该尺寸的人脸将被模型忽略。

步骤 6:绘制边界框

既然模型已经检测出图像中的人脸,让我们运行以下代码在这些人脸周围绘制一个边界框:

for (x, y, w, h) in face:
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 4)

face 变量是一个包含四个值的数组:检测到人脸的 x、y 轴坐标以及其宽度和高度。上述代码会遍历所有识别出的人脸,并根据这些数值绘制边界框。

参数 0,255,0 表示边界框的颜色为绿色,4 表示线条粗细。

步骤 7:显示图像

为显示带有人脸检测结果的图像,我们需要先将图像从 BGR 格式转换为 RGB:

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

现在,使用 Matplotlib 库来显示图像:

import matplotlib.pyplot as plt



plt.figure(figsize=(20,10))
plt.imshow(img_rgb)
plt.axis('off')

上述代码应生成如下输出:

face detection result

很好!

模型已成功检测出图中的人脸,并在其周围绘制了边界框。

使用 OpenCV 进行实时人脸检测

既然我们已经用 OpenCV 成功完成了静态图像的人脸检测,接下来看看如何在实时视频流中实现相同操作。

步骤 1:前置准备

首先,和上一节一样,导入 OpenCV 并加载 Haar 级联模型。若您已在前文运行过,可跳过此代码块:

import cv2

face_classifier = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

步骤 2:访问摄像头

现在,我们需要访问设备的摄像头以读取实时视频流。可使用以下代码实现:

video_capture = cv2.VideoCapture(0)

注意我们向 VideoCapture() 传入了参数 0,这告诉 OpenCV 使用设备的默认摄像头。如果您的设备连接了多个摄像头,可以相应更改该参数的取值。

步骤 3:在视频流中识别人脸

现在,让我们创建一个函数来检测视频流中的人脸,并在其周围绘制边界框:

def detect_bounding_box(vid):
    gray_image = cv2.cvtColor(vid, cv2.COLOR_BGR2GRAY)
    faces = face_classifier.detectMultiScale(gray_image, 1.1, 5, minSize=(40, 40))
    for (x, y, w, h) in faces:
        cv2.rectangle(vid, (x, y), (x + w, y + h), (0, 255, 0), 4)
    return faces

detect_bounding_box 函数将视频帧作为输入。

在该函数中,我们沿用之前的代码,先将帧转换为灰度,再执行人脸检测。

随后,继续使用与此前相同的 scaleFactor、minNeighbors 与 minSize 参数来检测人脸。

最后,我们在帧上绘制一圈线宽为 4 的绿色边界框。

步骤 4:创建实时人脸检测循环

现在,我们需要创建一个无限循环,从摄像头捕获视频帧并对其应用人脸检测函数:

while True:

    result, video_frame = video_capture.read()  # read frames from the video
    if result is False:
        break  # terminate the loop if the frame is not read successfully

    faces = detect_bounding_box(
        video_frame
    )  # apply the function we created to the video frame

    cv2.imshow(
        "My Face Detection Project", video_frame
    )  # display the processed frame in a window named "My Face Detection Project"

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

video_capture.release()
cv2.destroyAllWindows()

运行上述代码后,您应能在屏幕上看到一个名为 My Face Detection Project 的窗口:

face detection result

无论您在画面中如何移动,算法都应能跟踪您的脸部并在其周围绘制绿色边界框。

在上图中,模型既识别出了我的脸,也识别出了我手持的驾照照片上的人脸。

您还可以通过举起多张照片,或让不同的人在摄像头前以各种角度站立,来测试该模型的效果。模型应能在不同背景或光照条件下识别出所有人脸。

若要退出程序,您可以按下键盘上的“q”键以跳出循环。

结语

总之,OpenCV 是一个功能强大且灵活的计算机视觉库,提供了丰富的预训练模型与工具,可以简化诸如人脸检测等复杂任务。

无论是处理静态图像还是实时视频流,OpenCV 都为开发者提供了一个易于上手的入口,以构建高效且可扩展的解决方案。

掌握本教程涵盖的技巧,您将能够在项目中进一步探索更高级的计算机视觉应用。

Natassha Selvaraj

作者

Natassha Selvaraj

Natassha 是一名在数据科学与营销交汇处工作的数据顾问。她相信,明智地使用数据,能够为个人和组织带来巨大的增长。作为一名自学成才的数据从业者,Natassha 热衷于撰写文章,帮助有志于数据科学的人士进入该行业。她在个人博客以及外部平台发表的文章,平均每月获得 20 万次浏览。

常见问题

OpenCV 能用于检测人脸以外的物体吗?

可以。OpenCV 支持使用其预训练的 Haar 级联分类器对眼睛、笑容、车辆、甚至车牌等多种对象进行检测。此外,OpenCV 还可与诸如 YOLO 或 SSD 等现代目标检测模型结合,以获得更稳健、准确的多目标检测能力。

如何在一张图像中处理多张人脸?

OpenCV 的 detectMultiScale() 方法会通过为每个人脸创建边界框,自动检测图像中的所有人脸。您可以遍历返回的边界框,分别处理或标注每张检测出的人脸。示例:

for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 4)  # Draw boxes for all detected faces
OpenCV 能与深度学习模型配合进行人脸检测吗?

可以。OpenCV 支持用于人脸检测的深度学习模型,例如使用 Caffe 或 TensorFlow 的预训练 DNN 模型。OpenCV 的 cv2.dnn 模块可集成这些模型,相较于 Haar 级联可实现更先进、更准确的人脸检测。例如,您可以使用 MobileNet-SSD 模型进行实时人脸检测:

net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'res10_300x300_ssd_iter_140000.caffemodel')

主题