diff --git a/binary_classified_image.jpg b/binary_classified_image.jpg new file mode 100644 index 0000000..b6b60b7 Binary files /dev/null and b/binary_classified_image.jpg differ diff --git a/landmarks.py b/landmarks.py index 83ce7d2..9b9cd6c 100644 --- a/landmarks.py +++ b/landmarks.py @@ -1,12 +1,15 @@ import warnings import os +from tabulate import tabulate +import math -warnings.filterwarnings("ignore", category=UserWarning, module="google.protobuf") +warnings.filterwarnings("ignore", + category=UserWarning, + module="google.protobuf") os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import argparse -import math import cv2 from mediapipe.python.solutions import pose import logging @@ -28,11 +31,9 @@ class Landmarker: self.side_image = cv2.imread(args.side_image) self.front_image_resized = cv2.resize( - self.front_image, (self.resized_height, self.resized_width) - ) + self.front_image, (self.resized_height, self.resized_width)) self.side_image_resized = cv2.resize( - self.side_image, (self.resized_height, self.resized_width) - ) + self.side_image, (self.resized_height, self.resized_width)) self.person_height = args.person_height self.pixel_height = args.pixel_height @@ -60,8 +61,14 @@ class Landmarker: def parse_args(self): parser = argparse.ArgumentParser() - parser.add_argument("--front", dest="front_image", type=str, help="Front image") - parser.add_argument("--side", dest="side_image", type=str, help="Side image") + parser.add_argument("--front", + dest="front_image", + type=str, + help="Front image") + parser.add_argument("--side", + dest="side_image", + type=str, + help="Side image") parser.add_argument( "--pose_detection_confidence", dest="pose_detection_confidence", @@ -98,13 +105,13 @@ class Landmarker: logging.warning("person's pixel height: %s", self.pixel_height) - front_results = self.process_images() + front_results, side_results = self.process_images() self.get_center_top_point(front_results) self.calculate_distance_betn_landmarks(front_results) - self.print_distance() + self.output() self.display_images() @@ -112,11 +119,9 @@ class Landmarker: def process_images(self): front_results = self.pose.process( - cv2.cvtColor(self.front_image_resized, cv2.COLOR_BGR2RGB) - ) + cv2.cvtColor(self.front_image_resized, cv2.COLOR_BGR2RGB)) side_results = self.pose.process( - cv2.cvtColor(self.side_image_resized, cv2.COLOR_BGR2RGB) - ) + cv2.cvtColor(self.side_image_resized, cv2.COLOR_BGR2RGB)) self.side_image_keypoints = self.side_image_resized.copy() self.front_image_keypoints = self.front_image_resized.copy() @@ -133,18 +138,7 @@ class Landmarker: side_results.pose_landmarks, # type: ignore# type: ignore self.landmarks_indices, ) - return front_results - - def calculate_distance(self, landmark1, landmark2): - l1 = landmark1.x, landmark1.y - l1 = int(l1[0] * self.resized_height), int(l1[1] * self.resized_width) - x1, y1 = l1 - l2 = landmark2.x, landmark2.y - l2 = int(l2[0] * self.resized_height), int(l2[1] * self.resized_width) - x2, y2 = l2 - pixel_distance = self.euclidean_distance(x1, x2, y1, y2) - real_distance = pixel_distance * self.pixel_to_metric_ratio() - return real_distance + return front_results, side_results def pixel_to_metric_ratio(self): self.pixel_height = self.pixel_distance * 2 @@ -162,50 +156,60 @@ class Landmarker: def circle(self, image, cx, cy): return cv2.circle(image, (cx, cy), 2, (255, 0, 0), -1) - def print_distance(self): - print( - "Distance between left shoulder and left elbow:", - self.distance_left_hand_up, - ) + def output(self): + table = [[ + "shoulder_elbow", + ], [ + "elbow_wrist", + ], [ + "hip_knee", + ], [ + "knee_ankel", + ]] + output = tabulate(table, + headers=["measurement", "value"], + tablefmt="plain") + print(output) - print( - "Distance between left elbow and left wrist:", - self.distance_left_hand_down, - ) + def get_landmarks(self): + self.landmarks_to_calculate = [[ + pose.PoseLandmark.LEFT_SHOULDER, pose.PoseLandmark.LEFT_ELBOW, + pose.PoseLandmark.LEFT_WRIST + ], + [ + pose.PoseLandmark.LEFT_HIP, + pose.PoseLandmark.LEFT_KNEE, + pose.PoseLandmark.LEFT_ANKLE + ]] + #return - print("Distance between left hip and left knee:", - self.distance_left_leg_up) - - print("Distance between left knee and left ankle:", - self.distance_left_leg_down) - - print("Distance between center and top point:", self.distance) - - print("Height of person:", self.distance * 2) - - def calculate_distance_betn_landmarks(self, front_results): + def calculate_distance_betn_landmarks(self, front_results, landmarks=[]): if not front_results.pose_landmarks: return landmarks = front_results.pose_landmarks.landmark + landmarks_to_calculate = [ + pose.PoseLandmark.LEFT_SHOULDER, pose.PoseLandmark.LEFT_ELBOW, + pose.PoseLandmark.LEFT_WRIST + ] + #landmarks_to_calculate = self.get_landmarks(); + for l in landmarks_to_calculate: + #l1 = int(l1[0] * self.resized_height), int(l1[1] * self.resized_width) + real_distance = 0 + for idx, l in enumerate(landmarks_to_calculate): + if idx < len(landmarks_to_calculate) - 1: + _current = landmarks[l.value] + _nextl = landmarks_to_calculate[idx + 1] + _next = landmarks[_nextl.value] + pixel_distance = self.euclidean_distance( + _current.x, _current.y, _next.x, _next.y) + print(pixel_distance) + real_distance += pixel_distance * self.pixel_to_metric_ratio( + ) + print(real_distance) - shoulder_left = landmarks[pose.PoseLandmark.LEFT_SHOULDER.value] - elbow_left = landmarks[pose.PoseLandmark.LEFT_ELBOW.value] - wrist_left = landmarks[pose.PoseLandmark.LEFT_WRIST.value] - hip_left = landmarks[pose.PoseLandmark.LEFT_HIP.value] - knee_left = landmarks[pose.PoseLandmark.LEFT_KNEE.value] - ankle_left = landmarks[pose.PoseLandmark.LEFT_ANKLE.value] - - self.distance_left_hand_up = self.calculate_distance(shoulder_left, elbow_left) - - self.distance_left_hand_down = self.calculate_distance(elbow_left, wrist_left) - - self.distance_left_leg_up = self.calculate_distance(hip_left, knee_left) - - self.distance_left_leg_down = self.calculate_distance(knee_left, ankle_left) - - def euclidean_distance(self, x1, x2, y1, y2): - distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) + def euclidean_distance(self, x1, y1, x2, y2): + distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) return distance def destroy(self): @@ -218,21 +222,20 @@ class Landmarker: cv2.waitKey(0) def get_center_top_point(self, side_results): - gray_image = cv2.cvtColor(self.side_image_keypoints, cv2.COLOR_BGR2GRAY) + gray_image = cv2.cvtColor(self.side_image_keypoints, + cv2.COLOR_BGR2GRAY) blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0) - roi = blurred_image[0 : int(self.side_image_resized.shape[0] / 2), :] + roi = blurred_image[0:int(self.side_image_resized.shape[0] / 2), :] self.edges = cv2.Canny(roi, 50, 150) - contours, _ = cv2.findContours( - self.edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE - ) + contours, _ = cv2.findContours(self.edges, cv2.RETR_EXTERNAL, + cv2.CHAIN_APPROX_SIMPLE) xt, yt = None, None self.topmost_point = None if contours: largest_contour = max(contours, key=cv2.contourArea) self.topmost_point = tuple( - largest_contour[largest_contour[:, :, 1].argmin()][0] - ) + largest_contour[largest_contour[:, :, 1].argmin()][0]) xt, yt = self.topmost_point self.circle(self.side_image_keypoints, xt, yt) @@ -258,9 +261,9 @@ class Landmarker: self.circle(self.side_image_keypoints, xc, yc) self.pixel_distance = self.euclidean_distance(xc, xt, yc, yt) - self.distance = ( - self.euclidean_distance(xc, xt, yc, yt) * self.pixel_to_metric_ratio() - ) + self.distance = (self.euclidean_distance(xc, xt, yc, yt) * + self.pixel_to_metric_ratio()) + return self.distance diff --git a/temp.py b/temp.py index 94f3df6..1feb889 100644 --- a/temp.py +++ b/temp.py @@ -1,40 +1,61 @@ +import warnings +import os +from tabulate import tabulate + +warnings.filterwarnings("ignore", + category=UserWarning, + module="google.protobuf") + +os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" + import argparse import math import cv2 -import mediapipe as mp +from mediapipe.python.solutions import pose +import logging class Landmarker: + resized_height = 256 + resized_width = 300 def __init__(self) -> None: args = self.parse_args() - if args.front_image == None: - raise Exception("front image needs to be passed") - if args.side_image == None: - raise Exception("side image needs to be passed") + if args.front_image is None: + raise Exception("Front image needs to be passed") + if args.side_image is None: + raise Exception("Side image needs to be passed") self.front_image = cv2.imread(args.front_image) self.side_image = cv2.imread(args.side_image) - mp_pose = mp.solutions.pose - self.pose = mp_pose.Pose( + self.front_image_resized = cv2.resize( + self.front_image, (self.resized_height, self.resized_width)) + self.side_image_resized = cv2.resize( + self.side_image, (self.resized_height, self.resized_width)) + + self.person_height = args.person_height + self.pixel_height = args.pixel_height + + self.pose = pose.Pose( static_image_mode=True, min_detection_confidence=args.pose_detection_confidence, min_tracking_confidence=args.pose_tracking_confidence, ) - self.resized_height = 256 - self.resized_width = int( - self.front_image.shape[1] * - (self.resized_height / self.front_image.shape[0])) - - self.front_image_resized = cv2.resize( - self.front_image, (self.resized_width, self.resized_height)) - self.side_image_resized = cv2.resize( - self.side_image, (self.resized_width, self.resized_height)) - self.landmarks_indices = [ - 11, 12, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28 + pose.PoseLandmark.LEFT_SHOULDER.value, + pose.PoseLandmark.RIGHT_SHOULDER.value, + pose.PoseLandmark.LEFT_ELBOW.value, + pose.PoseLandmark.RIGHT_ELBOW.value, + pose.PoseLandmark.LEFT_WRIST.value, + pose.PoseLandmark.RIGHT_WRIST.value, + pose.PoseLandmark.LEFT_HIP.value, + pose.PoseLandmark.RIGHT_HIP.value, + pose.PoseLandmark.LEFT_KNEE.value, + pose.PoseLandmark.RIGHT_KNEE.value, + pose.PoseLandmark.LEFT_ANKLE.value, + pose.PoseLandmark.RIGHT_ANKLE.value, ] def parse_args(self): @@ -47,19 +68,52 @@ class Landmarker: dest="side_image", type=str, help="Side image") - parser.add_argument("--pose_detection_confidence", - deafult=0.5, - dest="Pose detection confidence", - type=float, - help="Confidence score for pose detection") - parser.add_argument("--pose_tracking_confidence", - default=0.5, - dest="Pose detection tracking", - type=float, - help="Confidence score for pose tracking") + parser.add_argument( + "--pose_detection_confidence", + dest="pose_detection_confidence", + default=0.5, + type=float, + help="Confidence score for pose detection", + ) + parser.add_argument( + "--pose_tracking_confidence", + dest="pose_tracking_confidence", + default=0.5, + type=float, + help="Confidence score for pose tracking", + ) + parser.add_argument( + "--person_height", + default=153, + dest="person_height", + type=int, + help="Person height", + ) + parser.add_argument( + "--pixel_height", + dest="pixel_height", + type=int, + help="Pixel height of person", + ) return parser.parse_args() def run(self): + logging.warning("Person's height: %s", self.person_height) + logging.warning("Person's pixel height: %s", self.pixel_height) + + front_results, side_results = self.process_images() + + self.get_center_top_point(front_results) + + self.calculate_distance_for_landmarks(front_results) + + self.output() + + self.display_images() + + self.pose.close() + + def process_images(self): front_results = self.pose.process( cv2.cvtColor(self.front_image_resized, cv2.COLOR_BGR2RGB)) side_results = self.pose.process( @@ -68,143 +122,116 @@ class Landmarker: self.side_image_keypoints = self.side_image_resized.copy() self.front_image_keypoints = self.front_image_resized.copy() - if front_results.pose_landmarks: + if front_results.pose_landmarks: # type: ignore self.draw_landmarks( self.front_image_keypoints, - front_results.pose_landmarks, + front_results.pose_landmarks, # type: ignore self.landmarks_indices, ) - if side_results.pose_landmarks: + if side_results.pose_landmarks: # type: ignore self.draw_landmarks( self.side_image_keypoints, - side_results.pose_landmarks, + side_results.pose_landmarks, # type: ignore self.landmarks_indices, ) + return front_results, side_results - self.get_center_top_point(front_results) - - self.print_distance(front_results) - - self.display_images() - - self.pose.close() - - def calculate_distance(self, landmark1, landmark2): - l1 = landmark1.x, landmark1.y - l1 = int(l1[0] * self.resized_width), int(l1[1] * self.resized_height) - x1, y1 = l1 - l2 = landmark2.x, landmark2.y - l2 = int(l2[0] * self.resized_width), int(l2[1] * self.resized_height) - x2, y2 = l2 - pixel_distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) - real_distance = pixel_distance * self.pixel_to_metric_ratio() + def calculate_distance_for_landmarks(self, landmarks): + real_distance = 0 + if len(landmarks) > 1: + for i in range(0, len(landmarks) - 1): + _current = landmarks[self.landmarks_indices[i]] + _next = landmarks[self.landmarks_indices[i + 1]] + pixel_distance = self.euclidean_distance( + _current.x, _current.y, _next.x, _next.y) + real_distance += pixel_distance * self.pixel_to_metric_ratio() + logging.warning("Total real distance: %s", real_distance) return real_distance def pixel_to_metric_ratio(self): - height_person = 153 - pixel_distance_height = 255 - pixel_to_metric_ratio = height_person / pixel_distance_height - pixel_to_metric_ratio = round(pixel_to_metric_ratio) + self.pixel_height = self.pixel_distance * 2 + pixel_to_metric_ratio = self.person_height / self.pixel_height + logging.warning("Pixel to metric ratio: %s", pixel_to_metric_ratio) return pixel_to_metric_ratio def draw_landmarks(self, image, landmarks, indices): for idx in indices: - landmark = landmarks.landmark[idx] + landmark = landmarks[idx] h, w, _ = image.shape cx, cy = int(landmark.x * w), int(landmark.y * h) - cv2.circle(image, (cx, cy), 2, (255, 0, 0), -1) + self.circle(image, cx, cy) - def print_distance(self, front_results): - if not front_results.pose_landmarks: - return + def circle(self, image, cx, cy): + return cv2.circle(image, (cx, cy), 2, (255, 0, 0), -1) - landmarks = front_results.pose_landmarks.landmark + def output(self): + table = [["shoulder", 1], ["neck", 2]] + output = tabulate(table, + headers=["Measurement", "Value"], + tablefmt="plain") + print(output) - shoulder_left = landmarks[11] - elbow_left = landmarks[13] - wrist_left = landmarks[15] - hip_left = landmarks[23] - knee_left = landmarks[25] - ankle_left = landmarks[27] + def euclidean_distance(self, x1, y1, x2, y2): + distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) + return distance - distance_left_hand_up = self.calculate_distance( - shoulder_left, elbow_left) - print("Distance between left shoulder and left elbow:", - distance_left_hand_up) - - distance_left_hand_down = self.calculate_distance( - elbow_left, wrist_left) - print("Distance between left elbow and left wrist:", - distance_left_hand_down) - - distance_left_leg_up = self.calculate_distance(hip_left, knee_left) - print("Distance between left hip and left knee:", distance_left_leg_up) - - distance_left_leg_down = self.calculate_distance(knee_left, ankle_left) - print("Distance between left knee and left ankle:", - distance_left_leg_down) - - def destroy(): + def destroy(self): cv2.destroyAllWindows() def display_images(self): cv2.imshow("front_image_keypoints", self.front_image_keypoints) cv2.imshow("side_image_keypoints", self.side_image_keypoints) + cv2.imshow("edges", self.edges) + cv2.waitKey(0) - def get_center_top_point(self, front_results): - gray_image = cv2.cvtColor(self.front_image_keypoints, + def get_center_top_point(self, side_results): + gray_image = cv2.cvtColor(self.side_image_keypoints, cv2.COLOR_BGR2GRAY) blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0) - roi = blurred_image[0:int(self.resized_height / 2), :] - edges = cv2.Canny(roi, 50, 150) - contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, - cv2.CHAIN_APPROX_SIMPLE) - + roi = blurred_image[0:int(self.side_image_resized.shape[0] / 2), :] + self.edges = cv2.Canny(roi, 50, 150) + contours, _ = cv2.findContours(self.edges, cv2.RETR_EXTERNAL, + cv2.CHAIN_APPROX_SIMPLE) + xt, yt = None, None self.topmost_point = None - outermost_contours = [ - contours[i] for i in range(len(contours)) - if hierarchy[0][i][3] == -1 - ] - if outermost_contours: - largest_contour = max(outermost_contours, key=cv2.contourArea) + if contours: + largest_contour = max(contours, key=cv2.contourArea) self.topmost_point = tuple( largest_contour[largest_contour[:, :, 1].argmin()][0]) - # topmost_point = (int(topmost_point[0]*self.resized_width), int(topmost_point[1] * self.resized_height)) - topmost_point = (self.topmost_point[0], self.topmost_point[1]) - cv2.circle(self.front_image_keypoints, topmost_point, 2, - (255, 0, 0), -1) + xt, yt = self.topmost_point + self.circle(self.side_image_keypoints, xt, yt) - self.left_hip_idx = 23 - self.right_hip_idx = 24 + logging.warning("xt: %s", xt) + logging.warning("yt: %s", yt) + xc, yc = None, None + landmarks = side_results.pose_landmarks.landmark - self.center_point = None - - if front_results.pose_landmarks: - left_hip = front_results.pose_landmarks.landmark[self.left_hip_idx] - right_hip = front_results.pose_landmarks.landmark[ - self.right_hip_idx] + if side_results.pose_landmarks: # type: ignore + left_hip = landmarks[pose.PoseLandmark.LEFT_HIP.value] + right_hip = landmarks[pose.PoseLandmark.RIGHT_HIP.value] center_point = ( (left_hip.x + right_hip.x) / 2, (left_hip.y + right_hip.y) / 2, ) center_point = ( - int(center_point[0] * self.resized_width), - int(center_point[1] * self.resized_height), + int(center_point[0] * self.side_image_resized.shape[1]), + int(center_point[1] * self.side_image_resized.shape[0]), ) - cv2.circle(self.front_image_keypoints, center_point, 2, - (255, 0, 0), -1) xc, yc = center_point - xt, yt = topmost_point - distance = math.sqrt((xc - xt)**2 + (yc - yt)**2) - #return distance - return center_point + logging.warning("xc: %s", xc) + logging.warning("yc: %s", yc) + self.circle(self.side_image_keypoints, xc, yc) + + self.pixel_distance = self.euclidean_distance(xc, xt, yc, yt) + self.distance = self.pixel_distance * self.pixel_to_metric_ratio() + return self.distance l = Landmarker() try: l.run() -except: - pass +except Exception as e: + print(f"Error: {e}") finally: l.destroy() diff --git a/trial.py b/trial.py new file mode 100644 index 0000000..c417cea --- /dev/null +++ b/trial.py @@ -0,0 +1,38 @@ +import cv2 +import mediapipe as mp + + +def detect_points(image_path): + image = cv2.imread(image_path) + target_width = 256 + target_height = int(image.shape[0] * target_width / image.shape[1]) + + image = cv2.resize(image, (target_width, target_height)) + + mp_pose = mp.solutions.pose #type: ignore + + pose = mp_pose.Pose(static_image_mode=True, min_detection_confidence=0.5) + + image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + results = pose.process(image_rgb) + + if results.pose_landmarks: + for landmark in results.pose_landmarks.landmark: + cv2.circle( + image, + (int(landmark.x * image.shape[1]), + int(landmark.y * image.shape[0])), + 5, + (0, 255, 0), + -1, + ) + + cv2.imshow("Image", image) + cv2.waitKey(0) + cv2.destroyAllWindows() + + +image_path = "/home/aparna/projects/aubome/assets/aparna_side.jpg" + +detect_points(image_path)