{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":1251699,"sourceType":"datasetVersion","datasetId":719180},{"sourceId":6846343,"sourceType":"datasetVersion","datasetId":3935909}],"dockerImageVersionId":30015,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install torch torchvision\n\nimport torch\nimport torchvision\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\n\n# Check if GPU is available\nif torch.cuda.is_available():\n    device = torch.device('cuda')\n    print(\"CUDA is available. Using GPU.\")\nelse:\n    device = torch.device('cpu')\n    print(\"CUDA is not available. Using CPU.\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport random\nfrom PIL import Image , ImageDraw \nfrom collections import Counter\nfrom sklearn.model_selection import train_test_split\nimport torch\nimport torchvision\nfrom torchvision import transforms as T\nimport random\nfrom PIL import Image\nimport os\nfrom sklearn.model_selection import train_test_split\n# from torchvision.models.detection.faster_rcnn import FastRCNNPredictor\n\n\nfrom glob import glob\nfrom PIL import Image, ImageDraw, ImageEnhance\nfrom tqdm.notebook import tqdm\nimport pickle\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport tensorflow as tf\n\n\nfrom bokeh.plotting import figure\nfrom bokeh.io import output_notebook, show, output_file\nfrom bokeh.models import ColumnDataSource, HoverTool, Panel\n\nimport albumentations as albu \nfrom collections import namedtuple\nfrom albumentations.pytorch.transforms import ToTensorV2\n\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# load the dataset:","metadata":{}},{"cell_type":"code","source":"train_data = pd.read_csv('/kaggle/input/global-wheat-detection/train.csv')\nprint(\"Training Data Loaded\")\nprint(f\"Number of training images: {train_data['image_id'].nunique()}\")\nprint(f\"Number of bounding boxes: {len(train_data)}\")\n\n# Display a sample of the data\nprint(train_data.head())","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def group_boxes(group):\n    boundaries = group['bbox'].str.split(',', expand=True)\n    boundaries[0] = boundaries[0].str.slice(start=1)\n    boundaries[3] = boundaries[3].str.slice(stop=-1)\n\n    return boundaries.values.astype(float)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_data.groupby('image_id').apply(group_boxes)\nprint(train_df)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_image_ids = np.unique(train_df.index.values)[0:200]\nval_image_ids = np.unique(train_df.index.values)[3363:3373]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_image(image_id,address):\n    # image = Image.open(address+'train/' + image_id + \".jpg\")\n    image_path = os.path.join(image_folder, f\"{image_id}.jpg\")\n    image = Image.open(image_path)\n    image = image.resize((256, 256))\n\n    return np.asarray(image)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_pixels = {}\ntrain_labels = {}\nval_pixels = {}\nval_labels = {}\n\naddress = '/kaggle/input/global-wheat-detection/train'\nfor image_id in tqdm(train_image_ids):\n    train_pixels[image_id] = load_image(image_id,address)\n    train_labels[image_id] = train_df[image_id].copy()/4\n\n\nfor image_id in tqdm(val_image_ids):\n    val_pixels[image_id] = load_image(image_id,address)\n    val_labels[image_id] = train_df[image_id].copy()/4","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_images(train_pixels, bboxes,title,rows=3,colm=3,source='train',size=10):\n    fig, axs = plt.subplots(rows, colm, figsize=(10,10))\n    enum = list(enumerate(train_pixels.keys()))\n    le = len(train_pixels)\n    rands = np.random.choice(le,size=size,replace=False)\n    #for i in range(10):\n    #  rands.append(enum(np.random.randint.random(0,le)))\n    #rand_imgs = enumerate(train_pixels.keys())\n    i = 0\n    for row in range(rows):\n        for col in range(colm):\n            #idx = np.random.randint(len(train_pixels), size=1)[0]\n            #img_id = df.iloc[idx].image_id\n            name = enum[rands[i]][1]\n            if rows == 1:\n              axs[col].imshow(train_pixels[name])\n            else:\n              axs[row,col].imshow(train_pixels[name])\n            boxes = bboxes[name]\n            for box in boxes:\n              rect = patches.Rectangle((box[0],box[1]),box[2],box[3],linewidth=1,edgecolor='r',facecolor='none')\n              if rows == 1:\n                axs[col].add_patch(rect)\n              else:\n                axs[row,col].add_patch(rect)\n            i+=1\n\n    plt.suptitle(title)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_images(train_pixels, train_labels,'example of some data',rows=3,colm=3,source='train',size=10)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_hist(data,column, colors=[\"#94c8d8\", \"#ea5e51\"], bins=30, title=''):\n  hist, edges = np.histogram(data,bins=30)\n\n  hist_df = pd.DataFrame({column: hist,\n                             \"left\": edges[:-1],\n                             \"right\": edges[1:]})\n  hist_df[\"interval\"] = [\"%d to %d\" % (left, right) for left,\n                           right in zip(hist_df[\"left\"], hist_df[\"right\"])]\n\n  src = ColumnDataSource(hist_df)\n  plot = figure(height = 400, width = 600,\n          title = title,\n          x_axis_label = column,\n          y_axis_label = \"Count\")\n  plot.quad(bottom = 0, top = column,left = \"left\",\n        right = \"right\", source = src, fill_color = colors[0],\n        line_color = \"#35838d\", fill_alpha = 0.7,\n        hover_fill_alpha = 0.7, hover_fill_color = colors[1])\n  hover = HoverTool(tooltips = [('Interval', '@interval'),\n                              ('Count', str(\"@\" + column))])\n  plot.add_tools(hover)\n\n  output_notebook()\n  show(plot)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"count_labels = []\n\nfor data in train_labels.keys():\n  count_labels.append(len(train_labels[data]))\n#print(count_labels)\n#plt.hist(np.array(count_labels),bins=30,color='skyblue', edgecolor='black')\nshow_hist(np.array(count_labels),'count',title = \"Number of wheat spikes per image\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"boxes_size = []\nbig_images = []\nsmall_images = []\nfor index,data in enumerate(train_labels.keys()):\n  for box in train_labels[data]:\n    area = box[2]*box[3]\n    boxes_size.append(area)\n    if area > 4500: # need to changee\n      big_images.append(data)\n    elif area < 10 and area!=0:\n      small_images.append(data)\nshow_hist(np.array(boxes_size),'Area',bins=100,title = \"Area of a single bounding box\")\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"small_images = list(set(small_images))\nprint(f'number of images with small boxes: {len(small_images)}')\nf_subset = {key: train_pixels[key] for i,key in enumerate(small_images)}\nf_subset2 = {key: train_labels[key] for i,key in enumerate(small_images)}\nshow_images(f_subset,f_subset2,'small boxes',rows=1,colm=3,source='train',size=3)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"big_images = list(set(big_images))\nprint(f'number of images with big boxes: {len(big_images)}')\nf_subset = {key: train_pixels[key] for i,key in enumerate(big_images)}\nf_subset2 = {key: train_labels[key] for i,key in enumerate(big_images)}\nshow_images(f_subset,f_subset2,'big boxes',rows=1,colm=3,source='train',size=3)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After we saw that some of the boxes are very tiny or very big, we will dump them, they woldnt help us for the training of the model","metadata":{}},{"cell_type":"code","source":"def clean_labels(train_image_ids, train_labels):\n    good_labels = {}\n\n    for i, image_id in enumerate(train_image_ids):\n        good_labels[image_id] = []\n\n        for j, label in enumerate(train_labels[image_id]):\n\n            # remove huge bbox\n            if label[2] * label[3] > 8000 and i not in [1079, 1371, 2020]:\n                continue\n\n            # remove tiny bbox\n            elif label[2] < 5 or label[3] < 5:\n                continue\n\n            else:\n                good_labels[image_id].append(\n                    train_labels[image_id][j]\n                )\n        good_labels[image_id] = np.array(good_labels[image_id])\n\n    return good_labels\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels = clean_labels(train_image_ids, train_labels)\nshow_images(train_pixels, train_labels,'example of some data',rows=3,colm=3,source='train',size=10)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DataGenerator(tf.keras.utils.Sequence):\n\n    def __init__(self, image_ids, image_pixels, labels=None, batch_size=1, shuffle=False, augment=False):\n        self.image_ids = image_ids\n        self.image_pixels = image_pixels\n        self.labels = labels\n        self.batch_size = batch_size\n        self.shuffle = shuffle\n        self.augment = augment\n        self.on_epoch_end()\n\n        self.image_grid = self.form_image_grid()\n\n    def form_image_grid(self):\n          image_grid = np.zeros((32, 32, 4))\n\n          # x, y, width, height\n          cell = [0, 0, 256 / 32, 256 / 32]\n\n          for i in range(0, 32):\n              for j in range(0, 32):\n                  image_grid[i,j] = cell\n\n                  cell[0] = cell[0] + cell[2]\n\n              cell[0] = 0\n              cell[1] = cell[1] + cell[3]\n\n          return image_grid\n\n\n    def __len__(self):\n          return int(np.floor(len(self.image_ids) / self.batch_size))\n\n\n    def on_epoch_end(self):\n            self.indexes = np.arange(len(self.image_ids))\n\n            if self.shuffle == True:\n                np.random.shuffle(self.indexes)\n\n    def __getitem__(self, index):\n      indexes = self.indexes[index * self.batch_size:(index + 1) * self.batch_size]\n\n      batch_ids = [self.image_ids[i] for i in indexes]\n\n      X, y = self.__data_generation(batch_ids)\n\n      return X, y\n\n\n    def __data_generation(self, batch_ids):\n        X, y = [], []\n\n        # Generate data\n        for i, image_id in enumerate(batch_ids):\n            pixels = self.image_pixels[image_id]\n            bboxes = self.labels[image_id]\n\n            if self.augment:\n                pixels, bboxes = self.augment_image(pixels, bboxes)\n            else:\n                pixels = self.contrast_image(pixels)\n                bboxes = self.form_label_grid(bboxes)\n\n            X.append(pixels)\n            y.append(bboxes)\n\n        return np.array(X), np.array(y)\n\n    def augment_image(self, pixels, bboxes):\n        bbox_labels = np.ones(len(bboxes))\n\n        aug_result = self.train_augmentations(image=pixels, bboxes=bboxes, labels=bbox_labels)\n\n        bboxes = self.form_label_grid(aug_result['bboxes'])\n\n        return np.array(aug_result['image']) / 255, bboxes\n\n\n    def contrast_image(self, pixels):\n        aug_result = self.val_augmentations(image=pixels)\n        return np.array(aug_result['image']) / 255\n\n    train_augmentations = albu.Compose([\n        albu.RandomSizedCrop(\n            min_max_height=(200, 200),\n            height=256,\n            width=256,\n            p=0.8\n        ),\n        albu.OneOf([\n            albu.Flip(),\n            albu.RandomRotate90(),\n        ], p=1),\n        albu.OneOf([\n            albu.HueSaturationValue(),\n            albu.RandomBrightnessContrast()\n        ], p=1),albu.OneOf([\n            albu.GaussNoise(),\n            albu.GlassBlur(),\n            albu.ISONoise(),\n            albu.MultiplicativeNoise(),\n        ], p=0.5),\n        albu.CLAHE(p=1),\n        albu.ToGray(p=1),\n    ],\n    bbox_params={'format': 'coco', 'label_fields': ['labels']})\n\n    val_augmentations = albu.Compose([\n        albu.CLAHE(p=1),\n        albu.ToGray(p=1),\n    ])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_generator = DataGenerator(\n    train_image_ids,\n    train_pixels,\n    train_labels,\n    batch_size=6,\n    shuffle=True,\n    augment=True\n)\n\nval_generator = DataGenerator(\n    val_image_ids,\n    val_pixels,\n    val_labels,\n    batch_size=10,\n    shuffle=False,\n    augment=False\n)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#  Exploratory Data Analysis (understand the data):","metadata":{}},{"cell_type":"markdown","source":"**show some photos to see that the data has loaded succesfully:**","metadata":{}},{"cell_type":"code","source":"def display_random_images(df, image_folder, num_images=5):\n    # Get a random sample of image IDs\n    random_image_ids = random.sample(list(df['image_id'].unique()), num_images)\n\n    for image_id in random_image_ids:\n        # Load the image\n        image_path = os.path.join(image_folder, f\"{image_id}.jpg\")\n        image = Image.open(image_path)\n\n        # Get the bounding boxes for this image\n        image_data = df[df['image_id'] == image_id]\n        bboxes = image_data['bbox'].tolist()\n\n        # Plot the image\n        fig, ax = plt.subplots(1, figsize=(12, 12))\n        ax.imshow(image)\n        \n        # Add the bounding boxes\n        for bbox in bboxes:\n            bbox = [float(x) for x in bbox.strip('[]').split(',')]\n            rect = patches.Rectangle((bbox[0], bbox[1]), bbox[2], bbox[3], linewidth=2, edgecolor='r', facecolor='none')\n            ax.add_patch(rect)\n\n        plt.title(f\"Image ID: {image_id}\")\n        plt.show()\n\n# Load images from a folder called 'train' in the kaggle directory\nimage_folder = '/kaggle/input/global-wheat-detection/train'\ndisplay_random_images(train_df, image_folder, num_images=5)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**see the bounding boxes distribution in the photos:**","metadata":{}},{"cell_type":"code","source":"# Number of unique images\nprint(f\"Number of unique images: {train_df['image_id'].nunique()}\")\n\n# Number of bounding boxes per image\nbbox_counts = train_df.groupby('image_id').size()\nprint(f\"Average number of bounding boxes per image: {bbox_counts.mean()}\")\n\n# Plot distribution of bounding boxes per image\nplt.figure(figsize=(10,6))\nbbox_counts.plot(kind='hist', bins=30, title='Distribution of Bounding Boxes per Image')\nplt.xlabel('Number of Bounding Boxes')\nplt.ylabel('Frequency')\nplt.show()\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preprocessing:","metadata":{}},{"cell_type":"code","source":"# Keep a copy of the original DataFrame\noriginal_train_df = train_df.copy()\n\n# Function to split the data correctly\ndef split_data(df):\n    unique_image_ids = df['image_id'].unique()\n    \n    # Split the data into training and validation sets\n    train_ids, valid_ids = train_test_split(unique_image_ids, test_size=0.2, random_state=42)\n\n    train_df = df[df['image_id'].isin(train_ids)]\n    valid_df = df[df['image_id'].isin(valid_ids)]\n\n    return train_df, valid_df, unique_image_ids, train_ids, valid_ids\n\n# Reset the DataFrame to the original before each split\ntrain_df, valid_df, unique_image_ids, train_ids, valid_ids = split_data(original_train_df.copy())\n\n# Print the results of the split\nprint(f\"Total unique image IDs: {len(unique_image_ids)}\")\nprint(f\"Training image IDs: {len(train_ids)}\")\nprint(f\"Validation image IDs: {len(valid_ids)}\")\nprint(f\"Training set size: {len(train_df)}\")\nprint(f\"Validation set size: {len(valid_df)}\")\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Selection and Implementation:","metadata":{}},{"cell_type":"code","source":"from torchvision.models.detection import fasterrcnn_resnet50_fpn\n\n# Load a pre-trained Faster R-CNN model\nmodel = fasterrcnn_resnet50_fpn(pretrained=True)\n\n# Replace the classifier with a new one for wheat detection (2 classes: wheat, background)\nnum_classes = 2  # Wheat and background\nin_features = model.roi_heads.box_predictor.cls_score.in_features\nmodel.roi_heads.box_predictor = torchvision.models.detection.faster_rcnn.FastRCNNPredictor(in_features, num_classes)\n\nmodel = model.to(device)\n\nprint(\"Model initialized and ready for training.\")\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training:","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\nfrom torch.utils.data import DataLoader\n\n# Define optimizer and loss function\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n\n# Sample training loop\nnum_epochs = 10\nfor epoch in range(num_epochs):\n    print(f\"Starting epoch {epoch+1}/{num_epochs}\")\n    \n    # Set the model to training mode\n    model.train()\n    running_loss = 0.0\n    \n    for i, (images, targets) in enumerate(train_loader):\n        images = [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        # Zero the gradients\n        optimizer.zero_grad()\n\n        # Forward pass\n        loss_dict = model(images, targets)\n        losses = sum(loss for loss in loss_dict.values())\n        \n        # Backward pass\n        losses.backward()\n        optimizer.step()\n\n        running_loss += losses.item()\n        \n        # Debug statement for progress monitoring\n        if i % 10 == 0:\n            print(f\"Batch {i}, Loss: {losses.item()}\")\n\n    print(f\"Epoch {epoch+1} finished, Average Loss: {running_loss/len(train_loader)}\")\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save and Test the Model","metadata":{}},{"cell_type":"code","source":"# Save the trained model\ntorch.save(model.state_dict(), 'fasterrcnn_wheat_detection.pth')\nprint(\"Model saved successfully.\")\n\n# Test the model on a few validation images\nmodel.eval()\nwith torch.no_grad():\n    for i, (images, targets) in enumerate(valid_loader):\n        images = [image.to(device) for image in images]\n        outputs = model(images)\n        \n        # Visualize results or print debug information\n        print(f\"Output for image {i}: {outputs}\")\n        break\n","metadata":{},"execution_count":null,"outputs":[]}]}