{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":9988,"databundleVersionId":868324,"sourceType":"competition"}],"dockerImageVersionId":30559,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **🧩 Cell 1 – Install Dependencies**","metadata":{}},{"cell_type":"code","source":"!pip install -U ultralytics","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T12:53:09.990534Z","iopub.execute_input":"2025-12-23T12:53:09.991167Z","iopub.status.idle":"2025-12-23T12:53:24.166448Z","shell.execute_reply.started":"2025-12-23T12:53:09.991135Z","shell.execute_reply":"2025-12-23T12:53:24.165589Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **📂 Cell 2 – Imports & Paths**","metadata":{}},{"cell_type":"code","source":"import os\nimport ultralytics\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **📂 Cell 3 – Define Paths**","metadata":{}},{"cell_type":"code","source":"DATASET_ROOT = \"/kaggle/input/airbus-ship-detection\"\nWORK_DIR = \"/kaggle/working/airbus\"\n\nIMG_DIR = f\"{DATASET_ROOT}/train_v2\"\nCSV_PATH = f\"{DATASET_ROOT}/train_ship_segmentations_v2.csv\"\n\nIMG_OUT_TRAIN = f\"{WORK_DIR}/images/train\"\nIMG_OUT_VAL   = f\"{WORK_DIR}/images/val\"\n\nMASK_TRAIN = f\"{WORK_DIR}/masks/train\"\nMASK_VAL   = f\"{WORK_DIR}/masks/val\"\n\nLABEL_TRAIN = f\"{WORK_DIR}/labels/train\"\nLABEL_VAL   = f\"{WORK_DIR}/labels/val\"\n\nSKIP_DATASET_GEN = False\n\nfor p in [IMG_OUT_TRAIN, IMG_OUT_VAL, MASK_TRAIN, MASK_VAL, LABEL_TRAIN, LABEL_VAL]:\n    os.makedirs(p, exist_ok=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **🧠  Cell 4 – RLE Decode Function**","metadata":{}},{"cell_type":"code","source":"def rle_decode(mask_rle, shape=(768, 768)):\n    \"\"\"Convert RLE string to binary mask. Returns zeros if mask is empty/NaN.\"\"\"\n    if pd.isna(mask_rle) or not isinstance(mask_rle, str):\n        return np.zeros(shape, dtype=np.uint8)\n    \n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0::2], s[1::2])]\n    starts -= 1\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for start, length in zip(starts, lengths):\n        img[start:start+length] = 255\n    return img.reshape(shape).T","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **🔀 Cell 5 – Train / Val Split**","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport pandas as pd\nimport numpy as np\n\nCSV_PATH = \"/kaggle/input/airbus-ship-detection/train_ship_segmentations_v2.csv\"\nEMPTY_FRACTION = 0.10   # Max fraction of empty images\nSEED = 42\n\n# -------------------------------------------------\n# Read CSV and count ships per image\n# -------------------------------------------------\ndf = pd.read_csv(CSV_PATH)\n\nship_counts = (\n    df.assign(has_ship=~df[\"EncodedPixels\"].isna())\n      .groupby(\"ImageId\")[\"has_ship\"]\n      .sum()\n      .reset_index(name=\"ship_count\")\n)\n\n# -------------------------------------------------\n# Reduce empty images BEFORE splitting\n# -------------------------------------------------\nempty = ship_counts[ship_counts[\"ship_count\"] == 0]\nnon_empty = ship_counts[ship_counts[\"ship_count\"] > 0]\n\n# Target number of empty images\ntotal_target = int(len(non_empty) / (1 - EMPTY_FRACTION))\nn_empty_keep = max(int(total_target * EMPTY_FRACTION), 1)\n\nempty_sampled = empty.sample(n=min(len(empty), n_empty_keep), random_state=SEED)\n\n# Combine sampled empty + all non-empty\nreduced_ship_counts = pd.concat([non_empty, empty_sampled]).reset_index(drop=True)\n\n# -------------------------------------------------\n# Stratification bins\n# -------------------------------------------------\ndef ship_bin(n):\n    if n == 0:\n        return \"0\"\n    elif n == 1:\n        return \"1\"\n    elif n <= 3:\n        return \"2-3\"\n    else:\n        return \"4+\"\n\nreduced_ship_counts[\"bin\"] = reduced_ship_counts[\"ship_count\"].apply(ship_bin)\n\n# -------------------------------------------------\n# Stratified train / val split\n# -------------------------------------------------\ntrain_ids, val_ids = train_test_split(\n    reduced_ship_counts[\"ImageId\"],\n    test_size=0.15,\n    random_state=SEED,\n    stratify=reduced_ship_counts[\"bin\"]\n)\n\ntrain_ids = set(train_ids)\nval_ids = set(val_ids)\n\n# -------------------------------------------------\n# Diagnostics\n# -------------------------------------------------\ndef print_distribution(ids, name):\n    subset = reduced_ship_counts[reduced_ship_counts[\"ImageId\"].isin(ids)]\n    print(f\"\\n{name} distribution:\")\n    print(subset[\"bin\"].value_counts(normalize=True).sort_index())\n\nprint(\"Total images after empty reduction:\", len(reduced_ship_counts))\nprint_distribution(train_ids, \"Train (stratified)\")\nprint_distribution(val_ids, \"Validation (stratified)\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport random\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ndef show_examples_overlay(bin_label, n=3, img_dir=\"/kaggle/input/airbus-ship-detection/train_v2\"):\n    \"\"\"Show n random images from the filtered dataset with masks overlaid\"\"\"\n    imgs = reduced_ship_counts[reduced_ship_counts[\"bin\"] == bin_label][\"ImageId\"].tolist()\n    sample = random.sample(imgs, min(n, len(imgs)))\n    \n    n_cols = min(3, len(sample))\n    n_rows = (len(sample) + n_cols - 1) // n_cols\n    \n    fig, axes = plt.subplots(n_rows, n_cols, figsize=(5*n_cols, 5*n_rows))\n    axes = axes.flatten() if len(sample) > 1 else [axes]\n    \n    for ax, img_id in zip(axes, sample):\n        img = cv2.imread(f\"{img_dir}/{img_id}\")\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        # Combine all RLE masks for this image\n        masks_rle = df[df[\"ImageId\"] == img_id][\"EncodedPixels\"]\n        combined_mask = np.zeros((768,768), dtype=np.uint8)\n        for rle in masks_rle:\n            combined_mask += rle_decode(rle)\n        \n        # Overlay mask in red, semi-transparent\n        overlay = img.copy()\n        overlay[combined_mask>0] = [255,0,0]  # red mask\n        alpha = 0.4\n        img_overlay = cv2.addWeighted(overlay, alpha, img, 1-alpha, 0)\n        \n        ax.imshow(img_overlay)\n        ax.axis(\"off\")\n        ax.set_title(f\"{img_id} | {bin_label} ships\")\n    \n    # Hide unused axes\n    for ax in axes[len(sample):]:\n        ax.axis(\"off\")\n    \n    plt.tight_layout()\n    plt.show()\n\n# Example usage:\nshow_examples_overlay(\"0\", n=6)\nshow_examples_overlay(\"1\", n=6)\nshow_examples_overlay(\"2-3\", n=6)\nshow_examples_overlay(\"4+\", n=6)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **🖼️ Cell 6 – Copy Images + Create Binary Masks**","metadata":{}},{"cell_type":"code","source":"import cv2\nfrom tqdm import tqdm\n\ndef rle_to_yolo_txt(img_id, masks_df, output_dir):\n    masks_rle = masks_df[masks_df[\"ImageId\"] == img_id][\"EncodedPixels\"]\n    yolo_lines = []\n    for rle in masks_rle:\n        mask = rle_decode(rle)\n        contours, _ = cv2.findContours((mask > 0).astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n        for contour in contours:\n            if len(contour) < 3:\n                continue\n            contour = contour.squeeze()\n            line = [0]  # class index 0 = ship\n            for point in contour:\n                x, y = point\n                line.append(round(x / mask.shape[1], 6))\n                line.append(round(y / mask.shape[0], 6))\n            yolo_lines.append(\" \".join(map(str, line)))\n    os.makedirs(output_dir, exist_ok=True)\n    with open(os.path.join(output_dir, img_id.replace(\".jpg\", \".txt\")), \"w\") as f:\n        f.write(\"\\n\".join(yolo_lines))\n\n# Optional helper to skip already existing labels\ndef labels_complete(ids, output_dir):\n    existing = {f.replace(\".txt\",\"\") for f in os.listdir(output_dir) if f.endswith(\".txt\")}\n    return all(img_id.replace(\".jpg\",\"\") in existing for img_id in ids)","metadata":{"execution":{"iopub.status.busy":"2025-12-22T17:34:53.084669Z","iopub.execute_input":"2025-12-22T17:34:53.08539Z","iopub.status.idle":"2025-12-22T17:34:53.093079Z","shell.execute_reply.started":"2025-12-22T17:34:53.085359Z","shell.execute_reply":"2025-12-22T17:34:53.092114Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport shutil\n\nif not SKIP_DATASET_GEN:\n    # TRAIN\n    os.makedirs(IMG_OUT_TRAIN, exist_ok=True)\n    os.makedirs(LABEL_TRAIN, exist_ok=True)\n\n    for img_id in tqdm(train_ids, desc=\"Processing TRAIN\"):\n        dst_img_path = os.path.join(IMG_OUT_TRAIN, img_id)\n        if not os.path.exists(dst_img_path):\n            shutil.copy(os.path.join(IMG_DIR, img_id), dst_img_path)\n        dst_label_path = os.path.join(LABEL_TRAIN, img_id.replace(\".jpg\", \".txt\"))\n        if not os.path.exists(dst_label_path):\n            rle_to_yolo_txt(img_id, df, LABEL_TRAIN)\n\n    # VAL\n    os.makedirs(IMG_OUT_VAL, exist_ok=True)\n    os.makedirs(LABEL_VAL, exist_ok=True)\n\n    for img_id in tqdm(val_ids, desc=\"Processing VAL\"):\n        dst_img_path = os.path.join(IMG_OUT_VAL, img_id)\n        if not os.path.exists(dst_img_path):\n            shutil.copy(os.path.join(IMG_DIR, img_id), dst_img_path)\n        dst_label_path = os.path.join(LABEL_VAL, img_id.replace(\".jpg\", \".txt\"))\n        if not os.path.exists(dst_label_path):\n            rle_to_yolo_txt(img_id, df, LABEL_VAL)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T17:37:49.270066Z","iopub.execute_input":"2025-12-22T17:37:49.270363Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **📄Cell 8 – Create Dataset YAML**","metadata":{}},{"cell_type":"code","source":"yaml_content = f\"\"\"\npath: {WORK_DIR}\ntrain: images/train\nval: images/val\n\nnames:\n  0: ship\n\"\"\"\n\nwith open(\"airbus-seg.yaml\", \"w\") as f:\n    f.write(yaml_content)\n\nprint(yaml_content)","metadata":{"execution":{"iopub.status.busy":"2025-12-22T15:27:54.215628Z","iopub.status.idle":"2025-12-22T15:27:54.216012Z","shell.execute_reply.started":"2025-12-22T15:27:54.215853Z","shell.execute_reply":"2025-12-22T15:27:54.21587Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🚀**Cell 9 – Train YOLOv8 Instance Segmentation Model**","metadata":{}},{"cell_type":"code","source":"from ultralytics import YOLO\n\nmodel = YOLO(\"yolo11-seg.yaml\")  # nano model (fast, good for Kaggle)\n\nmodel.train(\n    data=\"airbus-seg.yaml\",\n    epochs=25,\n    imgsz=768,\n    batch=32,\n    device=0,\n    patience=3,\n    name=\"airbus_ship_seg_yolo11\"\n)","metadata":{"execution":{"iopub.status.busy":"2025-12-23T12:53:41.76003Z","iopub.execute_input":"2025-12-23T12:53:41.760372Z","iopub.status.idle":"2025-12-23T12:53:45.185857Z","shell.execute_reply.started":"2025-12-23T12:53:41.760341Z","shell.execute_reply":"2025-12-23T12:53:45.184565Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 📊**Cell 10 – Validation & Metrics**","metadata":{}},{"cell_type":"code","source":"# Path to your best model\nbest_model = \"runs/segment/airbus_ship_seg_yolo11/weights/best.pt\"\n\nmodel = YOLO(best_model)\nmetrics = model.val()\n\nprint(\"Segmentation mAP50:\", metrics.seg.map50)\n","metadata":{"execution":{"iopub.status.busy":"2025-12-23T08:46:16.48458Z","iopub.execute_input":"2025-12-23T08:46:16.485381Z","iopub.status.idle":"2025-12-23T08:46:16.807269Z","shell.execute_reply.started":"2025-12-23T08:46:16.485347Z","shell.execute_reply":"2025-12-23T08:46:16.806026Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **🧪 Optional – Inference Visualization**","metadata":{}},{"cell_type":"code","source":"from ultralytics.utils.benchmarks import benchmark\n\n# Run benchmark on GPU with FP32\nbenchmark(\n    model=best_model,\n    data=\"airbus-seg.yaml\",\n    imgsz=768,\n    half=True,\n    device=\"cuda:0\",  # change to \"cpu\" if desired\n    verbose=True\n)","metadata":{"execution":{"iopub.status.busy":"2025-12-22T13:22:22.817225Z","iopub.execute_input":"2025-12-22T13:22:22.817549Z","iopub.status.idle":"2025-12-22T13:22:22.854657Z","shell.execute_reply.started":"2025-12-22T13:22:22.817521Z","shell.execute_reply":"2025-12-22T13:22:22.853844Z"},"trusted":true},"outputs":[],"execution_count":null}]}