{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# MaskRCNN Predictions | HuBMAP - Hacking the Human Vasculature Competition","metadata":{}},{"cell_type":"markdown","source":"[EDA](https://www.kaggle.com/code/khalilrejiba/hubmap-vasculature-eda-interactive) | [Train](https://www.kaggle.com/code/khalilrejiba/hubmap-vasculature-maskrcnn-train)","metadata":{}},{"cell_type":"markdown","source":"## Utility Scripts","metadata":{}},{"cell_type":"code","source":"!pip install -q --no-index --no-deps /kaggle/input/hubmap-vasculature-maskrcnn-starter/wheels/pycocotools-2.0.6-cp310-cp310-linux_x86_64.whl","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%bash\ncp /kaggle/input/hubmap-vasculature-maskrcnn-starter/utils.py /kaggle/working\ncp /kaggle/input/hubmap-vasculature-maskrcnn-starter/engine.py /kaggle/working\ncp /kaggle/input/hubmap-vasculature-maskrcnn-starter/coco_eval.py /kaggle/working\ncp /kaggle/input/hubmap-vasculature-maskrcnn-starter/coco_utils.py /kaggle/working\ncp /kaggle/input/hubmap-vasculature-maskrcnn-starter/transforms.py /kaggle/working","metadata":{"papermill":{"duration":5.178028,"end_time":"2023-07-28T21:51:37.069741","exception":false,"start_time":"2023-07-28T21:51:31.891713","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import Statements","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport json\nfrom pathlib import Path\nimport gc\nimport matplotlib.pyplot as plt\nimport PIL\nimport skimage\nfrom shapely.geometry import LinearRing as ShapelyContour\nfrom shapely.geometry import Polygon as ShapelyPolygon\nimport random\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch import nn\nimport torchvision\ntorchvision.disable_beta_transforms_warning()\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection.mask_rcnn import MaskRCNNPredictor\nfrom engine import train_one_epoch, evaluate\nimport utils\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nfrom pycocotools import _mask as coco_mask\nimport base64\nimport zlib","metadata":{"id":"LDjuVFgexFfh","outputId":"f2e0c5c5-a8d0-4999-c94d-1660f153cd00","papermill":{"duration":5.759132,"end_time":"2023-07-28T21:51:42.861928","exception":false,"start_time":"2023-07-28T21:51:37.102796","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Setting Global Variables","metadata":{}},{"cell_type":"code","source":"# For reproducibility\nseed = 123\nrandom.seed(seed) # Used in Albumentations\nnp.random.seed(seed)\ntorch.manual_seed(seed);","metadata":{"papermill":{"duration":0.045883,"end_time":"2023-07-28T21:51:42.941059","exception":false,"start_time":"2023-07-28T21:51:42.895176","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_DIR = \"/kaggle/input/hubmap-hacking-the-human-vasculature/train\"\nTEST_DIR = \"/kaggle/input/hubmap-hacking-the-human-vasculature/test\"\nANNOT_PATH = \"/kaggle/input/hubmap-hacking-the-human-vasculature/polygons.jsonl\"\nWSI_TILE_CSV = \"/kaggle/input/hubmap-hacking-the-human-vasculature/tile_meta.csv\"\nIMG_SIZE = 512","metadata":{"papermill":{"duration":0.041576,"end_time":"2023-07-28T21:51:43.015002","exception":false,"start_time":"2023-07-28T21:51:42.973426","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"hidden_test_ids = [path.stem for path in Path(TEST_DIR).glob(\"*.tif\")]","metadata":{"papermill":{"duration":0.045584,"end_time":"2023-07-28T21:51:43.093619","exception":false,"start_time":"2023-07-28T21:51:43.048035","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class_names = [\"background\", \"blood_vessel\", \"glomerulus\", \"unsure\"]","metadata":{"papermill":{"duration":0.0722,"end_time":"2023-07-28T21:51:43.198697","exception":false,"start_time":"2023-07-28T21:51:43.126497","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preprocessing","metadata":{}},{"cell_type":"code","source":"class HuBMAPVasculatureTestset(Dataset):\n    \n    def __init__(self, ids, root=TEST_DIR, transforms=None):\n        self.root = root\n        self.ext = \"tif\"\n        self.ids = ids\n        self.transforms = transforms\n\n    def __getitem__(self, idx):\n        img_path = Path(self.root) / f\"{self.ids[idx]}.{self.ext}\"\n        img = PIL.Image.open(img_path).convert(\"RGB\")\n        img = np.array(img)\n        if self.transforms is not None:\n            img = self.transforms(image=img)[\"image\"]\n        return img / 255\n\n    def __len__(self):\n        return len(self.ids)","metadata":{"papermill":{"duration":0.041982,"end_time":"2023-07-28T21:52:50.551434","exception":false,"start_time":"2023-07-28T21:52:50.509452","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_instance_segmentation_model(num_classes=2, hidden_layer=256):\n    model = torchvision.models.detection.maskrcnn_resnet50_fpn(weights=\"DEFAULT\")\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n    in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels\n    model.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask,\n                                                       hidden_layer,\n                                                       num_classes)\n    return model","metadata":{"id":"YjNHjVMOyYlH","papermill":{"duration":0.042421,"end_time":"2023-07-28T21:52:50.625693","exception":false,"start_time":"2023-07-28T21:52:50.583272","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_transforms_pred(s=IMG_SIZE):\n    transforms = [\n        A.Resize(height=s, width=s),\n        ToTensorV2()\n    ]    \n    transforms = A.Compose(transforms)\n    return transforms","metadata":{"papermill":{"duration":0.039653,"end_time":"2023-07-28T21:52:50.771552","exception":false,"start_time":"2023-07-28T21:52:50.731899","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Making Predicitons ","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nSAVE_PATH = \"/kaggle/input/hubmap-vasculature-maskrcnn-train/maskrcnn_resnet50_fpn_finetune_10epochs.pth\"\nmodel = torchvision.models.detection.maskrcnn_resnet50_fpn(weights=None, weights_backbone=None)\nin_features = model.roi_heads.box_predictor.cls_score.in_features\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, len(class_names))\nin_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels\nmodel.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask,\n                                                   256,\n                                                   len(class_names))\nmodel.to(device)\nmodel.load_state_dict(torch.load(SAVE_PATH))","metadata":{"id":"zoenkCj18C4h","papermill":{"duration":4870.700029,"end_time":"2023-07-28T23:15:26.299371","exception":false,"start_time":"2023-07-28T21:54:15.599342","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def apply_nms_box(boxes, scores, iou_thres=0.5):\n    \"\"\"\n    Non Maximum Suppression using bounding boxes\n    \"\"\"\n    x0 = boxes[:, 0]\n    y0 = boxes[:, 1]\n    x1 = boxes[:, 2]\n    y1 = boxes[:, 3]\n\n    areas = (x1 - x0) * (y1 - y0)\n\n    indices = scores.argsort(descending=True)\n \n    revised_indices = []\n    \n    while len(indices) > 0:\n        \n        idx = indices[0]\n        indices = indices[1:]\n        revised_indices.append(idx.item())\n        \n        xx0 = torch.max(x0[indices], x0[idx])\n        yy0 = torch.max(y0[indices], y0[idx])\n        xx1 = torch.min(x1[indices], x1[idx])\n        yy1 = torch.min(y1[indices], y1[idx])\n         \n        w = torch.clamp(xx1 - xx0, min=0)\n        h = torch.clamp(yy1 - yy0, min=0)\n        intersection = w * h\n        union = areas[indices] - intersection\n        IoU = intersection / union\n\n        indices = indices[IoU < iou_thres]\n\n    return revised_indices","metadata":{"papermill":{"duration":0.055673,"end_time":"2023-07-28T23:15:26.943229","exception":false,"start_time":"2023-07-28T23:15:26.887556","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def apply_nms_mask(masks, scores, iou_thres=0.5):\n    \"\"\"\n    Non Maximum Suppression using masks\n    \"\"\"\n    indices = scores.argsort(descending=True)\n \n    revised_indices = []\n    \n    while len(indices) > 0:\n        \n        idx = indices[0]\n        indices = indices[1:]\n        revised_indices.append(idx.item())\n         \n        intersection = masks[indices] * masks[idx]\n        union = masks[indices] + masks[idx]\n        union = union.clip(0, 1)\n\n        IoU = intersection.sum(dim=(1, 2, 3)) / union.sum(dim=(1, 2, 3))\n\n        indices = indices[IoU < iou_thres]\n\n    return revised_indices","metadata":{"papermill":{"duration":0.054745,"end_time":"2023-07-28T23:15:27.040892","exception":false,"start_time":"2023-07-28T23:15:26.986147","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_predictions(model, img, label=1, conf_thres=0.5, iou_thres=0.6, nms_mask=True):\n    model.eval()\n    with torch.no_grad():\n        prediction = model([img.to(device)])\n    prediction = prediction[0]\n    \n    conf_filter = prediction['scores'] > conf_thres\n    label_filter = prediction['labels'] == label\n    for k, v in prediction.items():    \n        prediction[k] = v[conf_filter * label_filter]\n    \n    masks = prediction['masks']\n    boxes = prediction['boxes']\n    scores = prediction['scores']\n    \n    # Soft Masks to Binary Masks\n    masks = (masks >  0.5).byte()\n    \n    # Non Maximum Suppression\n    if nms_mask:\n        indices = apply_nms_mask(masks, scores, iou_thres)\n    else:\n        indices = apply_nms_box(boxes, scores, iou_thres)\n\n    return masks[indices].cpu().numpy(), boxes[indices].cpu().numpy(), scores[indices].cpu().numpy()","metadata":{"papermill":{"duration":0.054528,"end_time":"2023-07-28T23:15:27.138381","exception":false,"start_time":"2023-07-28T23:15:27.083853","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = HuBMAPVasculatureTestset(hidden_test_ids, transforms=get_transforms_pred())\n\nimg = test_dataset[np.random.randint(len(test_dataset))]\n\nplt.imshow(img.permute(1, 2, 0))\n\nmasks, _, scores = get_predictions(model, img)\n\nfor mask in masks:\n    mask = mask.squeeze()\n    polygon = skimage.measure.find_contours(mask)[0]\n    yy, xx = zip(*polygon)\n    plt.fill(xx, yy, facecolor='none', edgecolor=np.random.random(size=3), linewidth=2)","metadata":{"papermill":{"duration":0.739024,"end_time":"2023-07-28T23:15:27.922802","exception":false,"start_time":"2023-07-28T23:15:27.183778","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#  Submission","metadata":{"papermill":{"duration":0.078658,"end_time":"2023-07-28T23:15:31.346927","exception":false,"start_time":"2023-07-28T23:15:31.268269","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def encode_mask(mask):\n    mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1)\n    mask_to_encode = mask_to_encode.astype(np.uint8)\n    mask_to_encode = np.asfortranarray(mask_to_encode)\n    encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n    binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n    base64_str = base64.b64encode(binary_str)\n    return base64_str","metadata":{"papermill":{"duration":0.088661,"end_time":"2023-07-28T23:15:31.513693","exception":false,"start_time":"2023-07-28T23:15:31.425032","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_list = []\nfor i, test_id in enumerate(hidden_test_ids):\n    prediction_string = \"\"\n    \n    img = test_dataset[i]\n    _, h, w = img.shape\n    masks, _, scores = get_predictions(model, img)\n    masks_glomerulus, _, _ = get_predictions(model, img, label=2)\n    \n    for mask, score in zip(masks, scores):\n        \n        # Test if vessel is mainly outside glomerulus\n        mask_valid = True\n        for glm_msk in masks_glomerulus:\n            intersection = mask * glm_msk\n            if intersection.sum() < 0.5 * mask.sum():\n                continue\n            else:\n                mask_valid = False\n                break\n        \n        if mask_valid:\n            mask = mask.squeeze()\n            mask_str = encode_mask(mask).decode('UTF-8')\n            prediction_string += f\"0 {score} {mask_str} \"\n            \n    entry = {\n        \"id\": test_id,\n        \"height\": h,\n        \"width\": w,\n        \"prediction_string\": prediction_string,\n    }\n    submission_list.append(entry)","metadata":{"papermill":{"duration":0.226144,"end_time":"2023-07-28T23:15:31.817608","exception":false,"start_time":"2023-07-28T23:15:31.591464","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.DataFrame(submission_list)\nsubmission_df = submission_df.set_index('id')\nsubmission_df.to_csv(\"submission.csv\")","metadata":{"papermill":{"duration":0.092792,"end_time":"2023-07-28T23:15:31.991159","exception":false,"start_time":"2023-07-28T23:15:31.898367","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.head()","metadata":{"papermill":{"duration":0.096516,"end_time":"2023-07-28T23:15:32.165190","exception":false,"start_time":"2023-07-28T23:15:32.068674","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Modified from: https://pytorch.org/tutorials/intermediate/torchvision_tutorial.html","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"}}