{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Global Wheat Detection - Pseudo-labeling\n\nYou can get the training scripts [here](https://github.com/ufownl/global-wheat-detection).\n\n* YOLOv3 from [GluonCV](https://gluon-cv.mxnet.io/)\n* Use Darknet53 backbone\n* Use pseudo-labeling technique","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Dataset","execution_count":null},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport json\nimport random\nimport numpy as np\nimport mxnet as mx\nimport pandas as pd\nimport gluoncv as gcv\nfrom multiprocessing import cpu_count\nfrom multiprocessing.dummy import Pool\n\n\ndef load_dataset(root):\n    csv = pd.read_csv(os.path.join(root, \"train.csv\"))\n    data = {}\n    for i in csv.index:\n        key = csv[\"image_id\"][i]\n        bbox = json.loads(csv[\"bbox\"][i])\n        bbox = [bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3], 0.0]\n        if key in data:\n            data[key].append(bbox)\n        else:\n            data[key] = [bbox]\n    return sorted(\n        [(k, os.path.join(root, \"train\", k + \".jpg\"), v) for k, v in data.items()],\n        key=lambda x: x[0]\n    )\n\ndef load_image(path):\n    with open(path, \"rb\") as f:\n        buf = f.read()\n    return mx.image.imdecode(buf)\n\ndef get_batches(dataset, batch_size, width=512, height=512, net=None, ctx=mx.cpu()):\n    batches = len(dataset) // batch_size\n    if batches * batch_size < len(dataset):\n        batches += 1\n    sampler = Sampler(width, height, net)\n    with Pool(cpu_count() * 2) as p:\n        for i in range(batches):\n            start = i * batch_size\n            samples = p.map(sampler, dataset[start:start+batch_size])\n            stack_fn = [gcv.data.batchify.Stack()]\n            pad_fn = [gcv.data.batchify.Pad(pad_val=-1)]\n            if net is None:\n                batch = gcv.data.batchify.Tuple(*(stack_fn + pad_fn))(samples)\n            else:\n                batch = gcv.data.batchify.Tuple(*(stack_fn * 6 + pad_fn))(samples)\n            yield [x.as_in_context(ctx) for x in batch]\n\ndef gauss_blur(image, level):\n    return cv2.blur(image, (level * 2 + 1, level * 2 + 1))\n\ndef gauss_noise(image):\n    for i in range(image.shape[2]):\n        c = image[:, :, i]\n        diff = 255 - c.max();\n        noise = np.random.normal(0, random.randint(1, 6), c.shape)\n        noise = (noise - noise.min()) / (noise.max() - noise.min())\n        noise = diff * noise\n        image[:, :, i] = c + noise.astype(np.uint8)\n    return image\n\n\nclass Sampler:\n    def __init__(self, width, height, net=None, **kwargs):\n        self._net = net\n        if net is None:\n            self._transform = gcv.data.transforms.presets.yolo.YOLO3DefaultValTransform(width, height, **kwargs)\n        else:\n            self._transform = gcv.data.transforms.presets.yolo.YOLO3DefaultTrainTransform(width, height, net=net, **kwargs)\n\n    def __call__(self, data):\n        raw = load_image(data[1])\n        bboxes = np.array(data[2])\n        if not self._net is None:\n            raw = raw.asnumpy()\n            blur = random.randint(0, 3)\n            if blur > 0:\n                raw = gauss_blur(raw, blur)\n            raw = gauss_noise(raw)\n            raw = mx.nd.array(raw)\n            h, w, _ = raw.shape\n            raw, flips = gcv.data.transforms.image.random_flip(raw, py=0.5)\n            bboxes = gcv.data.transforms.bbox.flip(bboxes, (w, h), flip_y=flips[1])\n        res = self._transform(raw, bboxes)\n        return [mx.nd.array(x) for x in res]\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import mxnet as mx\nimport gluoncv as gcv\n\n\ndef load_model(path, ctx=mx.cpu()):\n    net = gcv.model_zoo.yolo3_darknet53_custom([\"wheat\"], pretrained_base=False)\n    net.set_nms(post_nms=150)\n    net.load_parameters(path, ctx=ctx)\n    return net\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Inference","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport time\nimport random\nimport mxnet as mx\nimport pandas as pd\nimport gluoncv as gcv\n\nmax_epochs = 8\nlearning_rate = 0.0001\nbatch_size = 16\nimg_s = 512\nthreshold = 0.1\ncontext = mx.gpu()\n\nprint(\"Loading model...\")\nmodel = load_model(\"/kaggle/input/global-wheat-detection-models/global-wheat-yolo3-darknet53.params\", ctx=context)\n\nprint(\"Loading test images...\")\ntest_images = [\n    (os.path.join(dirname, filename), os.path.splitext(filename)[0])\n        for dirname, _, filenames in os.walk('/kaggle/input/global-wheat-detection/test') for filename in filenames\n]\n\nprint(\"Pseudo labaling...\")\npseudo_set = []\nfor path, image_id in test_images:\n    print(path)\n    raw = load_image(path)\n    x, _ = gcv.data.transforms.presets.yolo.transform_test(raw, short=img_s)\n    classes, scores, bboxes = model(x.as_in_context(context))\n    bboxes[0, :, 0::2] = (bboxes[0, :, 0::2] / x.shape[3]).clip(0.0, 1.0) * raw.shape[1]\n    bboxes[0, :, 1::2] = (bboxes[0, :, 1::2] / x.shape[2]).clip(0.0, 1.0) * raw.shape[0]\n    label = [\n        [round(x) for x in bboxes[0, i].asnumpy().tolist()] + [0.0] for i in range(classes.shape[1])\n            if model.classes[int(classes[0, i].asscalar())] == \"wheat\" and scores[0, i].asscalar() > threshold\n    ]\n    if len(label) > 0:\n        pseudo_set.append((image_id, path, label))\n    \nprint(\"Loading training set...\")\ntraining_set = load_dataset(\"/kaggle/input/global-wheat-detection\") + pseudo_set\n\nprint(\"Re-training...\")\ntrainer = mx.gluon.Trainer(model.collect_params(), \"Nadam\", {\n    \"learning_rate\": learning_rate\n})\nfor epoch in range(max_epochs):\n    ts = time.time()\n    random.shuffle(training_set)\n    training_total_L = 0.0\n    training_batches = 0\n    for x, objectness, center_targets, scale_targets, weights, class_targets, gt_bboxes in get_batches(training_set, batch_size, width=img_s, height=img_s, net=model, ctx=context):\n        training_batches += 1\n        with mx.autograd.record():\n            obj_loss, center_loss, scale_loss, cls_loss = model(x, gt_bboxes, objectness, center_targets, scale_targets, weights, class_targets)\n            L = obj_loss + center_loss + scale_loss + cls_loss\n            L.backward()\n        trainer.step(x.shape[0])\n        training_batch_L = mx.nd.mean(L).asscalar()\n        if training_batch_L != training_batch_L:\n            raise ValueError()\n        training_total_L += training_batch_L\n        print(\"[Epoch %d  Batch %d]  batch_loss %.10f  average_loss %.10f  elapsed %.2fs\" % (\n            epoch, training_batches, training_batch_L, training_total_L / training_batches, time.time() - ts\n        ))\n    training_avg_L = training_total_L / training_batches\n    print(\"[Epoch %d]  training_loss %.10f  duration %.2fs\" % (epoch + 1, training_avg_L, time.time() - ts))\n\nprint(\"Inference...\")\nresults = []\nfor path, image_id in test_images:\n    print(path)\n    raw = load_image(path)\n    x, _ = gcv.data.transforms.presets.yolo.transform_test(raw, short=img_s)\n    classes, scores, bboxes = model(x.as_in_context(context))\n    bboxes[0, :, 0::2] = (bboxes[0, :, 0::2] / x.shape[3]).clip(0.0, 1.0) * raw.shape[1]\n    bboxes[0, :, 1::2] = (bboxes[0, :, 1::2] / x.shape[2]).clip(0.0, 1.0) * raw.shape[0]\n    bboxes[0, :, 2:4] -= bboxes[0, :, 0:2]\n    results.append({\n        \"image_id\": image_id,\n        \"PredictionString\": \" \".join([\n            \" \".join([str(x) for x in [scores[0, i].asscalar()] + [round(x) for x in bboxes[0, i].asnumpy().tolist()]])\n                for i in range(classes.shape[1])\n                    if model.classes[int(classes[0, i].asscalar())] == \"wheat\" and scores[0, i].asscalar() > threshold\n        ])\n    })\npd.DataFrame(results, columns=['image_id', 'PredictionString']).to_csv('submission.csv', index=False)\n","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}