用Python检测人脸特征
今天我们要学习如何用图像来检测人脸并提取面部特征,如眼睛、鼻子、嘴巴等。作为一个预处理步骤,我们可以做很多不可思议的事情,比如捕捉人脸来标记照片中的人(手动或通过机器学习),创建效果来 "增强 "我们的图像(类似于Snapchat等应用程序中的效果),对人脸做情感分析等等。
在过去,我们曾经介绍过如何使用OpenCV来检测图像中的形状,但今天我们将通过介绍DLib,以及从图像中抽象出人脸特征,将其提升到一个新的水平。
Dlib是一个先进的机器学习库,是为了解决复杂的现实世界的问题而创建的。这个库是用C++编程语言创建的,它可以与C/C++、Python和Java一起工作。
值得注意的是,本教程可能需要以前对OpenCV库有一些了解,如如何处理图像、打开相机、图像处理和一些小技巧。
它是如何工作的?
我们的脸有几个可以识别的特征,比如我们的眼睛、嘴巴、鼻子,等等。当我们使用DLib算法来检测这些特征时,我们实际上得到了一张围绕每个特征的点的地图。
这个由67个点(称为地标点)组成的地图可以识别以下特征。
点图
- 下颚点 = 0-16
- 右边的眉毛点 = 17-21
- 左边的眉毛点 = 22-26
- 鼻子点 = 27-35
- 右眼点 = 36-41
- 左眼穴 = 42-47
- 嘴部点数 = 48-60
- 嘴唇 = 61-67
现在我们知道了一些关于我们计划如何提取特征的情况,让我们开始编码。
安装要求
像往常一样,本文将用代码介绍例子,我将一步一步地指导你实现一个完全可行的人脸特征识别的例子。但在我们开始之前,你需要启动一个新的Python项目并安装3个不同的库。
- opencv-python
- dlib
如果你像我一样使用pipenv,你可以用下面的命令安装所有这些库。
pipenv install opencv-python, dlib
如果你在Mac上工作,以及某些版本的Linux,你在安装dlib时可能会有一些问题,如果你在安装过程中出现编译错误,请确保你检查你所使用的CMake库的版本。在Mac上要确保你有CMake可用,并且有正确的版本可以运行。
brew install cmake
对于其他操作系统,请在网上查询具体的支持。
第1步:加载和呈现图像
我们将从小事做起,在代码上做文章,直到我们有一个完整的工作实例。
通常情况下,我喜欢用绘图来渲染图像,但由于我们为后面的文章准备了一些很酷的东西,我们将做一些不同的事情,我们将创建一个窗口来显示我们工作的结果。
让我们跳进代码
import cv2
# read the image
img = cv2.imread("face.jpg")
# show the image
cv2.imshow(winname="Face", mat=img)
# Wait for a key press to exit
cv2.waitKey(delay=0)
# Close all windows
cv2.destroyAllWindows()
很简单,对吗?我们只是用imread加载图片,然后告诉OpenCV在winname中显示图片,这将打开窗口并给它一个标题。
之后,我们需要暂停执行,因为当脚本停止时,窗口将被销毁,所以我们用cv2.waitKey来保持窗口,直到有键被按下,之后,我们销毁窗口并退出脚本。
如果你使用这段代码,并在代码目录中添加一张名为face.jpg的图片,你应该得到如下的结果。
原始图像
第2步:人脸识别
到目前为止,我们还没有对图像做任何处理,只是把它呈现在一个窗口中,相当无聊,但现在我们将开始编码好东西,我们将从识别图像中哪里有一张脸开始。
为此,我们将使用Dlib函数get_frontal_face_detector() ,相当直观。不过有一点需要注意,这个函数只适用于灰度图像,所以我们必须先用OpenCV来做这个。
get_frontal_face_detector() 将返回一个detector ,这是一个我们可以用来检索人脸信息的函数。每个面孔都是一个对象,包含可以找到图像的点。
但让我们更好地在代码中看到它。
import cv2
import dlib
# Load the detector
detector = dlib.get_frontal_face_detector()
# read the image
img = cv2.imread("face.jpg")
# Convert image into grayscale
gray = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2GRAY)
# Use detector to find landmarks
faces = detector(gray)
for face in faces:
x1 = face.left() # left point
y1 = face.top() # top point
x2 = face.right() # right point
y2 = face.bottom() # bottom point
# Draw a rectangle
cv2.rectangle(img=img, pt1=(x1, y1), pt2=(x2, y2), color=(0, 255, 0), thickness=4)
# show the image
cv2.imshow(winname="Face", mat=img)
# Wait for a key press to exit
cv2.waitKey(delay=0)
# Close all windows
cv2.destroyAllWindows()
上面的代码将从图像中检索出所有的面孔,并在每个面孔上渲染一个矩形,从而得到一个如下的图像。
到目前为止,我们在寻找人脸方面做得很好,但我们仍然需要一些工作来提取所有的特征(地标)。接下来让我们来做这个工作。
第3步:识别脸部特征
你喜欢魔术吗?到目前为止,DLib的工作方式相当神奇,只用几行代码我们就可以实现很多,现在我们有了一个全新的问题,还会继续这么简单吗?
简短的回答是YES!原来DLib提供了一个叫做shape_predictor() 的函数,它将为我们完成所有的魔法,但有一个注意事项,它需要一个预先训练好的模型来工作。
有几个模型可以与shape_predictor ,我使用的模型可以在这里下载,但可以自由地尝试其他模型。
让我们看看现在的新代码是什么样子的
import cv2
import dlib
# Load the detector
detector = dlib.get_frontal_face_detector()
# Load the predictor
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# read the image
img = cv2.imread("face.jpg")
# Convert image into grayscale
gray = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2GRAY)
# Use detector to find landmarks
faces = detector(gray)
for face in faces:
x1 = face.left() # left point
y1 = face.top() # top point
x2 = face.right() # right point
y2 = face.bottom() # bottom point
# Look for the landmarks
landmarks = predictor(image=gray, box=face)
x = landmarks.part(27).x
y = landmarks.part(27).y
# Draw a circle
cv2.circle(img=img, center=(x, y), radius=5, color=(0, 255, 0), thickness=-1)
# show the image
cv2.imshow(winname="Face", mat=img)
# Wait for a key press to exit
cv2.waitKey(delay=0)
# Close all windows
cv2.destroyAllWindows()
像以前一样,我们总是建立在相同的代码上,现在使用我们的预测器函数为每张脸找到地标。现在我还是在做一些奇怪的事情,比如数字27在那里做什么?
landmarks = predictor(image=gray, box=face)
x = landmarks.part(27).x
y = landmarks.part(27).y
我们的预测函数将返回一个对象,其中包含所有符合脸部的68个点,根据我们之前看到的图,如果你注意一下,27号点正好在眼睛之间,所以如果所有的工作正常,你应该看到脸部的眼睛之间有一个绿色的点,就像这里一样。
我们已经非常接近了,现在让我们渲染所有的点,而不是只有一个。
import cv2
import numpy as np
import dlib
# Load the detector
detector = dlib.get_frontal_face_detector()
# Load the predictor
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# read the image
img = cv2.imread("face.jpg")
# Convert image into grayscale
gray = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2GRAY)
# Use detector to find landmarks
faces = detector(gray)
for face in faces:
x1 = face.left() # left point
y1 = face.top() # top point
x2 = face.right() # right point
y2 = face.bottom() # bottom point
# Create landmark object
landmarks = predictor(image=gray, box=face)
# Loop through all the points
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
# Draw a circle
cv2.circle(img=img, center=(x, y), radius=3, color=(0, 255, 0), thickness=-1)
# show the image
cv2.imshow(winname="Face", mat=img)
# Delay between every fram
cv2.waitKey(delay=0)
# Close all windows
cv2.destroyAllWindows()
Tada!魔术。
但是,如果你对所有的点不感兴趣呢?好吧......你实际上可以调整你的range 间隔,以获得上面术语表中指定的任何特征,就像我在这里做的那样。
令人惊讶,但我们能不能做一些更酷的事情呢?
第4步:实时检测
是的,你没看错!是的,这可能是你正在思考的问题!下一步是连接我们的网络摄像头,从你的视频流中进行实时地标识别。
你可以通过用你的摄像头迭代视频帧来对你的脸进行实时的面部地标检测,或者使用视频文件。如果你想使用你自己的摄像头,请参考下面的代码,但对于视频文件,请确保将数字0改为视频路径。
如果你想结束这个窗口,请按键盘上的ESC键。
import cv2
import dlib
# Load the detector
detector = dlib.get_frontal_face_detector()
# Load the predictor
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# read the image
cap = cv2.VideoCapture(0)
while True:
_, frame = cap.read()
# Convert image into grayscale
gray = cv2.cvtColor(src=frame, code=cv2.COLOR_BGR2GRAY)
# Use detector to find landmarks
faces = detector(gray)
for face in faces:
x1 = face.left() # left point
y1 = face.top() # top point
x2 = face.right() # right point
y2 = face.bottom() # bottom point
# Create landmark object
landmarks = predictor(image=gray, box=face)
# Loop through all the points
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
# Draw a circle
cv2.circle(img=frame, center=(x, y), radius=3, color=(0, 255, 0), thickness=-1)
# show the image
cv2.imshow(winname="Face", mat=frame)
# Exit when escape is pressed
if cv2.waitKey(delay=1) == 27:
break
# When everything done, release the video capture and video write objects
cap.release()
# Close all windows
cv2.destroyAllWindows()
最后的结果是这样的。

从原始视频创建的GIF,我不得不削减帧以使GIF有一个合适的大小。
从原始视频创建的GIF,我不得不削减帧,以使GIF的一个体面的大小。即使在光线不足的情况下,结果也相当准确,虽然上面的图片有一些错误,但在光线较好的情况下,效果非常好。
结语
OpenCV和DLib是强大的库,简化了ML和计算机视觉的工作。今天我们只是接触到了最基本的东西,还有很多东西可以从它们身上学到。