{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"! pip install ultralytics pycocotools -q","metadata":{"_uuid":"477e625c-8e43-4b8b-9129-ea625ad83f37","_cell_guid":"efdb0b6d-597b-4b43-b4d9-4a54d6eed559","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-08-12T09:46:29.602657Z","iopub.execute_input":"2023-08-12T09:46:29.602966Z","iopub.status.idle":"2023-08-12T09:47:11.923349Z","shell.execute_reply.started":"2023-08-12T09:46:29.602937Z","shell.execute_reply":"2023-08-12T09:47:11.921819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ultralytics import YOLO\nfrom ultralytics.utils.ops import scale_image","metadata":{"_uuid":"a7f5a358-d5eb-43bf-8c2a-bf6fd7d772ff","_cell_guid":"714b4157-48af-4be4-8a84-23568accaaa9","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-08-12T09:47:11.927608Z","iopub.execute_input":"2023-08-12T09:47:11.931974Z","iopub.status.idle":"2023-08-12T09:47:16.112637Z","shell.execute_reply.started":"2023-08-12T09:47:11.931930Z","shell.execute_reply":"2023-08-12T09:47:16.111279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"weights = [\n    \"/kaggle/input/dl-sprint-2/DL sprint 2.0 model/aug-fold0/best.pt\",\n    \"/kaggle/input/dl-sprint-2/DL sprint 2.0 model/aug-fold1/best.pt\",\n    \"/kaggle/input/dl-sprint-2/DL sprint 2.0 model/aug-fold2/best.pt\",\n    \"/kaggle/input/dl-sprint-2/DL sprint 2.0 model/aug-fold3/best.pt\",\n    \"/kaggle/input/dl-sprint-2/DL sprint 2.0 model/full/last.pt\",\n]","metadata":{"_uuid":"f56fee64-ba11-45c4-9313-1d306a25b841","_cell_guid":"c70b8920-8c01-4620-b3a2-2113cd1a972e","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-08-12T09:47:16.118111Z","iopub.execute_input":"2023-08-12T09:47:16.118789Z","iopub.status.idle":"2023-08-12T09:47:16.127778Z","shell.execute_reply.started":"2023-08-12T09:47:16.118751Z","shell.execute_reply":"2023-08-12T09:47:16.126188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torchvision.ops.boxes import box_iou\n\ndef voting_nms(bboxes: torch.Tensor, scores: torch.Tensor, iou_threshold: float) -> torch.Tensor:\n    device = bboxes.device\n    order = torch.argsort(-scores)\n    indices = torch.arange(bboxes.shape[0]).to(device)\n    keep = torch.ones_like(indices, dtype=torch.bool).to(device)\n    for i in indices:\n        if keep[i]:\n            bbox = bboxes[order[i]]\n            iou = box_iou(bbox[None,...], (bboxes[order[i + 1:]]) * keep[i + 1:][...,None])\n            overlapped = torch.nonzero(iou > iou_threshold)\n            keep[overlapped + i + 1] = 0\n            \n            if len(keep[overlapped + i + 1]) < 2:\n                keep[i] = 0\n    return order[keep]","metadata":{"execution":{"iopub.status.busy":"2023-08-12T09:47:16.133337Z","iopub.execute_input":"2023-08-12T09:47:16.134077Z","iopub.status.idle":"2023-08-12T09:47:16.144033Z","shell.execute_reply.started":"2023-08-12T09:47:16.134043Z","shell.execute_reply":"2023-08-12T09:47:16.142974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport torchvision\n\nclass YOLOv8Ensemble:\n    \n    def __init__(self, weights, iou=0.5, conf=.25, half=True, max_det=500):\n        self.models = [YOLO(weight) for weight in weights]\n        for model in self.models:\n            model.overrides[\"conf\"] = conf\n            model.overrides[\"iou\"] = 0.7\n            model.overrides[\"half\"] = half\n            model.overrides[\"agnostic_nms\"] = True\n            model.overrides[\"max_det\"] = max_det\n            \n        self.iou = iou\n        \n    def predict(self, images):\n        results = [model(images, verbose=False)[0] for model in self.models]\n        \n        boxes = []\n        scores = []\n        labels = []\n        masks = []\n        shape = results[0].orig_shape\n\n        for result in results:\n            if len(result.boxes) == 0:\n                continue\n            boxes.append(result.boxes.xyxy.half())\n            scores.append(result.boxes.conf.half())\n            labels.append(result.boxes.cls.short())\n            masks.append(result.masks.data.short())\n        \n        del result\n        del results\n\n        if len(boxes) == 0:\n            return {\"boxes\": np.array([]), \"scores\": np.array([]), \"labels\": np.array([]), \"masks\": np.array([]), \"shape\": shape}\n\n        boxes = torch.cat(boxes)\n        scores = torch.cat(scores)\n        labels = torch.cat(labels).short()\n        masks = torch.cat(masks).short()\n\n        nms_result = self.postprocess_single(boxes, scores, labels, masks)\n        nms_result[\"shape\"] = shape\n            \n        return nms_result\n            \n    def postprocess_single(self, boxes, scores, labels, masks):\n        unique_labels = labels.unique()\n        \n        new_boxes = []\n        new_scores = []\n        new_labels = []\n        new_masks = []\n        \n        for label in unique_labels:\n            cls_idx = labels==label\n            cls_boxes = boxes[cls_idx]\n            cls_scores = scores[cls_idx]\n            cls_labels = labels[cls_idx]\n            cls_masks =  masks[cls_idx]\n            \n            idx = voting_nms(cls_boxes, cls_scores, self.iou)\n            new_boxes.append(cls_boxes[idx].cpu())\n            new_scores.append(cls_scores[idx].cpu())\n            new_labels.append(cls_labels[idx].cpu())\n            new_masks.append(cls_masks[idx].cpu())\n        \n        new_boxes = torch.cat(new_boxes)\n        new_scores = torch.cat(new_scores)\n        new_labels = torch.cat(new_labels)\n        new_masks = torch.cat(new_masks)\n        \n        return {\n            \"boxes\": new_boxes.cpu().numpy(),\n            \"scores\": new_scores.cpu().numpy(),\n            \"labels\": new_labels.cpu().numpy(),\n            \"masks\": new_masks.cpu().numpy(),\n        }\n    \ndef resize_masks(masks, orig_shape):\n    new_masks = []\n    for mask in masks:\n        mask = scale_image(mask, orig_shape).reshape(orig_shape)\n        new_masks.append(mask)\n    return np.stack(new_masks)\n\ndef resize_mask(mask, orig_shape):\n    mask = scale_image(mask, orig_shape).reshape(orig_shape)\n    return mask","metadata":{"_uuid":"8168c4c7-ba06-4b19-95c6-7a67851dd781","_cell_guid":"6fae5a1d-2c27-4c63-ae8d-363ac1dc8f0c","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-08-12T09:47:16.145993Z","iopub.execute_input":"2023-08-12T09:47:16.146899Z","iopub.status.idle":"2023-08-12T09:47:16.173617Z","shell.execute_reply.started":"2023-08-12T09:47:16.146864Z","shell.execute_reply":"2023-08-12T09:47:16.172659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ensemble = YOLOv8Ensemble(weights, max_det=400)","metadata":{"execution":{"iopub.status.busy":"2023-08-12T09:47:16.175348Z","iopub.execute_input":"2023-08-12T09:47:16.176149Z","iopub.status.idle":"2023-08-12T09:47:20.536566Z","shell.execute_reply.started":"2023-08-12T09:47:16.176117Z","shell.execute_reply":"2023-08-12T09:47:20.535102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nfrom pycocotools.coco import COCO\n\ncoco_gt = COCO(\"/kaggle/input/dlsprint2/badlad/badlad-test-metadata.json\")\n\nIMG_DIR = \"/kaggle/input/dlsprint2/badlad/images/test\"\n\nimg_ids = coco_gt.getImgIds()\nimg_paths = [os.path.join(IMG_DIR, img[\"file_name\"]) for img in coco_gt.loadImgs(img_ids)]","metadata":{"execution":{"iopub.status.busy":"2023-08-12T09:47:20.541303Z","iopub.execute_input":"2023-08-12T09:47:20.543946Z","iopub.status.idle":"2023-08-12T09:47:20.915320Z","shell.execute_reply.started":"2023-08-12T09:47:20.543907Z","shell.execute_reply":"2023-08-12T09:47:20.913618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def rle_encode(mask: np.ndarray) -> str:\n    # flatten the mask column wise\n    pixels = mask.T.flatten()\n\n    # pad the list of pixels to ensure they have leading and trailing 0\n    use_padding = False\n    if pixels[0] or pixels[-1]:\n        use_padding = True\n        pixel_padded = np.zeros([len(pixels) + 2], dtype=pixels.dtype)\n        pixel_padded[1:-1] = pixels\n        pixels = pixel_padded\n\n    # get the pixel indices where consecutive pixels don't match => binary masks begin or end\n    # +1 to convert to inclusive start indices, exclusive end indices of masks\n    # +1 to convert to 1-indexed pixel ordering\n    rle = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    if use_padding:\n        rle = rle - 1  # remove the extra offset introduced by padding\n\n    # end pixel indices of masks - start pixel indices of masks = length of RLE runs\n    # store the RLE run lengths replacing the end pixel indices \n    rle[1::2] = rle[1::2] - rle[:-1:2]\n\n    return ' '.join(str(x) for x in rle)","metadata":{"execution":{"iopub.status.busy":"2023-08-12T09:47:20.917185Z","iopub.execute_input":"2023-08-12T09:47:20.917592Z","iopub.status.idle":"2023-08-12T09:47:20.926304Z","shell.execute_reply.started":"2023-08-12T09:47:20.917553Z","shell.execute_reply":"2023-08-12T09:47:20.925103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm.auto import tqdm\n\nid = []\npredicted = []\n\nfor original_id, path in zip(tqdm(img_ids), img_paths):\n    prediction = ensemble.predict(path)\n    labels = prediction[\"labels\"]\n    shape = prediction[\"shape\"]\n    masks = prediction[\"masks\"]\n    \n    for i in range(4):\n        output_mask = np.zeros(shape, dtype=\"int16\")\n        \n        for mask in masks[labels == i]:\n            output_mask |= resize_mask(mask, shape)\n        \n        id.append(f\"{original_id}_{i}\")\n        predicted.append(rle_encode(output_mask))\n    \n    if original_id % 1000 == 0:\n        print(f'{original_id}/13000')","metadata":{"execution":{"iopub.status.busy":"2023-08-12T09:47:20.928124Z","iopub.execute_input":"2023-08-12T09:47:20.928963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.DataFrame({\n    \"Id\": id,\n    \"Predicted\": predicted\n})","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.to_csv(\"submit.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}