Выполнение 2D Face Alignment снижает точность распознавания лиц - PullRequest
0 голосов
/ 19 апреля 2020

Я делал Распознавание лиц без выравнивания лица, но после прочтения некоторых блогов, которые инструктируют выполнять выравнивание лица в качестве важного процесса выравнивания лица, что имеет смысл, как и в большинстве реальных случаев при выводе мы не получаем полное фронтальное изображение человека. Таким образом, я реализовал выравнивание лица, используя сеть выравнивания лица , как упоминалось в этом репо, но, к моему удивлению, точность распознавания лица резко падает после этого.

Я проверил, если Выравнивание лица работает правильно, и эта часть полностью в порядке, я получаю идеально выровненные лица. Код выравнивания лица:

class AlignFace(object):
    def __init__(self, device='cuda', flip_input=False, load_model=True):
        super(AlignFace, self).__init__()
        if load_model:
            # Run the 3D face alignment on a test image, without CUDA.
            self.fa = fan.FaceAlignment(fan.LandmarksType._2D, device=device, flip_input=flip_input)
        self.landmark_positions = {
                          'face': (0, 17),
                          'eyebrow1': (17, 22),
                          'eyebrow2': (22, 27),
                          'nose': (27, 31),
                          'nostril': (31, 36),
                          'eye1': (36, 42),
                          'eye2': (42, 48),
                          'lips': (48, 60),
                          'teeth': (60, 68),
                  }

    def align_face(self, img_rgb, detected_faces, face_landmarks, scale=0.9, mtcnn=False):
        '''
        face alignment API for all image, get the landmark of eyes and nose and do warpaffine transformation
        :return: an aligned single face image
        '''
        aligned = []
        if not face_landmarks:
            face_landmarks = self.fa.get_landmarks(img_rgb, detected_faces)
        if len(face_landmarks):
            for face_landmark, detected_face in zip(face_landmarks, detected_faces):
                left_eye_center = face_landmark['left_eye'] if mtcnn else self._find_center_pt(face_landmark[self.landmark_positions['eye1'][0]:self.landmark_positions['eye1'][1], :])
                right_eye_center = face_landmark['right_eye'] if mtcnn else self._find_center_pt(face_landmark[self.landmark_positions['eye2'][0]:self.landmark_positions['eye2'][1], :])
                nose_center = face_landmark['nose'] if mtcnn else self._find_center_pt(face_landmark[self.landmark_positions['nose'][0]:self.landmark_positions['nose'][1], :])
                x1, y1, x2, y2 = detected_face
                face_img = img_rgb[y1:y2, x1:x2, :]
                if config.DEBUG: cv2.imwrite('data/actual.jpg', cv2.cvtColor(face_img, cv2.COLOR_RGB2BGR))
                h, w = face_img.shape[:2]
                trotate = self._get_rotation_matrix(left_eye_center, right_eye_center, nose_center, scale)
                warped = cv2.warpAffine(face_img, trotate, (w, h))
                aligned.append(warped)
                if config.DEBUG: cv2.imwrite('data/warped.jpg', cv2.cvtColor(warped, cv2.COLOR_RGB2BGR))
        return aligned

    def _find_center_pt(self, points):
        '''
        find centroid point by several points that given
        '''
        x = 0
        y = 0
        num = len(points)
        for pt in points:
            x += pt[0]
            y += pt[1]
        x //= num
        y //= num
        return (x,y)

    def _angle_between_2_pt(self, p1, p2):
        '''
        to calculate the angle rad by two points
        '''
        delta = 1e-3
        x1, y1 = p1
        x2, y2 = p2
        tan_angle = (y2 - y1) / ((x2 - x1) + delta)
        return (np.degrees(np.arctan(tan_angle)))

    def _get_rotation_matrix(self, left_eye_pt, right_eye_pt, nose_center, scale):
        '''
        to get a rotation matrix by using skimage, including rotate angle, transformation distance and the scale factor
        '''
        eye_angle = self._angle_between_2_pt(left_eye_pt, right_eye_pt)
        M = cv2.getRotationMatrix2D((nose_center[0]/2, nose_center[1]/2), eye_angle, scale )

        return M

    def _dist_nose_tip_center_and_img_center(self, nose_pt, img_shape):
        '''
        find the distance between nose tip's centroid and the centroid of original image
        '''
        y_img, x_img, _ = img_shape
        img_center = (x_img//2, y_img//2)
        return ((img_center[0] - nose_pt[0]), -(img_center[1] - nose_pt[1]))

    def _crop_face(self, img, face_loc, padding_size=0):
        '''
        crop face into small image, face only, but the size is not the same
        '''
        h, w, c = img.shape
        top = face_loc[0] - padding_size
        right = face_loc[1] + padding_size
        down = face_loc[2] + padding_size
        left = face_loc[3] - padding_size

        if top < 0:
            top = 0
        if right > w - 1:
            right = w - 1
        if down > h - 1:
            down = h - 1
        if left < 0:
            left = 0
        img_crop = img[top:down, left:right]
        return img_crop

    def _face_locations_raw(self, img, scale):
    #     img_scale = (tf.resize(img, (img.shape[0]//scale, img.shape[1]//scale)) * 255).astype(np.uint8)
        h, w, c = img.shape
        img_scale = cv2.resize(img, (int(img.shape[1]//scale), int(img.shape[0]//scale)))
        face_loc_small = fr.face_locations(img_scale)
        face_loc = []
        for ff in face_loc_small:
            tmp = [pt*scale for pt in ff]
            if tmp[1] >= w:
                tmp[1] = w
            if tmp[2] >= h:
                tmp[2] = h
            face_loc.append(tmp)
        return face_loc

    def _face_locations_small(self, img):
        for scale in [16, 8, 4, 2, 1]:
            face_loc = _face_locations_raw(img, scale)
            if face_loc != []:
                return face_loc
        return []

До выравнивания лица

Total number of test samples: 1002
Blurr frames: 18
Total number of test samples processed: 984
Total number of registered persons: 102
Total false positive: 26.0, Total false negative: 14.0
confidence threshold: 50,  precision: 94, recall: 97, f1: 96, accuracy: 92

После выравнивания лица

Total number of test samples: 1002
Blurr frames: 18
Total number of test samples processed: 984
Total number of registered persons: 102
Total false positive: 30.0, Total false negative: 48.0
confidence threshold: 50,  precision: 93, recall: 89, f1: 91, accuracy: 83

Q1: я делаю что-то не так в выравнивании лица?

Q2: нужно ли выравнивание лица? Как я могу использовать его для повышения точности?

enter image description here

PS: тестовые изображения в основном являются селфи, снятыми в дикий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...