{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":97837,"databundleVersionId":11654135,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =========================\n# 🔧 Step 1: Convert labels to YOLO format (if needed)\n# your raw labels are in format: class xmin ymin xmax ymax\n# YOLO expects: class x_center y_center width height (normalized)\n# =========================\nfrom pathlib import Path\nfrom PIL import Image\n\nlabels_dir = Path(\"/kaggle/input/a0-2025-object-detection/Dataset/Train/labels\")\nimages_dir = Path(\"/kaggle/input/a0-2025-object-detection/Dataset/Train/images\")\nout_dir = Path(\"/kaggle/working/labels_yolo\")\nout_dir.mkdir(parents=True, exist_ok=True)\n\nfor txt_path in labels_dir.glob(\"*.txt\"):\n    img_path = images_dir / (txt_path.stem + \".jpg\")\n    if not img_path.exists():\n        continue\n    with Image.open(img_path) as im:\n        w, h = im.size\n\n    new_lines = []\n    for line in txt_path.read_text().strip().splitlines():\n        parts = line.split()\n        cls = int(parts[0])  # already 0–5\n        xmin, ymin, xmax, ymax = map(float, parts[1:])\n        x_center = (xmin + xmax) / 2 / w\n        y_center = (ymin + ymax) / 2 / h\n        bw = (xmax - xmin) / w\n        bh = (ymax - ymin) / h\n        new_lines.append(f\"{cls} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}\")\n\n    (out_dir / txt_path.name).write_text(\"\\n\".join(new_lines))\nprint(\"✅ Labels converted to YOLO format in Train/labels_yolo\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 📄 Step 2: Create dataset YAML\n# =========================\ndataset_yaml = \"\"\"\npath: .\ntrain: Train/images\nval: Train/images   # if you have a val split, change this path\ntest: Test/images\n\nnames:\n  0: Apple\n  1: Banana\n  2: Grapes\n  3: Orange\n  4: Pineapple\n  5: Watermelon\n\"\"\"\n\nwith open(\"fruits.yaml\", \"w\") as f:\n    f.write(dataset_yaml)\nprint(\"✅ fruits.yaml created\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 🚀 Step 3: Train YOLOv8\n# =========================\nfrom ultralytics import YOLO\n\nmodel = YOLO(\"yolov8s.pt\")  # start with small model; can switch to yolov8m/l/x for higher accuracy\nmodel.train(\n    data=\"fruits.yaml\",\n    epochs=100,          # max epochs\n    patience=15,         # stop if no improvement for 15 epochs\n    imgsz=640,\n    batch=16,\n    workers=2,\n    optimizer=\"AdamW\",   # optional: try \"SGD\" or \"AdamW\"\n    lr0=1e-3,            # initial learning rate\n    lrf=1e-4,            # final learning rate at end of schedule\n    lr_scheduler=\"cosine\",  # cosine annealing (default), can try \"linear\" or \"onecycle\"\n    project=\"runs/train\",\n    name=\"fruits_yolo\",\n    exist_ok=True\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 📊 Step 4: Validate\n# =========================\nmetrics = model.val()\nprint(metrics)  # includes mAP@0.5","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 🔍 Step 5: Inference on Test set\n# =========================\ntest_dir = Path(\"/kaggle/input/a0-2025-object-detection/Dataset/Test/images\")\nresults = model.predict(\n    source=str(test_dir),\n    imgsz=640,\n    conf=0.001,   # low to keep all, will filter later\n    iou=0.5,\n    save=False,\n    save_txt=False,\n    save_conf=True\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 📝 Step 6: Generate submission.csv\n# Required format:\n# ID,bounding_boxes\n# bounding_boxes is a JSON string of [{\"x_min\":..,\"y_min\":..,\"x_max\":..,\"y_max\":..,\"class\":..,\"confidence\":..}, ...]\n# =========================\nimport pandas as pd\nimport json\n\nout_rows = []\nfor r in results:\n    img_name = Path(r.path).stem   # e.g., \"00001\"\n    # change to \"img1\" if contest requires \"imgN\"\n    img_id = img_name\n\n    boxes = []\n    if hasattr(r, \"boxes\"):\n        for box in r.boxes:\n            xyxy = box.xyxy.tolist()[0]  # [x1,y1,x2,y2]\n            conf = float(box.conf.tolist()[0])\n            cls = int(box.cls.tolist()[0])\n            boxes.append({\n                \"x_min\": int(xyxy[0]),\n                \"y_min\": int(xyxy[1]),\n                \"x_max\": int(xyxy[2]),\n                \"y_max\": int(xyxy[3]),\n                \"class\": cls,\n                \"confidence\": round(conf, 4)\n            })\n    out_rows.append({\"ID\": img_id, \"bounding_boxes\": json.dumps(boxes)})\n\ndf = pd.DataFrame(out_rows)\ndf.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(\"✅ submission.csv created\")\ndf.head()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}