{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":127283,"databundleVersionId":15634477,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\n!pip install -q ultralytics supervision opencv-python-headless motmetrics lap","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:23.791033Z","iopub.execute_input":"2026-07-30T19:36:23.79136Z","iopub.status.idle":"2026-07-30T19:36:27.312968Z","shell.execute_reply.started":"2026-07-30T19:36:23.791326Z","shell.execute_reply":"2026-07-30T19:36:27.311796Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Imports + Load YOLO Model","metadata":{}},{"cell_type":"code","source":"import kagglehub\n\n# Download latest version\npath = kagglehub.competition_download('accident')\n\nprint(\"Path to competition files:\", path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:27.315411Z","iopub.execute_input":"2026-07-30T19:36:27.315763Z","iopub.status.idle":"2026-07-30T19:36:28.124921Z","shell.execute_reply.started":"2026-07-30T19:36:27.31573Z","shell.execute_reply":"2026-07-30T19:36:28.124109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport torch\nimport numpy as np\nimport pandas as pd\nfrom ultralytics import YOLO\nimport supervision as sv\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\n\nmodel = YOLO(\"yolov8n.pt\") \n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Using device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:28.125894Z","iopub.execute_input":"2026-07-30T19:36:28.126121Z","iopub.status.idle":"2026-07-30T19:36:28.174873Z","shell.execute_reply.started":"2026-07-30T19:36:28.126097Z","shell.execute_reply":"2026-07-30T19:36:28.17407Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load Synthetic Index + Select One Video","metadata":{}},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\n\nSYNTHETIC_ROOT = Path(\"/kaggle/input/accident/sim_dataset\")\n\nprint(\"Files in sim_dataset:\")\nprint(list(SYNTHETIC_ROOT.iterdir()))\n\n\nlabels_path = SYNTHETIC_ROOT / \"labels.csv\"\ndf = pd.read_csv(labels_path)\n\nprint(\"\\nTotal synthetic videos:\", len(df))\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:28.176675Z","iopub.execute_input":"2026-07-30T19:36:28.177179Z","iopub.status.idle":"2026-07-30T19:36:28.206774Z","shell.execute_reply.started":"2026-07-30T19:36:28.177152Z","shell.execute_reply":"2026-07-30T19:36:28.20603Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Select One Video + Build Absolute Path","metadata":{}},{"cell_type":"code","source":"row = df.iloc[0]\n\nvideo_rel_path = row[\"rgb_path\"]\nvideo_path = SYNTHETIC_ROOT / video_rel_path\n\nprint(\"Selected video:\", video_path)\nprint(\"Accident type:\", row[\"type\"])\nprint(\"Ground truth accident frame:\", row[\"accident_frame\"])\nprint(\"Video exists:\", video_path.exists())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:28.207779Z","iopub.execute_input":"2026-07-30T19:36:28.208145Z","iopub.status.idle":"2026-07-30T19:36:28.214777Z","shell.execute_reply.started":"2026-07-30T19:36:28.208105Z","shell.execute_reply":"2026-07-30T19:36:28.214047Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Run YOLO on First Few Frames","metadata":{}},{"cell_type":"code","source":"cap = cv2.VideoCapture(str(video_path))\n\nassert cap.isOpened(), \"Error opening video\"\n\nfor i in range(10):\n    ret, frame = cap.read()\n    if not ret:\n        break\n\n    results = model(frame, verbose=False)[0]\n\n    annotated = results.plot()\n\n    plt.figure(figsize=(10,6))\n    plt.imshow(cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB))\n    plt.title(f\"Frame {i}\")\n    plt.axis(\"off\")\n    plt.show()\n\ncap.release()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:28.215796Z","iopub.execute_input":"2026-07-30T19:36:28.216213Z","iopub.status.idle":"2026-07-30T19:36:31.426197Z","shell.execute_reply.started":"2026-07-30T19:36:28.216176Z","shell.execute_reply":"2026-07-30T19:36:31.42535Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Initialize ByteTrack Tracker","metadata":{}},{"cell_type":"code","source":"tracker = sv.ByteTrack()\n\nVEHICLE_CLASSES = [2, 3, 5, 7]\n\nprint(\"Tracker initialized.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:31.427304Z","iopub.execute_input":"2026-07-30T19:36:31.427642Z","iopub.status.idle":"2026-07-30T19:36:31.432731Z","shell.execute_reply.started":"2026-07-30T19:36:31.427603Z","shell.execute_reply":"2026-07-30T19:36:31.431697Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Detection + Tracking Visualization","metadata":{}},{"cell_type":"code","source":"cap = cv2.VideoCapture(str(video_path))\n\nframe_count = 0\nMAX_FRAMES = 30  # debug\n\nbox_annotator = sv.BoxAnnotator()\nlabel_annotator = sv.LabelAnnotator()\n\nwhile frame_count < MAX_FRAMES:\n    ret, frame = cap.read()\n    if not ret:\n        break\n\n    # Run YOLO\n    results = model(frame, verbose=False)[0]\n\n    # Convert to supervision format\n    detections = sv.Detections.from_ultralytics(results)\n\n    # Filter vehicle classes\n    mask = np.isin(detections.class_id, VEHICLE_CLASSES)\n    detections = detections[mask]\n\n    # Update tracker\n    tracked = tracker.update_with_detections(detections)\n\n    # Create labels\n    labels = [f\"ID {tid}\" for tid in tracked.tracker_id]\n\n    # Annotate boxes\n    annotated = box_annotator.annotate(\n        scene=frame.copy(),\n        detections=tracked\n    )\n\n    # Annotate labels\n    annotated = label_annotator.annotate(\n        scene=annotated,\n        detections=tracked,\n        labels=labels\n    )\n\n    plt.figure(figsize=(10,6))\n    plt.imshow(cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB))\n    plt.title(f\"Frame {frame_count}\")\n    plt.axis(\"off\")\n    plt.show()\n\n    frame_count += 1\n\ncap.release()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:31.43364Z","iopub.execute_input":"2026-07-30T19:36:31.43399Z","iopub.status.idle":"2026-07-30T19:36:41.088897Z","shell.execute_reply.started":"2026-07-30T19:36:31.433933Z","shell.execute_reply":"2026-07-30T19:36:41.087818Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Extract Trajectories (Centers Over Time)","metadata":{}},{"cell_type":"code","source":"from collections import defaultdict\n\n# Reinitialize tracker (important)\ntracker = sv.ByteTrack()\n\ncap = cv2.VideoCapture(str(video_path))\n\ntrajectories = defaultdict(list)  \n# structure:\n# trajectories[track_id] = [(frame_idx, cx, cy, w, h), ...]\n\nframe_idx = 0\n\nwhile True:\n    ret, frame = cap.read()\n    if not ret:\n        break\n\n    results = model(frame, verbose=False)[0]\n    detections = sv.Detections.from_ultralytics(results)\n\n    # keep vehicles only\n    mask = np.isin(detections.class_id, VEHICLE_CLASSES)\n    detections = detections[mask]\n\n    tracked = tracker.update_with_detections(detections)\n\n    for i in range(len(tracked)):\n        track_id = tracked.tracker_id[i]\n        x1, y1, x2, y2 = tracked.xyxy[i]\n\n        cx = (x1 + x2) / 2\n        cy = (y1 + y2) / 2\n        w = x2 - x1\n        h = y2 - y1\n\n        trajectories[track_id].append((frame_idx, cx, cy, w, h))\n\n    frame_idx += 1\n\ncap.release()\n\nprint(\"Total frames processed:\", frame_idx)\nprint(\"Total tracked objects:\", len(trajectories))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:41.090183Z","iopub.execute_input":"2026-07-30T19:36:41.09056Z","iopub.status.idle":"2026-07-30T19:36:47.167921Z","shell.execute_reply.started":"2026-07-30T19:36:41.090533Z","shell.execute_reply":"2026-07-30T19:36:47.167217Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Compute Pairwise Distance + Basic Accident Score","metadata":{}},{"cell_type":"code","source":"import itertools\nimport numpy as np\n\n# Create frame-wise structure:\n# frame_objects[frame] = list of (track_id, cx, cy, w, h)\nframe_objects = defaultdict(list)\n\nfor track_id, points in trajectories.items():\n    for (frame_idx, cx, cy, w, h) in points:\n        frame_objects[frame_idx].append((track_id, cx, cy, w, h))\n\nmax_frame = max(frame_objects.keys())\n\naccident_scores = np.zeros(max_frame + 1)\n\nDIST_THRESHOLD = 50  # pixels (tune later)\nOVERLAP_THRESHOLD = 0.1  # IoU threshold\n\ndef compute_iou(box1, box2):\n    x11, y11, x12, y12 = box1\n    x21, y21, x22, y22 = box2\n\n    xi1 = max(x11, x21)\n    yi1 = max(y11, y21)\n    xi2 = min(x12, x22)\n    yi2 = min(y12, y22)\n\n    inter_area = max(0, xi2 - xi1) * max(0, yi2 - yi1)\n\n    box1_area = (x12 - x11) * (y12 - y11)\n    box2_area = (x22 - x21) * (y22 - y21)\n\n    union_area = box1_area + box2_area - inter_area\n    if union_area == 0:\n        return 0\n\n    return inter_area / union_area\n\n\nfor frame in range(max_frame + 1):\n    objs = frame_objects[frame]\n\n    for obj1, obj2 in itertools.combinations(objs, 2):\n        id1, cx1, cy1, w1, h1 = obj1\n        id2, cx2, cy2, w2, h2 = obj2\n\n        # distance\n        dist = np.sqrt((cx1 - cx2)**2 + (cy1 - cy2)**2)\n\n        # bounding boxes\n        box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n        box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n\n        iou = compute_iou(box1, box2)\n\n        if dist < DIST_THRESHOLD or iou > OVERLAP_THRESHOLD:\n            accident_scores[frame] += 1\n\n\npredicted_frame = np.argmax(accident_scores)\n\nprint(\"Predicted accident frame:\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.170383Z","iopub.execute_input":"2026-07-30T19:36:47.170725Z","iopub.status.idle":"2026-07-30T19:36:47.196565Z","shell.execute_reply.started":"2026-07-30T19:36:47.1707Z","shell.execute_reply":"2026-07-30T19:36:47.195667Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Add Velocity + Acceleration Signal","metadata":{}},{"cell_type":"code","source":"# Build velocity dictionary\n# velocities[track_id] = {frame: (vx, vy, speed)}\n\nvelocities = defaultdict(dict)\n\nfor track_id, points in trajectories.items():\n    for i in range(1, len(points)):\n        f_prev, cx_prev, cy_prev, _, _ = points[i-1]\n        f_curr, cx_curr, cy_curr, _, _ = points[i]\n\n        dt = f_curr - f_prev\n        if dt == 0:\n            continue\n\n        vx = (cx_curr - cx_prev) / dt\n        vy = (cy_curr - cy_prev) / dt\n        speed = np.sqrt(vx**2 + vy**2)\n\n        velocities[track_id][f_curr] = (vx, vy, speed)\n\n\n# Recompute accident scores with velocity signal\naccident_scores = np.zeros(max_frame + 1)\n\nDIST_THRESHOLD = 40\nIOU_THRESHOLD = 0.05\nDECEL_THRESHOLD = 2.0  # tuneable\n\nfor frame in range(2, max_frame + 1):\n    objs = frame_objects[frame]\n\n    for obj1, obj2 in itertools.combinations(objs, 2):\n        id1, cx1, cy1, w1, h1 = obj1\n        id2, cx2, cy2, w2, h2 = obj2\n\n        # Need velocity info\n        if frame not in velocities[id1] or frame not in velocities[id2]:\n            continue\n\n        _, _, speed1 = velocities[id1][frame]\n        _, _, speed2 = velocities[id2][frame]\n\n        # Previous speeds\n        if frame-1 in velocities[id1]:\n            _, _, prev_speed1 = velocities[id1][frame-1]\n        else:\n            continue\n\n        if frame-1 in velocities[id2]:\n            _, _, prev_speed2 = velocities[id2][frame-1]\n        else:\n            continue\n\n        decel1 = prev_speed1 - speed1\n        decel2 = prev_speed2 - speed2\n\n        # Distance\n        dist = np.sqrt((cx1 - cx2)**2 + (cy1 - cy2)**2)\n\n        # Boxes\n        box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n        box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n        iou = compute_iou(box1, box2)\n\n        score = 0\n\n        if dist < DIST_THRESHOLD:\n            score += 1\n        if iou > IOU_THRESHOLD:\n            score += 2\n        if decel1 > DECEL_THRESHOLD or decel2 > DECEL_THRESHOLD:\n            score += 2\n\n        accident_scores[frame] += score\n\n\npredicted_frame = np.argmax(accident_scores)\n\nprint(\"Predicted accident frame:\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.197619Z","iopub.execute_input":"2026-07-30T19:36:47.198003Z","iopub.status.idle":"2026-07-30T19:36:47.23819Z","shell.execute_reply.started":"2026-07-30T19:36:47.197964Z","shell.execute_reply":"2026-07-30T19:36:47.237556Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Add Relative Angle + Direction Change Signal","metadata":{}},{"cell_type":"code","source":"# Compute direction change per object\ndirection_change = defaultdict(dict)\n\nfor track_id, points in trajectories.items():\n    for i in range(2, len(points)):\n        f1, cx1, cy1, _, _ = points[i-2]\n        f2, cx2, cy2, _, _ = points[i-1]\n        f3, cx3, cy3, _, _ = points[i]\n\n        v1 = np.array([cx2 - cx1, cy2 - cy1])\n        v2 = np.array([cx3 - cx2, cy3 - cy2])\n\n        if np.linalg.norm(v1) == 0 or np.linalg.norm(v2) == 0:\n            continue\n\n        cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))\n        angle = np.arccos(np.clip(cos_angle, -1.0, 1.0))\n\n        direction_change[track_id][f3] = angle  # radians\n\n\n# Recompute accident score with direction change\naccident_scores = np.zeros(max_frame + 1)\n\nANGLE_THRESHOLD = 0.5  # radians (~28 degrees)\n\nfor frame in range(3, max_frame + 1):\n    objs = frame_objects[frame]\n\n    for obj1, obj2 in itertools.combinations(objs, 2):\n        id1, cx1, cy1, w1, h1 = obj1\n        id2, cx2, cy2, w2, h2 = obj2\n\n        if frame not in velocities[id1] or frame not in velocities[id2]:\n            continue\n\n        if frame not in direction_change[id1] and frame not in direction_change[id2]:\n            continue\n\n        # distance\n        dist = np.sqrt((cx1 - cx2)**2 + (cy1 - cy2)**2)\n\n        # boxes\n        box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n        box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n        iou = compute_iou(box1, box2)\n\n        score = 0\n\n        if dist < 50:\n            score += 1\n        if iou > 0.05:\n            score += 2\n\n        if id1 in direction_change and frame in direction_change[id1]:\n            if direction_change[id1][frame] > ANGLE_THRESHOLD:\n                score += 2\n\n        if id2 in direction_change and frame in direction_change[id2]:\n            if direction_change[id2][frame] > ANGLE_THRESHOLD:\n                score += 2\n\n        accident_scores[frame] += score\n\n\npredicted_frame = np.argmax(accident_scores)\n\nprint(\"Predicted accident frame:\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.239205Z","iopub.execute_input":"2026-07-30T19:36:47.239672Z","iopub.status.idle":"2026-07-30T19:36:47.3029Z","shell.execute_reply.started":"2026-07-30T19:36:47.239634Z","shell.execute_reply":"2026-07-30T19:36:47.302102Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Detect First Significant Spike","metadata":{}},{"cell_type":"code","source":"# Smooth scores slightly\nwindow = 5\nsmoothed_scores = np.convolve(accident_scores, np.ones(window)/window, mode='same')\n\n# Define dynamic threshold\nthreshold = np.percentile(smoothed_scores, 95)\n\n# Find first frame above threshold\ncandidate_frames = np.where(smoothed_scores > threshold)[0]\n\nif len(candidate_frames) > 0:\n    predicted_frame = candidate_frames[0]\nelse:\n    predicted_frame = np.argmax(smoothed_scores)\n\nprint(\"Predicted accident frame (early spike):\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.304289Z","iopub.execute_input":"2026-07-30T19:36:47.304982Z","iopub.status.idle":"2026-07-30T19:36:47.310927Z","shell.execute_reply.started":"2026-07-30T19:36:47.304955Z","shell.execute_reply":"2026-07-30T19:36:47.310328Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Detect Longest High-Score Segment","metadata":{}},{"cell_type":"code","source":"# Smooth scores\nwindow = 7\nsmoothed_scores = np.convolve(accident_scores, np.ones(window)/window, mode='same')\n\n# Threshold based on high percentile\nthreshold = np.percentile(smoothed_scores, 90)\n\n# Binary mask of high-score frames\nhigh_mask = smoothed_scores > threshold\n\n# Find continuous segments\nsegments = []\nstart = None\n\nfor i, val in enumerate(high_mask):\n    if val and start is None:\n        start = i\n    elif not val and start is not None:\n        segments.append((start, i-1))\n        start = None\n\nif start is not None:\n    segments.append((start, len(high_mask)-1))\n\n# Pick longest segment\nif len(segments) > 0:\n    longest_segment = max(segments, key=lambda x: x[1] - x[0])\n    predicted_frame = longest_segment[0]  # start of main crash segment\nelse:\n    predicted_frame = np.argmax(smoothed_scores)\n\nprint(\"Predicted accident frame (longest segment start):\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])\nprint(\"All high-score segments:\", segments)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.312033Z","iopub.execute_input":"2026-07-30T19:36:47.312332Z","iopub.status.idle":"2026-07-30T19:36:47.326298Z","shell.execute_reply.started":"2026-07-30T19:36:47.312306Z","shell.execute_reply":"2026-07-30T19:36:47.32555Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Backtrack to True Impact Start","metadata":{}},{"cell_type":"code","source":"# Use previous smoothed_scores and longest_segment\n\nsegment_start, segment_end = longest_segment\n\n# Look 15 frames before segment start\nsearch_start = max(0, segment_start - 20)\nsearch_region = smoothed_scores[search_start:segment_start]\n\n# Compute gradient\ngradients = np.diff(search_region)\n\n# Find first strong positive gradient\ngrad_threshold = np.percentile(gradients, 90)\n\ngrad_indices = np.where(gradients > grad_threshold)[0]\n\nif len(grad_indices) > 0:\n    predicted_frame = search_start + grad_indices[0]\nelse:\n    predicted_frame = segment_start\n\nprint(\"Predicted accident frame (refined):\", predicted_frame)\nprint(\"Ground truth frame:\", row[\"accident_frame\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.327455Z","iopub.execute_input":"2026-07-30T19:36:47.327996Z","iopub.status.idle":"2026-07-30T19:36:47.339213Z","shell.execute_reply.started":"2026-07-30T19:36:47.327972Z","shell.execute_reply":"2026-07-30T19:36:47.338542Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Compute Collision Center","metadata":{}},{"cell_type":"code","source":"# # Use main crash segment\n# segment_start, segment_end = longest_segment\n\n# best_iou = 0\n# impact_center = None\n# impact_frame = None\n\n# for frame in range(segment_start, segment_end + 1):\n#     objs = frame_objects[frame]\n\n#     for obj1, obj2 in itertools.combinations(objs, 2):\n#         id1, cx1, cy1, w1, h1 = obj1\n#         id2, cx2, cy2, w2, h2 = obj2\n\n#         box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n#         box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n\n#         iou = compute_iou(box1, box2)\n\n#         if iou > best_iou:\n#             best_iou = iou\n#             impact_center = ((cx1 + cx2)/2, (cy1 + cy2)/2)\n#             impact_frame = frame\n\n# print(\"Impact frame (max IoU):\", impact_frame)\n# print(\"Best IoU:\", best_iou)\n# print(\"Impact center (pixels):\", impact_center)\n\n# # Normalize\n# height, width = row[\"height\"], row[\"width\"]\n\n# center_x_norm = impact_center[0] / width\n# center_y_norm = impact_center[1] / height\n\n# print(\"Normalized center_x:\", center_x_norm)\n# print(\"Normalized center_y:\", center_y_norm)\n\n# print(\"Ground truth center_x:\", row[\"center_x\"])\n# print(\"Ground truth center_y:\", row[\"center_y\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.340509Z","iopub.execute_input":"2026-07-30T19:36:47.340884Z","iopub.status.idle":"2026-07-30T19:36:47.357089Z","shell.execute_reply.started":"2026-07-30T19:36:47.340841Z","shell.execute_reply":"2026-07-30T19:36:47.355872Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Use Intersection Center Instead of Midpoint","metadata":{}},{"cell_type":"code","source":"best_iou = 0\nimpact_frame = None\nbest_pair = None\nimpact_center = None\n\nfor frame in range(segment_start, segment_end + 1):\n\n    objs = frame_objects[frame]\n\n    for obj1, obj2 in itertools.combinations(objs, 2):\n\n        id1, cx1, cy1, w1, h1 = obj1\n        id2, cx2, cy2, w2, h2 = obj2\n\n        box1 = (\n            cx1 - w1/2,\n            cy1 - h1/2,\n            cx1 + w1/2,\n            cy1 + h1/2\n        )\n\n        box2 = (\n            cx2 - w2/2,\n            cy2 - h2/2,\n            cx2 + w2/2,\n            cy2 + h2/2\n        )\n\n        iou = compute_iou(box1, box2)\n\n        if iou > best_iou:\n\n            best_iou = iou\n\n            impact_frame = frame\n\n            best_pair = (id1, id2)\n\n            impact_center = (\n                (cx1 + cx2) / 2,\n                (cy1 + cy2) / 2\n            )\n\ncenter_x, center_y = impact_center\n\ncenter_x_norm = center_x / width\ncenter_y_norm = center_y / height","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.358064Z","iopub.execute_input":"2026-07-30T19:36:47.358338Z","iopub.status.idle":"2026-07-30T19:36:47.37401Z","shell.execute_reply.started":"2026-07-30T19:36:47.358289Z","shell.execute_reply":"2026-07-30T19:36:47.373372Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Estimate Crash Bounding Box Using Union of Two Vehicles","metadata":{}},{"cell_type":"code","source":"# best_iou = 0\n# impact_box = None\n# impact_frame = None\n\n# for frame in range(segment_start, segment_end + 1):\n#     objs = frame_objects[frame]\n\n#     for obj1, obj2 in itertools.combinations(objs, 2):\n#         id1, cx1, cy1, w1, h1 = obj1\n#         id2, cx2, cy2, w2, h2 = obj2\n\n#         box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n#         box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n\n#         iou = compute_iou(box1, box2)\n\n#         if iou > best_iou:\n#             best_iou = iou\n#             impact_frame = frame\n\n#             # union box\n#             ux1 = min(box1[0], box2[0])\n#             uy1 = min(box1[1], box2[1])\n#             ux2 = max(box1[2], box2[2])\n#             uy2 = max(box1[3], box2[3])\n\n#             impact_box = (ux1, uy1, ux2, uy2)\n\n# # Compute center of union box\n# center_x = (impact_box[0] + impact_box[2]) / 2\n# center_y = (impact_box[1] + impact_box[3]) / 2\n\n# center_x_norm = center_x / width\n# center_y_norm = center_y / height\n\n# print(\"Predicted center_x:\", center_x_norm)\n# print(\"Predicted center_y:\", center_y_norm)\n\n# print(\"Ground truth center_x:\", row[\"center_x\"])\n# print(\"Ground truth center_y:\", row[\"center_y\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.374908Z","iopub.execute_input":"2026-07-30T19:36:47.375224Z","iopub.status.idle":"2026-07-30T19:36:47.390856Z","shell.execute_reply.started":"2026-07-30T19:36:47.375189Z","shell.execute_reply":"2026-07-30T19:36:47.390088Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Identify Colliding Track IDs","metadata":{}},{"cell_type":"code","source":"# Identify dominant colliding pair in main crash segment\n\npair_scores = {}\n\nfor frame in range(segment_start, segment_end + 1):\n    objs = frame_objects[frame]\n\n    for obj1, obj2 in itertools.combinations(objs, 2):\n        id1, cx1, cy1, w1, h1 = obj1\n        id2, cx2, cy2, w2, h2 = obj2\n\n        box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n        box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n\n        iou = compute_iou(box1, box2)\n\n        if iou > 0.1:\n            pair = tuple(sorted([id1, id2]))\n            pair_scores[pair] = pair_scores.get(pair, 0) + iou\n\n# Find dominant pair\ndominant_pair = max(pair_scores.items(), key=lambda x: x[1])[0]\n\nprint(\"Dominant colliding pair:\", dominant_pair)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.391814Z","iopub.execute_input":"2026-07-30T19:36:47.392204Z","iopub.status.idle":"2026-07-30T19:36:47.41327Z","shell.execute_reply.started":"2026-07-30T19:36:47.392165Z","shell.execute_reply":"2026-07-30T19:36:47.412417Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Compute Physics-Based Impact Point","metadata":{}},{"cell_type":"code","source":"# Use dominant pair and impact_frame\nid1, id2 = dominant_pair\nframe = impact_frame\n\n# Helper: get state at exact frame\ndef get_state(track_id, frame):\n    for f, cx, cy, w, h in trajectories[track_id]:\n        if f == frame:\n            return cx, cy, w, h\n    return None\n\nstate1 = get_state(id1, frame)\nstate2 = get_state(id2, frame)\n\nif state1 is None or state2 is None:\n    print(\"State not found at impact frame.\")\nelse:\n    cx1, cy1, w1, h1 = state1\n    cx2, cy2, w2, h2 = state2\n\n    # Get velocities safely\n    if frame not in velocities[id1] or frame not in velocities[id2]:\n        print(\"Velocity missing at impact frame.\")\n    else:\n        vx1, vy1, speed1 = velocities[id1][frame]\n        vx2, vy2, speed2 = velocities[id2][frame]\n\n        # Choose vehicle with higher speed at impact\n        impact_vehicle = id1 if speed1 > speed2 else id2\n\n        if impact_vehicle == id1:\n            cx, cy, w, h = cx1, cy1, w1, h1\n            vx, vy = vx1, vy1\n        else:\n            cx, cy, w, h = cx2, cy2, w2, h2\n            vx, vy = vx2, vy2\n\n        # Normalize velocity vector\n        v_norm = np.sqrt(vx**2 + vy**2)\n        if v_norm > 0:\n            vx /= v_norm\n            vy /= v_norm\n\n        # Shift toward front of vehicle\n        impact_x = cx + vx * (w / 2)\n        impact_y = cy + vy * (h / 2)\n\n        center_x_norm = impact_x / width\n        center_y_norm = impact_y / height\n\n        print(\"Physics-based center_x:\", center_x_norm)\n        print(\"Physics-based center_y:\", center_y_norm)\n\n        print(\"Ground truth center_x:\", row[\"center_x\"])\n        print(\"Ground truth center_y:\", row[\"center_y\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.41446Z","iopub.execute_input":"2026-07-30T19:36:47.414768Z","iopub.status.idle":"2026-07-30T19:36:47.430326Z","shell.execute_reply.started":"2026-07-30T19:36:47.414742Z","shell.execute_reply":"2026-07-30T19:36:47.429278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\n\ndef extract_crash_clip(video_path, center_x_norm, center_y_norm,\n                       crash_frame, \n                       window=32,\n                       crop_size=400,\n                       output_size=224):\n    \n    cap = cv2.VideoCapture(str(video_path))\n    \n    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n    \n    # Frame range\n    half = window // 2\n    start_frame = max(0, crash_frame - half)\n    end_frame = min(total_frames, crash_frame + half)\n    \n    frames = []\n    \n    # Convert normalized center to pixel\n    cx = int(center_x_norm * width)\n    cy = int(center_y_norm * height)\n    \n    cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)\n    \n    for f in range(start_frame, end_frame):\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        # Crop around impact center\n        x1 = max(0, cx - crop_size // 2)\n        y1 = max(0, cy - crop_size // 2)\n        x2 = min(width, cx + crop_size // 2)\n        y2 = min(height, cy + crop_size // 2)\n        \n        crop = frame[y1:y2, x1:x2]\n        \n        # Resize\n        crop = cv2.resize(crop, (output_size, output_size))\n        \n        frames.append(crop)\n    \n    cap.release()\n    \n    clip = np.array(frames)  # shape (T, H, W, C)\n    \n    return clip\n\n\n# ---- Test extractor on this video ----\nclip = extract_crash_clip(\n    video_path,\n    center_x_norm,\n    center_y_norm,\n    predicted_frame\n)\n\nprint(\"Clip shape:\", clip.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:47.43147Z","iopub.execute_input":"2026-07-30T19:36:47.432185Z","iopub.status.idle":"2026-07-30T19:36:48.327178Z","shell.execute_reply.started":"2026-07-30T19:36:47.432142Z","shell.execute_reply":"2026-07-30T19:36:48.32612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build small synthetic training subset\nfrom tqdm import tqdm\n\nsynthetic_clips = []\nsynthetic_labels = []\n\nlabel_map = {label: idx for idx, label in enumerate(df[\"type\"].unique())}\nprint(\"Label map:\", label_map)\n\nfor idx in tqdm(range(20)):  # small test batch\n    row_i = df.iloc[idx]\n    \n    vid_path = SYNTHETIC_ROOT / row_i[\"rgb_path\"]\n    \n    crash_frame = int(row_i[\"accident_frame\"])\n    cx = row_i[\"center_x\"]\n    cy = row_i[\"center_y\"]\n    \n    clip = extract_crash_clip(\n        vid_path,\n        cx,\n        cy,\n        crash_frame\n    )\n    \n    if clip.shape[0] == 32:  # ensure full clip\n        synthetic_clips.append(clip)\n        synthetic_labels.append(label_map[row_i[\"type\"]])\n\nprint(\"Collected clips:\", len(synthetic_clips))\nprint(\"Example clip shape:\", synthetic_clips[0].shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:48.328224Z","iopub.execute_input":"2026-07-30T19:36:48.32878Z","iopub.status.idle":"2026-07-30T19:36:59.234448Z","shell.execute_reply.started":"2026-07-30T19:36:48.328736Z","shell.execute_reply":"2026-07-30T19:36:59.233393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nimport torchvision.transforms as T\n\n# Convert list to tensor\nX = np.stack(synthetic_clips)  # (N, T, H, W, C)\ny = np.array(synthetic_labels)\n\n# Convert to torch\nX = torch.tensor(X).permute(0,1,4,2,3).float() / 255.0  # (N, T, C, H, W)\ny = torch.tensor(y)\n\nprint(\"X shape:\", X.shape)\nprint(\"y shape:\", y.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:59.235661Z","iopub.execute_input":"2026-07-30T19:36:59.236362Z","iopub.status.idle":"2026-07-30T19:36:59.633113Z","shell.execute_reply.started":"2026-07-30T19:36:59.236335Z","shell.execute_reply":"2026-07-30T19:36:59.632262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn.functional as F\n\nclass CrashTypeModel(nn.Module):\n    def __init__(self, num_classes=5):\n        super().__init__()\n        \n        backbone = models.resnet18(weights=\"IMAGENET1K_V1\")\n        self.feature_extractor = nn.Sequential(*list(backbone.children())[:-1])  # remove final FC\n        \n        self.classifier = nn.Linear(512, num_classes)\n    \n    def forward(self, x):\n        # x shape: (B, T, C, H, W)\n        B, T, C, H, W = x.shape\n        \n        x = x.view(B*T, C, H, W)\n        \n        features = self.feature_extractor(x)  # (B*T, 512, 1, 1)\n        features = features.view(B, T, 512)\n        \n        # Temporal average pooling\n        features = features.mean(dim=1)  # (B, 512)\n        \n        out = self.classifier(features)\n        \n        return out\n\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nmodel_type = CrashTypeModel(num_classes=len(label_map)).to(device)\n\nprint(\"Model initialized.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:59.634195Z","iopub.execute_input":"2026-07-30T19:36:59.634534Z","iopub.status.idle":"2026-07-30T19:36:59.845358Z","shell.execute_reply.started":"2026-07-30T19:36:59.634495Z","shell.execute_reply":"2026-07-30T19:36:59.84467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Move data to device\nX_train = X.to(device)\ny_train = y.to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model_type.parameters(), lr=1e-4)\n\nmodel_type.train()\n\nEPOCHS = 5\n\nfor epoch in range(EPOCHS):\n    optimizer.zero_grad()\n    \n    outputs = model_type(X_train)\n    loss = criterion(outputs, y_train)\n    \n    loss.backward()\n    optimizer.step()\n    \n    preds = outputs.argmax(dim=1)\n    acc = (preds == y_train).float().mean()\n    \n    print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {loss.item():.4f} | Acc: {acc.item():.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:36:59.846647Z","iopub.execute_input":"2026-07-30T19:36:59.84703Z","iopub.status.idle":"2026-07-30T19:37:13.656644Z","shell.execute_reply.started":"2026-07-30T19:36:59.846979Z","shell.execute_reply":"2026-07-30T19:37:13.655954Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nimport random\n\nclass SyntheticCrashDataset(Dataset):\n    def __init__(self, dataframe, root_path, label_map,\n                 window=32, crop_size=400, output_size=224,\n                 augment=False):\n        \n        self.df = dataframe.reset_index(drop=True)\n        self.root = root_path\n        self.label_map = label_map\n        self.window = window\n        self.crop_size = crop_size\n        self.output_size = output_size\n        self.augment = augment\n        \n        # Simple augmentations (very important for domain gap)\n        self.augment_transforms = T.Compose([\n            T.RandomHorizontalFlip(),\n            T.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3),\n        ])\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        \n        video_path = self.root / row[\"rgb_path\"]\n        crash_frame = int(row[\"accident_frame\"])\n        cx = row[\"center_x\"]\n        cy = row[\"center_y\"]\n        \n        clip = extract_crash_clip(\n            video_path,\n            cx,\n            cy,\n            crash_frame,\n            window=self.window,\n            crop_size=self.crop_size,\n            output_size=self.output_size\n        )\n        \n        # If clip shorter than expected, pad\n        if clip.shape[0] < self.window:\n            pad_len = self.window - clip.shape[0]\n            pad = np.repeat(clip[-1][None], pad_len, axis=0)\n            clip = np.concatenate([clip, pad], axis=0)\n        \n        clip = torch.tensor(clip).permute(0,3,1,2).float() / 255.0\n        \n        # Apply augmentation frame-wise\n        if self.augment:\n            clip = torch.stack([self.augment_transforms(frame) for frame in clip])\n        \n        label = self.label_map[row[\"type\"]]\n        \n        return clip, torch.tensor(label)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:13.657802Z","iopub.execute_input":"2026-07-30T19:37:13.658636Z","iopub.status.idle":"2026-07-30T19:37:13.667619Z","shell.execute_reply.started":"2026-07-30T19:37:13.658607Z","shell.execute_reply":"2026-07-30T19:37:13.6666Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Train/val split\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.2,\n    stratify=df[\"type\"],\n    random_state=42\n)\n\nprint(\"Train size:\", len(train_df))\nprint(\"Val size:\", len(val_df))\n\n# Create datasets\ntrain_dataset = SyntheticCrashDataset(\n    train_df,\n    SYNTHETIC_ROOT,\n    label_map,\n    augment=True\n)\n\nval_dataset = SyntheticCrashDataset(\n    val_df,\n    SYNTHETIC_ROOT,\n    label_map,\n    augment=False\n)\n\n# DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=4, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=4, shuffle=False, num_workers=2)\n\n# Test one batch\nclips, labels = next(iter(train_loader))\n\nprint(\"Batch clip shape:\", clips.shape)\nprint(\"Batch labels shape:\", labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:13.668586Z","iopub.execute_input":"2026-07-30T19:37:13.668922Z","iopub.status.idle":"2026-07-30T19:37:20.944934Z","shell.execute_reply.started":"2026-07-30T19:37:13.668894Z","shell.execute_reply":"2026-07-30T19:37:20.943791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import copy\n# import torch\n# import torch.nn as nn\n\n# model_type = CrashTypeModel(num_classes=len(label_map)).to(device)\n\n# criterion = nn.CrossEntropyLoss()\n# optimizer = torch.optim.Adam(model_type.parameters(), lr=1e-4)\n\n# EPOCHS = 10\n\n# best_val_acc = 0\n# best_model = None\n\n# for epoch in range(EPOCHS):\n\n#     # ---------------- TRAIN ----------------\n#     model_type.train()\n\n#     train_loss = 0\n#     train_correct = 0\n#     total_train = 0\n\n#     for batch_idx, (clips, labels) in enumerate(train_loader):\n\n#         if batch_idx % 50 == 0:\n#             print(f\"Epoch {epoch+1}/{EPOCHS} | Batch {batch_idx}/{len(train_loader)}\")\n\n#         clips = clips.to(device, non_blocking=True)\n#         labels = labels.to(device, non_blocking=True)\n\n#         optimizer.zero_grad()\n\n#         outputs = model_type(clips)\n#         loss = criterion(outputs, labels)\n\n#         loss.backward()\n#         optimizer.step()\n\n#         train_loss += loss.item() * clips.size(0)\n\n#         preds = outputs.argmax(dim=1)\n#         train_correct += (preds == labels).sum().item()\n#         total_train += labels.size(0)\n\n#     train_loss /= total_train\n#     train_acc = train_correct / total_train\n\n#     # ---------------- VALIDATION ----------------\n#     model_type.eval()\n\n#     val_correct = 0\n#     total_val = 0\n\n#     with torch.no_grad():\n\n#         for clips, labels in val_loader:\n\n#             clips = clips.to(device, non_blocking=True)\n#             labels = labels.to(device, non_blocking=True)\n\n#             outputs = model_type(clips)\n#             preds = outputs.argmax(dim=1)\n\n#             val_correct += (preds == labels).sum().item()\n#             total_val += labels.size(0)\n\n#     val_acc = val_correct / total_val\n\n#     print(f\"\\nEpoch {epoch+1}/{EPOCHS}\")\n#     print(f\"Train Loss : {train_loss:.4f}\")\n#     print(f\"Train Acc  : {train_acc:.4f}\")\n#     print(f\"Val Acc    : {val_acc:.4f}\")\n#     print(\"-\" * 50)\n\n#     if val_acc > best_val_acc:\n#         best_val_acc = val_acc\n#         best_model = copy.deepcopy(model_type.state_dict())\n\n# print(f\"\\nBest Validation Accuracy: {best_val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:20.949714Z","iopub.execute_input":"2026-07-30T19:37:20.950104Z","iopub.status.idle":"2026-07-30T19:37:20.956046Z","shell.execute_reply.started":"2026-07-30T19:37:20.950072Z","shell.execute_reply":"2026-07-30T19:37:20.955213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# torch.save(best_model, \"crash_type_model.pth\")\n# print(\"Model saved.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:20.957364Z","iopub.execute_input":"2026-07-30T19:37:20.957727Z","iopub.status.idle":"2026-07-30T19:37:20.975449Z","shell.execute_reply.started":"2026-07-30T19:37:20.957683Z","shell.execute_reply":"2026-07-30T19:37:20.974617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_type_infer = CrashTypeModel(num_classes=len(label_map)).to(device)\n\nmodel_type_infer.load_state_dict(\n    torch.load(\"/kaggle/input/models/priyasharma040404/crash-model/pytorch/default/1/crash_type_model.pth\", map_location=device)\n)\n\nmodel_type_infer.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:20.976937Z","iopub.execute_input":"2026-07-30T19:37:20.977564Z","iopub.status.idle":"2026-07-30T19:37:21.263578Z","shell.execute_reply.started":"2026-07-30T19:37:20.977538Z","shell.execute_reply":"2026-07-30T19:37:21.262908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Rebuild model architecture\n# model_type_infer = CrashTypeModel(num_classes=len(label_map)).to(device)\n\n# # Load best weights\n# model_type_infer.load_state_dict(torch.load(\"crash_type_model.pth\"))\n# model_type_infer.eval()\n\n# print(\"Inference model loaded.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:21.264438Z","iopub.execute_input":"2026-07-30T19:37:21.264741Z","iopub.status.idle":"2026-07-30T19:37:21.268446Z","shell.execute_reply.started":"2026-07-30T19:37:21.264716Z","shell.execute_reply":"2026-07-30T19:37:21.267643Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Full predict_video() Pipeline","metadata":{}},{"cell_type":"code","source":"def predict_video(video_path):\n    \n    # ---------------------------\n    # 1️⃣ Run detection + tracking\n    # ---------------------------\n    tracker = sv.ByteTrack()\n    cap = cv2.VideoCapture(str(video_path))\n    \n    trajectories = defaultdict(list)\n    frame_objects = defaultdict(list)\n    \n    frame_idx = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        results = model(frame, verbose=False)[0]\n        detections = sv.Detections.from_ultralytics(results)\n        \n        mask = np.isin(detections.class_id, VEHICLE_CLASSES)\n        detections = detections[mask]\n        \n        tracked = tracker.update_with_detections(detections)\n        \n        for i in range(len(tracked)):\n            track_id = tracked.tracker_id[i]\n            x1, y1, x2, y2 = tracked.xyxy[i]\n            \n            cx = (x1 + x2) / 2\n            cy = (y1 + y2) / 2\n            w = x2 - x1\n            h = y2 - y1\n            \n            trajectories[track_id].append((frame_idx, cx, cy, w, h))\n            frame_objects[frame_idx].append((track_id, cx, cy, w, h))\n        \n        frame_idx += 1\n    \n    cap.release()\n    \n    if len(frame_objects) == 0:\n        return None\n    \n    max_frame = max(frame_objects.keys())\n    accident_scores = np.zeros(max_frame + 1)\n    \n    # ---------------------------\n    # 2️⃣ Basic overlap scoring\n    # ---------------------------\n    for frame in range(max_frame + 1):\n        objs = frame_objects[frame]\n        \n        for obj1, obj2 in itertools.combinations(objs, 2):\n            _, cx1, cy1, w1, h1 = obj1\n            _, cx2, cy2, w2, h2 = obj2\n            \n            box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n            box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n            \n            iou = compute_iou(box1, box2)\n            if iou > 0.05:\n                accident_scores[frame] += iou\n    \n    # Smooth scores\n    window = 7\n    smoothed_scores = np.convolve(accident_scores, np.ones(window)/window, mode='same')\n    \n    # Find longest high-energy segment\n    threshold = np.percentile(smoothed_scores, 90)\n    high_mask = smoothed_scores > threshold\n    \n    segments = []\n    start = None\n    \n    for i, val in enumerate(high_mask):\n        if val and start is None:\n            start = i\n        elif not val and start is not None:\n            segments.append((start, i-1))\n            start = None\n    if start is not None:\n        segments.append((start, len(high_mask)-1))\n    \n    if len(segments) == 0:\n        predicted_frame = np.argmax(smoothed_scores)\n        segment_start, segment_end = predicted_frame, predicted_frame\n    else:\n        segment_start, segment_end = max(segments, key=lambda x: x[1] - x[0])\n        predicted_frame = segment_start\n    \n    # ---------------------------\n    # 3️⃣ Estimate impact center\n    # ---------------------------\n    best_iou = 0\n    impact_box = None\n    \n    for frame in range(segment_start, segment_end + 1):\n        objs = frame_objects[frame]\n        \n        for obj1, obj2 in itertools.combinations(objs, 2):\n            _, cx1, cy1, w1, h1 = obj1\n            _, cx2, cy2, w2, h2 = obj2\n            \n            box1 = (cx1 - w1/2, cy1 - h1/2, cx1 + w1/2, cy1 + h1/2)\n            box2 = (cx2 - w2/2, cy2 - h2/2, cx2 + w2/2, cy2 + h2/2)\n            \n            iou = compute_iou(box1, box2)\n            if iou > best_iou:\n                best_iou = iou\n                ux1 = min(box1[0], box2[0])\n                uy1 = min(box1[1], box2[1])\n                ux2 = max(box1[2], box2[2])\n                uy2 = max(box1[3], box2[3])\n                impact_box = (ux1, uy1, ux2, uy2)\n    \n    if impact_box is None:\n        return None\n    \n    cap = cv2.VideoCapture(str(video_path))\n    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n    fps = cap.get(cv2.CAP_PROP_FPS)\n    cap.release()\n    \n    center_x = (impact_box[0] + impact_box[2]) / 2\n    center_y = (impact_box[1] + impact_box[3]) / 2\n    \n    center_x_norm = center_x / width\n    center_y_norm = center_y / height\n    \n    # ---------------------------\n    # 4️⃣ Extract crash clip\n    # ---------------------------\n    clip = extract_crash_clip(\n        video_path,\n        center_x_norm,\n        center_y_norm,\n        predicted_frame\n    )\n    \n    clip = torch.tensor(clip).permute(0,3,1,2).float() / 255.0\n    clip = clip.unsqueeze(0).to(device)\n    \n    # ---------------------------\n    # 5️⃣ Type classification\n    # ---------------------------\n    with torch.no_grad():\n\n        output = model_type_infer(clip)\n    \n        probs = torch.softmax(output, dim=1)\n    \n        confidence = probs.max().item()*100\n    \n        pred_class = output.argmax(dim=1).item()\n    \n    inv_label_map = {v: k for k, v in label_map.items()}\n    pred_type = inv_label_map[pred_class]\n    \n    accident_time_sec = predicted_frame / fps\n    \n    return accident_time_sec, center_x_norm, center_y_norm, pred_type","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:21.270091Z","iopub.execute_input":"2026-07-30T19:37:21.270385Z","iopub.status.idle":"2026-07-30T19:37:21.290611Z","shell.execute_reply.started":"2026-07-30T19:37:21.270362Z","shell.execute_reply":"2026-07-30T19:37:21.290047Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Test on one synthetic video","metadata":{}},{"cell_type":"code","source":"test_video_path = video_path  # using earlier selected synthetic video\n\nresult = predict_video(test_video_path)\n\nprint(\"Prediction output:\")\nprint(result)\n\nprint(\"\\nGround Truth:\")\nprint(\"Accident time (sec):\", row[\"accident_time\"])\nprint(\"Center_x:\", row[\"center_x\"])\nprint(\"Center_y:\", row[\"center_y\"])\nprint(\"Type:\", row[\"type\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:21.291557Z","iopub.execute_input":"2026-07-30T19:37:21.291874Z","iopub.status.idle":"2026-07-30T19:37:28.080397Z","shell.execute_reply.started":"2026-07-30T19:37:21.29181Z","shell.execute_reply":"2026-07-30T19:37:28.079677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Paths\n# TEST_ROOT = Path(\"/kaggle/input/accident\")\n# TEST_VIDEOS_DIR = TEST_ROOT / \"videos\"\n# TEST_META_PATH = TEST_ROOT / \"test_metadata.csv\"\n\n# # Load metadata\n# test_df = pd.read_csv(TEST_META_PATH)\n\n# print(\"Total test videos:\", len(test_df))\n\n# submission_rows = []\n\n# for idx, row_test in tqdm(test_df.iterrows(), total=len(test_df)):\n    \n#     video_rel_path = row_test[\"path\"]  # column name from submission format\n#     video_path = TEST_VIDEOS_DIR / Path(video_rel_path).name\n    \n#     try:\n#         result = predict_video(video_path)\n        \n#         if result is None:\n#             accident_time_sec = 0.0\n#             center_x_norm = 0.5\n#             center_y_norm = 0.5\n#             pred_type = \"rear-end\"\n#         else:\n#             accident_time_sec, center_x_norm, center_y_norm, pred_type = result\n        \n#     except Exception as e:\n#         print(f\"Error processing {video_rel_path}: {e}\")\n#         accident_time_sec = 0.0\n#         center_x_norm = 0.5\n#         center_y_norm = 0.5\n#         pred_type = \"rear-end\"\n    \n#     submission_rows.append({\n#         \"path\": video_rel_path,\n#         \"accident_time\": accident_time_sec,\n#         \"center_x\": float(center_x_norm),\n#         \"center_y\": float(center_y_norm),\n#         \"type\": pred_type\n#     })\n\n# submission_df = pd.DataFrame(submission_rows)\n\n# submission_df.to_csv(\"submission_baseline.csv\", index=False)\n\n# print(\"Baseline submission saved.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:28.081306Z","iopub.execute_input":"2026-07-30T19:37:28.081682Z","iopub.status.idle":"2026-07-30T19:37:28.086102Z","shell.execute_reply.started":"2026-07-30T19:37:28.081655Z","shell.execute_reply":"2026-07-30T19:37:28.085366Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Generate Baseline Submission","metadata":{}},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\nfrom tqdm import tqdm\n\n# ==========================================================\n# Paths\n# ==========================================================\n\nTEST_ROOT = Path(\"/kaggle/input/accident\")\nTEST_VIDEOS_DIR = TEST_ROOT / \"videos\"\nTEST_META_PATH = TEST_ROOT / \"test_metadata.csv\"\n\n# ==========================================================\n# Load Test Metadata\n# ==========================================================\n\ntest_df = pd.read_csv(TEST_META_PATH)\n\nprint(f\"Total Test Videos : {len(test_df)}\")\n\n# ==========================================================\n# DEBUG: Process only first 10 videos\n# ==========================================================\n\ntest_df = test_df.head(10)\n\nprint(f\"Running on {len(test_df)} videos...\\n\")\n\nsubmission_rows = []\n\n# ==========================================================\n# Prediction Loop\n# ==========================================================\n\nfor idx, row_test in tqdm(test_df.iterrows(), total=len(test_df)):\n\n    video_rel_path = row_test[\"path\"]\n\n    video_path = TEST_VIDEOS_DIR / Path(video_rel_path).name\n\n    try:\n\n        result = predict_video(video_path)\n\n        if result is None:\n\n            accident_time_sec = 0.0\n            center_x_norm = 0.5\n            center_y_norm = 0.5\n            pred_type = \"rear-end\"\n\n        else:\n\n            (\n                accident_time_sec,\n                center_x_norm,\n                center_y_norm,\n                pred_type\n            ) = result\n\n    except Exception as e:\n\n        print(f\"\\nError processing {video_rel_path}\")\n        print(e)\n\n        accident_time_sec = 0.0\n        center_x_norm = 0.5\n        center_y_norm = 0.5\n        pred_type = \"rear-end\"\n\n    # ------------------------------------\n    # Print Prediction\n    # ------------------------------------\n\n    print(\"\\n\" + \"=\" * 60)\n    print(f\"Video         : {video_rel_path}\")\n    print(f\"Accident Time : {accident_time_sec:.2f} sec\")\n    print(f\"Center X      : {center_x_norm:.3f}\")\n    print(f\"Center Y      : {center_y_norm:.3f}\")\n    print(f\"Type          : {pred_type}\")\n\n    submission_rows.append(\n        {\n            \"path\": video_rel_path,\n            \"accident_time\": float(accident_time_sec),\n            \"center_x\": float(center_x_norm),\n            \"center_y\": float(center_y_norm),\n            \"type\": pred_type,\n        }\n    )\n\n# ==========================================================\n# Create Submission DataFrame\n# ==========================================================\n\nsubmission_df = pd.DataFrame(submission_rows)\n\nprint(\"\\nPrediction DataFrame:\\n\")\n\ndisplay(submission_df)\n\n# ==========================================================\n# Save Debug Submission\n# ==========================================================\n\nsubmission_df.to_csv(\"submission_debug_10.csv\", index=False)\n\nprint(\"\\n✅ Debug submission saved as submission_debug_10.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:37:28.087057Z","iopub.execute_input":"2026-07-30T19:37:28.087364Z","iopub.status.idle":"2026-07-30T19:38:07.864604Z","shell.execute_reply.started":"2026-07-30T19:37:28.087329Z","shell.execute_reply":"2026-07-30T19:38:07.863706Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"video_path = \"/kaggle/input/datasets/priyasharma040404/road-acci/ALFF5620ljk.mp4\"\nprint(video_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T20:05:35.041266Z","iopub.execute_input":"2026-07-30T20:05:35.041658Z","iopub.status.idle":"2026-07-30T20:05:35.047664Z","shell.execute_reply.started":"2026-07-30T20:05:35.041625Z","shell.execute_reply":"2026-07-30T20:05:35.04647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ---------------------------\n# 6️⃣ Create annotated output video\n# ---------------------------\ncap = cv2.VideoCapture(str(video_path))\n\nwidth = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\nheight = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\nfps = cap.get(cv2.CAP_PROP_FPS)\n\nfourcc = cv2.VideoWriter_fourcc(*'mp4v')\nout = cv2.VideoWriter(\"predicted_output.mp4\", fourcc, fps, (width, height))\n\nframe_id = 0\n\nwhile True:\n\n    ret, frame = cap.read()\n    if not ret:\n        break\n\n    # Show annotation only for 2 seconds after accident\n    if predicted_frame <= frame_id <= predicted_frame + int(2 * fps):\n\n        # Draw impact point\n        cx = int(center_x)\n        cy = int(center_y)\n\n        cv2.circle(\n            frame,\n            (cx, cy),\n            8,\n            (0, 255, 255),\n            -1\n        )\n\n        cv2.putText(\n            frame,\n            \"ACCIDENT DETECTED\",\n            (20, 40),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            1,\n            (0, 0, 255),\n            2\n        )\n\n        cv2.putText(\n            frame,\n            f\"Type : {pred_type}\",\n            (20, 80),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.8,\n            (255, 255, 255),\n            2\n        )\n\n        cv2.putText(\n            frame,\n            f\"Time : {accident_time_sec:.2f}s\",\n            (20, 120),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.8,\n            (255, 255, 255),\n            2\n        )\n\n    cv2.putText(\n        frame,\n        f\"Frame : {frame_id}\",\n        (20, height - 20),\n        cv2.FONT_HERSHEY_SIMPLEX,\n        0.7,\n        (0, 255, 0),\n        2\n    )\n\n    out.write(frame)\n    frame_id += 1\n\ncap.release()\nout.release()\n\nprint(\"Annotated video saved as predicted_output.mp4\")\n\nreturn {\n    \"time\": accident_time_sec,\n    \"type\": pred_type,\n    \"center_x\": center_x_norm,\n    \"center_y\": center_y_norm,\n    \"video\": \"predicted_output.mp4\"\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T20:05:37.241411Z","iopub.execute_input":"2026-07-30T20:05:37.241727Z","iopub.status.idle":"2026-07-30T20:05:50.094989Z","shell.execute_reply.started":"2026-07-30T20:05:37.241699Z","shell.execute_reply":"2026-07-30T20:05:50.09392Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nsize = os.path.getsize(\"predicted_output.mp4\") / (1024*1024)\nprint(f\"{size:.2f} MB\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T20:00:07.916094Z","iopub.execute_input":"2026-07-30T20:00:07.916539Z","iopub.status.idle":"2026-07-30T20:00:07.921709Z","shell.execute_reply.started":"2026-07-30T20:00:07.916508Z","shell.execute_reply":"2026-07-30T20:00:07.920649Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result = predict_video(test_video_path)\n\nprint(result)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:51:33.990563Z","iopub.execute_input":"2026-07-30T19:51:33.991589Z","iopub.status.idle":"2026-07-30T19:51:40.478202Z","shell.execute_reply.started":"2026-07-30T19:51:33.991549Z","shell.execute_reply":"2026-07-30T19:51:40.477265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.plot(smoothed_scores)\nplt.xlabel(\"Frame\")\nplt.ylabel(\"Accident Score\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T20:03:54.621075Z","iopub.execute_input":"2026-07-30T20:03:54.621786Z","iopub.status.idle":"2026-07-30T20:03:54.737725Z","shell.execute_reply.started":"2026-07-30T20:03:54.621752Z","shell.execute_reply":"2026-07-30T20:03:54.737057Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import Video\n\nVideo(\"predicted_output.mp4\", embed=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:38:10.914877Z","iopub.status.idle":"2026-07-30T19:38:10.915444Z","shell.execute_reply.started":"2026-07-30T19:38:10.915259Z","shell.execute_reply":"2026-07-30T19:38:10.9153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# submission_df.head","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-30T19:38:10.916636Z","iopub.status.idle":"2026-07-30T19:38:10.916981Z","shell.execute_reply.started":"2026-07-30T19:38:10.916797Z","shell.execute_reply":"2026-07-30T19:38:10.916815Z"}},"outputs":[],"execution_count":null}]}