Download fall_detection_system.py from FatimaNoorAI/fall-detection7: direct link, hf CLI and curl.
- Browser
- Download file 6.5 kB
-
https://huggingface.co/FatimaNoorAI/fall-detection7/resolve/main/fall_detection_system.py
- Command line
-
hf download hf://FatimaNoorAI/fall-detection7/fall_detection_system.py
-
curl -L -o fall_detection_system.py https://huggingface.co/FatimaNoorAI/fall-detection7/resolve/main/fall_detection_system.py
6.5 kB
| # -*- coding: utf-8 -*- | |
| """fall_detection_system.ipynb | |
| Automatically generated by Colab. | |
| Original file is located at | |
| https://colab.research.google.com/drive/15iogmvi7LKpzek-Zt_uNfiQoYdvVO-nY | |
| """ | |
| #@title ⬇️ Install Ultralytics + deps | |
| !pip -q install ultralytics opencv-python numpy requests | |
| import torch | |
| print("Torch:", torch.__version__, "| CUDA available:", torch.cuda.is_available()) | |
| from google.colab import drive | |
| drive.mount('/content/drive') | |
| # ==== BIG, BOLD FALL MESSAGE VERSION (with extra red alert banner) ==== | |
| import cv2, math, os, time | |
| import numpy as np | |
| from ultralytics import YOLO | |
| # --- EDIT PATHS --- | |
| INPUT_VIDEO = "/content/drive/MyDrive/AI ML Projects/Fall Detection System/Fall Tiktok.mp4" | |
| OUT_VIDEO = "/content/drive/MyDrive/AI ML Projects/Fall Detection System/out_fall_annotated.mp4" | |
| MODEL_WEIGHTS = "yolov8n-pose.pt" # or "yolov8s-pose.pt" | |
| # --- RULE THRESHOLDS (make easier if needed) --- | |
| CONFIDENCE = 0.25 | |
| IMG_SIZE = 640 | |
| ANGLE_MAX_FALL = 35 # <= ~horizontal torso | |
| HW_MAX_FALL = 0.80 # <= flat-ish bbox (height/width) | |
| PERSIST_FRAMES = 10 # consecutive frames to confirm FALL (try 8–12) | |
| STAND_MIN_ANG = 60 # standing if >= | |
| # COCO-17 skeleton edges | |
| COCO_EDGES = [ | |
| (5,6),(5,7),(7,9),(6,8),(8,10),(5,11),(6,12),(11,12), | |
| (11,13),(13,15),(12,14),(14,16),(0,5),(0,6) | |
| ] | |
| def torso_angle_deg(kps): | |
| try: | |
| sx = (kps[5][0] + kps[6][0]) / 2.0 | |
| sy = (kps[5][1] + kps[6][1]) / 2.0 | |
| hx = (kps[11][0] + kps[12][0]) / 2.0 | |
| hy = (kps[11][1] + kps[12][1]) / 2.0 | |
| dx, dy = abs(sx - hx), abs(sy - hy) | |
| return math.degrees(math.atan2(dy, dx)) # 0≈horizontal, 90≈vertical | |
| except: | |
| return 90.0 | |
| def draw_skeleton(img, kps, color, t=2): | |
| for a,b in COCO_EDGES: | |
| x1,y1 = int(kps[a][0]), int(kps[a][1]) | |
| x2,y2 = int(kps[b][0]), int(kps[b][1]) | |
| cv2.line(img, (x1,y1), (x2,y2), color, t) | |
| for (x,y) in kps[:, :2]: | |
| cv2.circle(img, (int(x),int(y)), 3, color, -1) | |
| def center_banner(img, text, color=(0,0,255)): | |
| """Huge centered banner + top strip + red border.""" | |
| H, W = img.shape[:2] | |
| # Center banner | |
| font_scale = max(1.0, min(W, H) / 600.0) * 1.8 | |
| thickness = 5 | |
| (tw, th), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness) | |
| bx1 = max(10, (W - (tw + 80)) // 2) | |
| by1 = max(10, (H - (th + 80)) // 2) | |
| bx2 = min(W-10, bx1 + tw + 80) | |
| by2 = min(H-10, by1 + th + 80) | |
| overlay = img.copy() | |
| cv2.rectangle(overlay, (bx1, by1), (bx2, by2), color, -1) | |
| cv2.addWeighted(overlay, 0.45, img, 0.55, 0, img) | |
| tx = bx1 + (bx2 - bx1 - tw) // 2 | |
| ty = by1 + (by2 - by1 + th) // 2 | |
| # white text with black outline | |
| cv2.putText(img, text, (tx+2, ty+2), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0,0,0), thickness+2, cv2.LINE_AA) | |
| cv2.putText(img, text, (tx, ty), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255,255,255), thickness, cv2.LINE_AA) | |
| # Top strip banner | |
| strip_h = max(50, th + 30) | |
| cv2.rectangle(img, (0,0), (W, strip_h), color, -1) | |
| cv2.putText(img, "FALL DOWN DETECTED", (20, strip_h - 15), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,255), 3, cv2.LINE_AA) | |
| # Red border | |
| cv2.rectangle(img, (0,0), (W-1,H-1), color, 12) | |
| def box_label(img, x1, y1, txt, color): | |
| """Black bg + bold text always visible even near frame top.""" | |
| font_scale = 0.95 | |
| thickness = 2 | |
| (tw, th), _ = cv2.getTextSize(txt, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness) | |
| # Put label inside the box if the top is too near the frame edge | |
| y_text = y1 + th + 12 if y1 < (th + 14) else y1 - 8 | |
| # Compute background box | |
| bg_x1 = x1 | |
| bg_y1 = y_text - th - 10 if y1 >= (th + 14) else y1 + 2 | |
| bg_x2 = x1 + tw + 14 | |
| bg_y2 = y_text + 2 if y1 >= (th + 14) else y1 + th + 14 | |
| cv2.rectangle(img, (bg_x1, bg_y1), (bg_x2, bg_y2), (0,0,0), -1) | |
| cv2.putText(img, txt, (x1 + 6, y_text), cv2.FONT_HERSHEY_SIMPLEX, font_scale, color, thickness, cv2.LINE_AA) | |
| # --- Load model & video IO --- | |
| model = YOLO(MODEL_WEIGHTS) | |
| cap = cv2.VideoCapture(INPUT_VIDEO) | |
| if not cap.isOpened(): | |
| raise RuntimeError(f"Could not open: {INPUT_VIDEO}") | |
| fps = cap.get(cv2.CAP_PROP_FPS) or 25.0 | |
| W = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) or 1280) | |
| H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 720) | |
| os.makedirs(os.path.dirname(OUT_VIDEO), exist_ok=True) | |
| fourcc = cv2.VideoWriter_fourcc(*"mp4v") | |
| writer = cv2.VideoWriter(OUT_VIDEO, fourcc, fps, (W, H)) | |
| persist = {} # pid -> frames satisfying fall-like condition | |
| frame_idx = 0 | |
| while True: | |
| ok, frame = cap.read() | |
| if not ok: break | |
| any_fall = False | |
| r = model(frame, conf=CONFIDENCE, imgsz=IMG_SIZE, verbose=False)[0] | |
| if r.boxes is not None and r.keypoints is not None: | |
| boxes = r.boxes.xyxy.cpu().numpy() | |
| kpss = r.keypoints.xy.cpu().numpy() | |
| for i, box in enumerate(boxes): | |
| x1,y1,x2,y2 = map(int, box) | |
| h = max(1, y2-y1); w = max(1, x2-x1) | |
| hw = h/float(w) | |
| kps = kpss[i] | |
| ang = torso_angle_deg(kps) | |
| # fall-like this frame; then persistence to confirm | |
| fall_like = (ang <= ANGLE_MAX_FALL) and (hw <= HW_MAX_FALL) | |
| pid = i | |
| persist[pid] = persist.get(pid, 0) + 1 if fall_like else max(0, persist.get(pid, 0) - 1) | |
| fallen = persist[pid] >= PERSIST_FRAMES | |
| if fallen: | |
| any_fall = True | |
| color, thick = (0,0,255), 4 | |
| cv2.rectangle(frame, (x1,y1), (x2,y2), color, thick) | |
| draw_skeleton(frame, kps, color, t=2) | |
| # Super clear per-person message (inside or near the box) | |
| box_label(frame, x1, y1, "PERSON FALLEN", (0,0,255)) | |
| else: | |
| # show status for context (standing vs other) | |
| if ang >= STAND_MIN_ANG: | |
| color, label = (0,255,0), "STANDING" | |
| else: | |
| color, label = (0,165,255), "MOVING/OTHER" | |
| cv2.rectangle(frame, (x1,y1), (x2,y2), color, 2) | |
| draw_skeleton(frame, kps, color, t=2) | |
| box_label(frame, x1, y1, f"{label} θ={ang:.0f} h/w={hw:.2f}", color) | |
| # 🔴 EXTRA-LOUD global banner if any person is fallen | |
| if any_fall: | |
| center_banner(frame, "FALL DOWN DETECTED", color=(0,0,255)) | |
| writer.write(frame) | |
| frame_idx += 1 | |
| cap.release() | |
| writer.release() | |
| print(f"[OK] Saved annotated video to:\n{OUT_VIDEO}") |