faceswap/lib/align/constants.py
torzdf 1c081aea7d
Add ability to export and import alignment data (#1383)
* tools.alignments - add export job

* plugins.extract: Update __repr__ for ExtractorBatch dataclass

* plugins.extract: Initial implementation of external import plugins

* plugins.extract: Disable lm masks on ROI alignment data import

* lib.align: Add `landmark_type` property to AlignedFace and return dummy data for ROI Landmarks pose estimate

* plugins.extract: Add centering config item for align import and fix filename mapping for images

* plugins.extract: Log warning on downstream plugins on limited alignment data

* tools: Fix plugins for 4 point ROI landmarks (alignments, sort, mask)

* tools.manual: Fix for 2D-4 ROI landmarks

* training: Fix for 4 point ROI landmarks

* lib.convert: Average color plugin. Avoid divide by zero errors

* extract - external:
  - Default detector to 'external' when importing alignments
  - Handle different frame origin co-ordinates

* alignments: Store video extension in alignments file

* plugins.extract.external: Handle video file keys

* plugins.extract.external: Output warning if missing data

* locales + docs

* plugins.extract.align.external: Roll the corner points to top-left for different origins

* Clean up

* linting fix
2024-04-15 12:19:15 +01:00

112 lines
5.5 KiB
Python

#!/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
CenteringType = T.Literal["face", "head", "legacy"]
EXTRACT_RATIOS: dict[CenteringType, float] = {"legacy": 0.375, "face": 0.5, "head": 0.625}
"""dict[Literal["legacy", "face", head"] float]: The amount of padding applied to each
centering type when generating aligned faces """
class LandmarkType(Enum):
""" Enumeration for the landmark types that Faceswap supports """
LM_2D_4 = 1
LM_2D_51 = 2
LM_2D_68 = 3
LM_3D_26 = 4
@classmethod
def from_shape(cls, shape: tuple[int, ...]) -> LandmarkType:
""" The landmark type for a given shape
Parameters
----------
shape: tuple[int, ...]
The shape to get the landmark type for
Returns
-------
Type[LandmarkType]
The enum for the given shape
Raises
------
ValueError
If the requested shape is not valid
"""
shapes: dict[tuple[int, ...], LandmarkType] = {(4, 2): cls.LM_2D_4,
(51, 2): cls.LM_2D_51,
(68, 2): cls.LM_2D_68,
(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]
_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
"""dict[:class:`~LandmarkType, np.ndarray]: '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": (8, 11, False)},
LandmarkType.LM_2D_4: {"face": (0, 4, True)}}
"""dict[:class:`LandmarkType`, dict[str, tuple[int, int, bool]]: For each landmark type, stores
the (start index, end index, is polygon) information about each part of the face. """