|
|
import math |
|
|
import numpy as np |
|
|
|
|
|
from CPR_Module.Common.keypoints import CocoKeypoints |
|
|
from CPR_Module.Common.logging_config import cpr_logger |
|
|
|
|
|
class PostureAnalyzer: |
|
|
"""Analyzes the Rescuer's posture during CPR based on the rescuer's keypoints and the patient's chest region.""" |
|
|
|
|
|
def __init__(self, right_arm_angle_threshold, left_arm_angle_threshold, wrist_distance_threshold, history_length_to_average): |
|
|
self.history_length_to_average = history_length_to_average |
|
|
|
|
|
self.right_arm_angles = [] |
|
|
self.left_arm_angles = [] |
|
|
self.wrist_distances = [] |
|
|
|
|
|
self.right_arm_angle_threshold = right_arm_angle_threshold |
|
|
self.left_arm_angle_threshold = left_arm_angle_threshold |
|
|
self.wrist_distance_threshold = wrist_distance_threshold |
|
|
|
|
|
def _calculate_angle(self, a, b, c): |
|
|
"""Calculate angle between three points""" |
|
|
try: |
|
|
ang = math.degrees(math.atan2(c[1]-b[1], c[0]-b[0]) - |
|
|
math.atan2(a[1]-b[1], a[0]-b[0])) |
|
|
return ang + 360 if ang < 0 else ang |
|
|
except Exception as e: |
|
|
cpr_logger.error(f"Angle calculation error: {e}") |
|
|
return 0 |
|
|
|
|
|
def _check_bended_right_arm(self, keypoints): |
|
|
"""Check for right arm bending (returns warning)""" |
|
|
warnings = [] |
|
|
try: |
|
|
shoulder = keypoints[CocoKeypoints.RIGHT_SHOULDER.value] |
|
|
elbow = keypoints[CocoKeypoints.RIGHT_ELBOW.value] |
|
|
wrist = keypoints[CocoKeypoints.RIGHT_WRIST.value] |
|
|
|
|
|
right_angle = self._calculate_angle(wrist, elbow, shoulder) |
|
|
|
|
|
self.right_arm_angles.append(right_angle) |
|
|
|
|
|
avg_right = np.mean(self.right_arm_angles[-self.history_length_to_average:] if self.right_arm_angles else 0) |
|
|
|
|
|
if avg_right > self.right_arm_angle_threshold: |
|
|
warnings.append("Right arm bent!") |
|
|
|
|
|
return warnings |
|
|
|
|
|
except Exception as e: |
|
|
cpr_logger.error(f"Right arm check error: {e}") |
|
|
|
|
|
return warnings |
|
|
|
|
|
def _check_bended_left_arm(self, keypoints): |
|
|
"""Check for left arm bending (returns warning)""" |
|
|
warnings = [] |
|
|
try: |
|
|
shoulder = keypoints[CocoKeypoints.LEFT_SHOULDER.value] |
|
|
elbow = keypoints[CocoKeypoints.LEFT_ELBOW.value] |
|
|
wrist = keypoints[CocoKeypoints.LEFT_WRIST.value] |
|
|
|
|
|
left_angle = self._calculate_angle(wrist, elbow, shoulder) |
|
|
|
|
|
self.left_arm_angles.append(left_angle) |
|
|
|
|
|
avg_left = np.mean(self.left_arm_angles[-self.history_length_to_average:] if self.left_arm_angles else 0) |
|
|
|
|
|
if avg_left < self.left_arm_angle_threshold: |
|
|
warnings.append("Left arm bent!") |
|
|
|
|
|
return warnings |
|
|
|
|
|
except Exception as e: |
|
|
cpr_logger.error(f"Left arm check error: {e}") |
|
|
|
|
|
return warnings |
|
|
|
|
|
def _check_hands_on_chest(self, keypoints, chest_params): |
|
|
"""Check if both hands are on the chest (returns warnings)""" |
|
|
|
|
|
|
|
|
left_wrist = keypoints[CocoKeypoints.LEFT_WRIST.value] |
|
|
right_wrist = keypoints[CocoKeypoints.RIGHT_WRIST.value] |
|
|
|
|
|
warnings = [] |
|
|
try: |
|
|
|
|
|
if chest_params is None: |
|
|
return ["Both hands not on chest!"] |
|
|
|
|
|
cx, cy, cw, ch = chest_params |
|
|
left_in = right_in = False |
|
|
|
|
|
|
|
|
if left_wrist is not None: |
|
|
left_in = (cx - cw/2 < left_wrist[0] < cx + cw/2) and \ |
|
|
(cy - ch/2 < left_wrist[1] < cy + ch/2) |
|
|
|
|
|
|
|
|
if right_wrist is not None: |
|
|
right_in = (cx - cw/2 < right_wrist[0] < cx + cw/2) and \ |
|
|
(cy - ch/2 < right_wrist[1] < cy + ch/2) |
|
|
|
|
|
|
|
|
if not left_in and not right_in: |
|
|
warnings.append("Both hands not on chest!") |
|
|
else: |
|
|
if not left_in: |
|
|
warnings.append("Left hand not on chest!") |
|
|
if not right_in: |
|
|
warnings.append("Right hand not on chest!") |
|
|
|
|
|
except Exception as e: |
|
|
cpr_logger.error(f"Hands check error: {e}") |
|
|
|
|
|
return warnings |
|
|
|
|
|
def validate_posture(self, keypoints, chest_params): |
|
|
"""Run all posture validations (returns aggregated warnings)""" |
|
|
warnings = [] |
|
|
|
|
|
warnings += self._check_hands_on_chest(keypoints, chest_params) |
|
|
|
|
|
if ("Right hand not on chest!" not in warnings) and ("Both hands not on chest!" not in warnings): |
|
|
warnings += self._check_bended_right_arm(keypoints) |
|
|
|
|
|
if ("Left hand not on chest!" not in warnings) and ("Both hands not on chest!" not in warnings): |
|
|
warnings += self._check_bended_left_arm(keypoints) |
|
|
|
|
|
return warnings |
|
|
|