import streamlit as st import cv2 import numpy as np from ultralytics import YOLO import requests import math import time import os from tempfile import NamedTemporaryFile import folium from streamlit_folium import st_folium # ============================================================ # CONFIGURATION # ============================================================ MODEL_PATH = "model/best.pt" CLASS_NAMES = {0: "Accident", 1: "Non-accident", 2: "Fire"} ALERT_CLASSES = {"Accident", "Fire"} DEFAULT_CONFIDENCE = 0.3 HOSPITAL_RADIUS_M = 5000 # ============================================================ # PAGE CONFIG # ============================================================ st.set_page_config( page_title="AccidentAI β Real-Time Detection & Alert", page_icon="π¨", layout="wide", initial_sidebar_state="expanded", ) # ============================================================ # CUSTOM CSS # ============================================================ st.markdown(""" """, unsafe_allow_html=True) # ============================================================ # MODEL LOADING # ============================================================ @st.cache_resource def load_model(): if not os.path.exists(MODEL_PATH): st.error(f"Model file not found at `{MODEL_PATH}`.") st.stop() return YOLO(MODEL_PATH) model = load_model() # ============================================================ # UTILITY FUNCTIONS # ============================================================ def haversine_km(lat1, lon1, lat2, lon2): """Return distance in km between two lat/lon points.""" R = 6371 dlat = math.radians(lat2 - lat1) dlon = math.radians(lon2 - lon1) a = ( math.sin(dlat / 2) ** 2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2) ** 2 ) return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) # ============================================================ # DETECTION FUNCTIONS # ============================================================ def detect_in_image(image_array, conf): """Run YOLO on a single image. Returns (detections, annotated_rgb).""" results = model.predict(image_array, conf=conf, verbose=False) detections = [] for result in results: for box in result.boxes: cls_id = int(box.cls[0]) detections.append( { "label": CLASS_NAMES.get(cls_id, "Unknown"), "confidence": float(box.conf[0]), } ) annotated_bgr = results[0].plot() annotated_rgb = cv2.cvtColor(annotated_bgr, cv2.COLOR_BGR2RGB) return detections, annotated_rgb def detect_in_video(video_path, conf, frame_skip=4, progress_cb=None): """Run YOLO on video frames. Returns (detections, best_annotated_rgb).""" cap = cv2.VideoCapture(video_path) total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 1 detections = [] best_frame = None best_conf = 0 idx = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break if idx % frame_skip == 0: results = model.predict(frame, conf=conf, verbose=False) for result in results: for box in result.boxes: cls_id = int(box.cls[0]) c = float(box.conf[0]) label = CLASS_NAMES.get(cls_id, "Unknown") if label in ALERT_CLASSES: detections.append( {"label": label, "confidence": c, "frame": idx} ) if c > best_conf: best_conf = c best_frame = results[0].plot() idx += 1 if progress_cb: progress_cb(min(idx / total, 1.0)) cap.release() if best_frame is not None: best_frame = cv2.cvtColor(best_frame, cv2.COLOR_BGR2RGB) return detections, best_frame # ============================================================ # LOCATION & HOSPITAL FUNCTIONS # ============================================================ def get_ip_location(): """Free IP-based geolocation (no API key required).""" try: r = requests.get("http://ip-api.com/json/", timeout=5) d = r.json() if d.get("status") == "success": return { "lat": d["lat"], "lon": d["lon"], "city": d.get("city", ""), "region": d.get("regionName", ""), "country": d.get("country", ""), } except Exception: pass return None def fetch_hospitals(lat, lon, radius_m=5000): """Query OpenStreetMap Overpass API for hospitals within radius.""" query = f""" [out:json][timeout:10]; ( node["amenity"="hospital"](around:{radius_m},{lat},{lon}); way["amenity"="hospital"](around:{radius_m},{lat},{lon}); relation["amenity"="hospital"](around:{radius_m},{lat},{lon}); ); out center body; """ try: r = requests.post( "https://overpass-api.de/api/interpreter", data={"data": query}, timeout=15, ) elements = r.json().get("elements", []) except Exception: return [] hospitals = [] for el in elements: tags = el.get("tags", {}) if el["type"] == "node": h_lat, h_lon = el["lat"], el["lon"] else: c = el.get("center", {}) h_lat = c.get("lat", lat) h_lon = c.get("lon", lon) dist = haversine_km(lat, lon, h_lat, h_lon) hospitals.append( { "name": tags.get("name", "Unnamed Hospital"), "lat": h_lat, "lon": h_lon, "distance_km": round(dist, 2), "phone": tags.get("phone", tags.get("contact:phone", "β")), "beds": tags.get("beds", "β"), "emergency": tags.get("emergency", "unknown"), } ) hospitals.sort(key=lambda h: h["distance_km"]) return hospitals def build_map(lat, lon, hospitals, radius_m=5000): """Create a folium map with accident marker, radius circle, and hospitals.""" m = folium.Map(location=[lat, lon], zoom_start=14, tiles="CartoDB dark_matter") # Accident location folium.Marker( [lat, lon], popup="π¨ Accident", icon=folium.Icon(color="red", icon="exclamation-triangle", prefix="fa"), ).add_to(m) # Radius folium.Circle( [lat, lon], radius=radius_m, color="#ff3b30", fill=True, fill_opacity=0.06, weight=2, dash_array="6 4", ).add_to(m) # Hospitals for h in hospitals: folium.Marker( [h["lat"], h["lon"]], popup=f"π₯ {h['name']} β {h['distance_km']} km", icon=folium.Icon(color="green", icon="plus-square", prefix="fa"), ).add_to(m) return m # ============================================================ # SIDEBAR # ============================================================ with st.sidebar: st.markdown("## βοΈ Detection Settings") confidence = st.slider( "Confidence threshold", 0.10, 0.90, DEFAULT_CONFIDENCE, 0.05 ) st.markdown("---") st.markdown("## π Location") st.caption( "Location is auto-detected via IP. On cloud deployments this returns " "the server location β use manual override for accurate testing." ) use_manual = st.checkbox("Use manual coordinates") if use_manual: manual_lat = st.number_input("Latitude", value=28.6139, format="%.4f") manual_lon = st.number_input("Longitude", value=77.2090, format="%.4f") else: manual_lat, manual_lon = None, None st.markdown("---") st.markdown("## βΉοΈ About") st.markdown( "**AccidentAI** uses a custom-trained **YOLOv8** model to detect " "accidents and fires in CCTV footage, then automatically locates " "nearby hospitals and sends simulated emergency alerts." ) st.markdown( "Built with Ultralytics, Streamlit, OpenStreetMap Overpass API, and Folium." ) # ============================================================ # HERO # ============================================================ st.markdown( """
Real-Time Accident Detection & Emergency Alert System