Hussein El-Hadidy
commited on
Commit
·
1a5dbca
1
Parent(s):
5601c7f
Final
Browse files- .gitignore +3 -1
- CPR/CPRAnalyzer.py +1 -1
- CPR/pose_estimation.py +1 -1
- app.py +6 -0
- backupbackend.zip +3 -0
- main.py +76 -17
- yolo11n-pose.pt +3 -0
.gitignore
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
__pycache__/*
|
| 2 |
venv/*
|
| 3 |
uploads/*
|
| 4 |
-
runs/*
|
|
|
|
|
|
|
|
|
| 1 |
__pycache__/*
|
| 2 |
venv/*
|
| 3 |
uploads/*
|
| 4 |
+
runs/*
|
| 5 |
+
screenshots/*
|
| 6 |
+
*.pyc
|
CPR/CPRAnalyzer.py
CHANGED
|
@@ -232,7 +232,7 @@ class CPRAnalyzer:
|
|
| 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"
|
| 236 |
"description": description
|
| 237 |
})
|
| 238 |
return result
|
|
|
|
| 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"{os.path.basename(path)}", # Adjust if hosted elsewhere
|
| 236 |
"description": description
|
| 237 |
})
|
| 238 |
return result
|
CPR/pose_estimation.py
CHANGED
|
@@ -8,7 +8,7 @@ class PoseEstimator:
|
|
| 8 |
"""Human pose estimation using YOLO"""
|
| 9 |
|
| 10 |
def __init__(self, model_path="yolo11n-pose.pt", min_confidence=0.2):
|
| 11 |
-
self.model = YOLO(model_path)
|
| 12 |
self.min_confidence = min_confidence
|
| 13 |
|
| 14 |
def detect_poses(self, frame):
|
|
|
|
| 8 |
"""Human pose estimation using YOLO"""
|
| 9 |
|
| 10 |
def __init__(self, model_path="yolo11n-pose.pt", min_confidence=0.2):
|
| 11 |
+
self.model = YOLO(model_path).to('cuda')
|
| 12 |
self.min_confidence = min_confidence
|
| 13 |
|
| 14 |
def detect_poses(self, frame):
|
app.py
CHANGED
|
@@ -249,6 +249,12 @@ async def process_video(file: UploadFile = File(...)):
|
|
| 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)
|
|
|
|
| 249 |
metrics = analyzer.get_compression_metrics()
|
| 250 |
warnings = analyzer.get_posture_warning_results()
|
| 251 |
|
| 252 |
+
# Upload each warning to cloudinary
|
| 253 |
+
for w in warnings:
|
| 254 |
+
local_path = w['image_url']
|
| 255 |
+
upload_result = cloudinary.uploader.upload(local_path, folder="posture_warnings")
|
| 256 |
+
w['image_url'] = upload_result['secure_url']
|
| 257 |
+
|
| 258 |
# Estimate score (adjust this logic as needed)
|
| 259 |
penalty = len(warnings) * 0.1
|
| 260 |
rate_score = min(metrics["average_compression_rate"] / 120, 1.0)
|
backupbackend.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31c7f003c89e9daecb3cf71a0806f668967632fd7a9eaa0c588ca666e7c87d83
|
| 3 |
+
size 39047568
|
main.py
CHANGED
|
@@ -21,6 +21,10 @@ from fastapi import HTTPException
|
|
| 21 |
from fastapi import WebSocket, WebSocketDisconnect
|
| 22 |
import base64
|
| 23 |
import cv2
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
|
|
@@ -194,28 +198,83 @@ async def process_video(file: UploadFile = File(...)):
|
|
| 194 |
|
| 195 |
print("File content type:", file.content_type)
|
| 196 |
print("File filename:", file.filename)
|
|
|
|
| 197 |
# Save uploaded file
|
| 198 |
video_path = os.path.join(UPLOAD_DIR, file.filename)
|
| 199 |
with open(video_path, "wb") as buffer:
|
| 200 |
shutil.copyfileobj(file.file, buffer)
|
| 201 |
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
|
| 221 |
@app.post("/process_image")
|
|
|
|
| 21 |
from fastapi import WebSocket, WebSocketDisconnect
|
| 22 |
import base64
|
| 23 |
import cv2
|
| 24 |
+
import time
|
| 25 |
+
from CPR.CPRAnalyzer import CPRAnalyzer
|
| 26 |
+
import tempfile
|
| 27 |
+
|
| 28 |
|
| 29 |
|
| 30 |
|
|
|
|
| 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 |
+
# Upload each warning to cloudinary
|
| 253 |
+
for w in warnings:
|
| 254 |
+
filename = w['image_url'] # just the filename
|
| 255 |
+
local_path = os.path.join("screenshots", filename)
|
| 256 |
+
upload_result = cloudinary.uploader.upload(local_path, folder="posture_warnings")
|
| 257 |
+
w['image_url'] = upload_result['secure_url']
|
| 258 |
+
|
| 259 |
+
# Estimate score (adjust this logic as needed)
|
| 260 |
+
penalty = len(warnings) * 0.1
|
| 261 |
+
rate_score = min(metrics["average_compression_rate"] / 120, 1.0)
|
| 262 |
+
depth_score = min(metrics["average_compression_depth"] / 5.0, 1.0)
|
| 263 |
+
average_score = max(0.0, (rate_score + depth_score)/2 - penalty)
|
| 264 |
+
|
| 265 |
+
chunks.append({
|
| 266 |
+
"average_score": round(average_score, 2),
|
| 267 |
+
"average_rate": round(metrics["average_compression_rate"], 1),
|
| 268 |
+
"average_depth": round(metrics["average_compression_depth"], 1),
|
| 269 |
+
"posture_warnings": warnings
|
| 270 |
+
})
|
| 271 |
+
|
| 272 |
+
chunk_index += 1
|
| 273 |
+
|
| 274 |
+
cap.release()
|
| 275 |
+
print(f"[END] Total processing time: {time.time() - start_time:.2f}s")
|
| 276 |
+
|
| 277 |
+
return JSONResponse(content={"chunks": chunks})
|
| 278 |
|
| 279 |
|
| 280 |
@app.post("/process_image")
|
yolo11n-pose.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:869e83fcdffdc7371fa4e34cd8e51c838cc729571d1635e5141e3075e9319dc0
|
| 3 |
+
size 6255593
|