{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":92399,"databundleVersionId":11038207,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install decord torch torchvision yolov5  # Install required libraries","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T22:05:23.853043Z","iopub.execute_input":"2025-04-02T22:05:23.853482Z","iopub.status.idle":"2025-04-02T22:05:39.713412Z","shell.execute_reply.started":"2025-04-02T22:05:23.853449Z","shell.execute_reply":"2025-04-02T22:05:39.711986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=FutureWarning, module=\"yolov5\")\n\nimport pandas as pd\nimport os\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import MobileNetV2\nfrom decord import VideoReader\nimport cv2\nimport torch\nfrom yolov5 import YOLOv5\n\n# Load Excel file\nexcel_path = '/kaggle/input/nexar-collision-prediction/train.csv'  # Update this path\ndf = pd.read_csv(excel_path)\n\nprint(\"Excel Data Sample:\")\nprint(df.head())\nprint(\"\\nNaN Check:\")\nprint(df.isna().sum())\n\n# Video folder path\nvideo_folder = '/kaggle/input/nexar-collision-prediction/train'\n\ndef get_video_path(video_id):\n    return os.path.join(video_folder, f\"{str(video_id).zfill(5)}.mp4\")\n\ndf['video_path'] = df['id'].apply(get_video_path)\ndf['video_exists'] = df['video_path'].apply(os.path.exists)\nprint(\"\\nMissing Videos:\")\nprint(df[~df['video_exists']])\ndf = df[df['video_exists']]\n\n# Select 300 videos with target = 1 and 300 with target = 0\ndf_target_1 = df[df['target'] == 1].head(300)\ndf_target_0 = df[df['target'] == 0].head(300)\ndf_train = pd.concat([df_target_1, df_target_0])\nprint(f\"Total training samples before processing: {len(df_train)}\")\n\n# Load YOLOv5 model\nyolo_model = YOLOv5(\"yolov5s.pt\", device=\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Function to extract frame and bounding boxes\ndef extract_frame_and_boxes(video_path, timestamp):\n    vr = VideoReader(video_path)\n    fps = vr.get_avg_fps()\n    total_frames = len(vr)\n    \n    if pd.isna(timestamp):\n        frame_num = np.random.randint(0, total_frames) if total_frames > 0 else 0\n    else:\n        frame_num = min(int(timestamp * fps), total_frames - 1)\n    \n    if frame_num >= total_frames or frame_num < 0:\n        return None, []\n    \n    frame = vr[frame_num].asnumpy()\n    results = yolo_model.predict(frame)\n    boxes = []\n    for det in results.pred[0]:\n        if det[5] in [2, 7]:  # Car or truck\n            x1, y1, x2, y2 = map(int, det[:4])\n            boxes.append([x1, y1, x2, y2])\n    return frame, boxes\n\n# Preprocess for MobileNetV2\ndef preprocess_input(image):\n    if image is None:\n        return None\n    resized = cv2.resize(image, (224, 224))\n    rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)\n    normalized = tf.keras.applications.mobilenet_v2.preprocess_input(rgb)\n    return np.expand_dims(normalized, axis=0)\n\n# Load pre-trained MobileNetV2\nbase_model = MobileNetV2(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\nfeature_extractor = models.Sequential([base_model, layers.GlobalAveragePooling2D()])\nfeature_extractor.trainable = False\n\n# Extract features and bounding box info\ndef extract_features_and_boxes(frame, boxes, model):\n    preprocessed = preprocess_input(frame)\n    if preprocessed is None:\n        return None, None, None\n    \n    features = model.predict(preprocessed, verbose=0).flatten()\n    dashcam_pos = (frame.shape[1] // 2, frame.shape[0])\n    box_features = []\n    for box in boxes:\n        x1, y1, x2, y2 = box\n        centroid = ((x1 + x2) // 2, (y1 + y2) // 2)\n        distance = np.sqrt((centroid[0] - dashcam_pos[0])**2 + (centroid[1] - dashcam_pos[1])**2)\n        box_features.append(distance)\n    \n    min_distance = min(box_features) if box_features else 1000\n    combined_features = np.concatenate([features, [min_distance]])\n    return combined_features, boxes, min_distance\n\n# Collect training data with dynamic Collision threshold\nX_train = []\ny_train = []\ndistances_target_1 = []\n\nfor index, row in df_train.iterrows():\n    video_path = row['video_path']\n    time_of_event = row['time_of_event']\n    time_of_alert = row['time_of_alert']\n    target = row['target']\n    \n    frame, boxes = extract_frame_and_boxes(video_path, time_of_event)\n    if frame is not None:\n        features, _, min_distance = extract_features_and_boxes(frame, boxes, feature_extractor)\n        if features is not None:\n            X_train.append(features)\n            if target == 0:\n                y_train.append(0)  # Normal\n            else:\n                distances_target_1.append(min_distance)  # Collect distances for target = 1\n            y_train.append(target)  # Temporary label (0 or 1)\n        else:\n            print(f\"Failed to extract features for {video_path}\")\n    else:\n        print(f\"Failed to extract frame for {video_path}\")\n\n# Calculate dynamic Collision threshold (25th percentile of target = 1 distances)\nCOLLISION_THRESHOLD = np.percentile(distances_target_1, 25) if distances_target_1 else 200\nprint(f\"Dynamic Collision Threshold: {COLLISION_THRESHOLD:.2f} pixels\")\n\n# Reassign labels with Collision class\nfor i in range(len(y_train)):\n    if y_train[i] == 1:  # Only for target = 1 samples\n        min_distance = X_train[i][-1]\n        time_diff = abs(df_train.iloc[i]['time_of_event'] - df_train.iloc[i]['time_of_alert']) if not pd.isna(df_train.iloc[i]['time_of_alert']) else float('inf')\n        if min_distance < COLLISION_THRESHOLD or time_diff < 0.5:\n            y_train[i] = 2  # Collision\n        else:\n            y_train[i] = 1  # Caution\n\nX_train = np.array(X_train)\ny_train = np.array(y_train)\nprint(f\"Collected {len(X_train)} samples with features: {X_train.shape}\")\nprint(f\"Initial distribution: Normal={np.sum(y_train == 0)}, Caution={np.sum(y_train == 1)}, Collision={np.sum(y_train == 2)}\")\n\n# Oversample Collision to 150 for emphasis (half of Normal/Caution)\ncollision_indices = np.where(y_train == 2)[0]\ntarget_collision_count = 150\nif len(collision_indices) > 0 and len(collision_indices) < target_collision_count:\n    oversample_indices = np.random.choice(collision_indices, target_collision_count - len(collision_indices), replace=True)\n    X_train = np.concatenate([X_train, X_train[oversample_indices]])\n    y_train = np.concatenate([y_train, y_train[oversample_indices]])\n\nprint(f\"Balanced distribution: Normal={np.sum(y_train == 0)}, Caution={np.sum(y_train == 1)}, Collision={np.sum(y_train == 2)}\")\n\n# Build and train model\nmodel = models.Sequential([\n    layers.Input(shape=(1281,)),\n    layers.Dense(128, activation='relu'),\n    layers.Dropout(0.5),\n    layers.Dense(64, activation='relu'),\n    layers.Dropout(0.5),\n    layers.Dense(3, activation='softmax')  # 3 classes\n])\n\nmodel.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\nmodel.fit(X_train, y_train, epochs=10, batch_size=32, validation_split=0.2)\nmodel.save('/kaggle/working/collision_model_3class.h5')\n\n# Test on 00023.mp4\nvideo_path = '/kaggle/input/nexar-collision-prediction/test/00314.mp4'\noutput_video_path = '/kaggle/working/predicted_00314_3rdclass.mp4'\n\nvr = VideoReader(video_path)\nfps = vr.get_avg_fps()\ntotal_frames = len(vr)\ncap = cv2.VideoCapture(video_path)\nfourcc = cv2.VideoWriter_fourcc(*'mp4v')\nvideo_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (int(cap.get(3)), int(cap.get(4))))\n\ncaution_count = 0\ncollision_count = 0\nframe_count = 0\n\nfor i in range(total_frames):\n    frame = vr[i].asnumpy()\n    features, boxes, min_distance = extract_features_and_boxes(frame, extract_frame_and_boxes(video_path, i/fps)[1], feature_extractor)\n    if features is not None:\n        pred = model.predict(np.expand_dims(features, axis=0), verbose=0)\n        class_id = np.argmax(pred)\n        \n        for box in boxes:\n            x1, y1, x2, y2 = box\n            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)\n        \n        if class_id == 1:\n            cv2.putText(frame, \"CAUTION: Vehicle Close!\", (50, 100),\n                        cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)\n            border_color = (0, 255, 255)\n            caution_count += 1\n        elif class_id == 2:\n            cv2.putText(frame, \"COLLISION IMMINENT!\", (50, 100),\n                        cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)\n            border_color = (0, 0, 255)\n            collision_count += 1\n        else:\n            border_color = (0, 255, 0)\n        \n        cv2.rectangle(frame, (0, 0), (frame.shape[1], frame.shape[0]), border_color, 10)\n        video_writer.write(frame)\n        frame_count += 1\n\ncap.release()\nvideo_writer.release()\nprint(f\"Processed {frame_count} frames. Caution frames: {caution_count} ({(caution_count / frame_count) * 100:.2f}%), Collision frames: {collision_count} ({(collision_count / frame_count) * 100:.2f}%)\")\nprint(f\"Output saved to: {output_video_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T22:06:00.748365Z","iopub.execute_input":"2025-04-02T22:06:00.748833Z","iopub.status.idle":"2025-04-02T22:21:37.425912Z","shell.execute_reply.started":"2025-04-02T22:06:00.748797Z","shell.execute_reply":"2025-04-02T22:21:37.424110Z"}},"outputs":[],"execution_count":null}]}