import cv2 import math import numpy as np import face_alignment class FaceDetect: def __init__(self, device, detector): # landmarks will be detected by face_alignment library. Set device = 'cuda' if use GPU. self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, device=device, face_detector=detector) def align(self, image): landmarks = self.__get_max_face_landmarks(image) if landmarks is None: return None else: return self.__rotate(image, landmarks) def __get_max_face_landmarks(self, image): preds = self.fa.get_landmarks(image) if preds is None: return None elif len(preds) == 1: return preds[0] else: # find max face areas = [] for pred in preds: landmarks_top = np.min(pred[:, 1]) landmarks_bottom = np.max(pred[:, 1]) landmarks_left = np.min(pred[:, 0]) landmarks_right = np.max(pred[:, 0]) areas.append((landmarks_bottom - landmarks_top) * (landmarks_right - landmarks_left)) max_face_index = np.argmax(areas) return preds[max_face_index] @staticmethod def __rotate(image, landmarks): # rotation angle left_eye_corner = landmarks[36] right_eye_corner = landmarks[45] radian = np.arctan((left_eye_corner[1] - right_eye_corner[1]) / (left_eye_corner[0] - right_eye_corner[0])) # image size after rotating height, width, _ = image.shape cos = math.cos(radian) sin = math.sin(radian) new_w = int(width * abs(cos) + height * abs(sin)) new_h = int(width * abs(sin) + height * abs(cos)) # translation Tx = new_w // 2 - width // 2 Ty = new_h // 2 - height // 2 # affine matrix M = np.array([[cos, sin, (1 - cos) * width / 2. - sin * height / 2. + Tx], [-sin, cos, sin * width / 2. + (1 - cos) * height / 2. + Ty]]) image_rotate = cv2.warpAffine(image, M, (new_w, new_h), borderValue=(255, 255, 255)) landmarks = np.concatenate([landmarks, np.ones((landmarks.shape[0], 1))], axis=1) landmarks_rotate = np.dot(M, landmarks.T).T return image_rotate, landmarks_rotate if __name__ == '__main__': img = cv2.cvtColor(cv2.imread('3989161_1.jpg'), cv2.COLOR_BGR2RGB) fd = FaceDetect(device='cpu') face_info = fd.align(img) if face_info is not None: image_align, landmarks_align = face_info for i in range(landmarks_align.shape[0]): cv2.circle(image_align, (int(landmarks_align[i][0]), int(landmarks_align[i][1])), 2, (255, 0, 0), -1) cv2.imwrite('image_align.png', cv2.cvtColor(image_align, cv2.COLOR_RGB2BGR))