Hussein El-Hadidy commited on
Commit
32a7274
·
1 Parent(s): ea99063
Files changed (2) hide show
  1. CPR/CPRAnalyzer.py +41 -0
  2. app.py +64 -9
CPR/CPRAnalyzer.py CHANGED
@@ -9,6 +9,8 @@ from CPR.role_classifier import RoleClassifier
9
  from CPR.chest_initializer import ChestInitializer
10
  from CPR.metrics_calculator import MetricsCalculator
11
  from CPR.posture_analyzer import PostureAnalyzer
 
 
12
 
13
  class CPRAnalyzer:
14
  """Main CPR analysis pipeline with execution tracing"""
@@ -39,6 +41,11 @@ class CPRAnalyzer:
39
  self.metrics_calculator = MetricsCalculator(shoulder_width_cm=45)
40
  self.posture_analyzer = PostureAnalyzer()
41
 
 
 
 
 
 
42
  # Window configuration
43
  self.window_name = "CPR Analysis"
44
  #cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL)
@@ -155,6 +162,18 @@ class CPRAnalyzer:
155
  warnings = self.posture_analyzer.validate_posture(keypoints, self.chest_initializer.chest_point)
156
  frame = self.posture_analyzer.display_warnings(frame, warnings)
157
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  if warnings:
159
  print(f"[WARNING] Posture issues: {', '.join(warnings)}")
160
  else:
@@ -206,6 +225,25 @@ class CPRAnalyzer:
206
  #cv2.imshow(self.window_name, resized)
207
  print(f"[DISPLAY] Resized to {new_w}x{new_h} (scale: {scale:.2f}) in {(time.time()-display_start)*1000:.1f}ms")
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  def _finalize_analysis(self):
210
  """Final analysis with detailed reporting"""
211
  print("\n[PHASE] Starting final analysis")
@@ -225,6 +263,9 @@ class CPRAnalyzer:
225
 
226
  print(f"[RESULTS] Compression Depth: {depth:.1f} cm")
227
  print(f"[RESULTS] Compression Rate: {rate:.1f} cpm")
 
 
 
228
 
229
  print("[VISUAL] Generating motion curve plot...")
230
  self.metrics_calculator.plot_motion_curve()
 
9
  from CPR.chest_initializer import ChestInitializer
10
  from CPR.metrics_calculator import MetricsCalculator
11
  from CPR.posture_analyzer import PostureAnalyzer
12
+ import os
13
+ import uuid
14
 
15
  class CPRAnalyzer:
16
  """Main CPR analysis pipeline with execution tracing"""
 
41
  self.metrics_calculator = MetricsCalculator(shoulder_width_cm=45)
42
  self.posture_analyzer = PostureAnalyzer()
43
 
44
+ self.collected_warnings = {}
45
+
46
+ self.average_compression_depth = 0
47
+ self.average_compression_rate = 0
48
+
49
  # Window configuration
50
  self.window_name = "CPR Analysis"
51
  #cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL)
 
162
  warnings = self.posture_analyzer.validate_posture(keypoints, self.chest_initializer.chest_point)
163
  frame = self.posture_analyzer.display_warnings(frame, warnings)
164
 
165
+ for warning in warnings:
166
+ if warning not in self.collected_warnings:
167
+ self.collected_warnings[warning] = []
168
+ if len(self.collected_warnings[warning]) < 2:
169
+ # Save the frame to disk or memory
170
+ filename = f"warning_{uuid.uuid4().hex}.jpg"
171
+ file_path = os.path.join("screenshots", filename)
172
+ os.makedirs("screenshots", exist_ok=True)
173
+ cv2.imwrite(file_path, frame)
174
+ self.collected_warnings[warning].append(file_path)
175
+ print(f"[CAPTURE] Saved warning screenshot: {file_path}")
176
+
177
  if warnings:
178
  print(f"[WARNING] Posture issues: {', '.join(warnings)}")
179
  else:
 
225
  #cv2.imshow(self.window_name, resized)
226
  print(f"[DISPLAY] Resized to {new_w}x{new_h} (scale: {scale:.2f}) in {(time.time()-display_start)*1000:.1f}ms")
227
 
228
+ def get_posture_warning_results(self):
229
+ """Return a list of posture warning entries with image URLs and descriptions"""
230
+ result = []
231
+ for description, paths in self.collected_warnings.items():
232
+ for path in paths:
233
+ # You might want to convert local paths to URLs if you're serving them via FastAPI
234
+ result.append({
235
+ "image_url": f"/static/{os.path.basename(path)}", # Adjust if hosted elsewhere
236
+ "description": description
237
+ })
238
+ return result
239
+
240
+ def get_compression_metrics(self):
241
+ """Return average compression depth and rate"""
242
+ return {
243
+ "average_compression_depth": self.average_compression_depth,
244
+ "average_compression_rate": self.average_compression_rate
245
+ }
246
+
247
  def _finalize_analysis(self):
248
  """Final analysis with detailed reporting"""
249
  print("\n[PHASE] Starting final analysis")
 
263
 
264
  print(f"[RESULTS] Compression Depth: {depth:.1f} cm")
265
  print(f"[RESULTS] Compression Rate: {rate:.1f} cpm")
266
+
267
+ self.average_compression_depth = depth
268
+ self.average_compression_rate = rate
269
 
270
  print("[VISUAL] Generating motion curve plot...")
271
  self.metrics_calculator.plot_motion_curve()
app.py CHANGED
@@ -23,6 +23,7 @@ import base64
23
  import cv2
24
  import time
25
  from CPR.CPRAnalyzer import CPRAnalyzer
 
26
 
27
 
28
 
@@ -197,22 +198,76 @@ async def process_video(file: UploadFile = File(...)):
197
 
198
  print("File content type:", file.content_type)
199
  print("File filename:", file.filename)
 
200
  # Save uploaded file
201
  video_path = os.path.join(UPLOAD_DIR, file.filename)
202
  with open(video_path, "wb") as buffer:
203
  shutil.copyfileobj(file.file, buffer)
204
 
205
- start_time = time.time()
206
  print("[START] CPR Analysis started")
 
207
 
208
- analyzer = CPRAnalyzer(video_path)
209
- analyzer.run_analysis()
210
-
211
- end_time = time.time()
212
- print(f"[END] Total execution time: {end_time - start_time:.2f}s")
213
-
214
-
215
- return JSONResponse(content={"message": "Video processed successfully", "result_dir": "runs/detect/testResult"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
 
218
  @app.post("/process_image")
 
23
  import cv2
24
  import time
25
  from CPR.CPRAnalyzer import CPRAnalyzer
26
+ import tempfile
27
 
28
 
29
 
 
198
 
199
  print("File content type:", file.content_type)
200
  print("File filename:", file.filename)
201
+
202
  # Save uploaded file
203
  video_path = os.path.join(UPLOAD_DIR, file.filename)
204
  with open(video_path, "wb") as buffer:
205
  shutil.copyfileobj(file.file, buffer)
206
 
 
207
  print("[START] CPR Analysis started")
208
+ start_time = time.time()
209
 
210
+ cap = cv2.VideoCapture(video_path)
211
+ fps = cap.get(cv2.CAP_PROP_FPS)
212
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
213
+ duration_seconds = total_frames / fps
214
+ chunk_duration = 10 # seconds
215
+ frames_per_chunk = int(fps * chunk_duration)
216
+
217
+ chunks = []
218
+ chunk_index = 0
219
+ current_frame = 0
220
+
221
+ while current_frame < total_frames:
222
+ # Read the chunk into memory
223
+ frames = []
224
+ for _ in range(frames_per_chunk):
225
+ ret, frame = cap.read()
226
+ if not ret:
227
+ break
228
+ frames.append(frame)
229
+ current_frame += 1
230
+
231
+ if not frames:
232
+ break
233
+
234
+ # Save chunk to temp video
235
+ temp_chunk_path = os.path.join(tempfile.gettempdir(), f"chunk_{chunk_index}.mp4")
236
+ height, width = frames[0].shape[:2]
237
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
238
+ out = cv2.VideoWriter(temp_chunk_path, fourcc, fps, (width, height))
239
+ for f in frames:
240
+ out.write(f)
241
+ out.release()
242
+
243
+ # Analyze chunk
244
+ print(f"[CHUNK {chunk_index}] Processing chunk at {temp_chunk_path}")
245
+ analyzer = CPRAnalyzer(temp_chunk_path)
246
+ analyzer.run_analysis()
247
+
248
+ # Gather results
249
+ metrics = analyzer.get_compression_metrics()
250
+ warnings = analyzer.get_posture_warning_results()
251
+
252
+ # Estimate score (adjust this logic as needed)
253
+ penalty = len(warnings) * 0.1
254
+ rate_score = min(metrics["average_compression_rate"] / 120, 1.0)
255
+ depth_score = min(metrics["average_compression_depth"] / 5.0, 1.0)
256
+ average_score = max(0.0, (rate_score + depth_score)/2 - penalty)
257
+
258
+ chunks.append({
259
+ "average_score": round(average_score, 2),
260
+ "average_rate": round(metrics["average_compression_rate"], 1),
261
+ "average_depth": round(metrics["average_compression_depth"], 1),
262
+ "posture_warnings": warnings
263
+ })
264
+
265
+ chunk_index += 1
266
+
267
+ cap.release()
268
+ print(f"[END] Total processing time: {time.time() - start_time:.2f}s")
269
+
270
+ return JSONResponse(content={"chunks": chunks})
271
 
272
 
273
  @app.post("/process_image")