fall-detection7 / fall_detection_system.py
FatimaNoorAI's picture
Upload 2 files
a7f2140 verified
Raw History Blame Contribute Delete
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}")