From 005ee85d3896a41d4fac9b5381ebcdeefd2773bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laure=CE=B7t?= Date: Thu, 19 Jan 2023 09:08:25 +0100 Subject: [PATCH] feat: active bodypart --- src/active_bodypart.py | 88 ++++++++++++++++++++++++++++++++++++++++++ src/bodypart.py | 11 +++++- src/environment.py | 10 +++-- 3 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 src/active_bodypart.py diff --git a/src/active_bodypart.py b/src/active_bodypart.py new file mode 100644 index 0000000..d8b5cce --- /dev/null +++ b/src/active_bodypart.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +from mediapipe.python.solutions.drawing_utils import _normalized_to_pixel_coordinates + +from bodypart import BodyPart + +if TYPE_CHECKING: + from environment import Environment + + +def landmark2vec(landmark, width, height): + """Convert a landmark to numpy vector.""" + return np.array( + _normalized_to_pixel_coordinates( + np.clip(landmark.x, 0, 1), + np.clip(landmark.y, 0, 1), + width, + height, + ) + ) + + +class ActiveBodyPart(BodyPart): + """An active body part.""" + + def __init__( + self, + env: Environment, + image_path: str, + position: np.ndarray, + height: float, + keypoints: tuple[int, int, int, int], + ) -> None: + """Initialize the active part.""" + super().__init__(env, image_path, position, height) + self.keypoints = keypoints + + def modulate_height(self) -> None: + """Modulate the height of the part.""" + if self.env.results.multi_face_landmarks: + face_landmarks = self.env.results.multi_face_landmarks[0] + + left = landmark2vec( + face_landmarks.landmark[self.keypoints[0]], + self.env.camera_width, + self.env.camera_height, + ) + right = landmark2vec( + face_landmarks.landmark[self.keypoints[1]], + self.env.camera_width, + self.env.camera_height, + ) + top = landmark2vec( + face_landmarks.landmark[self.keypoints[3]], + self.env.camera_width, + self.env.camera_height, + ) + bottom = landmark2vec( + face_landmarks.landmark[self.keypoints[2]], + self.env.camera_width, + self.env.camera_height, + ) + + vertical = np.linalg.norm(top - bottom) + horizontal = np.linalg.norm(right - left) + + active_ratio = np.clip(0, 1, horizontal / vertical * 4) + print(active_ratio) + + self.nominal_box = ( + np.array( + [ + self.ratio * self.env.x + self.env.y * active_ratio, + self.ratio * -self.env.x + self.env.y * active_ratio, + self.ratio * -self.env.x + -self.env.y * active_ratio, + self.ratio * self.env.x + -self.env.y * active_ratio, + ] + ) + * self.height + ) + + def draw(self) -> None: + """Draw the active part on the screen.""" + self.modulate_height() + super().draw() diff --git a/src/bodypart.py b/src/bodypart.py index f4dfc03..c70f22c 100644 --- a/src/bodypart.py +++ b/src/bodypart.py @@ -12,13 +12,20 @@ if TYPE_CHECKING: class BodyPart: """A basic body part.""" - def __init__(self, env: Environment, image_path: str, position, height) -> None: + def __init__( + self, + env: Environment, + image_path: str, + position: np.ndarray, + height: float, + ) -> None: """Initialize the part.""" self.env = env self.image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) self.ratio = self.image.shape[1] / self.image.shape[0] + self.height = height self.position = position self.old_bounding_box = np.zeros((4, 1, 2)) @@ -61,7 +68,7 @@ class BodyPart: self.env.dist_coeff, ) - # interpolation with self.old_box + # interpolation with self.old_bounding_box bounding_box = (bounding_box + self.old_bounding_box) / 2 self.old_bounding_box = bounding_box diff --git a/src/environment.py b/src/environment.py index 0dcac65..030bd48 100644 --- a/src/environment.py +++ b/src/environment.py @@ -4,6 +4,7 @@ import cv2 import mediapipe as mp import numpy as np +from active_bodypart import ActiveBodyPart from bodypart import BodyPart from face_geometry import PCF, get_metric_landmarks @@ -72,17 +73,19 @@ class Environment: np.array([-13, -6, 0.1]), 1, ), - BodyPart( + ActiveBodyPart( self, "assets/eyeL.png", np.array([2.5, 2.5, 0.1]), 0.3, + (145, 159, 133, 33), ), - BodyPart( + ActiveBodyPart( self, "assets/eyeR.png", np.array([-2.5, 2.5, 0.1]), 0.3, + (374, 386, 362, 263), ), BodyPart( self, @@ -90,11 +93,12 @@ class Environment: np.array([0, 6.5, 0.5]), 0.3, ), - BodyPart( + ActiveBodyPart( self, "assets/mouth.png", np.array([0, -3.5, 0.1]), 0.15, + (14, 13, 308, 78), ), ]