#!/usr/bin/env python3 """Constants that are required across faceswap's lib.align package""" from __future__ import annotations import typing as T from enum import Enum import numpy as np from lib.utils import get_module_objects CenteringType = T.Literal["face", "head", "legacy"] class LandmarkType(Enum): """Enumeration for the landmark types that Faceswap supports """ LM_2D_4 = 1 LM_2D_51 = 2 LM_2D_68 = 3 LM_2D_98 = 4 LM_3D_26 = 5 @classmethod def from_shape(cls, shape: tuple[int, int]) -> LandmarkType: """The landmark type for a given shape Parameters ---------- shape The shape to get the landmark type for Returns ------- The enum for the given shape Raises ------ ValueError If the requested shape is not valid """ shapes: dict[tuple[int, int], LandmarkType] = {(4, 2): cls.LM_2D_4, (51, 2): cls.LM_2D_51, (68, 2): cls.LM_2D_68, (98, 2): cls.LM_2D_98, (26, 3): cls.LM_3D_26} if shape not in shapes: raise ValueError(f"The given shape {shape} is not valid. Valid shapes: {list(shapes)}") return shapes[shape] EXTRACT_RATIOS: dict[CenteringType, float] = {"legacy": 0.375, "face": 0.5, "head": 0.625} """The amount of padding applied to each centering type when generating aligned faces""" MEAN_FACE: dict[LandmarkType, np.ndarray] = { LandmarkType.LM_2D_4: np.array( [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]), # Clockwise from TL LandmarkType.LM_2D_51: np.array([ [0.010086, 0.106454], [0.085135, 0.038915], [0.191003, 0.018748], [0.300643, 0.034489], [0.403270, 0.077391], [0.596729, 0.077391], [0.699356, 0.034489], [0.808997, 0.018748], [0.914864, 0.038915], [0.989913, 0.106454], [0.500000, 0.203352], [0.500000, 0.307009], [0.500000, 0.409805], [0.500000, 0.515625], [0.376753, 0.587326], [0.435909, 0.609345], [0.500000, 0.628106], [0.564090, 0.609345], [0.623246, 0.587326], [0.131610, 0.216423], [0.196995, 0.178758], [0.275698, 0.179852], [0.344479, 0.231733], [0.270791, 0.245099], [0.192616, 0.244077], [0.655520, 0.231733], [0.724301, 0.179852], [0.803005, 0.178758], [0.868389, 0.216423], [0.807383, 0.244077], [0.729208, 0.245099], [0.264022, 0.780233], [0.350858, 0.745405], [0.438731, 0.727388], [0.500000, 0.742578], [0.561268, 0.727388], [0.649141, 0.745405], [0.735977, 0.780233], [0.652032, 0.864805], [0.566594, 0.902192], [0.500000, 0.909281], [0.433405, 0.902192], [0.347967, 0.864805], [0.300252, 0.784792], [0.437969, 0.778746], [0.500000, 0.785343], [0.562030, 0.778746], [0.699747, 0.784792], [0.563237, 0.824182], [0.500000, 0.831803], [0.436763, 0.824182]]), LandmarkType.LM_3D_26: np.array([ [4.056931, -11.432347, 1.636229], # 8 chin LL [1.833492, -12.542305, 4.061275], # 7 chin L [0.0, -12.901019, 4.070434], # 6 chin C [-1.833492, -12.542305, 4.061275], # 5 chin R [-4.056931, -11.432347, 1.636229], # 4 chin RR [6.825897, 1.275284, 4.402142], # 33 L eyebrow L [1.330353, 1.636816, 6.903745], # 29 L eyebrow R [-1.330353, 1.636816, 6.903745], # 34 R eyebrow L [-6.825897, 1.275284, 4.402142], # 38 R eyebrow R [1.930245, -5.060977, 5.914376], # 54 nose LL [0.746313, -5.136947, 6.263227], # 53 nose L [0.0, -5.485328, 6.76343], # 52 nose C [-0.746313, -5.136947, 6.263227], # 51 nose R [-1.930245, -5.060977, 5.914376], # 50 nose RR [5.311432, 0.0, 3.987654], # 13 L eye L [1.78993, -0.091703, 4.413414], # 17 L eye R [-1.78993, -0.091703, 4.413414], # 25 R eye L [-5.311432, 0.0, 3.987654], # 21 R eye R [2.774015, -7.566103, 5.048531], # 43 mouth L [0.509714, -7.056507, 6.566167], # 42 mouth top L [0.0, -7.131772, 6.704956], # 41 mouth top C [-0.509714, -7.056507, 6.566167], # 40 mouth top R [-2.774015, -7.566103, 5.048531], # 39 mouth R [-0.589441, -8.443925, 6.109526], # 46 mouth bottom R [0.0, -8.601736, 6.097667], # 45 mouth bottom C [0.589441, -8.443925, 6.109526]])} # 44 mouth bottom L """'Mean' landmark points for various landmark types. Used for aligning faces""" LANDMARK_PARTS: dict[LandmarkType, dict[str, tuple[int, int, bool]]] = { LandmarkType.LM_2D_68: {"mouth_outer": (48, 60, True), "mouth_inner": (60, 68, True), "right_eyebrow": (17, 22, False), "left_eyebrow": (22, 27, False), "right_eye": (36, 42, True), "left_eye": (42, 48, True), "nose": (27, 36, False), "jaw": (0, 17, False), "chin": (7, 9, False)}, LandmarkType.LM_2D_98: {"mouth_outer": (76, 88, True), "mouth_inner": (88, 96, True), "right_eyebrow": (33, 42, True), "left_eyebrow": (42, 51, True), "right_eye": (60, 68, True), "left_eye": (68, 76, True), "nose": (51, 60, False), "jaw": (0, 33, False), "chin": (14, 19, False)}, LandmarkType.LM_2D_4: {"face": (0, 4, True)} } """For each landmark type, stores the (start index, end index, is polygon) information about each part of the face.""" LANDMARK_MASK_PARTS: dict[LandmarkType, dict[str, list[tuple[int, int]]]] = { LandmarkType.LM_2D_68: {"right_jaw": [(0, 9), (17, 18)], "left_jaw": [(8, 17), (26, 27)], "right_cheek": [(17, 20), (8, 9)], "left_cheek": [(24, 27), (8, 9)], "nose_ridge": [(19, 25), (8, 9)], "right_eye": [(17, 22), (27, 28), (31, 36), (8, 9)], "left_eye": [(22, 27), (27, 28), (31, 36), (8, 9)], "nose": [(27, 31), (31, 36)]}, LandmarkType.LM_2D_98: {"right_jaw": [(0, 17), (33, 34)], "left_jaw": [(16, 33), (46, 47)], "right_cheek": [(33, 36), (16, 17)], "left_cheek": [(44, 47), (16, 17)], "nose_ridge": [(35, 45), (16, 17)], "right_eye": [(33, 38), (51, 52), (55, 60), (16, 17)], "left_eye": [(42, 47), (51, 52), (55, 60), (16, 17)], "nose": [(51, 55), (55, 60)]} } """For each landmark type, stores the (start index, end index) information about each part of the face that makes a face mask.""" MAP_2D_68 = { LandmarkType.LM_2D_98: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, # Jaw 33, 34, 35, 36, 37, # Right eyebrow 42, 43, 44, 45, 46, # Left eyebrow 51, 52, 53, 54, 55, 56, 57, 58, 59, # Nose 60, 61, 63, 64, 65, 67, # Right eye 68, 69, 71, 72, 73, 75, # Left eye 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, # Outer mouth 88, 89, 90, 91, 92, 93, 94, 95] # Inner mouth } """Mapping of non 68 point 2D landmarks to 68 point landmarks""" __all__ = get_module_objects(__name__)