Spaces:
Sleeping
Sleeping
File size: 5,853 Bytes
ed84fba |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
import logging
import cv2
import numpy as np
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
class Mask():
""" Parent class for masks
the output mask will be <mask_type>.mask
channels: 1, 3 or 4:
1 - Returns a single channel mask
3 - Returns a 3 channel mask
4 - Returns the original image with the mask in the alpha channel """
def __init__(self, landmarks, face, channels=4):
logger.info("Initializing %s: (face_shape: %s, channels: %s, landmarks: %s)",
self.__class__.__name__, face.shape, channels, landmarks)
self.landmarks = landmarks
self.face = face
self.channels = channels
mask = self.build_mask()
self.mask = self.merge_mask(mask)
logger.info("Initialized %s", self.__class__.__name__)
def build_mask(self):
""" Override to build the mask """
raise NotImplementedError
def merge_mask(self, mask):
""" Return the mask in requested shape """
logger.info("mask_shape: %s", mask.shape)
assert self.channels in (1, 3, 4), "Channels should be 1, 3 or 4"
assert mask.shape[2] == 1 and mask.ndim == 3, "Input mask be 3 dimensions with 1 channel"
if self.channels == 3:
retval = np.tile(mask, 3)
elif self.channels == 4:
retval = np.concatenate((self.face, mask), -1)
else:
retval = mask
logger.info("Final mask shape: %s", retval.shape)
return retval
class dfl_full(Mask): # pylint: disable=invalid-name
""" DFL facial mask """
def build_mask(self):
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
nose_ridge = (self.landmarks[27:31], self.landmarks[33:34])
jaw = (self.landmarks[0:17],
self.landmarks[48:68],
self.landmarks[0:1],
self.landmarks[8:9],
self.landmarks[16:17])
eyes = (self.landmarks[17:27],
self.landmarks[0:1],
self.landmarks[27:28],
self.landmarks[16:17],
self.landmarks[33:34])
parts = [jaw, nose_ridge, eyes]
for item in parts:
merged = np.concatenate(item)
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
return mask
class components(Mask): # pylint: disable=invalid-name
""" Component model mask """
def build_mask(self):
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
r_jaw = (self.landmarks[0:9], self.landmarks[17:18])
l_jaw = (self.landmarks[8:17], self.landmarks[26:27])
r_cheek = (self.landmarks[17:20], self.landmarks[8:9])
l_cheek = (self.landmarks[24:27], self.landmarks[8:9])
nose_ridge = (self.landmarks[19:25], self.landmarks[8:9],)
r_eye = (self.landmarks[17:22],
self.landmarks[27:28],
self.landmarks[31:36],
self.landmarks[8:9])
l_eye = (self.landmarks[22:27],
self.landmarks[27:28],
self.landmarks[31:36],
self.landmarks[8:9])
nose = (self.landmarks[27:31], self.landmarks[31:36])
parts = [r_jaw, l_jaw, r_cheek, l_cheek, nose_ridge, r_eye, l_eye, nose]
for item in parts:
merged = np.concatenate(item)
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
return mask
class extended(Mask): # pylint: disable=invalid-name
""" Extended mask
Based on components mask. Attempts to extend the eyebrow points up the forehead
"""
def build_mask(self):
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
landmarks = self.landmarks.copy()
# mid points between the side of face and eye point
ml_pnt = (landmarks[36] + landmarks[0]) // 2
mr_pnt = (landmarks[16] + landmarks[45]) // 2
# mid points between the mid points and eye
ql_pnt = (landmarks[36] + ml_pnt) // 2
qr_pnt = (landmarks[45] + mr_pnt) // 2
# Top of the eye arrays
bot_l = np.array((ql_pnt, landmarks[36], landmarks[37], landmarks[38], landmarks[39]))
bot_r = np.array((landmarks[42], landmarks[43], landmarks[44], landmarks[45], qr_pnt))
# Eyebrow arrays
top_l = landmarks[17:22]
top_r = landmarks[22:27]
# Adjust eyebrow arrays
landmarks[17:22] = top_l + ((top_l - bot_l) // 2)
landmarks[22:27] = top_r + ((top_r - bot_r) // 2)
r_jaw = (landmarks[0:9], landmarks[17:18])
l_jaw = (landmarks[8:17], landmarks[26:27])
r_cheek = (landmarks[17:20], landmarks[8:9])
l_cheek = (landmarks[24:27], landmarks[8:9])
nose_ridge = (landmarks[19:25], landmarks[8:9],)
r_eye = (landmarks[17:22], landmarks[27:28], landmarks[31:36], landmarks[8:9])
l_eye = (landmarks[22:27], landmarks[27:28], landmarks[31:36], landmarks[8:9])
nose = (landmarks[27:31], landmarks[31:36])
parts = [r_jaw, l_jaw, r_cheek, l_cheek, nose_ridge, r_eye, l_eye, nose]
for item in parts:
merged = np.concatenate(item)
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
return mask
class facehull(Mask): # pylint: disable=invalid-name
""" Basic face hull mask """
def build_mask(self):
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
hull = cv2.convexHull( # pylint: disable=no-member
np.array(self.landmarks).reshape((-1, 2)))
cv2.fillConvexPoly(mask, hull, 255.0, lineType=cv2.LINE_AA) # pylint: disable=no-member
return mask |