Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import cv2 | |
| from os.path import join | |
| import dlib | |
| import os | |
| from PIL import Image | |
| import urllib.request | |
| import imageio | |
| width_ratio = 1.5 | |
| top_ratio = 1.5 | |
| gap_ratio = 0.1 | |
| down_ratio = 4.5 | |
| chin_width_ratio = 2.8 | |
| forehead_ratio = 0.3 | |
| verb = False | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| PREDICTOR_PATH = os.path.join(BASE_DIR, "shape_predictor_68_face_landmarks.dat") | |
| eye_cascade = cv2.CascadeClassifier(os.path.join(BASE_DIR, "haarcascade_eye.xml")) | |
| assert not eye_cascade.empty() | |
| SCALE_FACTOR = 1 | |
| FEATHER_AMOUNT = 11 | |
| FACE_POINTS = list(range(17, 68)) | |
| MOUTH_POINTS = list(range(48, 61)) | |
| RIGHT_BROW_POINTS = list(range(17, 22)) | |
| LEFT_BROW_POINTS = list(range(22, 27)) | |
| RIGHT_EYE_POINTS = list(range(36, 42)) | |
| LEFT_EYE_POINTS = list(range(42, 48)) | |
| NOSE_POINTS = list(range(27, 35)) | |
| JAW_POINTS = list(range(0, 17)) | |
| OVERLAY_POINTS = [ | |
| LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS, | |
| NOSE_POINTS + MOUTH_POINTS, | |
| ] | |
| detector = dlib.get_frontal_face_detector() | |
| predictor = dlib.shape_predictor(PREDICTOR_PATH) | |
| class TooManyFaces(Exception): | |
| pass | |
| class NoFaces(Exception): | |
| pass | |
| def get_landmarks(im): | |
| rects = detector(im, 1) | |
| if len(rects) > 1: | |
| raise TooManyFaces | |
| if len(rects) == 0: | |
| raise NoFaces | |
| return np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()]) | |
| def read_imgURL(URL): | |
| with urllib.request.urlopen(URL) as url: | |
| with open('temp.jpg', 'wb') as f: | |
| f.write(url.read()) | |
| img = Image.open('temp.jpg') | |
| img = np.array(img) | |
| return img | |
| def draw_convex_hull(im, points, color): | |
| points = cv2.convexHull(points) | |
| cv2.fillConvexPoly(im, points, color=color) | |
| def get_face_mask(im, landmarks): | |
| im = np.zeros(im.shape[:2], dtype=np.float64) | |
| for group in OVERLAY_POINTS: | |
| draw_convex_hull(im, | |
| landmarks[group], | |
| color=1) | |
| im = np.array([im, im, im]).transpose((1, 2, 0)) | |
| im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0 | |
| im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) | |
| return im | |
| def read_im_and_landmarks(fname): | |
| im = np.array(fname) | |
| im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR, | |
| im.shape[0] * SCALE_FACTOR)) | |
| s = get_landmarks(im) | |
| return im, s | |
| def warp_im(im, M, dshape): | |
| output_im = np.zeros(dshape, dtype=im.dtype) | |
| cv2.warpAffine(im, | |
| M[:2], | |
| (dshape[1], dshape[0]), | |
| dst=output_im, | |
| borderMode=cv2.BORDER_TRANSPARENT, | |
| flags=cv2.WARP_INVERSE_MAP) | |
| return output_im | |
| def infer_chin_region(eye, width_ratio, down_ratio, left_or_right): | |
| region1 = [0] * 4 | |
| if left_or_right == 'right': #assuming it is the absolute right chin | |
| region1[0] = int(max(0, int(eye[0] - 0.5 * eye[2]))) #chin region should go lefwards | |
| region1[2] = int(0.5 * eye[2]) | |
| else: # assuming it is the absolute left chin | |
| region1[0] = int(eye[0] + eye[2]) # chin region should go rightwards | |
| region1[2] = int(0.5 * eye[2]) | |
| region1[1] = int(eye[1] + eye[3]) | |
| region1[3] = int(1.5 * eye[3]) | |
| return region1 | |
| def detect_face_direction(gray, face, eye, down_ratio, chin_width_ratio): | |
| region1 = [0] * 4 # assuming this is the left eye, forhead should go rightward | |
| region2 = [0] * 4 # assuming this is the right eye, forhead should go leftward | |
| print(eye[0]) | |
| region1 = infer_chin_region(eye[0], chin_width_ratio, down_ratio, 'left') #region1 is from eye to right | |
| region2 = infer_chin_region(eye[0], chin_width_ratio, down_ratio, 'right') # region2 is from eye to left | |
| std1 = np.std(gray[region1[1]:(region1[1]+region1[3]), region1[0]:(region1[0]+region1[2])]) | |
| std2 = np.std(gray[region2[1]:(region2[1]+region2[3]), region2[0]:(region2[0]+region2[2])]) | |
| face_direction = "" | |
| if std1 > std2: #eye right has higher variance than eye left | |
| face_direction = "right" | |
| else: | |
| face_direction = "left" | |
| return face_direction | |
| def extract_cheek_region(face_x_min, face_x_max, face_y_max, eye_landmarks, left_or_right): | |
| if left_or_right == "Left": | |
| cheek_region_min_x = eye_landmarks[0,0] | |
| cheek_region_max_x = int(face_x_max - 0.05 * (face_x_max - min(eye_landmarks[:,0]))) | |
| else: | |
| cheek_region_max_x = max(eye_landmarks[:,0])[0,0] | |
| #print (max(eye_landmarks[:,0])[0,0]) | |
| #cheek_region_max_x = max(eye_landmarks[:, 0]) | |
| cheek_region_min_x = int(face_x_min + 0.1 * (cheek_region_max_x - face_x_min)) | |
| cheek_region_min_y = int(max(eye_landmarks[:,1]) + 0.2 * (max(eye_landmarks[:,1]) - min(eye_landmarks[:,1]))) | |
| cheek_region_max_y = int(face_y_max - 0.1 * (face_y_max - max(eye_landmarks[:,1]))) | |
| return [cheek_region_min_x, cheek_region_min_y, cheek_region_max_x, cheek_region_max_y] | |
| def extract_patches(imagefile, dimension_dict, face_loc_dict, image_dim, croppedFaces_Dir): | |
| imageName = "temp" | |
| img, landmarks = read_im_and_landmarks(imagefile) | |
| face_detected = True | |
| img_height, img_width = img.shape[0:2] | |
| image_dim = [img_height, img_width] | |
| min_dim = min(img_height, img_width) | |
| min_face_size = min(min_dim * 0.2, min_dim * 0.2) | |
| min_eye = min_face_size * 0.2 | |
| min_eye_area = min_eye ** 2 | |
| gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) | |
| if face_detected: | |
| mask = get_face_mask(img, landmarks) | |
| face_x_min = int(max(0, np.asarray(min(landmarks[:,0])).flatten()[0])) | |
| face_x_max = int(min(img_width, np.asarray(max(landmarks[:,0])).flatten()[0])) | |
| face_y_min = int(max(0, np.asarray(min(landmarks[:,1])).flatten()[0])) | |
| face_y_max = int(min(img_height, np.asarray(max(landmarks[:,1])).flatten()[0])) | |
| face_loc_dict['face_loc'] = [face_x_min, face_x_max, face_y_min, face_y_max] | |
| face_height = face_y_max - face_y_min | |
| forehead_height = int(face_height * forehead_ratio) | |
| new_face_y_min = max(0, face_y_min - forehead_height) | |
| right_brow_landmarks = landmarks[RIGHT_BROW_POINTS,:] | |
| left_brow_landmarks = landmarks[LEFT_BROW_POINTS,:] | |
| right_eye_landmarks = landmarks[RIGHT_EYE_POINTS,:] | |
| left_eye_landmarks = landmarks[LEFT_EYE_POINTS,:] | |
| mouse_landmarks = landmarks[MOUTH_POINTS,:] | |
| ######################## | |
| # Get the forehead patch | |
| ######################## | |
| [right_brow_min_x, left_brow_max_x] = \ | |
| [max(0, np.min(np.array(right_brow_landmarks[:,0]))), min(img_width, np.max(np.array(left_brow_landmarks[:,0])))] | |
| brow_min_y = min(np.min(np.array(right_brow_landmarks[:,1])),np.min(np.array(left_brow_landmarks[:,1]))) | |
| forehead_x_min = right_brow_min_x | |
| forehead_x_max = left_brow_max_x | |
| forehead_y_min = max(0, brow_min_y - forehead_height) | |
| forehead_y_max = min(brow_min_y, forehead_y_min + forehead_height) | |
| forehead_region = img[forehead_y_min:forehead_y_max, forehead_x_min:forehead_x_max, :] | |
| #print ('forehead dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (forehead_x_min, forehead_x_max, forehead_y_min, forehead_y_max)) | |
| key_name = 'landmark_fh' | |
| dimension_dict[key_name] = [forehead_x_min, forehead_x_max, forehead_y_min, forehead_y_max] | |
| forehead_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| #forehead_region = cv2.cvtColor(forehead_region, cv2.COLOR_BGR2RGB) | |
| imageio.imwrite(forehead_file_name, forehead_region) | |
| chin_x_min = np.max(np.array(right_eye_landmarks[:,0])) | |
| chin_x_max = np.min(np.array(left_eye_landmarks[:,0])) | |
| chin_y_min = np.max(np.array(mouse_landmarks[:,1])) | |
| chin_y_max = face_y_max | |
| chin_region = img[chin_y_min:chin_y_max, chin_x_min:chin_x_max, :] | |
| #print ('chin dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (chin_x_min, chin_x_max, chin_y_min, chin_y_max)) | |
| key_name = 'landmark_chin' | |
| dimension_dict[key_name] = [chin_x_min, chin_x_max, chin_y_min, chin_y_max] | |
| chin_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| #chin_region = cv2.cvtColor(chin_region, cv2.COLOR_BGR2RGB) | |
| imageio.imwrite(chin_file_name, chin_region) | |
| ########################## | |
| # Get the cheeks patch | |
| ########################## | |
| # Decide whether it is a side view or not | |
| left_eye_width = np.max(np.array(left_eye_landmarks[:,0])) - np.min(np.array(left_eye_landmarks[:,0])) | |
| right_eye_width = np.max(np.array(right_eye_landmarks[:,0])) - np.min(np.array(right_eye_landmarks[:,0])) | |
| right_face = True | |
| left_face = True | |
| if float(right_eye_width) / float(left_eye_width) >= 1.15: # right eye is bigger than left eye, showing the right face | |
| left_face = False | |
| elif float(left_eye_width) / float(right_eye_width) >= 1.15: # left eye is bigger than right eye, showing the left face | |
| right_face = False | |
| if right_face: | |
| right_cheek_region = extract_cheek_region(face_x_min, face_x_max, face_y_max, right_eye_landmarks, "Right") | |
| cheek_region = img[right_cheek_region[1]:right_cheek_region[3], right_cheek_region[0]:right_cheek_region[2], :] | |
| #print ('right cheek dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (right_cheek_region[0], right_cheek_region[2], right_cheek_region[1], right_cheek_region[3])) | |
| key_name = 'landmark_rc' | |
| dimension_dict[key_name] = [right_cheek_region[0], right_cheek_region[2], right_cheek_region[1], right_cheek_region[3]] | |
| cheek_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| #cheek_region = cv2.cvtColor(cheek_region, cv2.COLOR_BGR2RGB) | |
| imageio.imwrite(cheek_file_name, cheek_region) | |
| if left_face: | |
| left_cheek_region = extract_cheek_region(face_x_min, face_x_max, face_y_max, left_eye_landmarks, "Left") | |
| cheek_region = img[left_cheek_region[1]:left_cheek_region[3], left_cheek_region[0]:left_cheek_region[2], :] | |
| #print ('left cheek dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (left_cheek_region[0], left_cheek_region[2], left_cheek_region[1], left_cheek_region[3])) | |
| key_name = 'landmark_lc' | |
| dimension_dict[key_name] = [left_cheek_region[0], left_cheek_region[2], left_cheek_region[1], left_cheek_region[3]] | |
| cheek_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| #cheek_region = cv2.cvtColor(cheek_region, cv2.COLOR_BGR2RGB) | |
| imageio.imwrite(cheek_file_name, cheek_region) | |
| if not face_detected: | |
| print("Face not detected by landmarks model...") | |
| # Use the OneEye model to detect one eye, and infer the face region based on the eye location | |
| eye_detected = False | |
| roi_gray = gray | |
| roi_color = img | |
| roi_color = cv2.cvtColor(roi_color, cv2.COLOR_BGR2RGB) | |
| eyes = eye_cascade.detectMultiScale(roi_gray, 1.1, 5) | |
| max_area = 0 | |
| eye_count = 0 | |
| max_index = 0 | |
| for (ex,ey,ew,eh) in eyes: # there might be multiple eyes detected. Choose the biggest one | |
| if ew*eh >= max_area and ex >= img_width * 0.1 and ex <= img_width * 0.9: | |
| max_area = ew*eh | |
| max_index = eye_count | |
| eye_count += 1 | |
| if max_area >= min_eye_area: | |
| eye_detected = True | |
| (ex, ey, ew, eh) = eyes[max_index] | |
| if float(ew) / float(img_width) > 0.15 or float(eh) / float(img_height) > 0.15: # detected eye too large | |
| # resize the detected eye | |
| center_x = ex + ew/2 | |
| center_y = ey + eh/2 | |
| resized_w = min(img_width * 0.15, img_height * 0.15) | |
| ex = int(center_x - resized_w/2) | |
| ey = int(center_y - resized_w/2) | |
| ew = int(resized_w) | |
| eh = int(resized_w) | |
| eyes1 = np.array([ex, ey, resized_w, resized_w]).reshape((1,4)) | |
| else: | |
| eyes1 = np.array(eyes[max_index]).reshape((1,4)) | |
| face1 = np.array(()) | |
| face_direction = detect_face_direction(gray, face1, eyes1, down_ratio, chin_width_ratio) | |
| if face_direction == "left": | |
| print("Left eye detected") | |
| face_min_x = eyes1[0, 0] | |
| face_max_x = min(img_width, int(eyes1[0,0] + (chin_width_ratio + 0.5) * eyes1[0, 2])) | |
| forehead_max_x = min(img_width, int(eyes1[0,0] + width_ratio * eyes1[0, 2])) | |
| forehead_min_x = face_min_x | |
| cheek_min_x = int(eyes1[0, 0] + 0.5 * eyes1[0,2]) | |
| cheek_max_x = face_max_x | |
| else: | |
| print("Right eye detected") | |
| face_min_x = max(0, int(eyes1[0, 0] - chin_width_ratio * eyes1[0, 2])) | |
| face_max_x = eyes1[0, 0] + eyes1[0, 2] | |
| forehead_min_x = max(0, int(eyes1[0, 0] - width_ratio * eyes1[0, 2])) | |
| forehead_max_x = min(img_width, int(eyes1[0, 0] + width_ratio * eyes1[0, 2])) | |
| cheek_max_x = int(eyes1[0,0] + 0.5*eyes1[0,2]) | |
| cheek_min_x = face_min_x | |
| forehead_min_y = max(0, int(eyes1[0, 1] - top_ratio * eyes1[0,3])) | |
| forehead_max_y = max(0, int(eyes1[0, 1] - 0.5 * eyes1[0, 3])) | |
| forehead_ok = False | |
| # Get the forehead region | |
| if forehead_max_y - forehead_min_y >= 0.7 * eyes1[0, 3]: | |
| forehead_ok = True | |
| forehead_region = img[forehead_min_y:forehead_max_y, forehead_min_x: forehead_max_x, :] | |
| #print ('forehead dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (forehead_min_x, forehead_max_x, forehead_min_y, forehead_max_y)) | |
| key_name = 'oneeye_fh' | |
| dimension_dict[key_name] = [forehead_min_x, forehead_max_x, forehead_min_y, forehead_max_y] | |
| forehead_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| imageio.imwrite(forehead_file_name, forehead_region) | |
| # Get the cheek region | |
| cheek_min_y = int(eyes1[0, 1] + eyes1[0, 3]) | |
| cheek_max_y = min(img_height, int(eyes1[0, 1] + down_ratio * eyes1[0, 3])) | |
| cheek_region = img[cheek_min_y: cheek_max_y, cheek_min_x: cheek_max_x, :] | |
| #print ('cheek dim (x_min, x_max, y_min, y_max): %i,%i, %i, %i' % (cheek_min_x, cheek_max_x, cheek_min_y, cheek_max_y)) | |
| key_name = 'oneeye_cheek' | |
| dimension_dict[key_name] = [cheek_min_x, cheek_max_x, cheek_min_y, cheek_max_y] | |
| face_loc_dict['face_loc'] = [face_min_x, face_max_x, forehead_min_y, cheek_max_y] | |
| #cheek_region = cv2.cvtColor(cheek_region, cv2.COLOR_BGR2RGB) | |
| if face_direction == "left": | |
| cheek_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| elif face_direction == "right": | |
| cheek_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| else: | |
| cheek_file_name = join(croppedFaces_Dir, key_name +".jpg") | |
| imageio.imwrite(cheek_file_name, cheek_region) | |
| if (not face_detected) and (not eye_detected): | |
| print("No chin or forehead detected, output the original file %s.jpg"%imageName) | |
| img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) | |
| outfile = join(croppedFaces_Dir, imageName+".jpg") | |
| imageio.imwrite(outfile, img) | |
| return dimension_dict, face_loc_dict, image_dim |