dlib的使用

本章要点:

  • dlib的安装
  • dlib简单使用
  • 人脸检测、识别
  • 人脸合成

因为要用dlib做一个人脸识别,昨晚搞了一下dlib python的安装和使用,在macOS下遇到了一些问题。

dlib的安装

主要安装步骤非常简单,如果pip能自己搞定依赖问题就好了。

安装步骤

dlib依赖boost,所以理想情况下只需要

1
2
3
$ brew install boost-python
$ sudo pip install dlib
$ sudo pip install scikit-image

因为下面要跑一个示例的需要,最后一步安装了scikit-image。

关闭SIP

如果系统开启了系统完整性保护SIP(SystemIntegrity Protection),需要:

  • 重启OS,按住⌘+R
  • 从菜单中进入终端,输入csrutil disable,关闭SIP
  • 重启OS

完成这些步骤之后再操作前面的dlib安装步骤,安装成功后记得同样的步骤,通过命令csrutil enable恢复SIP

遇到的问题

折腾最久的是这个错误:

1
2
3
4
5
#error "DLIB_NO_GUI_SUPPORT is defined so you can't use the GUI code. Turn DLIB_NO_GUI_SUPPORT off if you want to use it."
^
/usr/local/include/dlib/gui_core/gui_core_kernel_2.h:12:2: error: "Also make sure you have libx11-dev installed on your system"
#error "Also make sure you have libx11-dev installed on your system"
^

不知道最根本的原因是啥,尝试了N种办法,最后我更新了一把Xcode:
$ sudo xcode-select --install
就OK了。

dlib的使用

检测人脸区域

1
2
3
4
5
6
img = cv2.imread('images/girls.jpg')
rgbImg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
detector = dlib.get_frontal_face_detector() # 正脸检测器
faces = detector(rgbImg, 1) # 返回脸的信息
for face in faces: # 框出每张脸
cv2.rectangle(img, (face.left(), face.top()), (face.right(), face.bottom()), (0, 255, 0), 1)

如图,输入一个多人图片,通过正脸检测器可以检测出每张脸的矩形区域

检测人脸特征点

1
2
3
4
5
6
7
8
9
10
11
img = cv2.imread('images/girls.jpg')
rgbImg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
detector = dlib.get_frontal_face_detector()
faces = detector(rgbImg, 1)
for face in faces:
# 识别出关键点,keyPts的类型是dlib.points
keyPts = self.shapePredictor(img, face).parts()
landmarks = numpy.matrix([[p.x,p.y] for p in keyPts])
# 画出关键点
pts = numpy.array([landmarks], numpy.int32)
cv2.polylines(img, pts.reshape(-1, 1, 2), True, (0, 255, 0), 2)

计算人脸特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
img = io.imread(imgFile) # 输入单人头像
detector = dlib.get_frontal_face_detector()
# 检测出人脸区域
faces = detector(img, 1)
...
faceRect = faces[0]
# 训练好的人脸关键点检测器数据
predictorPath = 'extdata/shape_predictor_68_face_landmarks.dat'
# 训练好的ResNet人脸识别模型
faceRecModelPath = 'extdata/dlib_face_recognition_resnet_model_v1.dat'
shapePredictor = dlib.shape_predictor(predictorPath) # 人脸关键点监测器
faceRec = dlib.face_recognition_model_v1(faceRecModelPath) # 人脸识别模型
# 关键点检测
shape = shapePredictor(img, faceRect)
# 描述项提取, 128D向量
faceDescriptor = faceRec.compute_face_descriptor(img, shape)
# 转换为numpu array
return numpy.array(faceDescriptor)

其中:
shape_predictor_68_face_landmarks.dat是已经训练好的人脸关键点检测器数据
dlib_face_recognition_resnet_model_v1.dat是已经训练好的ResNet人脸识别模型
我在faceRek/setup.sh中写了工程依赖的外部数据,运行python代码前先执行
sh setup.sh

该段代码提取到的128D向量形式如下:

1
2
3
4
5
6
7
$ python -m unittest sample.DLibUT.test02
14:40 0117 DEBUG
[ -1.32817209e-01 7.43134990e-02 7.68444687e-02 -1.05091885e-01
-1.46399811e-01 4.86346148e-03 -1.23881295e-01 -1.71705186e-01
... ... ... ...
-1.90864969e-03 1.68816522e-02 -2.53906399e-01 1.30065437e-02
1.57092661e-01 -9.88340452e-02 9.43705738e-02 5.99186197e-02]

人脸识别

现有一堆候选人照片,给定一个待识别人照片,识别出他是候选人中的哪一个,这是人脸识别解决的主要问题。应用上一节计算出的人脸特征,计算待识别人和每位候选人之间的特征差,求最小的那个候选人,就是人脸识别的主要方法。
使用numpy.linalg.norm(candDesc - testDesc)可以将128D向量转化成一个数值,求这个最小值即可。在faceRek/sample.DLibUT.test03有完整的代码,识别结果如下,蓝色是识别正确的,红色是识别错误的:

它把莫文蔚识成了ruru 😂

人脸合成

我本来想找的合成是根据一张面孔的特征去变换另一张面孔,但我只找到了两张面孔特征部分的叠加,暂时也先收着吧。不忍心拿美女开刀,就用我自己的看效果吧🤣

训练自己的人脸关键点模型

dlib-models中Davis King提到:shape_predictor_68_face_landmarks.dat所使用的数据集许可证不包括商业用途,因此该训练模型也不能用于商业产品。

而且该模型压缩尺寸有61M,解开后99M,作者在Real-Time Face Pose Estimation的中有大量的答疑,关于性能、和编译等问题,Davis King真是勤奋!

作者提供的素材就这些,足够了!我的编译环境是mac OS,git clone dlib到本地,创建目录dlib/examples/build,然后使用cmake:

有一个警告但是可以忽略。使用Xcode打开project,我打算训练数据集dlib_faces_5points,将其目录下的两个xml文件分别改名为:
test_cleaned.xml => testing_with_face_landmarks.xml
train_cleaned.xml => training_with_face_landmarks.xml
在Xcode中点击菜单Product > Scheme > Edit Scheme…,设置命令行的输入参数:

然后就可以运行了,不过要做好思想准备,我把training_with_face_landmarks.xml中的5000多张图砍到200多张,整个训练和测试过程需要半个小时;如果把5000多张图跑一遍,从log输出的提示上来看,需要24小时!我不知道那300多万张ibug的数据跑完得要多长时间。

训练完成后,会自动跑出评测结果,看起来还不错:

1
2
mean training error: 0.011435
mean testing error: 0.00745601

接下来用python验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class DLibTrainingUT(unittest.TestCase):
def setUp(self):
...
self.mDetector = dlib.get_frontal_face_detector() # 正脸检测器
def getFaceLandmarksFromImg(self, img, predictorPath):
# 检测出人脸区域
faces = self.mDetector(img, 1)
...
faceRect = faces[0]
self.mShapePredictor = dlib.shape_predictor(predictorPath) # 人脸关键点监测器
# 提取关键点
keyPts = self.mShapePredictor(img, faceRect).parts()
landmarks = numpy.matrix([[p.x, p.y] for p in keyPts])
return landmarks
def test01(self):
''' 验证自己训练的模型效果 '''
imgFile = 'images/2-0.jpg'
img = cv2.imread(imgFile)
modelPath = '/Users/palance/Documents/SubVersions/dlib/examples/build/Debug/sp.dat'
landmarks = self.getFaceLandmarksFromImg(img, modelPath)
pts = numpy.array(landmarks, numpy.int32)
cv2.polylines(img, pts.reshape(-1, 1, 2), True, (0, 255, 0), 2)
font = cv2.FONT_HERSHEY_SIMPLEX
index = 0
for pt in landmarks:
x, y = pt[0, 0], pt[0, 1]
text = '%d' % index
cv2.putText(img, text, (x, y), font, 0.5, (255, 0, 0))
index += 1
self.waitToClose(img)

结果如下:

虽然只用了200多张图片,结果好像还不错!

参考

dlib github
dlib 官网
scikit-image 官网
scikit-learn 官网
《40行代码的人脸识别实践》
《OpenCV实践之路——用dlib库进行人脸检测与人脸标记(Python)》
《One Millisecond Face Alignment with an Ensemble of Regression Trees》
《Orthogonal Procrustes problem》
《Transformation matrix》
《Switching Eds: Face swapping with Python, dlib, and OpenCV》
《教你用200行Python代码“换脸”》
《手把手:使用OpenCV进行面部合成— C++ / Python》
https://github.com/iamwx/FaceMorph
人脸关键点检测器的训练

本文涉及的所有代码都已提交到github上:
https://github.com/palanceli/facemojiSample/tree/master/faceRek