shape_predictor.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import numpy as np
  2. import PIL
  3. import PIL.Image
  4. import sys
  5. import os
  6. import glob
  7. import scipy
  8. import scipy.ndimage
  9. import dlib
  10. from drive import open_url
  11. from pathlib import Path
  12. import argparse
  13. from bicubic import BicubicDownSample
  14. import torchvision
  15. """
  16. brief: face alignment with FFHQ method (https://github.com/NVlabs/ffhq-dataset)
  17. author: lzhbrian (https://lzhbrian.me)
  18. date: 2020.1.5
  19. note: code is heavily borrowed from
  20. https://github.com/NVlabs/ffhq-dataset
  21. http://dlib.net/face_landmark_detection.py.html
  22. requirements:
  23. apt install cmake
  24. conda install Pillow numpy scipy
  25. pip install dlib
  26. # download face landmark model from:
  27. # http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
  28. """
  29. def get_landmark(filepath,predictor):
  30. """get landmark with dlib
  31. :return: np.array shape=(68, 2)
  32. """
  33. detector = dlib.get_frontal_face_detector()
  34. img = dlib.load_rgb_image(filepath)
  35. dets = detector(img, 1)
  36. filepath = Path(filepath)
  37. print(f"{filepath.name}: Number of faces detected: {len(dets)}")
  38. shapes = [predictor(img, d) for k, d in enumerate(dets)]
  39. lms = [np.array([[tt.x, tt.y] for tt in shape.parts()]) for shape in shapes]
  40. return lms
  41. def align_face(filepath,predictor):
  42. """
  43. :param filepath: str
  44. :return: list of PIL Images
  45. """
  46. lms = get_landmark(filepath,predictor)
  47. imgs = []
  48. for lm in lms:
  49. lm_chin = lm[0: 17] # left-right
  50. lm_eyebrow_left = lm[17: 22] # left-right
  51. lm_eyebrow_right = lm[22: 27] # left-right
  52. lm_nose = lm[27: 31] # top-down
  53. lm_nostrils = lm[31: 36] # top-down
  54. lm_eye_left = lm[36: 42] # left-clockwise
  55. lm_eye_right = lm[42: 48] # left-clockwise
  56. lm_mouth_outer = lm[48: 60] # left-clockwise
  57. lm_mouth_inner = lm[60: 68] # left-clockwise
  58. # Calculate auxiliary vectors.
  59. eye_left = np.mean(lm_eye_left, axis=0)
  60. eye_right = np.mean(lm_eye_right, axis=0)
  61. eye_avg = (eye_left + eye_right) * 0.5
  62. eye_to_eye = eye_right - eye_left
  63. mouth_left = lm_mouth_outer[0]
  64. mouth_right = lm_mouth_outer[6]
  65. mouth_avg = (mouth_left + mouth_right) * 0.5
  66. eye_to_mouth = mouth_avg - eye_avg
  67. # Choose oriented crop rectangle.
  68. x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1]
  69. x /= np.hypot(*x)
  70. x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8)
  71. y = np.flipud(x) * [-1, 1]
  72. c = eye_avg + eye_to_mouth * 0.1
  73. quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y])
  74. qsize = np.hypot(*x) * 2
  75. # read image
  76. img = PIL.Image.open(filepath)
  77. output_size = 1024
  78. transform_size = 4096
  79. enable_padding = True
  80. # Shrink.
  81. shrink = int(np.floor(qsize / output_size * 0.5))
  82. if shrink > 1:
  83. rsize = (int(np.rint(float(img.size[0]) / shrink)), int(np.rint(float(img.size[1]) / shrink)))
  84. img = img.resize(rsize, PIL.Image.ANTIALIAS)
  85. quad /= shrink
  86. qsize /= shrink
  87. # Crop.
  88. border = max(int(np.rint(qsize * 0.1)), 3)
  89. crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))),
  90. int(np.ceil(max(quad[:, 1]))))
  91. crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), min(crop[2] + border, img.size[0]),
  92. min(crop[3] + border, img.size[1]))
  93. if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]:
  94. img = img.crop(crop)
  95. quad -= crop[0:2]
  96. # Pad.
  97. pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))),
  98. int(np.ceil(max(quad[:, 1]))))
  99. pad = (max(-pad[0] + border, 0), max(-pad[1] + border, 0), max(pad[2] - img.size[0] + border, 0),
  100. max(pad[3] - img.size[1] + border, 0))
  101. if enable_padding and max(pad) > border - 4:
  102. pad = np.maximum(pad, int(np.rint(qsize * 0.3)))
  103. img = np.pad(np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect')
  104. h, w, _ = img.shape
  105. y, x, _ = np.ogrid[:h, :w, :1]
  106. mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], np.float32(w - 1 - x) / pad[2]),
  107. 1.0 - np.minimum(np.float32(y) / pad[1], np.float32(h - 1 - y) / pad[3]))
  108. blur = qsize * 0.02
  109. img += (scipy.ndimage.gaussian_filter(img, [blur, blur, 0]) - img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0)
  110. img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0)
  111. img = PIL.Image.fromarray(np.uint8(np.clip(np.rint(img), 0, 255)), 'RGB')
  112. quad += pad[:2]
  113. # Transform.
  114. img = img.transform((transform_size, transform_size), PIL.Image.QUAD, (quad + 0.5).flatten(),
  115. PIL.Image.BILINEAR)
  116. if output_size < transform_size:
  117. img = img.resize((output_size, output_size), PIL.Image.ANTIALIAS)
  118. # Save aligned image.
  119. imgs.append(img)
  120. return imgs