{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import numpy as np \nimport pandas as pd\nimport ast\n\n# Read wheat detection dataset\n\ndata = pd.read_csv('/kaggle/input/global-wheat-detection/train.csv')\ndata.head()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# Separate the bbox column\ndf = data.bbox.str.rstrip(']')\ndf = df.str.lstrip('[') \ndf = df.str.replace(\",\",'')\ndata[[\"x\", \"y\",\"h\", \"w\"]] = pd.DataFrame([x.split(' ') for x in df.tolist()], index= data.index)\n# Add xmin and xmax\nxmin = data.iloc[:, 5].astype('float32')\nymin = data.iloc[:, 6].astype('float32')\ndata['xmax'] = xmin + data.iloc[:, 7].astype('float32')\ndata['ymax'] = ymin + data.iloc[:, 8].astype('float32')\n# Add class name\ndata['class_name'] = 'wheat'\n# Add the extension to the images\ndata['image_id'] = data['image_id'] + '.jpg'\ndata.head()\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nclasses = data['class_name'].unique().tolist()\n\ndata['class_int'] = data['class_name'].map(lambda x: classes.index(x))\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Prepare the dataset according to GluonCV requirements**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport time\nfrom matplotlib import pyplot as plt\nimport numpy as np\nimport mxnet as mx\nfrom mxnet import autograd, gluon\nimport gluoncv as gcv\nfrom gluoncv.utils import download, viz\nimport cv2\n\nclass wheat_data(gluon.data.Dataset):\n    def __init__(self, csv_file,img_dir):\n        self.data_info = csv_file \n        self.image_arr = self.data_info['image_id'].unique() \n        self.bbox_arr = self.data_info.iloc[:, 3]\n        self.img_dir = img_dir\n        \n    def __getitem__(self, idx):\n        image_arr = self.image_arr[idx]\n        image = mx.image.imread(f'{self.img_dir}/{image_arr}')\n        img_path = f'{self.img_dir}/{image_arr}'\n        num_bbox = len(self.bbox_arr)\n            \n        data = self.data_info[self.data_info['image_id'] == image_arr]\n        boxes = data[['x','y','xmax','ymax']].values\n        \n        image_id = data[['class_int']].values\n        img_shape = image.shape\n        \n        return img_path, img_shape,np.array(boxes), np.array(image_id),idx \n    \n\n    def __len__(self):\n        return len(self.image_arr)\n        \n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = wheat_data(data,'/kaggle/input/global-wheat-detection/train/')\nprint(dataset[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def write_line(img_path, im_shape, boxes, ids, idx):\n    h, w, c = im_shape\n    # for header, we use minimal length 2, plus width and height\n    # with A: 4, B: 5, C: width, D: height\n    A = 4\n    B = 5\n    C = w\n    D = h\n    # concat id and bboxes\n    labels = np.hstack((ids.reshape(-1, 1), boxes)).astype('float')\n    # normalized bboxes (recommanded)\n    labels[:, (1, 3)] /= float(w)\n    labels[:, (2, 4)] /= float(h)\n    # flatten\n    labels = labels.flatten().tolist()\n    str_idx = [str(idx)]\n    str_header = [str(x) for x in [A, B, C, D]]\n    str_labels = [str(x) for x in labels]\n    str_path = [img_path]\n    line = '\\t'.join(str_idx + str_header + str_labels + str_path) + '\\n'\n    return line","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with open('/kaggle/working/wheat_train.lst', 'w') as fw:\n    for img_path, im_shape, all_boxes, all_ids,i in dataset:\n        line = write_line(img_path, im_shape, all_boxes, all_ids, i)\n        #print(line)\n        fw.write(line)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from gluoncv.data import LstDetection\nfrom gluoncv.utils import download, viz\n\ndataset = LstDetection('wheat_train.lst', root=os.path.expanduser('.'))\n\nimage, label = dataset[0]\nclasses = ['wheat']\nax = viz.plot_bbox(image, bboxes=label[:, :4], labels=label[:, 4:5], class_names=classes)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"net = gcv.model_zoo.get_model('ssd_512_mobilenet1.0_custom', classes=classes,\n    pretrained_base=False, transfer='voc')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_dataloader(net, train_dataset, data_shape, batch_size, num_workers):\n    from gluoncv.data.batchify import Tuple, Stack, Pad\n    from gluoncv.data.transforms.presets.ssd import SSDDefaultTrainTransform\n    width, height = data_shape, data_shape\n    # use fake data to generate fixed anchors for target generation\n    with autograd.train_mode():\n        _, _, anchors = net(mx.nd.zeros((1, 3, height, width)))\n    batchify_fn = Tuple(Stack(), Stack(), Stack())  # stack image, cls_targets, box_targets\n    train_loader = gluon.data.DataLoader(\n        train_dataset.transform(SSDDefaultTrainTransform(width, height, anchors)),\n        batch_size, True, batchify_fn=batchify_fn, last_batch='rollover', num_workers=num_workers)\n    return train_loader\n\ntrain_data = get_dataloader(net, dataset, 512, 1, 0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\na = mx.nd.zeros((1,), ctx=mx.gpu(0))\nctx = [mx.gpu(0)]\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"net.collect_params().reset_ctx(ctx)\ntrainer = gluon.Trainer(\n    net.collect_params(), 'sgd',\n    {'learning_rate': 0.0002, 'wd': 0.0005, 'momentum': 0.9})\n\nmbox_loss = gcv.loss.SSDMultiBoxLoss()\nce_metric = mx.metric.Loss('CrossEntropy')\nsmoothl1_metric = mx.metric.Loss('SmoothL1')\n\nfor epoch in range(0, 2):\n    ce_metric.reset()\n    smoothl1_metric.reset()\n    tic = time.time()\n    btic = time.time()\n    net.hybridize(static_alloc=True, static_shape=True)\n    for i, batch in enumerate(train_data):\n        batch_size = batch[0].shape[0]\n        data = gluon.utils.split_and_load(batch[0], ctx_list=ctx, batch_axis=0)\n        cls_targets = gluon.utils.split_and_load(batch[1], ctx_list=ctx, batch_axis=0)\n        box_targets = gluon.utils.split_and_load(batch[2], ctx_list=ctx, batch_axis=0)\n        with autograd.record():\n            cls_preds = []\n            box_preds = []\n            for x in data:\n                cls_pred, box_pred, _ = net(x)\n                cls_preds.append(cls_pred)\n                box_preds.append(box_pred)\n            sum_loss, cls_loss, box_loss = mbox_loss(\n                cls_preds, box_preds, cls_targets, box_targets)\n            autograd.backward(sum_loss)\n        # since we have already normalized the loss, we don't want to normalize\n        # by batch-size anymore\n        trainer.step(1)\n        ce_metric.update(0, [l * batch_size for l in cls_loss])\n        smoothl1_metric.update(0, [l * batch_size for l in box_loss])\n        name1, loss1 = ce_metric.get()\n        name2, loss2 = smoothl1_metric.get()\n        if i % 20 == 0:\n            print('[Epoch {}][Batch {}], Speed: {:.3f} samples/sec, {}={:.3f}, {}={:.3f}'.format(\n                epoch, i, batch_size/(time.time()-btic), name1, loss1, name2, loss2))\n        btic = time.time()\n\nnet.save_parameters('/kaggle/working/ssd_512_mobilenet1.0_wheat.params')        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"classes = ['wheat']\nnet = gcv.model_zoo.get_model('ssd_512_mobilenet1.0_custom', classes=classes, pretrained_base=False)\nnet.load_parameters('/kaggle/working/ssd_512_mobilenet1.0_wheat.params')\n\n\nsub = pd.read_csv('/kaggle/input/global-wheat-detection/sample_submission.csv')\nimg_id = sub['image_id'].values\nsubmission = []\n\nfor img in img_id:\n     #print(img)\n    prediction_string = []\n    x, image = gcv.data.transforms.presets.ssd.load_test(f'/kaggle/input/global-wheat-detection/test/{img}.jpg', 224)\n    cid, score, bbox = net(x)\n    #print(score[0][0].asnumpy().squeeze().astype(float))\n    for (x_min,y_min,x_max,y_max),s in zip(bbox[0].asnumpy(),score[0].asnumpy().squeeze().astype(float)):\n        x = round(x_min)\n        y = round(y_min)\n        h = round(x_max-x_min)\n        w = round(y_max-y_min)\n        prediction_string.append(f\"{s} {x} {y} {h} {w}\")\n    prediction_string = \" \".join(prediction_string)\n    \n    submission.append([img,prediction_string])\n\nsample_submission = pd.DataFrame(submission, columns=[\"image_id\",\"PredictionString\"])\nsample_submission.to_csv('/kaggle/working/submission.csv', index=False)\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mysub = pd.read_csv('/kaggle/working/submission.csv')\nmysub.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**References**\n\n> Link for model fine tuning\n\nhttps://gluon-cv.mxnet.io/build/examples_detection/finetune_detection.html\n\n> Link for data prepation \n\nhttps://gluon-cv.mxnet.io/build/examples_datasets/detection_custom.html#sphx-glr-build-examples-datasets-detection-custom-py\n","execution_count":null}],"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}