{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import mediapipe as mp\n", "import cv2\n", "import numpy as np\n", "import pandas as pd\n", "import datetime\n", "\n", "import pickle\n", "\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Drawing helpers\n", "mp_drawing = mp.solutions.drawing_utils\n", "mp_pose = mp.solutions.pose" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Set up important functions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Determine important landmarks for plank\n", "IMPORTANT_LMS = [\n", " \"NOSE\",\n", " \"LEFT_SHOULDER\",\n", " \"RIGHT_SHOULDER\",\n", " \"RIGHT_ELBOW\",\n", " \"LEFT_ELBOW\",\n", " \"RIGHT_WRIST\",\n", " \"LEFT_WRIST\",\n", " \"LEFT_HIP\",\n", " \"RIGHT_HIP\",\n", "]\n", "\n", "# Generate all columns of the data frame\n", "\n", "HEADERS = [\"label\"] # Label column\n", "\n", "for lm in IMPORTANT_LMS:\n", " HEADERS += [f\"{lm.lower()}_x\", f\"{lm.lower()}_y\", f\"{lm.lower()}_z\", f\"{lm.lower()}_v\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def rescale_frame(frame, percent=50):\n", " '''\n", " Rescale a frame from OpenCV to a certain percentage compare to its original frame\n", " '''\n", " width = int(frame.shape[1] * percent/ 100)\n", " height = int(frame.shape[0] * percent/ 100)\n", " dim = (width, height)\n", " return cv2.resize(frame, dim, interpolation =cv2.INTER_AREA)\n", "\n", "\n", "def save_frame_as_image(frame, message: str = None):\n", " '''\n", " Save a frame as image to display the error\n", " '''\n", " now = datetime.datetime.now()\n", "\n", " if message:\n", " cv2.putText(frame, message, (50, 150), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 0, 0), 1, cv2.LINE_AA)\n", " \n", " print(\"Saving ...\")\n", " cv2.imwrite(f\"../data/logs/bicep_{now}.jpg\", frame)\n", "\n", "\n", "def calculate_angle(point1: list, point2: list, point3: list) -> float:\n", " '''\n", " Calculate the angle between 3 points\n", " Unit of the angle will be in Degree\n", " '''\n", " point1 = np.array(point1)\n", " point2 = np.array(point2)\n", " point3 = np.array(point3)\n", "\n", " # Calculate algo\n", " angleInRad = np.arctan2(point3[1] - point2[1], point3[0] - point2[0]) - np.arctan2(point1[1] - point2[1], point1[0] - point2[0])\n", " angleInDeg = np.abs(angleInRad * 180.0 / np.pi)\n", "\n", " angleInDeg = angleInDeg if angleInDeg <= 180 else 360 - angleInDeg\n", " return angleInDeg\n", "\n", "\n", "def extract_important_keypoints(results, important_landmarks: list) -> list:\n", " '''\n", " Extract important keypoints from mediapipe pose detection\n", " '''\n", " landmarks = results.pose_landmarks.landmark\n", "\n", " data = []\n", " for lm in important_landmarks:\n", " keypoint = landmarks[mp_pose.PoseLandmark[lm].value]\n", " data.append([keypoint.x, keypoint.y, keypoint.z, keypoint.visibility])\n", " \n", " return np.array(data).flatten().tolist()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. OOP method for Analyze Pose of each arm\n", "\n", "To be easier to detect both arm at the same time, I choose to do the calculation for bicep counter and error detection with OOP.\n", "\n", "*Note: Every If any joints from an arm is appeared to be in poor visibility according to mediapipe, that arm will be skip*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class BicepPoseAnalysis:\n", " def __init__(self, side: str, stage_down_threshold: float, stage_up_threshold: float, peak_contraction_threshold: float, loose_upper_arm_angle_threshold: float, visibility_threshold: float):\n", " # Initialize thresholds\n", " self.stage_down_threshold = stage_down_threshold\n", " self.stage_up_threshold = stage_up_threshold\n", " self.peak_contraction_threshold = peak_contraction_threshold\n", " self.loose_upper_arm_angle_threshold = loose_upper_arm_angle_threshold\n", " self.visibility_threshold = visibility_threshold\n", "\n", " self.side = side\n", " self.counter = 0\n", " self.stage = \"down\"\n", " self.is_visible = True\n", " self.detected_errors = {\n", " \"LOOSE_UPPER_ARM\": 0,\n", " \"PEAK_CONTRACTION\": 0,\n", " }\n", "\n", " # Params for loose upper arm error detection\n", " self.loose_upper_arm = False\n", "\n", " # Params for peak contraction error detection\n", " self.peak_contraction_angle = 1000\n", " self.peak_contraction_frame = None\n", " \n", " def get_joints(self, landmarks) -> bool:\n", " '''\n", " Check for joints' visibility then get joints coordinate\n", " '''\n", " side = self.side.upper()\n", "\n", " # Check visibility\n", " joints_visibility = [ landmarks[mp_pose.PoseLandmark[f\"{side}_SHOULDER\"].value].visibility, landmarks[mp_pose.PoseLandmark[f\"{side}_ELBOW\"].value].visibility, landmarks[mp_pose.PoseLandmark[f\"{side}_WRIST\"].value].visibility ]\n", "\n", " is_visible = all([ vis > self.visibility_threshold for vis in joints_visibility ])\n", " self.is_visible = is_visible\n", "\n", " if not is_visible:\n", " return self.is_visible\n", " \n", " # Get joints' coordinates\n", " self.shoulder = [ landmarks[mp_pose.PoseLandmark[f\"{side}_SHOULDER\"].value].x, landmarks[mp_pose.PoseLandmark[f\"{side}_SHOULDER\"].value].y ]\n", " self.elbow = [ landmarks[mp_pose.PoseLandmark[f\"{side}_ELBOW\"].value].x, landmarks[mp_pose.PoseLandmark[f\"{side}_ELBOW\"].value].y ]\n", " self.wrist = [ landmarks[mp_pose.PoseLandmark[f\"{side}_WRIST\"].value].x, landmarks[mp_pose.PoseLandmark[f\"{side}_WRIST\"].value].y ]\n", "\n", " return self.is_visible\n", " \n", " def analyze_pose(self, landmarks, frame):\n", " '''\n", " - Bicep Counter\n", " - Errors Detection\n", " '''\n", " self.get_joints(landmarks)\n", "\n", " # Cancel calculation if visibility is poor\n", " if not self.is_visible:\n", " return (None, None)\n", "\n", " # * Calculate curl angle for counter\n", " bicep_curl_angle = int(calculate_angle(self.shoulder, self.elbow, self.wrist))\n", " if bicep_curl_angle > self.stage_down_threshold:\n", " self.stage = \"down\"\n", " elif bicep_curl_angle < self.stage_up_threshold and self.stage == \"down\":\n", " self.stage = \"up\"\n", " self.counter += 1\n", " \n", " # * Calculate the angle between the upper arm (shoulder & joint) and the Y axis\n", " shoulder_projection = [ self.shoulder[0], 1 ] # Represent the projection of the shoulder to the X axis\n", " ground_upper_arm_angle = int(calculate_angle(self.elbow, self.shoulder, shoulder_projection))\n", "\n", " # * Evaluation for LOOSE UPPER ARM error\n", " if ground_upper_arm_angle > self.loose_upper_arm_angle_threshold:\n", " # Limit the saved frame\n", " if not self.loose_upper_arm:\n", " self.loose_upper_arm = True\n", " # save_frame_as_image(frame, f\"Loose upper arm: {ground_upper_arm_angle}\")\n", " self.detected_errors[\"LOOSE_UPPER_ARM\"] += 1\n", " else:\n", " self.loose_upper_arm = False\n", " \n", " # * Evaluate PEAK CONTRACTION error\n", " if self.stage == \"up\" and bicep_curl_angle < self.peak_contraction_angle:\n", " # Save peaked contraction every rep\n", " self.peak_contraction_angle = bicep_curl_angle\n", " self.peak_contraction_frame = frame\n", " \n", " elif self.stage == \"down\":\n", " # * Evaluate if the peak is higher than the threshold if True, marked as an error then saved that frame\n", " if self.peak_contraction_angle != 1000 and self.peak_contraction_angle >= self.peak_contraction_threshold:\n", " # save_frame_as_image(self.peak_contraction_frame, f\"{self.side} - Peak Contraction: {self.peak_contraction_angle}\")\n", " self.detected_errors[\"PEAK_CONTRACTION\"] += 1\n", " \n", " # Reset params\n", " self.peak_contraction_angle = 1000\n", " self.peak_contraction_frame = None\n", " \n", " return (bicep_curl_angle, ground_upper_arm_angle)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Bicep Detection" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "VIDEO_PATH1 = \"../data/db_curl/bc_test_1.mp4\"\n", "VIDEO_PATH2 = \"../data/db_curl/bc_test_2.mp4\"\n", "VIDEO_PATH3 = \"../data/db_curl/bc_test_3.mp4\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load input scaler\n", "with open(\"./model/input_scaler.pkl\", \"rb\") as f:\n", " input_scaler = pickle.load(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 3.1. Detection with SKLearn model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load model\n", "with open(\"./model/KNN_model.pkl\", \"rb\") as f:\n", " sklearn_model = pickle.load(f)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cap = cv2.VideoCapture(VIDEO_PATH3)\n", "\n", "VISIBILITY_THRESHOLD = 0.65\n", "\n", "# Params for counter\n", "STAGE_UP_THRESHOLD = 90\n", "STAGE_DOWN_THRESHOLD = 120\n", "\n", "# Params to catch FULL RANGE OF MOTION error\n", "PEAK_CONTRACTION_THRESHOLD = 60\n", "\n", "# LOOSE UPPER ARM error detection\n", "LOOSE_UPPER_ARM = False\n", "LOOSE_UPPER_ARM_ANGLE_THRESHOLD = 40\n", "\n", "# STANDING POSTURE error detection\n", "POSTURE_ERROR_THRESHOLD = 0.7\n", "posture = \"C\"\n", "\n", "# Init analysis class\n", "left_arm_analysis = BicepPoseAnalysis(side=\"left\", stage_down_threshold=STAGE_DOWN_THRESHOLD, stage_up_threshold=STAGE_UP_THRESHOLD, peak_contraction_threshold=PEAK_CONTRACTION_THRESHOLD, loose_upper_arm_angle_threshold=LOOSE_UPPER_ARM_ANGLE_THRESHOLD, visibility_threshold=VISIBILITY_THRESHOLD)\n", "\n", "right_arm_analysis = BicepPoseAnalysis(side=\"right\", stage_down_threshold=STAGE_DOWN_THRESHOLD, stage_up_threshold=STAGE_UP_THRESHOLD, peak_contraction_threshold=PEAK_CONTRACTION_THRESHOLD, loose_upper_arm_angle_threshold=LOOSE_UPPER_ARM_ANGLE_THRESHOLD, visibility_threshold=VISIBILITY_THRESHOLD)\n", "\n", "with mp_pose.Pose(min_detection_confidence=0.8, min_tracking_confidence=0.8) as pose:\n", " while cap.isOpened():\n", " ret, image = cap.read()\n", "\n", " if not ret:\n", " break\n", "\n", " # Reduce size of a frame\n", " image = rescale_frame(image, 50)\n", " # image = cv2.flip(image, 1)\n", " \n", " video_dimensions = [image.shape[1], image.shape[0]]\n", "\n", " # Recolor image from BGR to RGB for mediapipe\n", " image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n", " image.flags.writeable = False\n", "\n", " results = pose.process(image)\n", "\n", " if not results.pose_landmarks:\n", " print(\"No human found\")\n", " continue\n", "\n", " # Recolor image from BGR to RGB for mediapipe\n", " image.flags.writeable = True\n", " image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n", "\n", " # Draw landmarks and connections\n", " mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, mp_drawing.DrawingSpec(color=(244, 117, 66), thickness=2, circle_radius=2), mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2, circle_radius=1))\n", "\n", " # Make detection\n", " try:\n", " landmarks = results.pose_landmarks.landmark\n", " \n", " (left_bicep_curl_angle, left_ground_upper_arm_angle) = left_arm_analysis.analyze_pose(landmarks=landmarks, frame=image)\n", " (right_bicep_curl_angle, right_ground_upper_arm_angle) = right_arm_analysis.analyze_pose(landmarks=landmarks, frame=image)\n", "\n", " # Extract keypoints from frame for the input\n", " row = extract_important_keypoints(results, IMPORTANT_LMS)\n", " X = pd.DataFrame([row], columns=HEADERS[1:])\n", " X = pd.DataFrame(input_scaler.transform(X))\n", "\n", "\n", " # Make prediction and its probability\n", " predicted_class = sklearn_model.predict(X)[0]\n", " prediction_probabilities = sklearn_model.predict_proba(X)[0]\n", " class_prediction_probability = round(prediction_probabilities[np.argmax(prediction_probabilities)], 2)\n", "\n", " if class_prediction_probability >= POSTURE_ERROR_THRESHOLD:\n", " posture = predicted_class\n", "\n", " # Visualization\n", " # Status box\n", " cv2.rectangle(image, (0, 0), (500, 40), (245, 117, 16), -1)\n", "\n", " # Display probability\n", " cv2.putText(image, \"RIGHT\", (15, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.counter) if right_arm_analysis.is_visible else \"UNK\", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Display Left Counter\n", " cv2.putText(image, \"LEFT\", (95, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.counter) if left_arm_analysis.is_visible else \"UNK\", (100, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # * Display error\n", " # Right arm error\n", " cv2.putText(image, \"R_PC\", (165, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.detected_errors[\"PEAK_CONTRACTION\"]), (160, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", " cv2.putText(image, \"R_LUA\", (225, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.detected_errors[\"LOOSE_UPPER_ARM\"]), (220, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Left arm error\n", " cv2.putText(image, \"L_PC\", (300, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.detected_errors[\"PEAK_CONTRACTION\"]), (295, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", " cv2.putText(image, \"L_LUA\", (380, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.detected_errors[\"LOOSE_UPPER_ARM\"]), (375, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Lean back error\n", " cv2.putText(image, \"LB\", (460, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(f\"{posture}, {predicted_class}, {class_prediction_probability}\"), (440, 30), cv2.FONT_HERSHEY_COMPLEX, 0.3, (255, 255, 255), 1, cv2.LINE_AA)\n", "\n", "\n", " # * Visualize angles\n", " # Visualize LEFT arm calculated angles\n", " if left_arm_analysis.is_visible:\n", " cv2.putText(image, str(left_bicep_curl_angle), tuple(np.multiply(left_arm_analysis.elbow, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_ground_upper_arm_angle), tuple(np.multiply(left_arm_analysis.shoulder, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)\n", "\n", "\n", " # Visualize RIGHT arm calculated angles\n", " if right_arm_analysis.is_visible:\n", " cv2.putText(image, str(right_bicep_curl_angle), tuple(np.multiply(right_arm_analysis.elbow, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_ground_upper_arm_angle), tuple(np.multiply(right_arm_analysis.shoulder, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 0), 1, cv2.LINE_AA)\n", " \n", " except Exception as e:\n", " print(f\"Error: {e}\")\n", " \n", " cv2.imshow(\"CV2\", image)\n", "\n", " # if left_arm_analysis.loose_upper_arm:\n", " # save_frame_as_image(image, \"\")\n", " \n", " # Press Q to close cv2 window\n", " if cv2.waitKey(1) & 0xFF == ord('q'):\n", " break\n", "\n", " cap.release()\n", " cv2.destroyAllWindows()\n", "\n", " # (Optional)Fix bugs cannot close windows in MacOS (https://stackoverflow.com/questions/6116564/destroywindow-does-not-close-window-on-mac-using-python-and-opencv)\n", " for i in range (1, 5):\n", " cv2.waitKey(1)\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 3.2. Detection with Deep Learning model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load model\n", "with open(\"./model/bicep_model_deep_learning.pkl\", \"rb\") as f:\n", " DL_model = pickle.load(f)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cap = cv2.VideoCapture(VIDEO_PATH3)\n", "\n", "VISIBILITY_THRESHOLD = 0.65\n", "\n", "# Params for counter\n", "STAGE_UP_THRESHOLD = 90\n", "STAGE_DOWN_THRESHOLD = 120\n", "\n", "# Params to catch FULL RANGE OF MOTION error\n", "PEAK_CONTRACTION_THRESHOLD = 60\n", "\n", "# LOOSE UPPER ARM error detection\n", "LOOSE_UPPER_ARM = False\n", "LOOSE_UPPER_ARM_ANGLE_THRESHOLD = 40\n", "\n", "# STANDING POSTURE error detection\n", "POSTURE_ERROR_THRESHOLD = 0.95\n", "posture = 0\n", "\n", "# Init analysis class\n", "left_arm_analysis = BicepPoseAnalysis(side=\"left\", stage_down_threshold=STAGE_DOWN_THRESHOLD, stage_up_threshold=STAGE_UP_THRESHOLD, peak_contraction_threshold=PEAK_CONTRACTION_THRESHOLD, loose_upper_arm_angle_threshold=LOOSE_UPPER_ARM_ANGLE_THRESHOLD, visibility_threshold=VISIBILITY_THRESHOLD)\n", "\n", "right_arm_analysis = BicepPoseAnalysis(side=\"right\", stage_down_threshold=STAGE_DOWN_THRESHOLD, stage_up_threshold=STAGE_UP_THRESHOLD, peak_contraction_threshold=PEAK_CONTRACTION_THRESHOLD, loose_upper_arm_angle_threshold=LOOSE_UPPER_ARM_ANGLE_THRESHOLD, visibility_threshold=VISIBILITY_THRESHOLD)\n", "\n", "with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:\n", " while cap.isOpened():\n", " ret, image = cap.read()\n", "\n", " if not ret:\n", " break\n", "\n", " # Reduce size of a frame\n", " image = rescale_frame(image, 50)\n", " # image = cv2.flip(image, 1)\n", " \n", " video_dimensions = [image.shape[1], image.shape[0]]\n", "\n", " # Recolor image from BGR to RGB for mediapipe\n", " image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n", " image.flags.writeable = False\n", "\n", " results = pose.process(image)\n", "\n", " if not results.pose_landmarks:\n", " print(\"No human found\")\n", " continue\n", "\n", " # Recolor image from BGR to RGB for mediapipe\n", " image.flags.writeable = True\n", " image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n", "\n", " # Draw landmarks and connections\n", " mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, mp_drawing.DrawingSpec(color=(244, 117, 66), thickness=2, circle_radius=2), mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2, circle_radius=1))\n", "\n", " # Make detection\n", " try:\n", " landmarks = results.pose_landmarks.landmark\n", " \n", " (left_bicep_curl_angle, left_ground_upper_arm_angle) = left_arm_analysis.analyze_pose(landmarks=landmarks, frame=image)\n", " (right_bicep_curl_angle, right_ground_upper_arm_angle) = right_arm_analysis.analyze_pose(landmarks=landmarks, frame=image)\n", "\n", " # Extract keypoints from frame for the input\n", " row = extract_important_keypoints(results, IMPORTANT_LMS)\n", " X = pd.DataFrame([row, ], columns=HEADERS[1:])\n", " X = pd.DataFrame(input_scaler.transform(X))\n", "\n", " # Make prediction and its probability\n", " prediction = DL_model.predict(X)\n", " predicted_class = np.argmax(prediction, axis=1)[0]\n", " prediction_probability = round(max(prediction.tolist()[0]), 2)\n", "\n", " if prediction_probability >= POSTURE_ERROR_THRESHOLD:\n", " posture = predicted_class\n", "\n", " # Visualization\n", " # Status box\n", " cv2.rectangle(image, (0, 0), (500, 40), (245, 117, 16), -1)\n", "\n", " # Display probability\n", " cv2.putText(image, \"RIGHT\", (15, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.counter) if right_arm_analysis.is_visible else \"UNK\", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Display Left Counter\n", " cv2.putText(image, \"LEFT\", (95, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.counter) if left_arm_analysis.is_visible else \"UNK\", (100, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # * Display error\n", " # Right arm error\n", " cv2.putText(image, \"R_PC\", (165, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.detected_errors[\"PEAK_CONTRACTION\"]), (160, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", " cv2.putText(image, \"R_LUA\", (225, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_arm_analysis.detected_errors[\"LOOSE_UPPER_ARM\"]), (220, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Left arm error\n", " cv2.putText(image, \"L_PC\", (300, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.detected_errors[\"PEAK_CONTRACTION\"]), (295, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", " cv2.putText(image, \"L_LUA\", (380, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_arm_analysis.detected_errors[\"LOOSE_UPPER_ARM\"]), (375, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 2, cv2.LINE_AA)\n", "\n", " # Lean back error\n", " cv2.putText(image, \"LB\", (460, 12), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(\"C\" if posture == 0 else \"L\") + f\" ,{predicted_class}, {prediction_probability}\", (440, 30), cv2.FONT_HERSHEY_COMPLEX, 0.3, (255, 255, 255), 1, cv2.LINE_AA)\n", "\n", "\n", " # * Visualize angles\n", " # Visualize LEFT arm calculated angles\n", " if left_arm_analysis.is_visible:\n", " cv2.putText(image, str(left_bicep_curl_angle), tuple(np.multiply(left_arm_analysis.elbow, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(left_ground_upper_arm_angle), tuple(np.multiply(left_arm_analysis.shoulder, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)\n", "\n", "\n", " # Visualize RIGHT arm calculated angles\n", " if right_arm_analysis.is_visible:\n", " cv2.putText(image, str(right_bicep_curl_angle), tuple(np.multiply(right_arm_analysis.elbow, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 0), 1, cv2.LINE_AA)\n", " cv2.putText(image, str(right_ground_upper_arm_angle), tuple(np.multiply(right_arm_analysis.shoulder, video_dimensions).astype(int)), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 0), 1, cv2.LINE_AA)\n", " \n", " except Exception as e:\n", " print(f\"Error: {e}\")\n", " \n", " cv2.imshow(\"CV2\", image)\n", " \n", " # Press Q to close cv2 window\n", " if cv2.waitKey(1) & 0xFF == ord('q'):\n", " break\n", "\n", " cap.release()\n", " cv2.destroyAllWindows()\n", "\n", " # (Optional)Fix bugs cannot close windows in MacOS (https://stackoverflow.com/questions/6116564/destroywindow-does-not-close-window-on-mac-using-python-and-opencv)\n", " for i in range (1, 5):\n", " cv2.waitKey(1)\n", " \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.13 (conda)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.13" }, "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "9260f401923fb5c4108c543a7d176de9733d378b3752e49535ad7c43c2271b65" } } }, "nbformat": 4, "nbformat_minor": 2 }