前言
- 头部姿态估计是计算机视觉中的一个具有挑战性的问题,因为它需要完成多个步骤。
- 首先,我们需要在画面中定位人脸,然后识别出各种面部特征点。
- 如今,当人脸正对摄像头时,识别人脸似乎是一个简单的任务。但问题在于,当人脸处于某个角度时,由于头部的移动,某些面部特征点可能不可见。
- 接下来,我们需要将这些点转换成三维坐标来计算倾斜角度。听起来工作量很大?别担心,我们会一步步来,并参考两个非常棒的资源,这将使我们的工作变得容易得多。
目录
- 要求
- 人脸检测
- 面部特征点检测
- 姿态估计
要求
对于这个项目,我们需要OpenCV和TensorFlow,让我们安装它们吧。
使用pip
pip install opencv-python
pip install tensorflow
使用conda
conda install -c conda-forge opencv
conda install -c conda-forge tensorflow
人脸检测
我们的第一步是在图像上找到人脸,以便我们可以在上面找到面部特征点。为此,我们将使用OpenCV DNN模块的一个Caffe模型。如果你想知道它与其他模型(如Haar级联或Dlib的正面人脸检测器)相比如何,或者你想深入了解它,可以参阅这篇文章:
import cv2
import numpy as np
modelFile = "models/res10_300x300_ssd_iter_140000.caffemodel"
configFile = "models/deploy.prototxt.txt"
net = cv2.dnn.readNetFromCaffe(configFile, modelFile)
img = cv2.imread('test.jpg')
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0,
(300, 300), (104.0, 117.0, 123.0))
net.setInput(blob)
faces = net.forward()
# 在图片上绘制人脸框
for i in range(faces.shape[2]):confidence = faces[0, 0, i, 2]if confidence > 0.5:box = faces[0, 0, i, 3:7] * np.array([w, h, w, h])(x, y, x1, y1) = box.astype("int")cv2.rectangle(img, (x, y), (x1, y1), (0, 0, 255), 2)
加载网络使用cv2.dnn.readNetFromCaffe
并传递模型层和权重作为参数。它在调整为300x300大小的图像上表现最好。
面部特征点检测
最常用的可能是Dlib的面部特征点检测,它可以提供68个特征点,但准确性不高。相反,我们将使用Yin Guobing在GitHub仓库提供的面部特征点检测器。它同样提供了68个特征点,并且是一个基于TensorFlow的CNN,在五个数据集上训练!预训练模型可在此处找到。作者撰写了一系列文章解释了包括背景、数据集、预处理、模型架构、训练和部署等内容,这里强烈推荐你阅读。
在这一系列的第一篇文章中,他描述了视频中面部特征点稳定性的问题,并介绍了现有的解决方案,如OpenFace和Dlib的面部特征点检测,以及可用的数据集。第三篇文章全是关于数据预处理和使其准备好使用。接下来的两篇文章的工作是提取面部并应用面部特征点以准备训练CNN,并将它们存储为TFRecord文件。第六篇文章使用TensorFlow训练了一个模型。在这篇文章中,我们可以看到损失函数在训练中的重要性,因为最初他使用了tf.losses.mean_pairwise_squared_error
,它基于点之间的关系进行优化以最小化损失,但无法很好地泛化。相比之下,使用tf.losses.mean_squared_error
时效果很好。在最后一篇文章中,模型被导出为API,并展示了如何在Python中使用它。
该模型接受包含脸部的128x128大小的方框,并返回68个面部特征点。下面提供的代码来自此处,也可以用来在其上绘制3D注释框。代码经过修改,可以在所有脸上绘制面部特征点,而不仅仅是原始代码中的一个。
这段代码将在脸上绘制面部特征点。
绘制面部特征点
使用draw_annotation_box()
函数,我们还可以如下所示绘制注释框。
带有注释框
姿态估计
这是Learn OpenCV上的一篇好文章,它解释了如何在图像上进行头部姿态检测,并详细说明了将点转换到三维空间以及使用cv2.solvePnP
来寻找旋转和平移向量的数学原理。快速浏览那篇文章将有助于理解其内在运作,因此我在这里只简要介绍。
我们需要脸部的六个点,即鼻尖、下巴、嘴唇左右极点以及左眼左角和右眼右角。我们取这些面部特征点的标准三维坐标,并尝试估计鼻尖处的旋转和平移向量。现在,为了准确估计,我们需要相机的固有参数,如焦距、光学中心和径向畸变参数。我们可以估算前两者,并假设后者不存在以简化工作。在获得所需向量后,我们可以将那些三维点投影到二维表面上,也就是我们的图像。
如果我们仅使用现有代码并找到与x轴的角度,我们可以得到如下结果。
结果
它非常适合记录头部上下移动,但不适用于左右移动。那么如何做到这一点呢?好吧,上面我们看到了脸部上的注释框。如果我们能够利用它来测量左右移动。
带有注释框
我们可以找到两条深蓝色线之间的中间线作为指针,并找到与y轴的角度来确定移动角度。
结果
结合这两者,我们可以得到我们想要的方向。完整的代码也可以在我的GitHub仓库中找到,还有其他用于在线监考解决方案的各种子模型。
在我测试i5处理器时,即使显示图像,我也能获得健康的每秒6.76帧的速度,而面部特征点检测模型只需要0.05秒就可以找到它们。