diff --git a/lib/cli.py b/lib/cli.py index aa4f877d9e..01b7548284 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -706,6 +706,8 @@ def get_optional_arguments(): "\nL|components: An improved face hull mask using a facehull of 8 facial " "parts." "\nL|dfl_full: An improved face hull mask using a facehull of 3 facial parts." + "\nL|extended: Based on components mask. Extends the eyebrow points to " + "further up the forehead. May perform badly on difficult angles." "\nL|facehull: Face cutout based on landmarks." "\nL|predicted: The predicted mask generated from the model. If the model was " "not trained with a mask then this will fallback to " diff --git a/lib/model/masks.py b/lib/model/masks.py index ae8a2d6f67..0f86f16834 100644 --- a/lib/model/masks.py +++ b/lib/model/masks.py @@ -121,6 +121,50 @@ def build_mask(self): 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 + # mid points between the side of face and eye point + ml_pnt = (np.array(landmarks[36]) + np.array(landmarks[0])) // 2 + mr_pnt = (np.array(landmarks[16]) + np.array(landmarks[45])) // 2 + + # mid points between the mid points and eye + ql_pnt = (np.array(landmarks[36]) + ml_pnt) // 2 + qr_pnt = (np.array(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 = np.array(landmarks[17:22]) + top_r = np.array(landmarks[22:27]) + + # Adjust eyebrow arrays + landmarks[17:22] = (top_l + ((top_l - bot_l) // 2)).tolist() + landmarks[22:27] = (top_r + ((top_r - bot_r) // 2)).tolist() + + 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): diff --git a/plugins/train/_config.py b/plugins/train/_config.py index ec67a99267..1d338b23ba 100644 --- a/plugins/train/_config.py +++ b/plugins/train/_config.py @@ -49,6 +49,8 @@ def set_globals(self): "facial parts" "\n\t dfl_full: An improved face hull mask using a facehull of 3 " "facial parts" + "\n\t extended: Based on components mask. Extends the eyebrow points to " + "further up the forehead. May perform badly on difficult angles." "\n\t facehull: Face cutout based on landmarks") self.add_item( section=section, title="icnr_init", datatype=bool, default=False,