{"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":[{"sourceType":"competition","sourceId":19989,"databundleVersionId":1160143}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport ast\nimport random\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\n\nimport torch\nimport torchvision\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:31:51.612883Z","iopub.execute_input":"2026-05-13T08:31:51.613064Z","iopub.status.idle":"2026-05-13T08:32:02.911646Z","shell.execute_reply.started":"2026-05-13T08:31:51.613044Z","shell.execute_reply":"2026-05-13T08:32:02.910979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/competitions/global-wheat-detection\"\nTRAIN_DIR = f\"{DATA_DIR}/train\"\nTEST_DIR = f\"{DATA_DIR}/test\"\n\nSEED = 42\nIMG_SIZE = 512\nBATCH_SIZE = 4\nNUM_EPOCHS = 3\n\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:02.912927Z","iopub.execute_input":"2026-05-13T08:32:02.913305Z","iopub.status.idle":"2026-05-13T08:32:02.925822Z","shell.execute_reply.started":"2026-05-13T08:32:02.913279Z","shell.execute_reply":"2026-05-13T08:32:02.924978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(f\"{DATA_DIR}/train.csv\")\n\ntrain_df[\"bbox\"] = train_df[\"bbox\"].apply(ast.literal_eval)\n\ntrain_df[[\"x\", \"y\", \"w\", \"h\"]] = pd.DataFrame(\n    train_df[\"bbox\"].tolist(),\n    index=train_df.index\n)\n\ntrain_df[\"x2\"] = train_df[\"x\"] + train_df[\"w\"]\ntrain_df[\"y2\"] = train_df[\"y\"] + train_df[\"h\"]\n\nimage_ids = train_df[\"image_id\"].unique().tolist()\n\nrandom.shuffle(image_ids)\n\nvalid_size = int(len(image_ids) * 0.1)\n\nvalid_ids = image_ids[:valid_size]\ntrain_ids = image_ids[valid_size:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:02.926806Z","iopub.execute_input":"2026-05-13T08:32:02.927061Z","iopub.status.idle":"2026-05-13T08:32:04.969207Z","shell.execute_reply.started":"2026-05-13T08:32:02.927040Z","shell.execute_reply":"2026-05-13T08:32:04.968599Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class WheatDataset(Dataset):\n    def __init__(self, image_ids, df, image_dir, img_size=512, train=True):\n        self.image_ids = image_ids\n        self.df = df\n        self.image_dir = image_dir\n        self.img_size = img_size\n        self.train = train\n\n    def __len__(self):\n        return len(self.image_ids)\n\n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n        image_path = os.path.join(self.image_dir, image_id + \".jpg\")\n\n        image = cv2.imread(image_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        h0, w0 = image.shape[:2]\n\n        records = self.df[self.df[\"image_id\"] == image_id]\n        boxes = records[[\"x\", \"y\", \"x2\", \"y2\"]].values.astype(np.float32)\n\n        scale_x = self.img_size / w0\n        scale_y = self.img_size / h0\n\n        boxes[:, [0, 2]] *= scale_x\n        boxes[:, [1, 3]] *= scale_y\n\n        image = cv2.resize(image, (self.img_size, self.img_size))\n\n        if self.train and random.random() < 0.5:\n            image = image[:, ::-1, :].copy()\n            boxes[:, [0, 2]] = self.img_size - boxes[:, [2, 0]]\n\n        image = image.astype(np.float32) / 255.0\n        image = torch.tensor(image).permute(2, 0, 1)\n\n        boxes = torch.as_tensor(boxes, dtype=torch.float32)\n        labels = torch.ones((boxes.shape[0],), dtype=torch.int64)\n\n        target = {\n            \"boxes\": boxes,\n            \"labels\": labels\n        }\n\n        return image, target","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:04.970099Z","iopub.execute_input":"2026-05-13T08:32:04.970363Z","iopub.status.idle":"2026-05-13T08:32:04.978416Z","shell.execute_reply.started":"2026-05-13T08:32:04.970340Z","shell.execute_reply":"2026-05-13T08:32:04.977864Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:04.979286Z","iopub.execute_input":"2026-05-13T08:32:04.979627Z","iopub.status.idle":"2026-05-13T08:32:05.094828Z","shell.execute_reply.started":"2026-05-13T08:32:04.979588Z","shell.execute_reply":"2026-05-13T08:32:05.093969Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = WheatDataset(\n    train_ids,\n    train_df,\n    TRAIN_DIR,\n    img_size=IMG_SIZE,\n    train=True\n)\n\nvalid_dataset = WheatDataset(\n    valid_ids,\n    train_df,\n    TRAIN_DIR,\n    img_size=IMG_SIZE,\n    train=False\n)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=2,\n    collate_fn=collate_fn\n)\n\nvalid_loader = DataLoader(\n    valid_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=2,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:05.095874Z","iopub.execute_input":"2026-05-13T08:32:05.096661Z","iopub.status.idle":"2026-05-13T08:32:05.107716Z","shell.execute_reply.started":"2026-05-13T08:32:05.096634Z","shell.execute_reply":"2026-05-13T08:32:05.107107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_model():\n    model = torchvision.models.detection.fasterrcnn_resnet50_fpn(\n        weights=\"DEFAULT\",\n        min_size=IMG_SIZE,\n        max_size=IMG_SIZE\n    )\n\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n\n    model.roi_heads.box_predictor = FastRCNNPredictor(\n        in_features,\n        num_classes=2\n    )\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:05.109751Z","iopub.execute_input":"2026-05-13T08:32:05.110411Z","iopub.status.idle":"2026-05-13T08:32:05.120935Z","shell.execute_reply.started":"2026-05-13T08:32:05.110388Z","shell.execute_reply":"2026-05-13T08:32:05.120266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)\n\nmodel = get_model()\nmodel.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:05.121804Z","iopub.execute_input":"2026-05-13T08:32:05.122084Z","iopub.status.idle":"2026-05-13T08:32:07.381974Z","shell.execute_reply.started":"2026-05-13T08:32:05.122054Z","shell.execute_reply":"2026-05-13T08:32:07.381252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = torch.optim.SGD(\n    model.parameters(),\n    lr=0.005,\n    momentum=0.9,\n    weight_decay=0.0005\n)\n\nscheduler = torch.optim.lr_scheduler.StepLR(\n    optimizer,\n    step_size=2,\n    gamma=0.5\n)\n\nscaler = torch.amp.GradScaler(\"cuda\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:07.382952Z","iopub.execute_input":"2026-05-13T08:32:07.383316Z","iopub.status.idle":"2026-05-13T08:32:07.388142Z","shell.execute_reply.started":"2026-05-13T08:32:07.383293Z","shell.execute_reply":"2026-05-13T08:32:07.387423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(NUM_EPOCHS):\n    model.train()\n    total_loss = 0\n\n    pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}/{NUM_EPOCHS}\")\n\n    for images, targets in pbar:\n        images = [img.to(device) for img in images]\n        targets = [\n            {k: v.to(device) for k, v in t.items()}\n            for t in targets\n        ]\n\n        optimizer.zero_grad()\n\n        with torch.amp.autocast(\"cuda\"):\n            loss_dict = model(images, targets)\n            loss = sum(loss for loss in loss_dict.values())\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        total_loss += loss.item()\n        pbar.set_postfix(loss=loss.item())\n\n    scheduler.step()\n\n    print(f\"Epoch {epoch+1} Loss: {total_loss / len(train_loader):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:32:07.389098Z","iopub.execute_input":"2026-05-13T08:32:07.389496Z","iopub.status.idle":"2026-05-13T08:40:55.583886Z","shell.execute_reply.started":"2026-05-13T08:32:07.389461Z","shell.execute_reply":"2026-05-13T08:40:55.583036Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), \"fasterrcnn_wheat_best.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:55.585203Z","iopub.execute_input":"2026-05-13T08:40:55.585579Z","iopub.status.idle":"2026-05-13T08:40:55.836383Z","shell.execute_reply.started":"2026-05-13T08:40:55.585507Z","shell.execute_reply":"2026-05-13T08:40:55.835773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\nimages, targets = next(iter(valid_loader))\n\nwith torch.no_grad():\n    outputs = model([img.to(device) for img in images])\n\nimg = images[0].permute(1, 2, 0).cpu().numpy().copy()\n\nboxes = outputs[0][\"boxes\"].detach().cpu().numpy()\nscores = outputs[0][\"scores\"].detach().cpu().numpy()\n\nfor box, score in zip(boxes, scores):\n    if score < 0.5:\n        continue\n\n    x1, y1, x2, y2 = box.astype(int)\n    cv2.rectangle(img, (x1, y1), (x2, y2), (1, 0, 0), 2)\n\nplt.figure(figsize=(8, 8))\nplt.imshow(img)\nplt.axis(\"off\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:55.837307Z","iopub.execute_input":"2026-05-13T08:40:55.837869Z","iopub.status.idle":"2026-05-13T08:40:56.874786Z","shell.execute_reply.started":"2026-05-13T08:40:55.837843Z","shell.execute_reply":"2026-05-13T08:40:56.873866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class WheatTestDataset(Dataset):\n    def __init__(self, image_dir, img_size=512):\n        self.image_dir = image_dir\n        self.img_size = img_size\n        self.image_files = sorted(os.listdir(image_dir))\n\n    def __len__(self):\n        return len(self.image_files)\n\n    def __getitem__(self, idx):\n        file_name = self.image_files[idx]\n        image_id = file_name.replace(\".jpg\", \"\")\n\n        image_path = os.path.join(self.image_dir, file_name)\n\n        image = cv2.imread(image_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        h0, w0 = image.shape[:2]\n\n        image = cv2.resize(image, (self.img_size, self.img_size))\n        image = image.astype(np.float32) / 255.0\n        image = torch.tensor(image).permute(2, 0, 1)\n\n        return image, image_id, w0, h0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:56.876351Z","iopub.execute_input":"2026-05-13T08:40:56.876742Z","iopub.status.idle":"2026-05-13T08:40:56.882995Z","shell.execute_reply.started":"2026-05-13T08:40:56.876701Z","shell.execute_reply":"2026-05-13T08:40:56.882401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataset = WheatTestDataset(TEST_DIR, img_size=IMG_SIZE)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=1,\n    shuffle=False,\n    num_workers=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:56.884074Z","iopub.execute_input":"2026-05-13T08:40:56.884421Z","iopub.status.idle":"2026-05-13T08:40:56.905067Z","shell.execute_reply.started":"2026-05-13T08:40:56.884384Z","shell.execute_reply":"2026-05-13T08:40:56.904307Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = []\n\nmodel.eval()\n\nfor images, image_ids, orig_w, orig_h in tqdm(test_loader):\n    images = [img.to(device) for img in images]\n\n    with torch.no_grad():\n        outputs = model(images)\n\n    boxes = outputs[0][\"boxes\"].detach().cpu().numpy()\n    scores = outputs[0][\"scores\"].detach().cpu().numpy()\n\n    orig_w = orig_w.item()\n    orig_h = orig_h.item()\n\n    scale_x = orig_w / IMG_SIZE\n    scale_y = orig_h / IMG_SIZE\n\n    pred_strings = []\n\n    for box, score in zip(boxes, scores):\n        if score < 0.45:\n            continue\n\n        x1, y1, x2, y2 = box\n\n        x1 *= scale_x\n        x2 *= scale_x\n        y1 *= scale_y\n        y2 *= scale_y\n\n        w = x2 - x1\n        h = y2 - y1\n\n        pred_strings.append(\n            f\"{score:.4f} {x1:.0f} {y1:.0f} {w:.0f} {h:.0f}\"\n        )\n\n    results.append({\n        \"image_id\": image_ids[0],\n        \"PredictionString\": \" \".join(pred_strings)\n    })","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:56.905929Z","iopub.execute_input":"2026-05-13T08:40:56.906483Z","iopub.status.idle":"2026-05-13T08:40:57.626166Z","shell.execute_reply.started":"2026-05-13T08:40:56.906460Z","shell.execute_reply":"2026-05-13T08:40:57.625092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame(results)\n\nsubmission.to_csv(\"submission.csv\", index=False)\n\ndisplay(submission)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:57.627764Z","iopub.execute_input":"2026-05-13T08:40:57.628116Z","iopub.status.idle":"2026-05-13T08:40:57.655991Z","shell.execute_reply.started":"2026-05-13T08:40:57.628084Z","shell.execute_reply":"2026-05-13T08:40:57.655321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-13T08:40:57.657012Z","iopub.execute_input":"2026-05-13T08:40:57.657303Z","iopub.status.idle":"2026-05-13T08:40:57.818791Z","shell.execute_reply.started":"2026-05-13T08:40:57.657281Z","shell.execute_reply":"2026-05-13T08:40:57.818038Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}