Bootstrap

Openface学习(一):使用dlib探测人脸并与模板人脸对齐

Openface学习(一):使用dlib探测人脸并与模板人脸对齐

 

学习了开源人脸识别程序Openface的代码。Openface的思路是先将人脸从图像中提取出来,再通过FaceNet,即使用了triplet loss的神经网络将图像分类或者识别。

本篇文章的代码的作用是,使用dlib库将人脸探测、提取的方法集合成类,方便调用。

本篇文章的代码的思路是,在通用模板中的68个人脸标记点的基础上,使用dlib库以及预训练出的参数,确定输入图像的人脸标记点,再使用opencv的内置函数进行仿射变换,得到输出对齐的图像,以便下一步训练时使用。

# 使用dlib进行人脸切割,变换

import cv2
import dlib
import numpy as np

# 这是人脸的特征点,一共68个
TEMPLATE = np.float32([( , )... # 这里填数据 ])

# 下面将人脸的关键点标准化,即寻找人脸在最左侧的点和最上侧的点,作为图像的边缘,
# 其他点以这两个边缘为基准平移;最后除以(最右侧-最左侧)和(最下侧-最上侧)
# 使人脸的标准点均匀地分布在图形里,恰好不丢失特征,也无多余像素。
(TEM_MIN, TEM_MAX) = (np.min(TEMPLATE, axis = 0), np.max(TEMPLATE, axis = 0))
STANDARD_TEMPLATE = (TEMPLATE - TEM_MIN) / (TEM_MAX - TEM_MIN)

class AlignDlib():
    INNER_EYES_AND_BOTTOM_LIP = [39, 42, 57] # 用于仿射变换的模板标记点(STANDARD_TEMPLATE)的索引
    OUTER_EYES_AND_NOSE = [36, 45, 33]

    def __init__(self, FacePredictor):
        assert FacePredictor is not None

        self.detector = dlib.get_frontal_face_detector() # 默dlib认的脸探测对象
        self.predictor = dlib.shape_predictor(FacePredictor) # 根据FacePredictor参数构建预测人脸对象

    def getAFBB(self, RgbImg):
        # get all face bounding boxes
        assert RgbImg is not None

        try:
            return self.detector(RgbImg, 1) # 探测图像中的人脸一次
        except Exception as e:
            print("Warning: {}".format(e))
            return []

    def getLFBB(self, RgbImg, SkipMulti = False):
        # get largest face bounding box
        # SkipMulti的意思是跳过多个人脸
        # 这里有一个疑问:既然对齐脸只用了一次这个函数,而且是内部调用的,
        # 跳不跳过多个脸,有什么用呢?
        assert RgbImg is not None

        faces = self.getAFBB(RgbImg) # 探测图像中所有的脸
        if  (not SkipMulti and len(faces) > 0) or len(faces) == 1:
            return max(faces, key = lambda rect: rect.width() * rect.height())
        else:
            return None

    def findL(self, RgbImg, bb): #寻找实际图像中人脸的标记点
        assert RgbImg is not None
        assert bb is not None

        points = self.predictor(RgbImg, bb)
        return list(map(lambda p: (p.x, p.y), points.parts()))

    def align(self, ImgDim, RgbImg, bb = None,
              landmarks = None, landmark_indices = 
              INNER_EYES_AND_BOTTOM_LIP,
              SkipMulti = False):

        assert RgbImg is not None
        assert landmark_indices is not None

        if ImgDim is None:
            ImgDim = 96

        if bb is None:
            # bb是已经裁剪过的人脸,是一个矩形
            bb = self.getLFBB(RgbImg, SkipMulti)
            if bb is None:
                return

        if landmarks is None:
            landmarks = self.findL(RgbImg, bb)

        np_landmarks = np.float32(landmarks) # 转成numpy的类型
        np_landmark_indices = np.array(landmark_indices)

        H = cv2.getAffineTransform(np_landmarks[
                                   np_landmark_indices], ImgDim * 
                                   STANDARD_TEMPLATE[
                                   np_landmark_indices]) # 这是opencv自带的获得仿射变换矩阵的函数
        thumb_nail = cv2.warpAffine(RgbImg, H, (ImgDim, ImgDim)) # 这是执行仿射变换,最后图像和模板标记点对齐

        return thumb_nail # 得到对齐的图像
;