{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1 align=center> Global Wheat Detection with Keras RetinaNet </h1>\n\n### This Notebook is for Training Purpose of Keras RetinaNet.\n### RetinaNet is very slow as compared to F-RCNN so I've kept epochs and steps per epoch small for fast commiting purpose.\n### I will make another notebook for inference Shortly.\n\n### Credits for the EDA goes to [THIS Notebook](https://www.kaggle.com/devvindan/wheat-detection-eda)\n\n<h3 align=center style=color:red>Upvote If you find this kernel interesting</h3>","execution_count":null},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"scrolled":false},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport cv2\nfrom PIL import Image, ImageDraw\nfrom ast import literal_eval\nimport matplotlib.pyplot as plt\nimport urllib\nfrom tqdm.notebook import tqdm","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Installing Keras-RetinaNet ","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!git clone https://github.com/fizyr/keras-retinanet.git","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%cd keras-retinanet/\n\n!pip install .","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!python setup.py build_ext --inplace","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nfrom keras_retinanet import models\nfrom keras_retinanet.utils.image import read_image_bgr, preprocess_image, resize_image\nfrom keras_retinanet.utils.visualization import draw_box, draw_caption\nfrom keras_retinanet.utils.colors import label_color","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Let's look at the data","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"root = \"/kaggle/input/global-wheat-detection/\"\ntrain_img = root+\"train\"\ntest_img = root+\"test\"\ntrain_csv = root+\"train.csv\"\nsample_submission = root+\"sample_submission.csv\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train = pd.read_csv(train_csv)\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":" Single Image has multiple bbox","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"print(f\"Total Bboxes: {train.shape[0]}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# EDA","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Let's Check the Dimensions of images","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train['width'].unique() == train['height'].unique() == [1024]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def get_bbox_area(bbox):\n    bbox = literal_eval(bbox)\n    return bbox[2] * bbox[3]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train['bbox_area'] = train['bbox'].apply(get_bbox_area)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train['bbox_area'].value_counts().hist(bins=10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"unique_images = train['image_id'].unique()\nlen(unique_images)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"num_total = len(os.listdir(train_img))\nnum_annotated = len(unique_images)\n\nprint(f\"There are {num_annotated} annotated images and {num_total - num_annotated} images without annotations.\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Sources of Data","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"sources = train['source'].unique()\nprint(f\"There are {len(sources)} sources of data: {sources}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train['source'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's look at how many bounding boxes do we have for each image:","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"plt.hist(train['image_id'].value_counts(), bins=10)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Max number of bounding boxes is 116, whereas min (annotated) number is 1 ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Visualizing images","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def show_images(images, num = 5):\n    \n    images_to_show = np.random.choice(images, num)\n\n    for image_id in images_to_show:\n\n        image_path = os.path.join(train_img, image_id + \".jpg\")\n        image = Image.open(image_path)\n\n        # get all bboxes for given image in [xmin, ymin, width, height]\n        bboxes = [literal_eval(box) for box in train[train['image_id'] == image_id]['bbox']]\n\n        # visualize them\n        draw = ImageDraw.Draw(image)\n        for bbox in bboxes:    \n            draw.rectangle([bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]], width=3)\n\n        plt.figure(figsize = (15,15))\n        plt.imshow(image)\n        plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"show_images(unique_images)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"What can we tell from visualizations:\n\n* there are plenty of overlappind bounding boxes\n* all photos seem to be taken vertically \n* all plants are can be rotated differently, there is no single orientation. this means that different flip and roration augmentations should probably help\n* colors of wheet heads are quite different and seem to depend a little bit on the source\n* wheet heads themselves are seen from very different angles of view relevant to the observer","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Preprocessing Data for Input to RetinaNet","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"bboxs=[ bbox[1:-1].split(', ') for bbox in train['bbox']]\nbboxs=[ f\"{int(float(bbox[0]))},{int(float(bbox[1]))},{int(float(bbox[0]))+int(float(bbox[2]))},{int(float(bbox[1])) + int(float(bbox[3]))},wheat\" for bbox in bboxs]\ntrain['bbox_']=bboxs\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train_df=train[['image_id','bbox_']]\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df=train_df.sample(frac=1).reset_index(drop=True)\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Preparing Files to be given for training\n\n### Annotation file contains all the path of all images and their corresponding bounding boxes\n### Class file contains the number of classes but in our case it is just 1 (Wheat)","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"with open(\"annotations.csv\",\"w\") as file:\n    for idx in range(len(train_df)):\n        file.write(train_img+\"/\"+train_df.iloc[idx,0]+\".jpg\"+\",\"+train_df.iloc[idx,1]+\"\\n\")\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"with open(\"classes.csv\",\"w\") as file:\n    file.write(\"wheat,0\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Downloading the pretrained model","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"PRETRAINED_MODEL = './snapshots/_pretrained_model.h5'\n\nURL_MODEL = 'https://github.com/fizyr/keras-retinanet/releases/download/0.5.1/resnet50_coco_best_v2.1.0.h5'\nurllib.request.urlretrieve(URL_MODEL, PRETRAINED_MODEL)\n\nprint('Downloaded pretrained model to ' + PRETRAINED_MODEL)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Model Parameters","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 1\nBATCH_SIZE=8\nSTEPS = 100 #len(train_df)//BATCH_SIZE #Keeping it small for faster commit\nLR=1e-3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training Model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!keras_retinanet/bin/train.py --random-transform --weights {PRETRAINED_MODEL} --lr {LR} --batch-size {BATCH_SIZE} --steps {STEPS} --epochs {EPOCHS} --no-resize csv annotations.csv classes.csv","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading the trained model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls snapshots","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_path = os.path.join('snapshots', sorted(os.listdir('snapshots'), reverse=True)[0])\n\nmodel = models.load_model(model_path, backbone_name='resnet50')\nmodel = models.convert_model(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Predictions","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"li=os.listdir(test_img)\nli[:5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict(image):\n    image = preprocess_image(image.copy())\n    #image, scale = resize_image(image)\n\n    boxes, scores, labels = model.predict_on_batch(\n    np.expand_dims(image, axis=0)\n  )\n\n    #boxes /= scale\n\n    return boxes, scores, labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"THRES_SCORE = 0.5\n\ndef draw_detections(image, boxes, scores, labels):\n    for box, score, label in zip(boxes[0], scores[0], labels[0]):\n        if score < THRES_SCORE:\n            break\n\n        color = label_color(label)\n\n        b = box.astype(int)\n        draw_box(image, b, color=color)\n\n        caption = \"{:.3f}\".format(score)\n        draw_caption(image, b, caption)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def show_detected_objects(image_name):\n    img_path = test_img+'/'+image_name\n  \n    image = read_image_bgr(img_path)\n\n    boxes, scores, labels = predict(image)\n    print(boxes[0,0].shape)\n    draw = image.copy()\n    draw = cv2.cvtColor(draw, cv2.COLOR_BGR2RGB)\n\n    draw_detections(draw, boxes, scores, labels)\n    plt.figure(figsize=(15,10))\n    plt.axis('off')\n    plt.imshow(draw)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for img in li:\n    show_detected_objects(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds=[]\nimgid=[]\nfor img in tqdm(li,total=len(li)):\n    img_path = test_img+'/'+img\n    image = read_image_bgr(img_path)\n    boxes, scores, labels = predict(image)\n    boxes=boxes[0]\n    scores=scores[0]\n    for idx in range(boxes.shape[0]):\n        if scores[idx]>THRES_SCORE:\n            box,score=boxes[idx],scores[idx]\n            imgid.append(img.split(\".\")[0])\n            preds.append(\"{} {} {} {} {}\".format(score, int(box[0]), int(box[1]), int(box[2]-box[0]), int(box[3]-box[1])))\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub={\"image_id\":imgid, \"PredictionString\":preds}\nsub=pd.DataFrame(sub)\nsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_=sub.groupby([\"image_id\"])['PredictionString'].apply(lambda x: ' '.join(x)).reset_index()\nsub_","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samsub=pd.read_csv(\"/kaggle/input/global-wheat-detection/sample_submission.csv\")\nsamsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for idx,imgid in enumerate(samsub['image_id']):\n    samsub.iloc[idx,1]=sub_[sub_['image_id']==imgid].values[0,1]\n    \nsamsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samsub.to_csv('/kaggle/working/submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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"}},"nbformat":4,"nbformat_minor":4}