Spaces:
Runtime error
Runtime error
Download Master_script.py from noor122/smartparking: direct link, hf CLI and curl.
- Browser
- Download file 14.8 kB
-
https://huggingface.co/spaces/noor122/smartparking/resolve/main/Master_script.py
- Command line
-
hf download hf://spaces/noor122/smartparking/Master_script.py
-
curl -L -o Master_script.py https://huggingface.co/spaces/noor122/smartparking/resolve/main/Master_script.py
14.8 kB
| import os | |
| import cv2 | |
| import json | |
| import torch | |
| import torchvision | |
| import numpy as np | |
| import pandas as pd | |
| import psycopg2 | |
| from pathlib import Path | |
| from torchvision.models.detection.faster_rcnn import FastRCNNPredictor | |
| from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor | |
| from torchvision.transforms import functional as F | |
| import torchvision.ops as ops | |
| from datetime import datetime, timedelta | |
| import sys | |
| import io | |
| # إصلاح مشكلة الطباعة باللغة العربية في الكونسول | |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') | |
| # ========================================================== | |
| # 1. DATABASE & SYSTEM SETTINGS | |
| # ========================================================== | |
| DB_URI = 'postgresql://postgres.fjubdwibycchdqfgjsco:kksS8JWstgxvCC.@aws-0-eu-west-1.pooler.supabase.com:5432/postgres' | |
| VIDEO_CAM1 = r"scene22.mp4" | |
| VIDEO_CAM2 = r"scene11.mp4" | |
| MODEL_PATH = r"c:/Users/DELL/Desktop/mask_rcnn_custom.pth" | |
| OUTPUT_FOLDER = r"c:/Users/DELL/Videos/New folder/Outputs" | |
| CONF_THRESHOLD = 0.7 | |
| NMS_IOU_THRESHOLD = 0.3 | |
| FRAMES_TO_SKIP = 17 # من أداة المعايرة | |
| # إعدادات الحادثة | |
| OVERLAP_AREA_THRESHOLD = 10 | |
| NEAR_DISTANCE_THRESHOLD = 5 | |
| # إعدادات التتبع | |
| MAX_TRACK_DISTANCE = 400 # زودنا المسافة عشان التنقل بين الكاميرتين | |
| MAX_MISSING_FRAMES = 30 | |
| SUDDEN_STOP_SPEED_THRESHOLD = 3.0 | |
| LOW_SPEED_THRESHOLD = 0.8 | |
| output_dir = Path(OUTPUT_FOLDER) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| # ========================================================== | |
| # 2. DATABASE HELPERS | |
| # ========================================================== | |
| def get_latest_logged_in_car_id(): | |
| """بتجيب car_id لآخر عربية سجلت دخول بعد قراءة النمرة""" | |
| try: | |
| conn = psycopg2.connect(DB_URI) | |
| cursor = conn.cursor() | |
| query = """ | |
| SELECT r.car_id | |
| FROM logs l | |
| JOIN reservations r ON l.reserv_id = r.reserv_id | |
| ORDER BY l.log_in_t DESC | |
| LIMIT 1; | |
| """ | |
| cursor.execute(query) | |
| result = cursor.fetchone() | |
| conn.close() | |
| if result: | |
| return result[0] | |
| return None | |
| except Exception as e: | |
| print(f"[DB Error] مقدرتش أقرأ من قاعدة البيانات: {e}") | |
| return None | |
| def send_accident_notification(car_id_1, car_id_2): | |
| """بتروح تدور على أصحاب العربيات اللي عملوا حادثة وتبعتبهم إشعار في الداتابيز""" | |
| try: | |
| conn = psycopg2.connect(DB_URI) | |
| cursor = conn.cursor() | |
| # جلب الـ user_id الخاص بكل عربية من المتورطين في الحادثة | |
| cursor.execute("SELECT car_id, user_id FROM cars WHERE car_id IN (%s, %s)", (car_id_1, car_id_2)) | |
| users = cursor.fetchall() | |
| # توقيت مصر الحركي للسيستم | |
| now = datetime.utcnow() + timedelta(hours=3) | |
| for car_id, user_id in users: | |
| if user_id: | |
| title = "⚠️ Accident Alert!" | |
| message = f"Emergency: An accident involving your car (ID: {car_id}) was just detected in the smart garage." | |
| # إدخال الإشعار كـ Unread (False) | |
| cursor.execute(""" | |
| INSERT INTO notifications (user_id, title, message, is_read, created_at) | |
| VALUES (%s, %s, %s, %s, %s) | |
| """, (user_id, title, message, False, now)) | |
| conn.commit() | |
| conn.close() | |
| print(f"🚨 [Notification Sent] تم إرسال إشعار حادثة بنجاح لأصحاب السيارات: {car_id_1} و {car_id_2}") | |
| except Exception as e: | |
| print(f"[DB Error] مشكلة في إرسال إشعار الحادثة: {e}") | |
| # ========================================================== | |
| # 3. MATH & MASK HELPERS | |
| # ========================================================== | |
| def euclidean_distance(p1, p2): | |
| return float(np.linalg.norm(np.array(p1) - np.array(p2))) | |
| def mask_overlap_area(mask_a, mask_b): | |
| overlap = np.logical_and(mask_a, mask_b) | |
| return int(np.sum(overlap)) | |
| def mask_area(mask): | |
| return int(np.sum(mask)) | |
| def mask_min_distance(mask_a, mask_b): | |
| if np.logical_and(mask_a, mask_b).any(): | |
| return 0.0 | |
| mask_a_uint8 = mask_a.astype(np.uint8) | |
| mask_b_uint8 = mask_b.astype(np.uint8) | |
| if mask_a_uint8.sum() == 0 or mask_b_uint8.sum() == 0: | |
| return 999999.0 | |
| inv_b = 1 - mask_b_uint8 | |
| dist_to_b = cv2.distanceTransform(inv_b, cv2.DIST_L2, 5) | |
| distances = dist_to_b[mask_a_uint8.astype(bool)] | |
| if len(distances) == 0: | |
| return 999999.0 | |
| return float(np.min(distances)) | |
| def get_mask_centroid(mask): | |
| ys, xs = np.where(mask) | |
| if len(xs) == 0: return None | |
| return np.array([np.mean(xs), np.mean(ys)], dtype=np.float32) | |
| def box_center(box): | |
| x1, y1, x2, y2 = box | |
| return np.array([(x1 + x2) / 2, (y1 + y2) / 2], dtype=np.float32) | |
| # ========================================================== | |
| # 4. GLOBAL TRACKER (مدمج مع الداتابيز) | |
| # ========================================================== | |
| class GlobalTracker: | |
| def __init__(self, max_distance=400, max_missing=30): | |
| self.tracks = {} | |
| self.max_distance = max_distance | |
| self.max_missing = max_missing | |
| self.fallback_id = 9000 | |
| def update(self, detections): | |
| for tid in self.tracks: | |
| self.tracks[tid]["updated"] = False | |
| for det in detections: | |
| center = det["center"] | |
| best_track_id = None | |
| best_distance = float("inf") | |
| for tid, track in self.tracks.items(): | |
| if track["missing"] > self.max_missing: | |
| continue | |
| dist = euclidean_distance(center, track["center"]) | |
| if dist < best_distance: | |
| best_distance = dist | |
| best_track_id = tid | |
| if best_track_id is not None and best_distance <= self.max_distance: | |
| track = self.tracks[best_track_id] | |
| prev_center = track["center"] | |
| prev_speed = track.get("speed", 0.0) | |
| speed = euclidean_distance(center, prev_center) | |
| sudden_stop = (prev_speed >= SUDDEN_STOP_SPEED_THRESHOLD and speed <= LOW_SPEED_THRESHOLD) | |
| track["prev_center"] = prev_center | |
| track["center"] = center | |
| track["speed"] = speed | |
| track["prev_speed"] = prev_speed | |
| track["missing"] = 0 | |
| track["updated"] = True | |
| det["track_id"] = best_track_id | |
| det["speed"] = speed | |
| det["prev_speed"] = prev_speed | |
| det["sudden_stop"] = sudden_stop | |
| else: | |
| db_car_id = get_latest_logged_in_car_id() | |
| if db_car_id is not None and db_car_id not in self.tracks: | |
| tid = db_car_id | |
| else: | |
| tid = self.fallback_id | |
| self.fallback_id += 1 | |
| self.tracks[tid] = { | |
| "center": center, "prev_center": center, | |
| "speed": 0.0, "prev_speed": 0.0, | |
| "missing": 0, "updated": True | |
| } | |
| det["track_id"] = tid | |
| det["speed"] = 0.0 | |
| det["prev_speed"] = 0.0 | |
| det["sudden_stop"] = False | |
| for tid in list(self.tracks.keys()): | |
| if not self.tracks[tid]["updated"]: | |
| self.tracks[tid]["missing"] += 1 | |
| if self.tracks[tid]["missing"] > self.max_missing: | |
| del self.tracks[tid] | |
| return detections | |
| # ========================================================== | |
| # 5. MODEL LOADER | |
| # ========================================================== | |
| def get_model_instance_segmentation(num_classes): | |
| model = torchvision.models.detection.maskrcnn_resnet50_fpn(weights=None) | |
| in_features = model.roi_heads.box_predictor.cls_score.in_features | |
| model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes) | |
| in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels | |
| hidden_layer = 256 | |
| model.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask, hidden_layer, num_classes) | |
| return model | |
| # ========================================================== | |
| # 6. MAIN EXECUTION | |
| # ========================================================== | |
| def main(): | |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
| print(f"Using device: {device}") | |
| print("جاري تحميل الموديل...") | |
| model = get_model_instance_segmentation(2) | |
| model.load_state_dict(torch.load(MODEL_PATH, map_location=device)) | |
| if device.type == 'cuda': | |
| model = model.half() | |
| torch.backends.cudnn.benchmark = True | |
| model.to(device).eval() | |
| tracker = GlobalTracker(max_distance=MAX_TRACK_DISTANCE, max_missing=MAX_MISSING_FRAMES) | |
| cap1 = cv2.VideoCapture(VIDEO_CAM1) | |
| cap2 = cv2.VideoCapture(VIDEO_CAM2) | |
| print(f"جاري تخطي {FRAMES_TO_SKIP} فريم من الكاميرا الأولى لضبط التزامن...") | |
| for _ in range(FRAMES_TO_SKIP): | |
| cap1.read() | |
| print("بدء معالجة الشاشتين المدمجتين... اضغطي 'q' للخروج") | |
| frame_index = 0 | |
| alerted_pairs = set() # مجموعة لتخزين الكوبلز اللي اتبعتلهم نوتيفيكشن منعاً للتكرار فكل فريم | |
| while cap1.isOpened() or cap2.isOpened(): | |
| ret1, frame1 = cap1.read() | |
| ret2, frame2 = cap2.read() | |
| if not ret1 and not ret2: | |
| break | |
| frame_index += 1 | |
| target_width, target_height = 640, 480 | |
| if not ret1: | |
| frame1 = np.zeros((target_height, target_width, 3), dtype=np.uint8) | |
| else: | |
| frame1 = cv2.resize(frame1, (target_width, target_height)) | |
| if not ret2: | |
| frame2 = np.zeros((target_height, target_width, 3), dtype=np.uint8) | |
| else: | |
| frame2 = cv2.resize(frame2, (target_width, target_height)) | |
| combined_frame = np.hstack((frame2, frame1)) | |
| rgb = cv2.cvtColor(combined_frame, cv2.COLOR_BGR2RGB) | |
| tensor = F.to_tensor(rgb).unsqueeze(0).to(device) | |
| if device.type == 'cuda': tensor = tensor.half() | |
| with torch.no_grad(): | |
| preds = model(tensor)[0] | |
| keep = ops.nms(preds['boxes'], preds['scores'], NMS_IOU_THRESHOLD) | |
| boxes = preds['boxes'][keep].cpu().numpy() | |
| scores = preds['scores'][keep].cpu().numpy() | |
| masks = preds['masks'][keep].cpu().numpy() | |
| detections = [] | |
| for i in range(len(boxes)): | |
| if scores[i] > CONF_THRESHOLD: | |
| box = boxes[i].astype(int) | |
| mask_prob = masks[i, 0] | |
| binary_mask = mask_prob > 0.5 | |
| center = get_mask_centroid(binary_mask) | |
| if center is None: center = box_center(box) | |
| detections.append({ | |
| "box": box, "mask": binary_mask, "score": scores[i], | |
| "center": center, "area": mask_area(binary_mask) | |
| }) | |
| detections = tracker.update(detections) | |
| for det in detections: | |
| box = det["box"] | |
| mask = det["mask"] | |
| track_id = det["track_id"] | |
| color_mask = np.zeros_like(combined_frame, dtype=np.uint8) | |
| color_mask[mask] = [0, 255, 0] | |
| combined_frame = cv2.addWeighted(combined_frame, 1.0, color_mask, 0.4, 0) | |
| cv2.rectangle(combined_frame, (box[0], box[1]), (box[2], box[3]), (255, 255, 0), 2) | |
| label = f"CAR ID: {track_id}" | |
| cv2.putText(combined_frame, label, (box[0], box[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2) | |
| # لوجيك الحادثة | |
| for i in range(len(detections)): | |
| for j in range(i + 1, len(detections)): | |
| a, b = detections[i], detections[j] | |
| overlap_area = mask_overlap_area(a["mask"], b["mask"]) | |
| distance = mask_min_distance(a["mask"], b["mask"]) | |
| if overlap_area > OVERLAP_AREA_THRESHOLD or distance < NEAR_DISTANCE_THRESHOLD: | |
| ca, cb = a["center"].astype(int), b["center"].astype(int) | |
| cv2.line(combined_frame, tuple(ca), tuple(cb), (0, 0, 255), 3) | |
| mid = ((ca + cb) // 2).astype(int) | |
| alert = "ACCIDENT DETECTED!" | |
| (tw, th), _ = cv2.getTextSize(alert, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2) | |
| cv2.rectangle(combined_frame, (mid[0]-5, mid[1]-th-5), (mid[0]+tw+5, mid[1]+5), (0, 0, 0), -1) | |
| cv2.putText(combined_frame, alert, (mid[0], mid[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) | |
| # سحب الـ IDs الحقيقية لإرسال الإشعار لمرة واحدة فقط | |
| car_a_id = a["track_id"] | |
| car_b_id = b["track_id"] | |
| if car_a_id != 9000 and car_b_id != 9000: | |
| pair = tuple(sorted([car_a_id, car_b_id])) | |
| if pair not in alerted_pairs: | |
| send_accident_notification(car_a_id, car_b_id) | |
| alerted_pairs.add(pair) | |
| cv2.line(combined_frame, (640, 0), (640, 480), (255, 255, 255), 1) | |
| cv2.putText(combined_frame, "CAM 2 (Left)", (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2) | |
| cv2.putText(combined_frame, "CAM 1 (Right)", (660, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2) | |
| cv2.imshow("Smart Parking - Master Node", combined_frame) | |
| if cv2.waitKey(30) & 0xFF == ord('q'): | |
| break | |
| cap1.release() | |
| cap2.release() | |
| cv2.destroyAllWindows() | |
| if __name__ == "__main__": | |
| main() |