FatemaKotb commited on
Commit
cfbaa51
Β·
1 Parent(s): d26e0b7

CPR Code Refactor

Browse files
Files changed (41) hide show
  1. .gitignore +1 -0
  2. CPR/client.py +0 -34
  3. CPR/posture_analyzer.py +0 -132
  4. CPR/shoulders_analyzer.py +0 -30
  5. CPR/threaded_camera.py +0 -103
  6. CPRRealTime/analysis_socket_server.py +0 -65
  7. CPRRealTime/chest_initializer.py +0 -154
  8. CPRRealTime/client.py +0 -34
  9. CPRRealTime/keypoints.py +0 -22
  10. CPRRealTime/logging_config.py +0 -20
  11. CPRRealTime/role_classifier.py +0 -178
  12. CPRRealTime/warnings_overlayer.py +0 -147
  13. CPRRealTime/wrists_midpoint_analyzer.py +0 -63
  14. CPRRealTime/yolo11n-pose.pt +0 -3
  15. CPR_Module/Common/__init__.py +0 -0
  16. {CPR β†’ CPR_Module/Common}/analysis_socket_server.py +2 -1
  17. {CPR β†’ CPR_Module/Common}/chest_initializer.py +3 -2
  18. {CPR β†’ CPR_Module/Common}/keypoints.py +0 -0
  19. {CPR β†’ CPR_Module/Common}/logging_config.py +0 -0
  20. {CPRRealTime β†’ CPR_Module/Common}/posture_analyzer.py +3 -2
  21. {CPR β†’ CPR_Module/Common}/role_classifier.py +2 -2
  22. {CPRRealTime β†’ CPR_Module/Common}/shoulders_analyzer.py +3 -2
  23. {CPRRealTime β†’ CPR_Module/Common}/threaded_camera.py +2 -1
  24. {CPR β†’ CPR_Module/Common}/warnings_overlayer.py +1 -1
  25. {CPR β†’ CPR_Module/Common}/wrists_midpoint_analyzer.py +3 -2
  26. {CPR β†’ CPR_Module/Common}/yolo11n-pose.pt +0 -0
  27. {CPR β†’ CPR_Module/Educational_Mode}/CPRAnalyzer.py +13 -12
  28. CPR_Module/Educational_Mode/__init__.py +0 -0
  29. {CPR β†’ CPR_Module/Educational_Mode}/graph_plotter.py +3 -2
  30. {CPR β†’ CPR_Module/Educational_Mode}/metrics_calculator.py +2 -1
  31. {CPR β†’ CPR_Module/Educational_Mode}/pose_estimation.py +4 -3
  32. CPR_Module/Emergency_Mode/__init__.py +0 -0
  33. {CPRRealTime β†’ CPR_Module/Emergency_Mode}/graph_plotter.py +2 -1
  34. {CPRRealTime β†’ CPR_Module/Emergency_Mode}/main.py +13 -12
  35. {CPRRealTime β†’ CPR_Module/Emergency_Mode}/metrics_calculator.py +2 -1
  36. {CPRRealTime β†’ CPR_Module/Emergency_Mode}/pose_estimation.py +6 -6
  37. CPR_Module/__init__.py +0 -0
  38. Demo/__init__.py +0 -0
  39. Demo/demo.py +75 -0
  40. README.md +168 -40
  41. app.py +4 -4
.gitignore CHANGED
@@ -1,5 +1,6 @@
1
  __pycache__/*
2
  venv/*
 
3
  uploads/*
4
  runs/*
5
  screenshots/*
 
1
  __pycache__/*
2
  venv/*
3
+ backendEnv/*
4
  uploads/*
5
  runs/*
6
  screenshots/*
CPR/client.py DELETED
@@ -1,34 +0,0 @@
1
- import socket
2
- import json
3
-
4
- from CPR.logging_config import cpr_logger
5
-
6
- HOST = 'localhost' # The server's hostname or IP address
7
- PORT = 5000 # The port used by the server
8
-
9
- #! Not an error
10
-
11
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
12
- s.connect((HOST, PORT))
13
- #^ Set as an error for cleaner logging purposes
14
- cpr_logger.error(f"Connected to {HOST}:{PORT}")
15
-
16
- try:
17
- while True:
18
- data = s.recv(1024)
19
- if not data:
20
- break
21
-
22
- # Split messages (in case multiple JSONs in buffer)
23
- for line in data.decode('utf-8').split('\n'):
24
- if line.strip():
25
- try:
26
- warnings = json.loads(line)
27
- cpr_logger.error("\nReceived warnings:")
28
- cpr_logger.error(f"Status: {warnings['status']}")
29
- cpr_logger.error(f"Posture Warnings: {warnings['posture_warnings']}")
30
- cpr_logger.error(f"Rate/Depth Warnings: {warnings['rate_and_depth_warnings']}")
31
- except json.JSONDecodeError:
32
- cpr_logger.error("Received invalid JSON")
33
- except KeyboardInterrupt:
34
- cpr_logger.error("Disconnecting...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPR/posture_analyzer.py DELETED
@@ -1,132 +0,0 @@
1
- # posture_analyzer.py
2
- import math
3
- import cv2
4
- import numpy as np
5
- from CPR.keypoints import CocoKeypoints
6
- from CPR.logging_config import cpr_logger
7
-
8
- class PostureAnalyzer:
9
- """Posture analysis and visualization with comprehensive validation"""
10
-
11
- def __init__(self, right_arm_angle_threshold, left_arm_angle_threshold, wrist_distance_threshold, history_length_to_average):
12
- self.history_length_to_average = history_length_to_average
13
-
14
- self.right_arm_angles = []
15
- self.left_arm_angles = []
16
- self.wrist_distances = []
17
-
18
- self.right_arm_angle_threshold = right_arm_angle_threshold
19
- self.left_arm_angle_threshold = left_arm_angle_threshold
20
- self.wrist_distance_threshold = wrist_distance_threshold
21
-
22
- def _calculate_angle(self, a, b, c):
23
- """Calculate angle between three points"""
24
- try:
25
- ang = math.degrees(math.atan2(c[1]-b[1], c[0]-b[0]) -
26
- math.atan2(a[1]-b[1], a[0]-b[0]))
27
- return ang + 360 if ang < 0 else ang
28
- except Exception as e:
29
- cpr_logger.error(f"Angle calculation error: {e}")
30
- return 0
31
-
32
- def _check_bended_right_arm(self, keypoints):
33
- """Check for right arm bending (returns warning)"""
34
- warnings = []
35
- try:
36
- shoulder = keypoints[CocoKeypoints.RIGHT_SHOULDER.value]
37
- elbow = keypoints[CocoKeypoints.RIGHT_ELBOW.value]
38
- wrist = keypoints[CocoKeypoints.RIGHT_WRIST.value]
39
-
40
- right_angle = self._calculate_angle(wrist, elbow, shoulder)
41
-
42
- self.right_arm_angles.append(right_angle)
43
-
44
- avg_right = np.mean(self.right_arm_angles[-self.history_length_to_average:] if self.right_arm_angles else 0)
45
-
46
- if avg_right > self.right_arm_angle_threshold:
47
- warnings.append("Right arm bent!")
48
-
49
- return warnings
50
-
51
- except Exception as e:
52
- cpr_logger.error(f"Right arm check error: {e}")
53
-
54
- return warnings
55
-
56
- def _check_bended_left_arm(self, keypoints):
57
- """Check for left arm bending (returns warning)"""
58
- warnings = []
59
- try:
60
- shoulder = keypoints[CocoKeypoints.LEFT_SHOULDER.value]
61
- elbow = keypoints[CocoKeypoints.LEFT_ELBOW.value]
62
- wrist = keypoints[CocoKeypoints.LEFT_WRIST.value]
63
-
64
- left_angle = self._calculate_angle(wrist, elbow, shoulder)
65
-
66
- self.left_arm_angles.append(left_angle)
67
-
68
- avg_left = np.mean(self.left_arm_angles[-self.history_length_to_average:] if self.left_arm_angles else 0)
69
-
70
- if avg_left < self.left_arm_angle_threshold:
71
- warnings.append("Left arm bent!")
72
-
73
- return warnings
74
-
75
- except Exception as e:
76
- cpr_logger.error(f"Left arm check error: {e}")
77
-
78
- return warnings
79
-
80
- def _check_hands_on_chest(self, keypoints, chest_params):
81
- """Check individual hand positions and return specific warnings"""
82
-
83
- # Get the wrist keypoints
84
- left_wrist = keypoints[CocoKeypoints.LEFT_WRIST.value]
85
- right_wrist = keypoints[CocoKeypoints.RIGHT_WRIST.value]
86
-
87
- warnings = []
88
- try:
89
- if chest_params is None:
90
- return ["Both hands not on chest!"] # Fallback warning
91
-
92
- cx, cy, cw, ch = chest_params
93
- left_in = right_in = False
94
-
95
- # Check left hand
96
- if left_wrist is not None:
97
- left_in = (cx - cw/2 < left_wrist[0] < cx + cw/2) and \
98
- (cy - ch/2 < left_wrist[1] < cy + ch/2)
99
-
100
- # Check right hand
101
- if right_wrist is not None:
102
- right_in = (cx - cw/2 < right_wrist[0] < cx + cw/2) and \
103
- (cy - ch/2 < right_wrist[1] < cy + ch/2)
104
-
105
- # Determine warnings
106
- if not left_in and not right_in:
107
- warnings.append("Both hands not on chest!")
108
- else:
109
- if not left_in:
110
- warnings.append("Left hand not on chest!")
111
- if not right_in:
112
- warnings.append("Right hand not on chest!")
113
-
114
- except Exception as e:
115
- cpr_logger.error(f"Hands check error: {e}")
116
-
117
- return warnings
118
-
119
-
120
- def validate_posture(self, keypoints, chest_params):
121
- """Run all posture validations (returns aggregated warnings)"""
122
- warnings = []
123
-
124
- warnings += self._check_hands_on_chest(keypoints, chest_params)
125
-
126
- if ("Right hand not on chest!" not in warnings) and ("Both hands not on chest!" not in warnings):
127
- warnings += self._check_bended_right_arm(keypoints)
128
-
129
- if ("Left hand not on chest!" not in warnings) and ("Both hands not on chest!" not in warnings):
130
- warnings += self._check_bended_left_arm(keypoints)
131
-
132
- return warnings
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPR/shoulders_analyzer.py DELETED
@@ -1,30 +0,0 @@
1
- import numpy as np
2
- from CPR.keypoints import CocoKeypoints
3
- from CPR.logging_config import cpr_logger
4
-
5
- class ShouldersAnalyzer:
6
- """Analyzes shoulder distances and posture"""
7
-
8
- def __init__(self):
9
- self.shoulder_distance = None
10
- self.shoulder_distance_history = []
11
-
12
- def calculate_shoulder_distance(self, rescuer_keypoints):
13
- """Calculate and store shoulder distance"""
14
- if rescuer_keypoints is None:
15
- return
16
-
17
- try:
18
- left = rescuer_keypoints[CocoKeypoints.LEFT_SHOULDER.value]
19
- right = rescuer_keypoints[CocoKeypoints.RIGHT_SHOULDER.value]
20
-
21
- distance = np.linalg.norm(np.array(left) - np.array(right))
22
-
23
- return distance
24
- except Exception as e:
25
- cpr_logger.error(f"Shoulder distance error: {e}")
26
- return
27
-
28
- def reset_shoulder_distances(self):
29
- """Reset shoulder distances"""
30
- self.shoulder_distance_history = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPR/threaded_camera.py DELETED
@@ -1,103 +0,0 @@
1
- import threading
2
- from queue import Queue
3
- import queue
4
- import cv2
5
- from CPR.logging_config import cpr_logger
6
-
7
- class ThreadedCamera:
8
- def __init__(self, source, requested_fps = 30):
9
-
10
- # The constructor of OpenCV's VideoCapture class automatically opens the camera
11
- self.cap = cv2.VideoCapture(source)
12
- if not self.cap.isOpened():
13
- raise ValueError(f"[VIDEO CAPTURE] Unable to open camera source: {source}")
14
- cpr_logger.info(f"[VIDEO CAPTURE] Camera source opened: {source}")
15
-
16
- # Attempt to configure the camera to the requested FPS
17
- # Which is set to the value we have been working on with recorded videos
18
- # .set() returns True if the camera acknowledged the request, not if it actually achieved the FPS.
19
- set_success = self.cap.set(cv2.CAP_PROP_FPS, requested_fps)
20
-
21
- # Get the actual FPS from the camera
22
- # This is the FPS that the camera is actually using, which may differ from the requested FPS.
23
- actual_fps = self.cap.get(cv2.CAP_PROP_FPS)
24
- self.fps = actual_fps
25
-
26
- cpr_logger.info(f"[VIDEO CAPTURE] Requested FPS: {requested_fps}, Set Success: {set_success}, Actual FPS: {actual_fps}")
27
-
28
- # The buffer should be able to hold a lag of up to 2 seconds
29
- number_of_seconds_to_buffer = 5
30
- queue_size = int(actual_fps * number_of_seconds_to_buffer)
31
- self.q = Queue(maxsize=queue_size)
32
- cpr_logger.info(f"[VIDEO CAPTURE] Queue size: {queue_size}")
33
-
34
- # Set a flag to indicate that the camera is running
35
- self.running = threading.Event()
36
- self.running.set() # Initial state = running
37
- cpr_logger.info(f"[VIDEO CAPTURE] Camera running: {self.running.is_set()}")
38
-
39
- self.number_of_total_frames = 0
40
- self.number_of_dropped_frames = 0
41
-
42
- self.thread = None
43
-
44
- def start_capture(self):
45
- # Clear any existing frames in queue
46
- while not self.q.empty():
47
- self.q.get()
48
-
49
- # threading.Thread() initialize a new thread
50
- # target=self._reader specify the method (_reader) the thread will execute
51
- self.thread = threading.Thread(target=self._reader)
52
- cpr_logger.info(f"[VIDEO CAPTURE] Thread initialized: {self.thread}")
53
-
54
- # Set the thread as a daemon thread:
55
- # Daemon threads automatically exit when the main program exits
56
- # They run in the background and don't block program termination
57
- self.thread.daemon = True
58
- cpr_logger.info(f"[VIDEO CAPTURE] Thread daemon: {self.thread.daemon}")
59
-
60
- # Start the thread execution:
61
- # Call the _reader method in parallel with the main program
62
- self.thread.start()
63
-
64
- def _reader(self):
65
- while self.running.is_set():
66
- ret, frame = self.cap.read()
67
- if not ret:
68
- cpr_logger.info("Camera disconnected")
69
- self.q.put(None) # Sentinel for clean exit
70
- break
71
-
72
- try:
73
- self.number_of_total_frames += 1
74
- self.q.put(frame, timeout=0.1)
75
- except queue.Full:
76
- cpr_logger.info("Frame dropped")
77
- self.number_of_dropped_frames += 1
78
-
79
- def read(self):
80
- return self.q.get()
81
-
82
- def release(self):
83
- #! Not an error
84
- cpr_logger.error(f"[VIDEO CAPTURE] Total frames: {self.number_of_total_frames}, Dropped frames: {self.number_of_dropped_frames}")
85
-
86
- self.running.clear()
87
-
88
- # First release the capture to unblock pending reads
89
- self.cap.release() # MOVED THIS LINE UP
90
-
91
- # Then join the thread
92
- self.thread.join(timeout=1.0)
93
-
94
- if self.thread.is_alive():
95
- cpr_logger.info("Warning: Thread didn't terminate cleanly")
96
- # Removed redundant self.cap.release()
97
-
98
- def isOpened(self):
99
- return self.running.is_set() and self.cap.isOpened()
100
-
101
- def __del__(self):
102
- if self.running.is_set(): # Only release if not already done
103
- self.release()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/analysis_socket_server.py DELETED
@@ -1,65 +0,0 @@
1
- import socket
2
- import json
3
- from threading import Thread
4
- from queue import Queue
5
- import threading
6
- from CPRRealTime.logging_config import cpr_logger
7
- import queue
8
-
9
- class AnalysisSocketServer:
10
- def __init__(self, host='localhost', port=5000):
11
- self.host = host
12
- self.port = port
13
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
14
- self.conn = None
15
- self.running = False
16
- self.warning_queue = Queue()
17
- self.connection_event = threading.Event()
18
- cpr_logger.info(f"[SOCKET] Server initialized on {host}:{port}")
19
-
20
- def start_server(self):
21
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
22
- self.sock.bind((self.host, self.port))
23
- self.sock.listen()
24
- self.running = True
25
- Thread(target=self._accept_connections, daemon=True).start()
26
-
27
- def _accept_connections(self):
28
- while self.running:
29
- try:
30
- self.conn, addr = self.sock.accept()
31
- cpr_logger.info(f"[SOCKET] Connected by {addr}")
32
- self.connection_event.set() # Signal that connection was made
33
- Thread(target=self._handle_client, args=(self.conn,), daemon=True).start()
34
- except Exception as e:
35
- #! Not an error
36
- cpr_logger.error(f"[SOCKET] Connection error: {str(e)}")
37
-
38
- def wait_for_connection(self, timeout=None):
39
- """Block until a client connects"""
40
- #^ Set as an error for cleaner logging purposes
41
- cpr_logger.error("[SOCKET] Waiting for client connection...")
42
- self.connection_event.clear() # Reset the event
43
- return self.connection_event.wait(timeout)
44
-
45
- def _handle_client(self, conn):
46
- while self.running:
47
- try:
48
- # Block until a warning is available (reduces CPU usage)
49
- warnings = self.warning_queue.get(block=True, timeout=0.1)
50
- serialized = json.dumps(warnings) + "\n"
51
- conn.sendall(serialized.encode('utf-8'))
52
- except queue.Empty:
53
- continue # Timeout allows checking self.running periodically
54
- except (BrokenPipeError, ConnectionResetError):
55
- cpr_logger.error("[SOCKET] Client disconnected")
56
- break
57
- except Exception as e:
58
- cpr_logger.error(f"[SOCKET] Error: {str(e)}")
59
- break
60
- conn.close()
61
-
62
- def stop_server(self):
63
- self.running = False
64
- self.sock.close()
65
- cpr_logger.info("[SOCKET] Server stopped")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/chest_initializer.py DELETED
@@ -1,154 +0,0 @@
1
- import cv2
2
- import numpy as np
3
- from CPRRealTime.keypoints import CocoKeypoints
4
- from CPRRealTime.logging_config import cpr_logger
5
-
6
- class ChestInitializer:
7
- """Handles chest point detection with validations in estimation."""
8
-
9
- def __init__(self):
10
- self.chest_params = None
11
- self.chest_params_history = []
12
- self.expected_chest_params = None
13
-
14
- def estimate_chest_region(self, keypoints, bounding_box, frame_width, frame_height):
15
- """Estimate and validate chest region. Returns (cx, cy, cw, ch) or None."""
16
- try:
17
- # Unpack bounding box and calculate shoulder dimensions
18
- bbox_x1, bbox_y1, bbox_x2, bbox_y2 = bounding_box
19
- bbox_delta_y = abs(bbox_y2 - bbox_y1)
20
-
21
- # Keypoints for shoulders
22
- left_shoulder = keypoints[CocoKeypoints.LEFT_SHOULDER.value]
23
- right_shoulder = keypoints[CocoKeypoints.RIGHT_SHOULDER.value]
24
-
25
- # Midpoints calculation
26
- shoulder_center = np.array([(left_shoulder[0] + right_shoulder[0]) / 2,
27
- (left_shoulder[1] + right_shoulder[1]) / 2])
28
-
29
- #& Handing different patient positions
30
- # If the x-coordinate shoulder center is closer to that of the Bottom-Right bbox corner (2)
31
- # then the orientation is "right"
32
- # If the x-coordinate shoulder center is closer to that of the Top-Left bbox corner (1)
33
- # then the orientation is "left"
34
-
35
- if abs(shoulder_center[0] - bbox_x2) < abs(shoulder_center[0] - bbox_x1): # Orientation is "right"
36
- chest_center_from_shoulder_x = shoulder_center[0] - 0.3 * bbox_delta_y
37
- chest_center_from_shoulder_y = shoulder_center[1] - 0.1 * bbox_delta_y
38
- chest_center_from_shoulder = np.array([chest_center_from_shoulder_x, chest_center_from_shoulder_y])
39
- else: # Orientation is "left"
40
- chest_center_from_shoulder_x = shoulder_center[0] + 1.0 * bbox_delta_y
41
- chest_center_from_shoulder_y = shoulder_center[1] - 0.1 * bbox_delta_y
42
- chest_center_from_shoulder = np.array([chest_center_from_shoulder_x, chest_center_from_shoulder_y])
43
-
44
- # Chest dimensions (85% of shoulder width, 40% height)
45
- chest_dx = bbox_delta_y * 0.8
46
- chest_dy = bbox_delta_y * 1.75
47
-
48
- # Calculate region coordinates
49
- x1 = chest_center_from_shoulder[0] - chest_dx / 2
50
- y1 = chest_center_from_shoulder[1] - chest_dy / 2
51
- x2 = chest_center_from_shoulder[0] + chest_dx / 2
52
- y2 = chest_center_from_shoulder[1] + chest_dy / 2
53
-
54
- # Clamp to frame boundaries
55
- x1 = max(0, min(x1, frame_width - 1))
56
- y1 = max(0, min(y1, frame_height - 1))
57
- x2 = max(0, min(x2, frame_width - 1))
58
- y2 = max(0, min(y2, frame_height - 1))
59
-
60
- # Check validity
61
- if x2 <= x1 or y2 <= y1:
62
- return None
63
-
64
- # Adjusted parameters
65
- cx = (x1 + x2) / 2
66
- cy = (y1 + y2) / 2
67
- cw = x2 - x1
68
- ch = y2 - y1
69
-
70
- return (cx, cy, cw, ch)
71
-
72
- except (IndexError, TypeError, ValueError) as e:
73
- cpr_logger.error(f"Chest estimation error: {e}")
74
- return None
75
-
76
- def estimate_chest_region_weighted_avg(self, frame_width, frame_height, window_size=60, min_samples=3):
77
- """
78
- Calculate stabilized chest parameters using weighted averaging with boundary checks.
79
-
80
- Args:
81
- self.chest_params_history: List of recent chest parameters [(cx, cy, cw, ch), ...]
82
- frame_width: Width of the video frame
83
- frame_height: Height of the video frame
84
- window_size: Number of recent frames to consider (default: 5)
85
- min_samples: Minimum valid samples required (default: 3)
86
-
87
- Returns:
88
- Tuple of (cx, cy, cw, ch) as integers within frame boundaries,
89
- or None if insufficient data or invalid rectangle
90
- """
91
- if not self.chest_params_history:
92
- return None
93
-
94
- # Filter out None values and get recent frames
95
- valid_history = [h for h in self.chest_params_history[-window_size:] if h is not None]
96
-
97
- if len(valid_history) < min_samples:
98
- return None
99
-
100
- # Convert to numpy array (preserve floating-point precision)
101
- history_array = np.array(valid_history, dtype=np.float32)
102
-
103
- # Exponential weights (stronger emphasis on recent frames)
104
- weights = np.exp(np.linspace(1, 3, len(history_array)))
105
- weights /= weights.sum()
106
-
107
- try:
108
- # Calculate weighted average in float space
109
- cx, cy, cw, ch = np.average(history_array, axis=0, weights=weights)
110
-
111
- # Convert to rectangle coordinates (still floating point)
112
- x1 = max(0.0, cx - cw/2)
113
- y1 = max(0.0, cy - ch/2)
114
- x2 = min(float(frame_width - 1), cx + cw/2)
115
- y2 = min(float(frame_height - 1), cy + ch/2)
116
-
117
- # Only round to integers after all calculations
118
- x1, y1, x2, y2 = map(round, [x1, y1, x2, y2])
119
-
120
- # Validate rectangle
121
- if x2 <= x1 or y2 <= y1:
122
- return None
123
-
124
- return (
125
- (x1 + x2) // 2, # cx
126
- (y1 + y2) // 2, # cy
127
- x2 - x1, # cw
128
- y2 - y1 # ch
129
- )
130
-
131
- except Exception as e:
132
- cpr_logger.error(f"Chest region estimation error: {e}")
133
- return None
134
-
135
- def draw_expected_chest_region(self, frame):
136
- """Draws the chest region without validation."""
137
- if self.expected_chest_params is None:
138
- return frame
139
-
140
- cx, cy, cw, ch = self.expected_chest_params
141
- x1 = int(cx - cw / 2)
142
- y1 = int(cy - ch / 2)
143
- x2 = int(cx + cw / 2)
144
- y2 = int(cy + ch / 2)
145
-
146
- # Draw rectangle and center
147
- cv2.rectangle(frame, (x1, y1), (x2, y2), (128, 128, 0), 5)
148
-
149
- cv2.circle(frame, (int(cx), int(cy)), 8, (128, 128, 0), -1)
150
-
151
- cv2.putText(frame, "EXPECTED CHEST", (x1, max(10, y1 - 5)),
152
- cv2.FONT_HERSHEY_SIMPLEX, 0.8, (128, 128, 0), 2)
153
-
154
- return frame
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/client.py DELETED
@@ -1,34 +0,0 @@
1
- import socket
2
- import json
3
-
4
- from CPRRealTime.logging_config import cpr_logger
5
-
6
- HOST = 'localhost' # The server's hostname or IP address
7
- PORT = 5000 # The port used by the server
8
-
9
- #! Not an error
10
-
11
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
12
- s.connect((HOST, PORT))
13
- #^ Set as an error for cleaner logging purposes
14
- cpr_logger.error(f"Connected to {HOST}:{PORT}")
15
-
16
- try:
17
- while True:
18
- data = s.recv(1024)
19
- if not data:
20
- break
21
-
22
- # Split messages (in case multiple JSONs in buffer)
23
- for line in data.decode('utf-8').split('\n'):
24
- if line.strip():
25
- try:
26
- warnings = json.loads(line)
27
- cpr_logger.error("\nReceived warnings:")
28
- cpr_logger.error(f"Status: {warnings['status']}")
29
- cpr_logger.error(f"Posture Warnings: {warnings['posture_warnings']}")
30
- cpr_logger.error(f"Rate/Depth Warnings: {warnings['rate_and_depth_warnings']}")
31
- except json.JSONDecodeError:
32
- cpr_logger.error("Received invalid JSON")
33
- except KeyboardInterrupt:
34
- cpr_logger.error("Disconnecting...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/keypoints.py DELETED
@@ -1,22 +0,0 @@
1
- # keypoints.py
2
- from enum import Enum
3
-
4
- class CocoKeypoints(Enum):
5
- """Enum for COCO keypoints (17 points)"""
6
- NOSE = 0
7
- LEFT_EYE = 1
8
- RIGHT_EYE = 2
9
- LEFT_EAR = 3
10
- RIGHT_EAR = 4
11
- LEFT_SHOULDER = 5
12
- RIGHT_SHOULDER = 6
13
- LEFT_ELBOW = 7
14
- RIGHT_ELBOW = 8
15
- LEFT_WRIST = 9
16
- RIGHT_WRIST = 10
17
- LEFT_HIP = 11
18
- RIGHT_HIP = 12
19
- LEFT_KNEE = 13
20
- RIGHT_KNEE = 14
21
- LEFT_ANKLE = 15
22
- RIGHT_ANKLE = 16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/logging_config.py DELETED
@@ -1,20 +0,0 @@
1
- # logging_config.py
2
- import logging
3
-
4
- # 1. Set default log level here (change this value as needed)
5
- DEFAULT_LOG_LEVEL = logging.INFO # Switch to logging.ERROR for errors-only by default
6
-
7
- # 2. Configure logger with default level
8
- cpr_logger = logging.getLogger("CPR-Analyzer")
9
- cpr_logger.setLevel(DEFAULT_LOG_LEVEL)
10
-
11
- # 3. Create console handler with formatter
12
- console_handler = logging.StreamHandler()
13
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
14
- console_handler.setFormatter(formatter)
15
-
16
- # 4. Add handler to logger
17
- cpr_logger.addHandler(console_handler)
18
-
19
- # 5. Prevent propagation to root logger
20
- cpr_logger.propagate = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/role_classifier.py DELETED
@@ -1,178 +0,0 @@
1
- # role_classifier.py
2
- import cv2
3
- import numpy as np
4
- from ultralytics.utils.plotting import Annotator # Import YOLO's annotator
5
- from CPRRealTime.logging_config import cpr_logger
6
-
7
-
8
- class RoleClassifier:
9
- """Role classification and tracking using image processing"""
10
-
11
- def __init__(self, proximity_thresh=0.3):
12
- self.proximity_thresh = proximity_thresh
13
- self.rescuer_id = None
14
- self.rescuer_processed_results = None
15
- self.patient_processed_results = None
16
-
17
- def _calculate_verticality_score(self, bounding_box):
18
- """Calculate posture verticality score (0=horizontal, 1=vertical) using bounding box aspect ratio."""
19
- try:
20
- x1, y1, x2, y2 = bounding_box
21
- width = abs(x2 - x1)
22
- height = abs(y2 - y1)
23
-
24
- # Handle edge cases with invalid dimensions
25
- if width == 0 or height == 0:
26
- return -1
27
-
28
- return 1 if height > width else 0 # 1 for vertical, 0 for horizontal
29
-
30
- except (TypeError, ValueError) as e:
31
- cpr_logger.error(f"Verticality score calculation error: {e}")
32
- return -1
33
-
34
- def _calculate_bounding_box_center(self, bounding_box):
35
- """Calculate the center coordinates of a bounding box.
36
- """
37
- x1, y1, x2, y2 = bounding_box
38
- return (x1 + x2) / 2, (y1 + y2) / 2
39
-
40
- def _calculate_distance(self, point1, point2):
41
- """Calculate Euclidean distance between two points"""
42
- return ((point1[0]-point2[0])**2 + (point1[1]-point2[1])**2)**0.5
43
-
44
- def _calculate_bbox_areas(self, rescuer_bbox, patient_bbox):
45
- """
46
- Calculate bounding box areas for rescuer and patient.
47
-
48
- Args:
49
- rescuer_bbox: [x1, y1, x2, y2] coordinates of rescuer's bounding box
50
- patient_bbox: [x1, y1, x2, y2] coordinates of patient's bounding box
51
-
52
- Returns:
53
- Tuple: (rescuer_area, patient_area) in pixels
54
- """
55
- def compute_area(bbox):
56
- if bbox is None:
57
- return 0
58
- width = bbox[2] - bbox[0] # x2 - x1
59
- height = bbox[3] - bbox[1] # y2 - y1
60
- return abs(width * height) # Absolute value to handle negative coordinates
61
-
62
- return compute_area(rescuer_bbox), compute_area(patient_bbox)
63
-
64
- def classify_roles(self, results, prev_rescuer_processed_results=None, prev_patient_processed_results=None):
65
- """
66
- Classify roles of rescuer and patient based on detected keypoints and bounding boxes.
67
- """
68
-
69
- processed_results = []
70
-
71
- # Calculate combined area threshold if previous boxes exist
72
- threshold = None
73
- if prev_rescuer_processed_results and prev_patient_processed_results:
74
- prev_rescuer_bbox = prev_rescuer_processed_results["bounding_box"]
75
- prev_patient_bbox = prev_patient_processed_results["bounding_box"]
76
-
77
- rescuer_area = (prev_rescuer_bbox[2]-prev_rescuer_bbox[0])*(prev_rescuer_bbox[3]-prev_rescuer_bbox[1])
78
- patient_area = (prev_patient_bbox[2]-prev_patient_bbox[0])*(prev_patient_bbox[3]-prev_patient_bbox[1])
79
- threshold = rescuer_area + patient_area
80
-
81
- for i, (box, keypoints) in enumerate(zip(results.boxes.xywh.cpu().numpy(),
82
- results.keypoints.xy.cpu().numpy())):
83
- try:
84
- # Convert box to [x1,y1,x2,y2] format
85
- x_center, y_center, width, height = box
86
- bounding_box = [
87
- x_center - width/2, # x1
88
- y_center - height/2, # y1
89
- x_center + width/2, # x2
90
- y_center + height/2 # y2
91
- ]
92
-
93
- # Skip if box exceeds area threshold (when threshold exists)
94
- if threshold:
95
- box_area = width * height
96
- if box_area > threshold * 1.2: # 20% tolerance
97
- cpr_logger.info(f"Filtered oversized box {i} (area: {box_area:.1f} > threshold: {threshold:.1f})")
98
- continue
99
-
100
- # Calculate features
101
- verticality_score = self._calculate_verticality_score(bounding_box)
102
- #!We already have the center coordinates from the bounding box, no need to recalculate it.
103
- bounding_box_center = self._calculate_bounding_box_center(bounding_box)
104
-
105
- # Store valid results
106
- processed_results.append({
107
- 'original_index': i,
108
- 'bounding_box': bounding_box,
109
- 'bounding_box_center': bounding_box_center,
110
- 'verticality_score': verticality_score,
111
- 'keypoints': keypoints,
112
- })
113
-
114
- except Exception as e:
115
- cpr_logger.error(f"Error processing detection {i}: {e}")
116
- continue
117
-
118
- # Step 2: Identify the patient (horizontal posture)
119
- patient_candidates = [res for res in processed_results
120
- if res['verticality_score'] == 0]
121
-
122
- # If more than one horizontal person, select person with lowest center (likely lying down)
123
- if len(patient_candidates) > 1:
124
- patient_candidates = sorted(patient_candidates,
125
- key=lambda x: x['bounding_box_center'][1])[:1] # Sort by y-coordinate
126
-
127
- patient = patient_candidates[0] if patient_candidates else None
128
-
129
- # Step 3: Identify the rescuer
130
- rescuer = None
131
- if patient:
132
- # Find vertical people who aren't the patient
133
- potential_rescuers = [
134
- res for res in processed_results
135
- if res['verticality_score'] == 1
136
- #! Useless condition because the patient was horizontal
137
- and res['original_index'] != patient['original_index']
138
- ]
139
-
140
- if potential_rescuers:
141
- # Select rescuer closest to patient
142
- rescuer = min(potential_rescuers,
143
- key=lambda x: self._calculate_distance(
144
- x['bounding_box_center'],
145
- patient['bounding_box_center']))
146
-
147
- return rescuer, patient
148
-
149
- def draw_rescuer_and_patient(self, frame):
150
- # Create annotator object
151
- annotator = Annotator(frame)
152
-
153
- # Draw rescuer (A) with green box and keypoints
154
- if self.rescuer_processed_results:
155
- try:
156
- x1, y1, x2, y2 = map(int, self.rescuer_processed_results["bounding_box"])
157
- annotator.box_label((x1, y1, x2, y2), "Rescuer A", color=(0, 255, 0))
158
-
159
- if "keypoints" in self.rescuer_processed_results:
160
- keypoints = self.rescuer_processed_results["keypoints"]
161
- annotator.kpts(keypoints, shape=frame.shape[:2])
162
- except Exception as e:
163
- cpr_logger.error(f"Error drawing rescuer: {str(e)}")
164
-
165
- # Draw patient (B) with red box and keypoints
166
- if self.patient_processed_results:
167
- try:
168
- x1, y1, x2, y2 = map(int, self.patient_processed_results["bounding_box"])
169
- annotator.box_label((x1, y1, x2, y2), "Patient B", color=(0, 0, 255))
170
-
171
- if "keypoints" in self.patient_processed_results:
172
- keypoints = self.patient_processed_results["keypoints"]
173
- annotator.kpts(keypoints, shape=frame.shape[:2])
174
- except Exception as e:
175
- cpr_logger.error(f"Error drawing patient: {str(e)}")
176
-
177
- return annotator.result()
178
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/warnings_overlayer.py DELETED
@@ -1,147 +0,0 @@
1
- import cv2
2
- import numpy as np
3
- import os
4
- import sys
5
-
6
- from CPRRealTime.logging_config import cpr_logger
7
-
8
- class WarningsOverlayer:
9
- def __init__(self):
10
- self.WARNING_CONFIG = {
11
- # Posture Warnings
12
- "Right arm bent!": {
13
- "color": (52, 110, 235),
14
- "position": (50, 150)
15
- },
16
- "Left arm bent!": {
17
- "color": (52, 110, 235),
18
- "position": (50, 200)
19
- },
20
- "Left hand not on chest!": {
21
- "color": (161, 127, 18),
22
- "position": (50, 250)
23
- },
24
- "Right hand not on chest!": {
25
- "color": (161, 127, 18),
26
- "position": (50, 300)
27
- },
28
- "Both hands not on chest!": {
29
- "color": (161, 127, 18),
30
- "position": (50, 350)
31
- },
32
-
33
- # Rate/Depth Warnings
34
- "Depth too low!": {
35
- "color": (125, 52, 235),
36
- "position": (50, 400)
37
- },
38
- "Depth too high!": {
39
- "color": (125, 52, 235),
40
- "position": (50, 450)
41
- },
42
- "Rate too slow!": {
43
- "color": (235, 52, 214),
44
- "position": (50, 500)
45
- },
46
- "Rate too fast!": {
47
- "color": (235, 52, 214),
48
- "position": (50, 550)
49
- }
50
- }
51
-
52
- def add_warnings_to_processed_video(self, video_output_path, sampling_interval_frames, rate_and_depth_warnings, posture_warnings):
53
- """Process both warning types with identical handling"""
54
- cpr_logger.info("\n[POST-PROCESS] Starting warning overlay")
55
-
56
- # Read processed video with original parameters
57
- cap = cv2.VideoCapture(video_output_path)
58
- if not cap.isOpened():
59
- cpr_logger.info("[ERROR] Failed to open processed video")
60
- return
61
-
62
- # Get original video properties
63
- original_fourcc = int(cap.get(cv2.CAP_PROP_FOURCC))
64
- processed_fps = cap.get(cv2.CAP_PROP_FPS)
65
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
66
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
67
-
68
- # Create final writer with ORIGINAL codec and parameters
69
- base = os.path.splitext(video_output_path)[0]
70
- final_path = os.path.abspath(f"{base}_final.mp4")
71
- writer = cv2.VideoWriter(final_path, original_fourcc, processed_fps, (width, height))
72
-
73
- # Combine all warnings into unified list
74
- all_warnings = []
75
-
76
- # Process posture warnings
77
- for entry in posture_warnings:
78
- if warnings := entry.get('posture_warnings'):
79
- start = entry['start_frame'] // sampling_interval_frames
80
- end = entry['end_frame'] // sampling_interval_frames
81
- all_warnings.append((int(start), int(end), warnings))
82
-
83
- # Process rate/depth warnings
84
- for entry in rate_and_depth_warnings:
85
- if warnings := entry.get('rate_and_depth_warnings'):
86
- start = entry['start_frame'] // sampling_interval_frames
87
- end = entry['end_frame'] // sampling_interval_frames
88
- all_warnings.append((int(start), int(end), warnings))
89
-
90
- # Video processing loop
91
- frame_idx = 0
92
- while True:
93
- ret, frame = cap.read()
94
- if not ret:
95
- break
96
-
97
- # Check active warnings for current frame
98
- active_warnings = []
99
- for start, end, warnings in all_warnings:
100
- if start <= frame_idx <= end:
101
- active_warnings.extend(warnings)
102
-
103
- # Draw all warnings using unified config
104
- self._draw_warnings(frame, active_warnings)
105
-
106
- writer.write(frame)
107
- frame_idx += 1
108
-
109
- cap.release()
110
- writer.release()
111
- cpr_logger.info(f"\n[POST-PROCESS] Final output saved to: {final_path}")
112
-
113
- def _draw_warnings(self, frame, active_warnings):
114
- """Draw warnings using unified configuration"""
115
- drawn_positions = set() # Prevent overlapping
116
-
117
- for warning_text in active_warnings:
118
- if config := self.WARNING_CONFIG.get(warning_text):
119
- x, y = config['position']
120
-
121
- # Auto-stack if position occupied
122
- while (x, y) in drawn_positions:
123
- y += 50 # Move down by 50px
124
-
125
- self._draw_warning_banner(
126
- frame=frame,
127
- text=warning_text,
128
- color=config['color'],
129
- position=(x, y)
130
- )
131
- drawn_positions.add((x, y))
132
-
133
- def _draw_warning_banner(self, frame, text, color, position):
134
- """Base drawing function for warning banners"""
135
- (text_width, text_height), _ = cv2.getTextSize(
136
- text, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2)
137
-
138
- x, y = position
139
- # Background rectangle
140
- cv2.rectangle(frame,
141
- (x - 10, y - text_height - 10),
142
- (x + text_width + 10, y + 10),
143
- color, -1)
144
- # Text
145
- cv2.putText(frame, text, (x, y),
146
- cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
147
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/wrists_midpoint_analyzer.py DELETED
@@ -1,63 +0,0 @@
1
- import cv2
2
- import numpy as np
3
- from CPRRealTime.keypoints import CocoKeypoints
4
- from CPRRealTime.logging_config import cpr_logger
5
-
6
- class WristsMidpointAnalyzer:
7
- """Analyzes and tracks wrist midpoints for rescuer"""
8
-
9
- def __init__(self, allowed_distance_between_wrists=170):
10
- self.allowed_distance_between_wrists = allowed_distance_between_wrists
11
- self.midpoint = None
12
- self.midpoint_history = []
13
-
14
- def detect_wrists_midpoint(self, rescuer_keypoints):
15
- """Calculate midpoint between wrists in pixel coordinates"""
16
- try:
17
- if rescuer_keypoints is None:
18
- return None
19
-
20
- # Get wrist coordinates
21
- lw = rescuer_keypoints[CocoKeypoints.LEFT_WRIST.value]
22
- rw = rescuer_keypoints[CocoKeypoints.RIGHT_WRIST.value]
23
-
24
- # If the distance between wrists is too large, return None
25
- distance = np.linalg.norm(np.array(lw) - np.array(rw))
26
- if distance > self.allowed_distance_between_wrists:
27
- return None
28
-
29
- # Calculate midpoint
30
- midpoint = (
31
- int((lw[0] + rw[0]) / 2),
32
- int((lw[1] + rw[1]) / 2)
33
- )
34
-
35
- return midpoint
36
-
37
- except Exception as e:
38
- cpr_logger.error(f"Midpoint tracking error: {e}")
39
- return None
40
-
41
- def draw_midpoint(self, frame):
42
- """Visualize the midpoint on frame"""
43
-
44
- if self.midpoint is None:
45
- return frame
46
-
47
- try:
48
- # Draw visualization
49
- cv2.circle(frame, self.midpoint, 8, (0, 255, 0), -1)
50
- cv2.putText(
51
- frame, "MIDPOINT",
52
- (self.midpoint[0] + 5, self.midpoint[1] - 10),
53
- cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2
54
- )
55
-
56
- return frame
57
- except Exception as e:
58
- cpr_logger.error(f"Midpoint drawing error: {e}")
59
- return frame
60
-
61
- def reset_midpoint_history(self):
62
- """Reset midpoint history"""
63
- self.midpoint_history = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CPRRealTime/yolo11n-pose.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:869e83fcdffdc7371fa4e34cd8e51c838cc729571d1635e5141e3075e9319dc0
3
- size 6255593
 
 
 
 
CPR_Module/Common/__init__.py ADDED
File without changes
{CPR β†’ CPR_Module/Common}/analysis_socket_server.py RENAMED
@@ -3,9 +3,10 @@ import json
3
  from threading import Thread
4
  from queue import Queue
5
  import threading
6
- from CPR.logging_config import cpr_logger
7
  import queue
8
 
 
 
9
  class AnalysisSocketServer:
10
  def __init__(self, host='localhost', port=5000):
11
  self.host = host
 
3
  from threading import Thread
4
  from queue import Queue
5
  import threading
 
6
  import queue
7
 
8
+ from CPR_Module.Common.logging_config import cpr_logger
9
+
10
  class AnalysisSocketServer:
11
  def __init__(self, host='localhost', port=5000):
12
  self.host = host
{CPR β†’ CPR_Module/Common}/chest_initializer.py RENAMED
@@ -1,7 +1,8 @@
1
  import cv2
2
  import numpy as np
3
- from CPR.keypoints import CocoKeypoints
4
- from CPR.logging_config import cpr_logger
 
5
 
6
  class ChestInitializer:
7
  """Handles chest point detection with validations in estimation."""
 
1
  import cv2
2
  import numpy as np
3
+
4
+ from CPR_Module.Common.keypoints import CocoKeypoints
5
+ from CPR_Module.Common.logging_config import cpr_logger
6
 
7
  class ChestInitializer:
8
  """Handles chest point detection with validations in estimation."""
{CPR β†’ CPR_Module/Common}/keypoints.py RENAMED
File without changes
{CPR β†’ CPR_Module/Common}/logging_config.py RENAMED
File without changes
{CPRRealTime β†’ CPR_Module/Common}/posture_analyzer.py RENAMED
@@ -2,8 +2,9 @@
2
  import math
3
  import cv2
4
  import numpy as np
5
- from CPRRealTime.keypoints import CocoKeypoints
6
- from CPRRealTime.logging_config import cpr_logger
 
7
 
8
  class PostureAnalyzer:
9
  """Posture analysis and visualization with comprehensive validation"""
 
2
  import math
3
  import cv2
4
  import numpy as np
5
+
6
+ from CPR_Module.Common.keypoints import CocoKeypoints
7
+ from CPR_Module.Common.logging_config import cpr_logger
8
 
9
  class PostureAnalyzer:
10
  """Posture analysis and visualization with comprehensive validation"""
{CPR β†’ CPR_Module/Common}/role_classifier.py RENAMED
@@ -1,9 +1,9 @@
1
  # role_classifier.py
2
  import cv2
3
  import numpy as np
4
- from ultralytics.utils.plotting import Annotator # Import YOLO's annotator
5
- from CPR.logging_config import cpr_logger
6
 
 
7
 
8
  class RoleClassifier:
9
  """Role classification and tracking using image processing"""
 
1
  # role_classifier.py
2
  import cv2
3
  import numpy as np
4
+ from ultralytics.utils.plotting import Annotator
 
5
 
6
+ from CPR_Module.Common.logging_config import cpr_logger
7
 
8
  class RoleClassifier:
9
  """Role classification and tracking using image processing"""
{CPRRealTime β†’ CPR_Module/Common}/shoulders_analyzer.py RENAMED
@@ -1,6 +1,7 @@
1
  import numpy as np
2
- from CPRRealTime.keypoints import CocoKeypoints
3
- from CPRRealTime.logging_config import cpr_logger
 
4
 
5
  class ShouldersAnalyzer:
6
  """Analyzes shoulder distances and posture"""
 
1
  import numpy as np
2
+
3
+ from CPR_Module.Common.keypoints import CocoKeypoints
4
+ from CPR_Module.Common.logging_config import cpr_logger
5
 
6
  class ShouldersAnalyzer:
7
  """Analyzes shoulder distances and posture"""
{CPRRealTime β†’ CPR_Module/Common}/threaded_camera.py RENAMED
@@ -2,7 +2,8 @@ import threading
2
  from queue import Queue
3
  import queue
4
  import cv2
5
- from CPRRealTime.logging_config import cpr_logger
 
6
 
7
  class ThreadedCamera:
8
  def __init__(self, source, requested_fps = 30):
 
2
  from queue import Queue
3
  import queue
4
  import cv2
5
+
6
+ from CPR_Module.Common.logging_config import cpr_logger
7
 
8
  class ThreadedCamera:
9
  def __init__(self, source, requested_fps = 30):
{CPR β†’ CPR_Module/Common}/warnings_overlayer.py RENAMED
@@ -3,7 +3,7 @@ import numpy as np
3
  import os
4
  import sys
5
 
6
- from CPR.logging_config import cpr_logger
7
 
8
  class WarningsOverlayer:
9
  def __init__(self):
 
3
  import os
4
  import sys
5
 
6
+ from CPR_Module.Common.logging_config import cpr_logger
7
 
8
  class WarningsOverlayer:
9
  def __init__(self):
{CPR β†’ CPR_Module/Common}/wrists_midpoint_analyzer.py RENAMED
@@ -1,7 +1,8 @@
1
  import cv2
2
  import numpy as np
3
- from CPR.keypoints import CocoKeypoints
4
- from CPR.logging_config import cpr_logger
 
5
 
6
  class WristsMidpointAnalyzer:
7
  """Analyzes and tracks wrist midpoints for rescuer"""
 
1
  import cv2
2
  import numpy as np
3
+
4
+ from CPR_Module.Common.keypoints import CocoKeypoints
5
+ from CPR_Module.Common.logging_config import cpr_logger
6
 
7
  class WristsMidpointAnalyzer:
8
  """Analyzes and tracks wrist midpoints for rescuer"""
{CPR β†’ CPR_Module/Common}/yolo11n-pose.pt RENAMED
File without changes
{CPR β†’ CPR_Module/Educational_Mode}/CPRAnalyzer.py RENAMED
@@ -3,20 +3,21 @@ import cv2
3
  import time
4
  import math
5
  import numpy as np
6
- import os # Added for path handling
7
  import sys
8
 
9
- from CPR.pose_estimation import PoseEstimator
10
- from CPR.role_classifier import RoleClassifier
11
- from CPR.chest_initializer import ChestInitializer
12
- from CPR.metrics_calculator import MetricsCalculator
13
- from CPR.posture_analyzer import PostureAnalyzer
14
- from CPR.wrists_midpoint_analyzer import WristsMidpointAnalyzer
15
- from CPR.shoulders_analyzer import ShouldersAnalyzer
16
- from CPR.graph_plotter import GraphPlotter
17
- from CPR.warnings_overlayer import WarningsOverlayer
18
-
19
- from CPR.logging_config import cpr_logger
 
20
 
21
  class CPRAnalyzer:
22
  """Main CPR analysis pipeline with execution tracing"""
 
3
  import time
4
  import math
5
  import numpy as np
6
+ import os
7
  import sys
8
 
9
+ from CPR_Module.Educational_Mode.pose_estimation import PoseEstimator
10
+ from CPR_Module.Educational_Mode.metrics_calculator import MetricsCalculator
11
+ from CPR_Module.Educational_Mode.graph_plotter import GraphPlotter
12
+
13
+ from CPR_Module.Common.role_classifier import RoleClassifier
14
+ from CPR_Module.Common.chest_initializer import ChestInitializer
15
+ from CPR_Module.Common.posture_analyzer import PostureAnalyzer
16
+ from CPR_Module.Common.wrists_midpoint_analyzer import WristsMidpointAnalyzer
17
+ from CPR_Module.Common.shoulders_analyzer import ShouldersAnalyzer
18
+ from CPR_Module.Common.warnings_overlayer import WarningsOverlayer
19
+
20
+ from CPR_Module.Common.logging_config import cpr_logger
21
 
22
  class CPRAnalyzer:
23
  """Main CPR analysis pipeline with execution tracing"""
CPR_Module/Educational_Mode/__init__.py ADDED
File without changes
{CPR β†’ CPR_Module/Educational_Mode}/graph_plotter.py RENAMED
@@ -2,10 +2,11 @@ import numpy as np
2
  import matplotlib.pyplot as plt
3
  import sys
4
  import cv2
5
- from CPR.logging_config import cpr_logger
6
- from matplotlib.ticker import MultipleLocator
7
  import os
8
 
 
 
9
  class GraphPlotter:
10
  """Class to plot graphs for various metrics"""
11
 
 
2
  import matplotlib.pyplot as plt
3
  import sys
4
  import cv2
5
+ from CPR_Module.Common.logging_config import cpr_logger
 
6
  import os
7
 
8
+ from matplotlib.ticker import MultipleLocator
9
+
10
  class GraphPlotter:
11
  """Class to plot graphs for various metrics"""
12
 
{CPR β†’ CPR_Module/Educational_Mode}/metrics_calculator.py RENAMED
@@ -5,7 +5,8 @@ import matplotlib.pyplot as plt
5
  import sys
6
  import cv2
7
  import os
8
- from CPR.logging_config import cpr_logger
 
9
 
10
  class MetricsCalculator:
11
  """Rate and depth calculation from motion data with improved peak detection"""
 
5
  import sys
6
  import cv2
7
  import os
8
+
9
+ from CPR_Module.Common.logging_config import cpr_logger
10
 
11
  class MetricsCalculator:
12
  """Rate and depth calculation from motion data with improved peak detection"""
{CPR β†’ CPR_Module/Educational_Mode}/pose_estimation.py RENAMED
@@ -2,13 +2,14 @@
2
  import cv2
3
  import numpy as np
4
  from ultralytics import YOLO
5
- from CPR.keypoints import CocoKeypoints
6
- from CPR.logging_config import cpr_logger
 
7
 
8
  class PoseEstimator:
9
  """Human pose estimation using YOLO"""
10
 
11
- def __init__(self, model_path="yolo11n-pose.pt", min_confidence=0.2):
12
  self.model = YOLO(model_path)
13
  self.min_confidence = min_confidence
14
 
 
2
  import cv2
3
  import numpy as np
4
  from ultralytics import YOLO
5
+
6
+ from CPR_Module.Common.keypoints import CocoKeypoints
7
+ from CPR_Module.Common.logging_config import cpr_logger
8
 
9
  class PoseEstimator:
10
  """Human pose estimation using YOLO"""
11
 
12
+ def __init__(self, model_path="CPR_Module\Common\yolo11n-pose.pt", min_confidence=0.2):
13
  self.model = YOLO(model_path)
14
  self.min_confidence = min_confidence
15
 
CPR_Module/Emergency_Mode/__init__.py ADDED
File without changes
{CPRRealTime β†’ CPR_Module/Emergency_Mode}/graph_plotter.py RENAMED
@@ -2,10 +2,11 @@ import numpy as np
2
  import matplotlib.pyplot as plt
3
  import sys
4
  import cv2
5
- from CPRRealTime.logging_config import cpr_logger
6
  from matplotlib.ticker import MultipleLocator
7
  import os
8
 
 
 
9
  class GraphPlotter:
10
  """Class to plot graphs for various metrics"""
11
 
 
2
  import matplotlib.pyplot as plt
3
  import sys
4
  import cv2
 
5
  from matplotlib.ticker import MultipleLocator
6
  import os
7
 
8
+ from CPR_Module.Common.logging_config import cpr_logger
9
+
10
  class GraphPlotter:
11
  """Class to plot graphs for various metrics"""
12
 
{CPRRealTime β†’ CPR_Module/Emergency_Mode}/main.py RENAMED
@@ -6,18 +6,19 @@ import numpy as np
6
  import os # Added for path handling
7
  import sys
8
 
9
- from CPRRealTime.pose_estimation import PoseEstimator
10
- from CPRRealTime.role_classifier import RoleClassifier
11
- from CPRRealTime.chest_initializer import ChestInitializer
12
- from CPRRealTime.metrics_calculator import MetricsCalculator
13
- from CPRRealTime.posture_analyzer import PostureAnalyzer
14
- from CPRRealTime.wrists_midpoint_analyzer import WristsMidpointAnalyzer
15
- from CPRRealTime.shoulders_analyzer import ShouldersAnalyzer
16
- from CPRRealTime.graph_plotter import GraphPlotter
17
- from CPRRealTime.warnings_overlayer import WarningsOverlayer
18
- from CPRRealTime.threaded_camera import ThreadedCamera
19
- from CPRRealTime.analysis_socket_server import AnalysisSocketServer
20
- from CPRRealTime.logging_config import cpr_logger
 
21
 
22
  class CPRAnalyzer:
23
  """Main CPR analysis pipeline with execution tracing"""
 
6
  import os # Added for path handling
7
  import sys
8
 
9
+ from CPR_Module.Emergency_Mode.pose_estimation import PoseEstimator
10
+ from CPR_Module.Emergency_Mode.metrics_calculator import MetricsCalculator
11
+ from CPR_Module.Emergency_Mode.graph_plotter import GraphPlotter
12
+
13
+ from CPR_Module.Common.role_classifier import RoleClassifier
14
+ from CPR_Module.Common.chest_initializer import ChestInitializer
15
+ from CPR_Module.Common.posture_analyzer import PostureAnalyzer
16
+ from CPR_Module.Common.wrists_midpoint_analyzer import WristsMidpointAnalyzer
17
+ from CPR_Module.Common.shoulders_analyzer import ShouldersAnalyzer
18
+ from CPR_Module.Common.warnings_overlayer import WarningsOverlayer
19
+ from CPR_Module.Common.threaded_camera import ThreadedCamera
20
+ from CPR_Module.Common.analysis_socket_server import AnalysisSocketServer
21
+ from CPR_Module.Common.logging_config import cpr_logger
22
 
23
  class CPRAnalyzer:
24
  """Main CPR analysis pipeline with execution tracing"""
{CPRRealTime β†’ CPR_Module/Emergency_Mode}/metrics_calculator.py RENAMED
@@ -5,7 +5,8 @@ import matplotlib.pyplot as plt
5
  import sys
6
  import cv2
7
  import os
8
- from CPRRealTime.logging_config import cpr_logger
 
9
 
10
  class MetricsCalculator:
11
  """Rate and depth calculation from motion data with improved peak detection"""
 
5
  import sys
6
  import cv2
7
  import os
8
+
9
+ from CPR_Module.Common.logging_config import cpr_logger
10
 
11
  class MetricsCalculator:
12
  """Rate and depth calculation from motion data with improved peak detection"""
{CPRRealTime β†’ CPR_Module/Emergency_Mode}/pose_estimation.py RENAMED
@@ -2,20 +2,20 @@
2
  import cv2
3
  import numpy as np
4
  from ultralytics import YOLO
5
- from CPRRealTime.keypoints import CocoKeypoints
6
- from CPRRealTime.logging_config import cpr_logger
 
7
 
8
  class PoseEstimator:
9
  """Human pose estimation using YOLO"""
10
 
11
- def __init__(self, min_confidence, model_path="yolo11n-pose.pt"):
12
- #self.model = YOLO(model_path).to("")
13
  self.model = YOLO(model_path).to("cuda")
14
 
15
  if next(self.model.model.parameters()).is_cuda:
16
- print("βœ… YOLO model successfully loaded on CUDA (GPU).")
17
  else:
18
- print("❌ YOLO model is not on CUDA. Check your setup.")
19
 
20
  self.min_confidence = min_confidence
21
 
 
2
  import cv2
3
  import numpy as np
4
  from ultralytics import YOLO
5
+
6
+ from CPR_Module.Common.keypoints import CocoKeypoints
7
+ from CPR_Module.Common.logging_config import cpr_logger
8
 
9
  class PoseEstimator:
10
  """Human pose estimation using YOLO"""
11
 
12
+ def __init__(self, min_confidence, model_path="CPR_Module\Common\yolo11n-pose.pt"):
 
13
  self.model = YOLO(model_path).to("cuda")
14
 
15
  if next(self.model.model.parameters()).is_cuda:
16
+ cpr_logger.info("YOLO model loaded on CUDA (GPU).")
17
  else:
18
+ cpr_logger.warning("YOLO model is not on CUDA. Check your setup.")
19
 
20
  self.min_confidence = min_confidence
21
 
CPR_Module/__init__.py ADDED
File without changes
Demo/__init__.py ADDED
File without changes
Demo/demo.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import time
4
+ import argparse
5
+
6
+ from CPR_Module.Common.logging_config import cpr_logger
7
+ from CPR_Module.Educational_Mode.CPRAnalyzer import CPRAnalyzer
8
+
9
+ cpr_logger.info("TEST LOG: Script started successfully")
10
+
11
+ def main(input_video, output_dir=None):
12
+ # Configuration
13
+ requested_fps = 30
14
+ base_dir = os.getcwd()
15
+ cpr_logger.info(f"[CONFIG] Base directory: {base_dir}")
16
+
17
+ # Validate input file exists
18
+ if not os.path.exists(input_video):
19
+ cpr_logger.error(f"[ERROR] Input video not found at: {input_video}")
20
+ sys.exit(1)
21
+
22
+ # Extract original filename without extension
23
+ original_name = os.path.splitext(os.path.basename(input_video))[0]
24
+ cpr_logger.info(f"[CONFIG] Original video name: {original_name}")
25
+
26
+ # Create output directory if it doesn't exist
27
+ if output_dir:
28
+ os.makedirs(output_dir, exist_ok=True)
29
+ cpr_logger.info(f"[CONFIG] Output will be saved to: {output_dir}")
30
+
31
+ # Set output paths using original name
32
+ video_output_path = os.path.join(output_dir, f"{original_name}_output.mp4")
33
+ plot_output_path = os.path.join(output_dir, f"{original_name}_output.png")
34
+
35
+ # Log paths for verification
36
+ cpr_logger.info(f"[CONFIG] Video input: {input_video}")
37
+ cpr_logger.info(f"[CONFIG] Video output: {video_output_path}")
38
+ cpr_logger.info(f"[CONFIG] Plot output: {plot_output_path}")
39
+
40
+ # Initialize and run analyzer
41
+ initialization_start_time = time.time()
42
+ analyzer = CPRAnalyzer(input_video, video_output_path, plot_output_path, requested_fps)
43
+
44
+ initialization_end_time = time.time()
45
+ initialization_elapsed_time = initialization_end_time - initialization_start_time
46
+ cpr_logger.info(f"[TIMING] Initialization time: {initialization_elapsed_time:.2f}s")
47
+
48
+ try:
49
+ analyzer.run_analysis_video()
50
+ finally:
51
+ cpr_logger.info(f"[MAIN] CPR Analysis Terminated")
52
+
53
+
54
+ if __name__ == "__main__":
55
+ cpr_logger.info("Starting CPR Analysis Tool")
56
+ parser = argparse.ArgumentParser(description="CPR Analysis Tool")
57
+
58
+ cpr_logger.info("Parsing command line arguments")
59
+ parser.add_argument(
60
+ "-i", "--input",
61
+ required=True,
62
+ help="Path to input video file"
63
+ )
64
+ parser.add_argument(
65
+ "-o", "--output",
66
+ default=None,
67
+ help="Optional output directory path"
68
+ )
69
+ args = parser.parse_args()
70
+
71
+ cpr_logger.info(f"Input video: {args.input}")
72
+ cpr_logger.info(f"Output directory: {args.output if args.output else 'Not specified, using current directory'}")
73
+
74
+ cpr_logger.info("Starting main function")
75
+ main(args.input, args.output)
README.md CHANGED
@@ -1,40 +1,168 @@
1
- # El7a2ny Backend
2
-
3
- A mobile application built using Flutter. This README provides clear instructions for both developers and non-developers to run or test the app.
4
-
5
- ---
6
-
7
- ## El7a2ny Overview
8
-
9
- El7a2ny is an emergency mobile app that tackeles real-life emergency situations and help its users tackle them in a user-friendly way.
10
-
11
- The app includes 3 main features:
12
-
13
- - ECG Analysis, export your apple smart watch ECG PDF analysis, import it into our mobile, do a checkup and diagnose your ECG analysis to ensure it is normal or has a kind of abnormalities, and will guide you to the correct procedures accordingly.
14
-
15
- - Skin burns, got a burn at your home from a stove or boiling water? take a picture and upload it to the app, we will segment the burn, and classify the degree and prompt you with the correct guidlines accordingly.
16
-
17
- - CPR analysis, with our educational CPR mode, you will be able to do CPR at your home or at centers, capture a video of yourself doing the CPR (Cardiopulmonary resuscitation), upload it to the application, wait for a full informative analysis of the results and how we detect your rate and depth of motion along with posture warnings. Also experience the real-time feature of CPR where you can use the application in case of emergencies, mount the mobile in a portatit mode, then start your live stream, and you are good to go.
18
-
19
- ---
20
-
21
- ## Technical Setup
22
-
23
- This backend is built using FastAPI python framwork,it can be runned locally and it is also deployed on hugging face.
24
-
25
-
26
- # Instructions
27
-
28
- It is easier to access this backend through its deployed version on hugging face via docker through this base url for most of the applicaiton feature, this includes: SKin Burns, ECG Analysis and CPR Education Mode.
29
-
30
- "https://husseinhadidy-deploy-el7a2ny-application.hf.space"
31
-
32
- But running real-time mode of CPR can be tried locally for latency concerns, as hugging face is a free contianer that has low specs, so real-time and socket connection is not tested on the deployed instance.
33
-
34
- You can use the requirments.txt to install the requirments needed and run using (Python 3.10):
35
-
36
- ```bash
37
-
38
- uvicorn app:app --host 0.0.0.0 --port 8000
39
-
40
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ![Alt text](ReamdMeAssets/El7a2ny.png)
2
+
3
+ Ever wondered what you'd do if someone **collapsed** in front of you?<br>
4
+ El7a2ny is a mobile app that trains you to handle medical emergencies and guides you step-by-step as they happen.<br>
5
+ No medical degree needed β€” just follow the app, stay calm, and become the hero someone desperately needs.<br>
6
+
7
+ # πŸ“ Main Features
8
+
9
+ ## πŸ«€ ECG Analysis
10
+ Export your Apple Watch ECG PDF analysis and import it directly into our mobile application. Our advanced diagnostic system performs comprehensive checkups to determine if your ECG readings are normal or show abnormalities. Based on the analysis results, you'll receive personalized guidance and recommendations for appropriate next steps.
11
+
12
+ ## πŸ”₯ Burn Assessment
13
+ Accidentally burned yourself on a stove or with boiling water? Simply take a photo and upload it to the app. Our intelligent image processing will:
14
+ - Automatically segment and identify the burn area
15
+ - Classify the burn degree (1st, 2nd, or 3rd degree)
16
+ - Provide immediate care guidelines and treatment recommendations
17
+
18
+ ## πŸš‘ CPR Training & Analysis
19
+
20
+ ### Educational Mode
21
+ Perfect your CPR technique at home or in training centers:
22
+ - Record yourself performing CPR (Cardiopulmonary Resuscitation)
23
+ - Upload the video for comprehensive analysis
24
+ - Receive detailed feedback on compression rate, depth, and technique
25
+ - Get posture corrections and improvement suggestions
26
+
27
+ ### Real-Time Emergency Mode
28
+ For actual emergency situations:
29
+ - Mount your phone in portrait mode
30
+ - Start live CPR guidance with real-time feedback
31
+ - Get instant coaching during critical moments
32
+ - Ensure proper technique when every second counts
33
+
34
+ # πŸš€ Installation
35
+ 1. Clone the repository:
36
+ ```bash
37
+ git clone https://github.com/El7a2ny-Graduation-Project/Backend.git
38
+ ```
39
+ 2. Navigate to the project directory:
40
+ ```bash
41
+ cd Backend
42
+ ```
43
+ 3. Create a virtual environment (optional but recommended):
44
+ ```bash
45
+ python 3.10 -m venv venv
46
+ ```
47
+ 4. Activate the virtual environment:
48
+ - On Windows:
49
+ ```bash
50
+ venv\Scripts\activate
51
+ ```
52
+ - On macOS/Linux:
53
+ ```bash
54
+ source venv/bin/activate
55
+ ```
56
+ 5. Install the required packages:
57
+ ```bash
58
+ pip install -r requirements.txt
59
+ ```
60
+
61
+ # Technical Setup
62
+
63
+ ## Framework
64
+ This backend is built using the **FastAPI** Python framework and can be run both locally and accessed through our deployed instance on Hugging Face.
65
+
66
+ ## Deployment Options
67
+
68
+ ### 🌐 Production Deployment (Recommended)
69
+ For most application features, we recommend using our deployed instance on Hugging Face:
70
+
71
+ **Base URL:** `https://husseinhadidy-deploy-el7a2ny-application.hf.space`
72
+
73
+ **Supported Features:**
74
+ - Skin Burns Analysis
75
+ - ECG Analysis
76
+ - CPR Education Mode
77
+
78
+ ### 🏠 Local Deployment
79
+ For the **CPR Real-Time Mode**, local deployment is recommended due to latency requirements. The Hugging Face free container has limited specifications that may not provide optimal performance for real-time socket connections.
80
+
81
+ 3. **Run the Application**
82
+ ```bash
83
+ uvicorn app:app --host 0.0.0.0 --port 8000
84
+ ```
85
+
86
+ 2. **Access the API**
87
+ - Local URL: `http://localhost:8000`
88
+ - API Documentation: `http://localhost:8000/docs`
89
+
90
+ # 🐞 Bugs, Suggestions or Questions
91
+ Please be welcome to submit an issue.
92
+
93
+ # πŸŽ₯ Run Demo
94
+ ## CPR Module
95
+ 1. Navigate to the project directory:
96
+ ```bash
97
+ cd Backend
98
+ ```
99
+ 2. Run the test demo for the CPR's educational mode:
100
+ ```bash
101
+ python -m Demo.demo -i "path_to_your_video.mp4" -o "path_to_output_directory"
102
+ ```
103
+ For example:
104
+ ```bash
105
+ python -m Demo.demo -i "Demo/Dataset/02.mp4" -o "Demo/Output"
106
+ ```
107
+
108
+ # πŸ‘₯ Collaborators
109
+ <table>
110
+ <tr>
111
+ <td align="center">
112
+ <a href="https://github.com/FatemaKotb">
113
+ <img src="https://avatars.githubusercontent.com/u/101884853?v=4" width="100;" alt="Fatema-Kotb"/>
114
+ </a>
115
+ </td>
116
+ <td align="center">
117
+ <a href="https://github.com/Hussein-Hadidy">
118
+ <img src="https://avatars.githubusercontent.com/u/111172851?v=4" width="100;" alt="Hussein-Hadidy"/>
119
+ </a>
120
+ </td>
121
+ <td align="center">
122
+ <a href="https://github.com/rana-abdalla1">
123
+ <img src="https://avatars.githubusercontent.com/u/111182872?v=4" width="100;" alt="Rana-Abdalla"/>
124
+ </a>
125
+ </td>
126
+ <td align="center">
127
+ <a href="https://github.com/Sherif-Hatem1">
128
+ <img src="https://avatars.githubusercontent.com/u/125387417?v=4" width="100;" alt="Sherif-Hatem"/>
129
+ </a>
130
+ </td>
131
+ </tr>
132
+ <tr>
133
+ <td align="center">
134
+ <a href="https://github.com/FatemaKotb">
135
+ Fatema Kotb
136
+ </a>
137
+ </td>
138
+ <td align="center">
139
+ <a href="https://github.com/Hussein-Hadidy">
140
+ Hussein Hadidy
141
+ </a>
142
+ </td>
143
+ <td align="center">
144
+ <a href="https://github.com/rana-abdalla1">
145
+ Rana Abdalla
146
+ </a>
147
+ </td>
148
+ <td align="center">
149
+ <a href="https://github.com/Sherif-Hatem1">
150
+ Sherif Hatem
151
+ </a>
152
+ </td>
153
+ </tr>
154
+ <tr>
155
+ <td align="center">
156
+ CPR Module Implementation
157
+ </td>
158
+ <td align="center">
159
+ CPR Module Testing & Application Development
160
+ </td>
161
+ <td align="center">
162
+ ECG Abalysis Module Implementation
163
+ </td>
164
+ <td align="center">
165
+ Skin Burns Module Implementation & Application Development
166
+ </td>
167
+ </tr>
168
+ </table>
app.py CHANGED
@@ -23,24 +23,24 @@ from fastapi import WebSocket, WebSocketDisconnect
23
  import base64
24
  import cv2
25
  import time
26
- from CPR.CPRAnalyzer import CPRAnalyzer as OfflineAnalyzer
27
  import tempfile
28
  import matplotlib.pyplot as plt
29
  import json
30
  import asyncio
31
  import concurrent.futures
32
- from CPRRealTime.main import CPRAnalyzer as RealtimeAnalyzer
33
  from threading import Thread
34
  from starlette.responses import StreamingResponse
35
  import threading
36
  import queue
37
- from CPRRealTime.analysis_socket_server import AnalysisSocketServer # adjust if needed
38
- from CPRRealTime.logging_config import cpr_logger
39
  import logging
40
  import sys
41
  import re
42
  import signal
43
 
 
 
 
 
44
 
45
  app = FastAPI()
46
 
 
23
  import base64
24
  import cv2
25
  import time
 
26
  import tempfile
27
  import matplotlib.pyplot as plt
28
  import json
29
  import asyncio
30
  import concurrent.futures
 
31
  from threading import Thread
32
  from starlette.responses import StreamingResponse
33
  import threading
34
  import queue
 
 
35
  import logging
36
  import sys
37
  import re
38
  import signal
39
 
40
+ from CPR_Module.Educational_Mode.CPRAnalyzer import CPRAnalyzer as OfflineAnalyzer
41
+ from CPR_Module.Emergency_Mode.main import CPRAnalyzer as RealtimeAnalyzer
42
+ from CPR_Module.Common.analysis_socket_server import AnalysisSocketServer # adjust if needed
43
+ from CPR_Module.Common.logging_config import cpr_logger
44
 
45
  app = FastAPI()
46