Xiabou/codeformer
0
1"""2brief: face alignment with FFHQ method (https://github.com/NVlabs/ffhq-dataset)3author: lzhbrian (https://lzhbrian.me)4link: https://gist.github.com/lzhbrian/bde87ab23b499dd02ba4f588258f57d55date: 2020.1.56note: code is heavily borrowed from7 https://github.com/NVlabs/ffhq-dataset8 http://dlib.net/face_landmark_detection.py.html9requirements:10 conda install Pillow numpy scipy11 conda install -c conda-forge dlib12 # download face landmark model from:13 # http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz214"""15 16import cv217import dlib18import glob19import numpy as np20import os21import PIL22import PIL.Image23import scipy24import scipy.ndimage25import sys26import argparse27 28# download model from: http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz229predictor = dlib.shape_predictor('weights/dlib/shape_predictor_68_face_landmarks-fbdc2cb8.dat')30 31 32def get_landmark(filepath, only_keep_largest=True):33 """get landmark with dlib34 :return: np.array shape=(68, 2)35 """36 detector = dlib.get_frontal_face_detector()37 38 img = dlib.load_rgb_image(filepath)39 dets = detector(img, 1)40 41 # Shangchen modified42 print("Number of faces detected: {}".format(len(dets)))43 if only_keep_largest:44 print('Detect several faces and only keep the largest.')45 face_areas = []46 for k, d in enumerate(dets):47 face_area = (d.right() - d.left()) * (d.bottom() - d.top())48 face_areas.append(face_area)49 50 largest_idx = face_areas.index(max(face_areas))51 d = dets[largest_idx]52 shape = predictor(img, d)53 print("Part 0: {}, Part 1: {} ...".format(54 shape.part(0), shape.part(1)))55 else:56 for k, d in enumerate(dets):57 print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(58 k, d.left(), d.top(), d.right(), d.bottom()))59 # Get the landmarks/parts for the face in box d.60 shape = predictor(img, d)61 print("Part 0: {}, Part 1: {} ...".format(62 shape.part(0), shape.part(1)))63 64 t = list(shape.parts())65 a = []66 for tt in t:67 a.append([tt.x, tt.y])68 lm = np.array(a)69 # lm is a shape=(68,2) np.array70 return lm71 72def align_face(filepath, out_path):73 """74 :param filepath: str75 :return: PIL Image76 """77 try:78 lm = get_landmark(filepath)79 except:80 print('No landmark ...')81 return82 83 lm_chin = lm[0:17] # left-right84 lm_eyebrow_left = lm[17:22] # left-right85 lm_eyebrow_right = lm[22:27] # left-right86 lm_nose = lm[27:31] # top-down87 lm_nostrils = lm[31:36] # top-down88 lm_eye_left = lm[36:42] # left-clockwise89 lm_eye_right = lm[42:48] # left-clockwise90 lm_mouth_outer = lm[48:60] # left-clockwise91 lm_mouth_inner = lm[60:68] # left-clockwise92 93 # Calculate auxiliary vectors.94 eye_left = np.mean(lm_eye_left, axis=0)95 eye_right = np.mean(lm_eye_right, axis=0)96 eye_avg = (eye_left + eye_right) * 0.597 eye_to_eye = eye_right - eye_left98 mouth_left = lm_mouth_outer[0]99 mouth_right = lm_mouth_outer[6]100 mouth_avg = (mouth_left + mouth_right) * 0.5101 eye_to_mouth = mouth_avg - eye_avg102 103 # Choose oriented crop rectangle.104 x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1]105 x /= np.hypot(*x)106 x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8)107 y = np.flipud(x) * [-1, 1]108 c = eye_avg + eye_to_mouth * 0.1109 quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y])110 qsize = np.hypot(*x) * 2111 112 # read image113 img = PIL.Image.open(filepath)114 115 output_size = 512116 transform_size = 4096117 enable_padding = False118 119 # Shrink.120 shrink = int(np.floor(qsize / output_size * 0.5))121 if shrink > 1:122 rsize = (int(np.rint(float(img.size[0]) / shrink)),123 int(np.rint(float(img.size[1]) / shrink)))124 img = img.resize(rsize, PIL.Image.ANTIALIAS)125 quad /= shrink126 qsize /= shrink127 128 # Crop.129 border = max(int(np.rint(qsize * 0.1)), 3)130 crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))),131 int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1]))))132 crop = (max(crop[0] - border, 0), max(crop[1] - border, 0),133 min(crop[2] + border,134 img.size[0]), min(crop[3] + border, img.size[1]))135 if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]:136 img = img.crop(crop)137 quad -= crop[0:2]138 139 # Pad.140 pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))),141 int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1]))))142 pad = (max(-pad[0] + border,143 0), max(-pad[1] + border,144 0), max(pad[2] - img.size[0] + border,145 0), max(pad[3] - img.size[1] + border, 0))146 if enable_padding and max(pad) > border - 4:147 pad = np.maximum(pad, int(np.rint(qsize * 0.3)))148 img = np.pad(149 np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)),150 'reflect')151 h, w, _ = img.shape152 y, x, _ = np.ogrid[:h, :w, :1]153 mask = np.maximum(154 1.0 -155 np.minimum(np.float32(x) / pad[0],156 np.float32(w - 1 - x) / pad[2]), 1.0 -157 np.minimum(np.float32(y) / pad[1],158 np.float32(h - 1 - y) / pad[3]))159 blur = qsize * 0.02160 img += (scipy.ndimage.gaussian_filter(img, [blur, blur, 0]) -161 img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0)162 img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0)163 img = PIL.Image.fromarray(164 np.uint8(np.clip(np.rint(img), 0, 255)), 'RGB')165 quad += pad[:2]166 167 img = img.transform((transform_size, transform_size), PIL.Image.QUAD,168 (quad + 0.5).flatten(), PIL.Image.BILINEAR)169 170 if output_size < transform_size:171 img = img.resize((output_size, output_size), PIL.Image.ANTIALIAS)172 173 # Save aligned image.174 print('saveing: ', out_path)175 img.save(out_path)176 177 return img, np.max(quad[:, 0]) - np.min(quad[:, 0])178 179 180if __name__ == '__main__':181 parser = argparse.ArgumentParser()182 parser.add_argument('--in_dir', type=str, default='./inputs/whole_imgs')183 parser.add_argument('--out_dir', type=str, default='./inputs/cropped_faces')184 args = parser.parse_args()185 186 img_list = sorted(glob.glob(f'{args.in_dir}/*.png'))187 img_list = sorted(img_list)188 189 for in_path in img_list:190 out_path = os.path.join(args.out_dir, in_path.split("/")[-1]) 191 out_path = out_path.replace('.jpg', '.png')192 size_ = align_face(in_path, out_path)