{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":11017618,"sourceType":"datasetVersion","datasetId":6860091}],"dockerImageVersionId":29963,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport os, re\n\nimport torch\n\nimport torchvision\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor, FasterRCNN\nfrom torchvision.models.detection.backbone_utils import resnet_fpn_backbone\n\nfrom torch.utils.data import DataLoader, Dataset\n\nfrom matplotlib import pyplot as plt \nplt.rcParams['figure.figsize'] = (10.0, 10.0)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:27.335218Z","iopub.execute_input":"2025-03-20T10:55:27.33543Z","iopub.status.idle":"2025-03-20T10:55:29.54266Z","shell.execute_reply.started":"2025-03-20T10:55:27.335407Z","shell.execute_reply":"2025-03-20T10:55:29.541997Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/costum\"\nDEVICE = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nON_CPU = DEVICE == torch.device('cpu')\nTRAIN_BATCH_SIZE = 4 if ON_CPU else 6\nVALID_BATCH_SIZE = 2 if ON_CPU else 2\nNUM_EPOCHS = 50\nNEW_COLUMNS = ['x', 'y', 'w', 'h']","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.544733Z","iopub.execute_input":"2025-03-20T10:55:29.545068Z","iopub.status.idle":"2025-03-20T10:55:29.610122Z","shell.execute_reply.started":"2025-03-20T10:55:29.545041Z","shell.execute_reply":"2025-03-20T10:55:29.609248Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(os.path.join(DATA_DIR, \"train.csv\"))\ntest_df = pd.read_csv(os.path.join(DATA_DIR, \"valid.csv\"))\n\nUNIQ_TRAIN_IMAGE_IDS = train_df[\"image_id\"].unique()\n\ntrain_df.shape, test_df.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.612353Z","iopub.execute_input":"2025-03-20T10:55:29.612614Z","iopub.status.idle":"2025-03-20T10:55:29.672686Z","shell.execute_reply.started":"2025-03-20T10:55:29.612587Z","shell.execute_reply":"2025-03-20T10:55:29.67202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# expand the bbox coordinates into x, y, w, h\ndef expand_bbox(x):\n    # also convert everything to np.float\n    r = np.array(re.findall(\"([0-9]+[.]?[0-9]*)\", x), dtype=np.float)\n    if len(r) == 0:\n        r = [-1, -1, -1, -1]\n    return r\n\n# initialize new columns with -1\nfor new_column in NEW_COLUMNS:\n    train_df[new_column] = -1\n\ntrain_df[NEW_COLUMNS] = np.stack(train_df['bbox'].apply(lambda x: expand_bbox(x)))\ntrain_df.drop(columns=['bbox'], inplace=True)\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.674048Z","iopub.execute_input":"2025-03-20T10:55:29.674302Z","iopub.status.idle":"2025-03-20T10:55:29.744518Z","shell.execute_reply.started":"2025-03-20T10:55:29.674266Z","shell.execute_reply":"2025-03-20T10:55:29.743524Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_image(image_path):\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    assert image is not None, f\"IMAGE NOT FOUND AT {image_path}\"\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.745783Z","iopub.execute_input":"2025-03-20T10:55:29.746124Z","iopub.status.idle":"2025-03-20T10:55:29.750109Z","shell.execute_reply.started":"2025-03-20T10:55:29.746092Z","shell.execute_reply":"2025-03-20T10:55:29.749471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def draw_bboxes(boxes, image, color=(255,0,0)):\n    for box in boxes:\n        cv2.rectangle(\n            image,\n            (int(box[0]), int(box[1])),\n            (int(box[2]), int(box[3])),\n            color, 3\n        )\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.751299Z","iopub.execute_input":"2025-03-20T10:55:29.751507Z","iopub.status.idle":"2025-03-20T10:55:29.765977Z","shell.execute_reply.started":"2025-03-20T10:55:29.751485Z","shell.execute_reply":"2025-03-20T10:55:29.765324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_random_train_sample():\n    image_id = np.random.choice(UNIQ_TRAIN_IMAGE_IDS)\n    plt.title(image_id)\n    image = load_image(os.path.join(DATA_DIR, \"train\", f\"{image_id}.jpg\"))\n    bboxes = (train_df[train_df[\"image_id\"] == image_id][NEW_COLUMNS]).to_numpy()\n    bboxes[:, 2] = bboxes[:, 0] + bboxes[:, 2]\n    bboxes[:, 3] = bboxes[:, 1] + bboxes[:, 3]\n    plt.imshow(draw_bboxes(bboxes, image))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.767105Z","iopub.execute_input":"2025-03-20T10:55:29.767443Z","iopub.status.idle":"2025-03-20T10:55:29.779506Z","shell.execute_reply.started":"2025-03-20T10:55:29.767405Z","shell.execute_reply":"2025-03-20T10:55:29.77887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_random_train_sample()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:29.780574Z","iopub.execute_input":"2025-03-20T10:55:29.780946Z","iopub.status.idle":"2025-03-20T10:55:30.336858Z","shell.execute_reply.started":"2025-03-20T10:55:29.78091Z","shell.execute_reply":"2025-03-20T10:55:30.335921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class WheatDataset(Dataset):\n    def __init__(self, df, image_dir, transforms=None):\n        super().__init__()\n\n        self.df = df\n        self.image_dir = image_dir\n        self.transforms = transforms\n        self.image_ids = df['image_id'].unique()\n\n    def __len__(self) -> int:\n        return len(self.image_ids)\n\n    def __getitem__(self, idx: int):\n        image_id = self.image_ids[idx]\n        image = load_image(os.path.join(self.image_dir, f\"{image_id}.jpg\")).astype(np.float32)\n        image /= 255.0\n        # change the shape from [h,w,c] to [c,h,w]  \n        image = torch.from_numpy(image).permute(2,0,1)\n\n        records = self.df[self.df['image_id'] == image_id]\n\n        boxes = records[NEW_COLUMNS].values\n        area = boxes[:, 2] * boxes[:, 3]\n        area = torch.as_tensor(area, dtype=torch.float32)\n\n        # change the co-ordinates into expected [x, y, x+w, y+h] format\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        boxes = torch.as_tensor(boxes, dtype=torch.float32)\n\n        # since all the boxes are wheat, it's all 1s\n        labels = torch.ones((boxes.shape[0],), dtype=torch.int64)\n        \n        # consider iscrowd false for all the boxes\n        iscrowd = torch.zeros((boxes.shape[0],), dtype=torch.int64)\n\n        target = {}\n        target[\"boxes\"] = boxes\n        target[\"labels\"] = labels\n        target[\"image_id\"] = torch.tensor([idx])\n        target[\"area\"] = area\n        target[\"iscrowd\"] = iscrowd\n\n        if self.transforms:\n            pass\n\n        return image, target","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.338051Z","iopub.execute_input":"2025-03-20T10:55:30.338311Z","iopub.status.idle":"2025-03-20T10:55:30.349506Z","shell.execute_reply.started":"2025-03-20T10:55:30.338284Z","shell.execute_reply":"2025-03-20T10:55:30.348887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_model():\n    \"\"\"\n    https://stackoverflow.com/questions/58362892/resnet-18-as-backbone-in-faster-r-cnn\n    \"\"\"\n    backbone = resnet_fpn_backbone('resnet101', pretrained=True)\n    model = FasterRCNN(backbone, num_classes=7)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.350522Z","iopub.execute_input":"2025-03-20T10:55:30.350769Z","iopub.status.idle":"2025-03-20T10:55:30.365374Z","shell.execute_reply.started":"2025-03-20T10:55:30.350745Z","shell.execute_reply":"2025-03-20T10:55:30.364775Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 10% into validation\nn_validation = int(0.1* len(UNIQ_TRAIN_IMAGE_IDS))\nvalid_ids = UNIQ_TRAIN_IMAGE_IDS[-n_validation:]\ntrain_ids = UNIQ_TRAIN_IMAGE_IDS[:-n_validation]\n\ndf_in_valid = train_df[train_df['image_id'].isin(valid_ids)]\ndf_in_train = train_df[train_df['image_id'].isin(train_ids)]\n\nprint(\"%i training samples\\n%i validation samples\" % (len(df_in_train[\"image_id\"].unique()), len(df_in_valid[\"image_id\"].unique())))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.366688Z","iopub.execute_input":"2025-03-20T10:55:30.366967Z","iopub.status.idle":"2025-03-20T10:55:30.384502Z","shell.execute_reply.started":"2025-03-20T10:55:30.366939Z","shell.execute_reply":"2025-03-20T10:55:30.383862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = WheatDataset(df_in_train, os.path.join(DATA_DIR, \"train\"))\nvalid_dataset = WheatDataset(df_in_valid, os.path.join(DATA_DIR, \"train\"))\n\n# since our single getitem returns image, targets. [shape of targets is different depending on the number of bounding boxes in the image] ?\ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=TRAIN_BATCH_SIZE,\n    shuffle=True,\n    num_workers=1,\n    collate_fn=collate_fn\n)\n\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size=VALID_BATCH_SIZE,\n    shuffle=False,\n    num_workers=1,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.38549Z","iopub.execute_input":"2025-03-20T10:55:30.385707Z","iopub.status.idle":"2025-03-20T10:55:30.395112Z","shell.execute_reply.started":"2025-03-20T10:55:30.385687Z","shell.execute_reply":"2025-03-20T10:55:30.394366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Averager:\n    def __init__(self):\n        self.current_total = 0.0\n        self.iterations = 0.0\n\n    def send(self, value):\n        self.current_total += value\n        self.iterations += 1\n\n    @property\n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.current_total / self.iterations\n\n    def reset(self):\n        self.current_total = 0.0\n        self.iterations = 0.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.396259Z","iopub.execute_input":"2025-03-20T10:55:30.396538Z","iopub.status.idle":"2025-03-20T10:55:30.405128Z","shell.execute_reply.started":"2025-03-20T10:55:30.396514Z","shell.execute_reply":"2025-03-20T10:55:30.404491Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = get_model()\nmodel.to(DEVICE)\noptimizer = torch.optim.SGD(model.parameters(), lr=0.005, momentum=0.9, weight_decay=0.0005)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:30.407785Z","iopub.execute_input":"2025-03-20T10:55:30.408092Z","iopub.status.idle":"2025-03-20T10:55:40.654971Z","shell.execute_reply.started":"2025-03-20T10:55:30.408058Z","shell.execute_reply":"2025-03-20T10:55:40.654294Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_losses = Averager()\nval_losses = Averager()\nlowest_val_loss = float('inf')\n\n# has to be in train mode for both train and valid coz the outputs are different in two cases\nmodel.train()\nfor epoch in range(NUM_EPOCHS):\n    train_losses.reset()\n    val_losses.reset()\n\n    for batch_index, (images, targets) in enumerate(train_data_loader):\n        # move the images and targets to device\n        images = list(image.to(DEVICE) for image in images)\n        targets = [{k: v.to(DEVICE) for k, v in t.items()} for t in targets]\n\n        loss_dict = model(images,targets)\n        loss = sum(loss for loss in loss_dict.values())\n\n        # track the loss\n        train_losses.send(loss.item())\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        if batch_index % 50 == 0:\n            print(f\"Epoch: {epoch} Batch Index: {batch_index} Loss: {loss.item()}\")\n\n    # evaluate\n    with torch.no_grad():\n        for _, (images, targets) in enumerate(valid_data_loader):\n            # move the images and targets to device\n            images = list(image.to(DEVICE) for image in images)\n            targets = [{k: v.to(DEVICE) for k, v in t.items()} for t in targets]\n\n            val_loss_dict = model(images, targets)\n            val_loss = sum(loss for loss in val_loss_dict.values())\n\n            # track the loss\n            val_losses.send(val_loss.item())\n\n    if val_losses.value < lowest_val_loss:\n        torch.save(model.state_dict(), \"best_model.pth\")\n    else:\n        if lr_scheduler is not None:\n            lr_scheduler.step()\n    \n    # print stats\n    print(f\"Epoch #{epoch} TRAIN LOSS: {train_losses.value} VALIDATION LOSS: {val_losses.value}\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T10:55:40.656223Z","iopub.execute_input":"2025-03-20T10:55:40.656475Z","iopub.status.idle":"2025-03-20T11:10:14.728148Z","shell.execute_reply.started":"2025-03-20T10:55:40.656449Z","shell.execute_reply":"2025-03-20T11:10:14.72664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), 'fasterRCNN_101.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T11:10:14.730847Z","iopub.execute_input":"2025-03-20T11:10:14.731326Z","iopub.status.idle":"2025-03-20T11:10:15.060325Z","shell.execute_reply.started":"2025-03-20T11:10:14.731271Z","shell.execute_reply":"2025-03-20T11:10:15.059642Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torch\nimport cv2\nimport os\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.backbone_utils import resnet_fpn_backbone\nfrom sklearn.metrics import confusion_matrix, precision_score, recall_score, accuracy_score\n\n# --- CONFIGURATION ---\nDEVICE = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\nTEST_CSV_PATH = \"/kaggle/input/costum/test.csv\"\nTEST_IMAGES_DIR = \"/kaggle/input/costum/test\"\nIOU_THRESHOLD = 0.5\nIMAGES_PER_ROW = 4  # Nombre d'images affichées par ligne\n\n# --- CHARGER LES DONNÉES ---\ntest_df = pd.read_csv(TEST_CSV_PATH)\n\n# --- CHARGER LE MODÈLE ---\ndef get_model(num_classes=2):\n    backbone = resnet_fpn_backbone('resnet101', pretrained=True)\n    model = FasterRCNN(backbone, num_classes=num_classes)\n    return model\n\nmodel = get_model(num_classes=7)\nmodel.load_state_dict(torch.load(\"fasterRCNN_101.pth\", map_location=DEVICE))\nmodel.to(DEVICE)\nmodel.eval()\n\n# --- FONCTIONS UTILES ---\ndef preprocess_image(image_path):\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image_tensor = torch.tensor(image, dtype=torch.float32).permute(2, 0, 1) / 255.0\n    return image, image_tensor.unsqueeze(0)\n\ndef draw_boxes(image, boxes, color, label):\n    for box in boxes:\n        x1, y1, x2, y2 = map(int, box)\n        cv2.rectangle(image, (x1, y1), (x2, y2), color, 3)\n        cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)\n    return image\n\ndef get_real_boxes(image_id):\n    records = test_df[test_df[\"image_id\"] == image_id]\n    boxes = records['bbox'].apply(lambda x: np.array(eval(x))).values\n    if len(boxes) > 0:\n        boxes = np.stack(boxes)\n        boxes[:, 2] += boxes[:, 0]\n        boxes[:, 3] += boxes[:, 1]\n    return boxes if len(boxes) > 0 else np.array([])\n\ndef compute_iou(box1, box2):\n    x1, y1, x2, y2 = max(box1[0], box2[0]), max(box1[1], box2[1]), min(box1[2], box2[2]), min(box1[3], box2[3])\n    inter_area = max(0, x2 - x1) * max(0, y2 - y1)\n    box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])\n    box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])\n    union_area = box1_area + box2_area - inter_area\n    return inter_area / union_area if union_area > 0 else 0\n\n# --- CALCUL MATRICE DE CONFUSION ---\nTP, FP, FN, TN = 0, 0, 0, 0\n\ny_true, y_pred = [], []\n\nfor image_name in os.listdir(TEST_IMAGES_DIR):\n    image_id = image_name.split(\".\")[0]\n    image_path = os.path.join(TEST_IMAGES_DIR, image_name)\n    _, image_tensor = preprocess_image(image_path)\n\n    with torch.no_grad():\n        prediction = model(image_tensor.to(DEVICE))\n\n    pred_boxes = prediction[0]['boxes'].cpu().numpy()\n    pred_scores = prediction[0]['scores'].cpu().numpy()\n    pred_boxes = pred_boxes[pred_scores > 0.5]\n\n    real_boxes = get_real_boxes(image_id)\n\n    matched_preds = set()\n    matched_reals = set()\n    \n    for i, rb in enumerate(real_boxes):\n        detected = False\n        for j, pb in enumerate(pred_boxes):\n            if compute_iou(pb, rb) > IOU_THRESHOLD:\n                detected = True\n                matched_preds.add(j)\n                matched_reals.add(i)\n                break\n        if detected:\n            TP += 1\n        else:\n            FN += 1\n\n    FP += len(pred_boxes) - len(matched_preds)\n    TN += 0  # Pas de vrai négatif significatif dans ce cas\n\ny_true = [1] * TP + [0] * FN\ny_pred = [1] * TP + [1] * FP\n\n# --- CALCULER LES MÉTRIQUES ---\nprecision = TP / (TP + FP) if (TP + FP) > 0 else 0\nrecall = TP / (TP + FN) if (TP + FN) > 0 else 0\naccuracy = TP / (TP + FP + FN) if (TP + FP + FN) > 0 else 0\n\nprint(f\"Précision: {precision:.2f}\")\nprint(f\"Recall: {recall:.2f}\")\nprint(f\"Accuracy: {accuracy:.2f}\")\nprint(f\"TP: {TP}, FN: {FN}, FP: {FP}, TN: {TN}\")\n\n# --- AFFICHAGE MATRICE DE CONFUSION ---\nconf_matrix = [[TN, FP], [FN, TP]]\nplt.figure(figsize=(6, 5))\nsns.heatmap(conf_matrix, annot=True, fmt=\"d\", cmap=\"Blues\",\n            xticklabels=[\"Fond (0)\", \"Objet (1)\"],\n            yticklabels=[\"Fond (0)\", \"Objet (1)\"])\nplt.xlabel(\"Prédictions\")\nplt.ylabel(\"Réel\")\nplt.title(\"Matrice de Confusion (IoU > 0.5)\")\nplt.show()\n\n# --- AFFICHER LES IMAGES PAR LIGNES DE 4 ---\ntest_images = os.listdir(TEST_IMAGES_DIR)\nnum_images = len(test_images)\nrows = (num_images + IMAGES_PER_ROW - 1) // IMAGES_PER_ROW  # Nombre de lignes\n\nfig, axes = plt.subplots(rows, IMAGES_PER_ROW, figsize=(20, 5 * rows))\naxes = axes.flatten()\n\nfor idx, image_name in enumerate(test_images):\n    image_id = image_name.split(\".\")[0]\n    image_path = os.path.join(TEST_IMAGES_DIR, image_name)\n    image, image_tensor = preprocess_image(image_path)\n\n    with torch.no_grad():\n        prediction = model(image_tensor.to(DEVICE))\n\n    pred_boxes = prediction[0]['boxes'].cpu().numpy()\n    pred_scores = prediction[0]['scores'].cpu().numpy()\n    pred_boxes = pred_boxes[pred_scores > 0.5]\n\n    real_boxes = get_real_boxes(image_id)\n\n    image_with_boxes = draw_boxes(image.copy(), real_boxes, (0, 0, 255), \"GT\")\n    image_with_boxes = draw_boxes(image_with_boxes, pred_boxes, (0, 255, 0), \"Pred\")\n\n    axes[idx].imshow(image_with_boxes)\n    axes[idx].axis(\"off\")\n    axes[idx].set_title(image_name)\n\nfor i in range(idx + 1, len(axes)):\n    axes[i].axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T14:55:45.796646Z","iopub.execute_input":"2025-03-20T14:55:45.796955Z","iopub.status.idle":"2025-03-20T14:57:54.625675Z","shell.execute_reply.started":"2025-03-20T14:55:45.796923Z","shell.execute_reply":"2025-03-20T14:57:54.624817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torch\nimport cv2\nimport os\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.backbone_utils import resnet_fpn_backbone\nfrom sklearn.metrics import confusion_matrix, precision_score, recall_score, accuracy_score\n\n# --- CONFIGURATION ---\nDEVICE = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\nTEST_CSV_PATH = \"/kaggle/input/costum/test.csv\"\nTEST_IMAGES_DIR = \"/kaggle/input/costum/test\"\nIOU_THRESHOLD = 0.5\nBATCH_SIZE = 10  # Nombre d'images affichées par lot\n\n# --- CHARGER LES DONNÉES ---\ntest_df = pd.read_csv(TEST_CSV_PATH)\n\n# --- CHARGER LE MODÈLE ---\ndef get_model(num_classes=2):\n    backbone = resnet_fpn_backbone('resnet101', pretrained=True)\n    model = FasterRCNN(backbone, num_classes=num_classes)\n    return model\n\nmodel = get_model(num_classes=7)\nmodel.load_state_dict(torch.load(\"fasterRCNN_101.pth\", map_location=DEVICE))\nmodel.to(DEVICE)\nmodel.eval()\n\n# --- FONCTIONS UTILES ---\ndef preprocess_image(image_path):\n    \"\"\"Charge et prétraite une image.\"\"\"\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image_tensor = torch.tensor(image, dtype=torch.float32).permute(2, 0, 1) / 255.0\n    return image, image_tensor.unsqueeze(0)  # Image originale + Tensor\n\ndef draw_boxes(image, boxes, color, label):\n    \"\"\"Dessine des boîtes sur une image.\"\"\"\n    for box in boxes:\n        x1, y1, x2, y2 = map(int, box)\n        cv2.rectangle(image, (x1, y1), (x2, y2), color, 3)\n        cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)\n    return image\n\ndef get_real_boxes(image_id):\n    \"\"\"Récupère les boîtes réelles depuis test_df.\"\"\"\n    records = test_df[test_df[\"image_id\"] == image_id]\n    boxes = records['bbox'].apply(lambda x: np.array(eval(x))).values\n    if len(boxes) > 0:\n        boxes = np.stack(boxes)\n        boxes[:, 2] += boxes[:, 0]  # Convertir largeur en x2\n        boxes[:, 3] += boxes[:, 1]  # Convertir hauteur en y2\n    return boxes if len(boxes) > 0 else np.array([])\n\ndef compute_iou(box1, box2):\n    \"\"\"Calcule l'IoU entre deux boîtes.\"\"\"\n    x1, y1, x2, y2 = max(box1[0], box2[0]), max(box1[1], box2[1]), min(box1[2], box2[2]), min(box1[3], box2[3])\n    inter_area = max(0, x2 - x1) * max(0, y2 - y1)\n    box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])\n    box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])\n    union_area = box1_area + box2_area - inter_area\n    return inter_area / union_area if union_area > 0 else 0\n\n\n\n# --- AFFICHER LES IMAGES PAR LOTS ---\ntest_images = os.listdir(TEST_IMAGES_DIR)\nfor i in range(0, len(test_images), BATCH_SIZE):\n    batch_images = test_images[i:i + BATCH_SIZE]\n    fig, axes = plt.subplots(len(batch_images), 1, figsize=(40, 20 * len(batch_images)))\n    if len(batch_images) == 1:\n        axes = [axes]\n    \n    for j, image_name in enumerate(batch_images):\n        image_id = image_name.split(\".\")[0]\n        image_path = os.path.join(TEST_IMAGES_DIR, image_name)\n        image, image_tensor = preprocess_image(image_path)\n\n        with torch.no_grad():\n            prediction = model(image_tensor.to(DEVICE))\n\n        pred_boxes = prediction[0]['boxes'].cpu().numpy()\n        pred_scores = prediction[0]['scores'].cpu().numpy()\n        pred_boxes = pred_boxes[pred_scores > 0.5]  # Filtrer par seuil\n\n        real_boxes = get_real_boxes(image_id)\n\n        image_with_boxes = draw_boxes(image.copy(), real_boxes, (0, 255, 0), \"GT\")  # Vert\n        image_with_boxes = draw_boxes(image_with_boxes, pred_boxes, (255, 0, 0), \"Pred\")  # Bleu\n\n        axes[j].imshow(image_with_boxes)\n        axes[j].axis(\"off\")\n        axes[j].set_title(image_name)\n    \n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-20T15:04:42.642575Z","iopub.execute_input":"2025-03-20T15:04:42.642903Z","iopub.status.idle":"2025-03-20T15:05:17.651243Z","shell.execute_reply.started":"2025-03-20T15:04:42.642876Z","shell.execute_reply":"2025-03-20T15:05:17.648709Z"}},"outputs":[],"execution_count":null}]}