|
import mediapipe as mp |
|
import cv2 |
|
import numpy as np |
|
import datetime |
|
import os |
|
import math |
|
from django.conf import settings |
|
|
|
|
|
mp_drawing = mp.solutions.drawing_utils |
|
mp_pose = mp.solutions.pose |
|
|
|
|
|
def calculate_angle(point1: list, point2: list, point3: list) -> float: |
|
"""Calculate the angle between 3 points |
|
|
|
Args: |
|
point1 (list): Point 1 coordinate |
|
point2 (list): Point 2 coordinate |
|
point3 (list): Point 3 coordinate |
|
|
|
Returns: |
|
float: angle in degree |
|
""" |
|
point1 = np.array(point1) |
|
point2 = np.array(point2) |
|
point3 = np.array(point3) |
|
|
|
|
|
angleInRad = np.arctan2(point3[1] - point2[1], point3[0] - point2[0]) - np.arctan2( |
|
point1[1] - point2[1], point1[0] - point2[0] |
|
) |
|
angleInDeg = np.abs(angleInRad * 180.0 / np.pi) |
|
|
|
angleInDeg = angleInDeg if angleInDeg <= 180 else 360 - angleInDeg |
|
return angleInDeg |
|
|
|
|
|
def calculate_distance(pointX: list, pointY: list) -> float: |
|
"""Calculate distance between 2 points in a frame |
|
|
|
Args: |
|
pointX (list): First point coordinate |
|
pointY (list): Second point coordinate |
|
|
|
Returns: |
|
float: _description_ |
|
""" |
|
|
|
x1, y1 = pointX |
|
x2, y2 = pointY |
|
|
|
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) |
|
|
|
|
|
def extract_important_keypoints(results, important_landmarks: list) -> list: |
|
"""Extract important landmarks' data from MediaPipe output |
|
|
|
Args: |
|
results : MediaPipe Pose output |
|
important_landmarks (list): list of important landmarks |
|
|
|
Returns: |
|
list: list of important landmarks' data from MediaPipe output |
|
""" |
|
landmarks = results.pose_landmarks.landmark |
|
|
|
data = [] |
|
for lm in important_landmarks: |
|
keypoint = landmarks[mp_pose.PoseLandmark[lm].value] |
|
data.append([keypoint.x, keypoint.y, keypoint.z, keypoint.visibility]) |
|
|
|
return np.array(data).flatten().tolist() |
|
|
|
|
|
def get_drawing_color(error: bool) -> tuple: |
|
"""Get drawing color for MediaPipe Pose |
|
|
|
Args: |
|
error (bool): True if correct pose, False if incorrect pose |
|
|
|
Returns: |
|
tuple: RGB colors |
|
""" |
|
LIGHT_BLUE = (244, 117, 66) |
|
LIGHT_PINK = (245, 66, 230) |
|
|
|
LIGHT_RED = (29, 62, 199) |
|
LIGHT_YELLOW = (1, 143, 241) |
|
|
|
return (LIGHT_YELLOW, LIGHT_RED) if error else (LIGHT_BLUE, LIGHT_PINK) |
|
|
|
|
|
|
|
def rescale_frame(frame, percent=50): |
|
"""Rescale a frame from OpenCV to a certain percentage compare to its original frame |
|
|
|
Args: |
|
frame: OpenCV frame |
|
percent (int, optional): percent to resize an old frame. Defaults to 50. |
|
|
|
Returns: |
|
_type_: OpenCV frame |
|
""" |
|
width = int(frame.shape[1] * percent / 100) |
|
height = int(frame.shape[0] * percent / 100) |
|
dim = (width, height) |
|
return cv2.resize(frame, dim, interpolation=cv2.INTER_AREA) |
|
|
|
|
|
def save_frame_as_image(frame, message: str = None): |
|
""" |
|
Save a frame as image to display the error |
|
""" |
|
now = datetime.datetime.now() |
|
|
|
if message: |
|
cv2.putText( |
|
frame, |
|
message, |
|
(50, 150), |
|
cv2.FONT_HERSHEY_COMPLEX, |
|
0.4, |
|
(0, 0, 0), |
|
1, |
|
cv2.LINE_AA, |
|
) |
|
|
|
print("Saving ...") |
|
cv2.imwrite(f"../data/logs/bicep_{now}.jpg", frame) |
|
|
|
|
|
|
|
def get_static_file_url(file_name: str) -> str: |
|
"""Return static url of a file |
|
|
|
Args: |
|
file_name (str) |
|
|
|
Returns: |
|
str: Full absolute path of the file. Return None if file is not found |
|
""" |
|
|
|
path = f"{settings.STATICFILES_DIRS[0]}/{file_name}" |
|
print(path) |
|
|
|
return path if os.path.exists(path) else None |
|
|