| import cv2 |
| import numpy as np |
| from CPR_Module.Common.keypoints import CocoKeypoints |
| from CPR_Module.Common.logging_config import cpr_logger |
|
|
| class ChestInitializer: |
| """Handles chest point detection with validations in estimation.""" |
| |
| def __init__(self): |
| self.chest_params = None |
| self.chest_params_history = [] |
| self.expected_chest_params = None |
|
|
| def estimate_chest_region(self, keypoints, bounding_box, frame_width, frame_height): |
| """Estimate and validate chest region. Returns (cx, cy, cw, ch) or None.""" |
| try: |
| |
| bbox_x1, bbox_y1, bbox_x2, bbox_y2 = bounding_box |
| bbox_delta_y = abs(bbox_y2 - bbox_y1) |
|
|
| |
| left_shoulder = keypoints[CocoKeypoints.LEFT_SHOULDER.value] |
| right_shoulder = keypoints[CocoKeypoints.RIGHT_SHOULDER.value] |
|
|
| |
| shoulder_center = np.array([(left_shoulder[0] + right_shoulder[0]) / 2, |
| (left_shoulder[1] + right_shoulder[1]) / 2]) |
| |
| |
| chest_center_from_shoulder_x = shoulder_center[0] - 0.3 * bbox_delta_y |
| chest_center_from_shoulder_y = shoulder_center[1] - 0.1 * bbox_delta_y |
| chest_center_from_shoulder = np.array([chest_center_from_shoulder_x, chest_center_from_shoulder_y]) |
|
|
| |
| chest_dx = bbox_delta_y * 0.8 |
| chest_dy = bbox_delta_y * 1.75 |
|
|
| |
| x1 = chest_center_from_shoulder[0] - chest_dx / 2 |
| y1 = chest_center_from_shoulder[1] - chest_dy / 2 |
| x2 = chest_center_from_shoulder[0] + chest_dx / 2 |
| y2 = chest_center_from_shoulder[1] + chest_dy / 2 |
|
|
| |
| x1 = max(0, min(x1, frame_width - 1)) |
| y1 = max(0, min(y1, frame_height - 1)) |
| x2 = max(0, min(x2, frame_width - 1)) |
| y2 = max(0, min(y2, frame_height - 1)) |
|
|
| |
| if x2 <= x1 or y2 <= y1: |
| return None |
|
|
| |
| cx = (x1 + x2) / 2 |
| cy = (y1 + y2) / 2 |
| cw = x2 - x1 |
| ch = y2 - y1 |
|
|
| return (cx, cy, cw, ch) |
|
|
| except (IndexError, TypeError, ValueError) as e: |
| print(f"Chest estimation error: {e}") |
| return None |
|
|
| def estimate_chest_region_weighted_avg(self, frame_width, frame_height, window_size=60, min_samples=3): |
| """ |
| Calculate stabilized chest parameters using weighted averaging with boundary checks. |
| |
| Args: |
| self.chest_params_history: List of recent chest parameters [(cx, cy, cw, ch), ...] |
| frame_width: Width of the video frame |
| frame_height: Height of the video frame |
| window_size: Number of recent frames to consider (default: 5) |
| min_samples: Minimum valid samples required (default: 3) |
| |
| Returns: |
| Tuple of (cx, cy, cw, ch) as integers within frame boundaries, |
| or None if insufficient data or invalid rectangle |
| """ |
| if not self.chest_params_history: |
| return None |
| |
| |
| valid_history = [h for h in self.chest_params_history[-window_size:] if h is not None] |
| |
| if len(valid_history) < min_samples: |
| return None |
| |
| |
| history_array = np.array(valid_history, dtype=np.float32) |
| |
| |
| weights = np.exp(np.linspace(1, 3, len(history_array))) |
| weights /= weights.sum() |
| |
| try: |
| |
| cx, cy, cw, ch = np.average(history_array, axis=0, weights=weights) |
| |
| |
| x1 = max(0.0, cx - cw/2) |
| y1 = max(0.0, cy - ch/2) |
| x2 = min(float(frame_width - 1), cx + cw/2) |
| y2 = min(float(frame_height - 1), cy + ch/2) |
| |
| |
| x1, y1, x2, y2 = map(round, [x1, y1, x2, y2]) |
| |
| |
| if x2 <= x1 or y2 <= y1: |
| return None |
| |
| return ( |
| (x1 + x2) // 2, |
| (y1 + y2) // 2, |
| x2 - x1, |
| y2 - y1 |
| ) |
| |
| except Exception as e: |
| print(f"Chest region estimation error: {e}") |
| return None |
| |
| def draw_expected_chest_region(self, frame): |
| """Draws the chest region without validation.""" |
| if self.expected_chest_params is None: |
| return frame |
|
|
| cx, cy, cw, ch = self.expected_chest_params |
| x1 = int(cx - cw / 2) |
| y1 = int(cy - ch / 2) |
| x2 = int(cx + cw / 2) |
| y2 = int(cy + ch / 2) |
|
|
| |
| cv2.rectangle(frame, (x1, y1), (x2, y2), (128, 128, 0), 5) |
|
|
| cv2.circle(frame, (int(cx), int(cy)), 8, (128, 128, 0), -1) |
|
|
| cv2.putText(frame, "EXPECTED CHEST", (x1, max(10, y1 - 5)), |
| cv2.FONT_HERSHEY_SIMPLEX, 0.8, (128, 128, 0), 2) |
| |
| return frame |
|
|