{"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"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":52279,"databundleVersionId":5822112,"sourceType":"competition"},{"sourceId":6063590,"sourceType":"datasetVersion","datasetId":3470137},{"sourceId":6065790,"sourceType":"datasetVersion","datasetId":3470738},{"sourceId":6198509,"sourceType":"datasetVersion","datasetId":3392021},{"sourceId":6225321,"sourceType":"datasetVersion","datasetId":3575473},{"sourceId":7015190,"sourceType":"datasetVersion","datasetId":4033399},{"sourceId":7046253,"sourceType":"datasetVersion","datasetId":4054687},{"sourceId":7046256,"sourceType":"datasetVersion","datasetId":4054689},{"sourceId":7048751,"sourceType":"datasetVersion","datasetId":4056328},{"sourceId":7056031,"sourceType":"datasetVersion","datasetId":4061417},{"sourceId":7061380,"sourceType":"datasetVersion","datasetId":4065244}],"dockerImageVersionId":30588,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# README\n\nFor replicating our top-performing results as presented in the study, submit the following script to the HuBMAP - Hacking the Human Vasculature competition.\n\nThe visualization of results discussed in the paper can be generated using the code provided at the end of this script.","metadata":{}},{"cell_type":"markdown","source":"### Install MMdetectron 3.1 offline","metadata":{}},{"cell_type":"code","source":"!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/pycocotools-2.0.6-cp310-cp310-linux_x86_64.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/torch-1.12.1+cu116-cp310-cp310-linux_x86_64.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/torchvision-0.13.1+cu116-cp310-cp310-linux_x86_64.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/mmcv-2.0.1-cp310-cp310-manylinux1_x86_64.whl \n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/openmim-0.3.9-py2.py3-none-any.whl\n# !pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/mmengine-0.7.4-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/mmengine-0.8.3-py3-none-any.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/addict-2.4.0-py3-none-any.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/mmdet-3.1.0-py3-none-any.whl\n!pip install --no-index --no-deps /kaggle/input/mmdetectron-31-wheel/terminaltables-3.1.10-py2.py3-none-any.whl\n\n!pip install -q --no-index /kaggle/input/vasculature-packages/mmengine-0.8.3-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:36:45.667931Z","iopub.execute_input":"2023-12-09T02:36:45.669042Z","iopub.status.idle":"2023-12-09T02:37:32.413401Z","shell.execute_reply.started":"2023-12-09T02:36:45.668991Z","shell.execute_reply":"2023-12-09T02:37:32.412145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q --no-index /kaggle/input/vasculature-packages/ordered_set-4.1.0-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/model_index-0.1.11-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/einops-0.6.1-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/mat4py-0.5.0-py2.py3-none-any.whl\n!pip install --no-deps --no-index /kaggle/input/vasculature-packages/mmpretrain-1.0.1-py2.py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:37:32.416144Z","iopub.execute_input":"2023-12-09T02:37:32.417178Z","iopub.status.idle":"2023-12-09T02:38:22.261754Z","shell.execute_reply.started":"2023-12-09T02:37:32.417136Z","shell.execute_reply":"2023-12-09T02:38:22.260571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport cv2\nimport torch\nimport pycocotools\nimport numpy as np\nimport pandas as pd","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.263227Z","iopub.execute_input":"2023-12-09T02:38:22.263523Z","iopub.status.idle":"2023-12-09T02:38:22.268967Z","shell.execute_reply.started":"2023-12-09T02:38:22.263496Z","shell.execute_reply":"2023-12-09T02:38:22.267919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile /opt/conda/lib/python3.10/site-packages/skimage/morphology/__init__.py\n\nfrom .binary import (binary_closing, binary_dilation, binary_erosion,\n                     binary_opening)\nfrom .gray import (black_tophat, closing, dilation, erosion, opening,\n                   white_tophat)\nfrom .isotropic import (isotropic_erosion, isotropic_dilation,\n                        isotropic_opening, isotropic_closing)\nfrom .footprints import (ball, cube, diamond, disk, ellipse,\n                         footprint_from_sequence, octagon, octahedron,\n                         rectangle, square, star)\nfrom ..measure._label import label\nfrom ._skeletonize import medial_axis, skeletonize, skeletonize_3d, thin\n# from .convex_hull import convex_hull_image, convex_hull_object\nfrom .grayreconstruct import reconstruction\nfrom .misc import remove_small_holes, remove_small_objects\nfrom .extrema import h_maxima, h_minima, local_minima, local_maxima\nfrom ._flood_fill import flood, flood_fill\nfrom .max_tree import (area_opening, area_closing, diameter_closing,\n                       diameter_opening, max_tree,\n                       max_tree_local_maxima)\n\n__all__ = ['area_closing',\n           'area_opening',\n           'ball',\n           'binary_closing',\n           'binary_dilation',\n           'binary_erosion',\n           'binary_opening',\n           'black_tophat',\n           'closing',\n           'convex_hull_image',\n           'convex_hull_object',\n           'cube',\n           'diameter_closing',\n           'diameter_opening',\n           'diamond',\n           'dilation',\n           'disk',\n           'ellipse',\n           'erosion',\n           'flood',\n           'flood_fill',\n           'footprint_from_sequence',\n           'h_maxima',\n           'h_minima',\n           'isotropic_closing',\n           'isotropic_dilation',\n           'isotropic_erosion',\n           'isotropic_opening',\n           'label',\n           'local_maxima',\n           'local_minima',\n           'max_tree',\n           'max_tree_local_maxima',\n           'medial_axis',\n           'octagon',\n           'octahedron',\n           'opening',\n           'reconstruction',\n           'rectangle',\n           'remove_small_holes',\n           'remove_small_objects',\n           'skeletonize',\n           'skeletonize_3d',\n           'square',\n           'star',\n           'thin',\n           'white_tophat'\n]","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.271504Z","iopub.execute_input":"2023-12-09T02:38:22.271794Z","iopub.status.idle":"2023-12-09T02:38:22.284518Z","shell.execute_reply.started":"2023-12-09T02:38:22.271768Z","shell.execute_reply":"2023-12-09T02:38:22.283601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from skimage.morphology import binary_dilation","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.285682Z","iopub.execute_input":"2023-12-09T02:38:22.285960Z","iopub.status.idle":"2023-12-09T02:38:22.297844Z","shell.execute_reply.started":"2023-12-09T02:38:22.285936Z","shell.execute_reply":"2023-12-09T02:38:22.297058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_seed(seed=42):\n    import random\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\nset_seed()","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.298995Z","iopub.execute_input":"2023-12-09T02:38:22.299340Z","iopub.status.idle":"2023-12-09T02:38:22.310300Z","shell.execute_reply.started":"2023-12-09T02:38:22.299284Z","shell.execute_reply":"2023-12-09T02:38:22.309314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import zlib\nimport base64\nfrom pycocotools import _mask as coco_mask\n\ndef encode_binary_mask(mask: np.ndarray):\n    \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n\n    # check input mask --\n    if mask.dtype != bool:\n        raise ValueError(\n            \"encode_binary_mask expects a binary mask, received dtype == %s\" %\n            mask.dtype)\n\n    mask = np.squeeze(mask)\n    if len(mask.shape) != 2:\n        raise ValueError(\n            \"encode_binary_mask expects a 2d mask, received shape == %s\" %\n            mask.shape)\n\n    # convert input mask to expected COCO API input --\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\n    # RLE encode mask --\n    encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n\n    # compress and base64 encoding --\n    binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n    base64_str = base64.b64encode(binary_str)\n    return base64_str","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.311490Z","iopub.execute_input":"2023-12-09T02:38:22.311793Z","iopub.status.idle":"2023-12-09T02:38:22.321821Z","shell.execute_reply.started":"2023-12-09T02:38:22.311768Z","shell.execute_reply":"2023-12-09T02:38:22.320882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nimport warnings\nimport numpy as np\nfrom numba import jit","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.323009Z","iopub.execute_input":"2023-12-09T02:38:22.323377Z","iopub.status.idle":"2023-12-09T02:38:22.333319Z","shell.execute_reply.started":"2023-12-09T02:38:22.323353Z","shell.execute_reply":"2023-12-09T02:38:22.332498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"@jit(nopython=True)\ndef bb_intersection_over_union(A, B) -> float:\n    xA = max(A[0], B[0])\n    yA = max(A[1], B[1])\n    xB = min(A[2], B[2])\n    yB = min(A[3], B[3])\n\n    # compute the area of intersection rectangle\n    interArea = max(0, xB - xA) * max(0, yB - yA)\n\n    if interArea == 0:\n        return 0.0\n\n    # compute the area of both the prediction and ground-truth rectangles\n    boxAArea = (A[2] - A[0]) * (A[3] - A[1])\n    boxBArea = (B[2] - B[0]) * (B[3] - B[1])\n\n    iou = interArea / float(boxAArea + boxBArea - interArea)\n    return iou\n\n\ndef prefilter_boxes(boxes, scores, labels, weights, thr):\n    # Create dict with boxes stored by its label\n    new_boxes = dict()\n\n    for t in range(len(boxes)):\n\n        if len(boxes[t]) != len(scores[t]):\n            print('Error. Length of boxes arrays not equal to length of scores array: {} != {}'.format(len(boxes[t]), len(scores[t])))\n            sys.exit()\n\n        if len(boxes[t]) != len(labels[t]):\n            print('Error. Length of boxes arrays not equal to length of labels array: {} != {}'.format(len(boxes[t]), len(labels[t])))\n            sys.exit()\n\n        for j in range(len(boxes[t])):\n            score = scores[t][j]\n            if score < thr:\n                continue\n            label = int(labels[t][j])\n            box_part = boxes[t][j]\n            x1 = max(float(box_part[0]), 0.)\n            y1 = max(float(box_part[1]), 0.)\n            x2 = max(float(box_part[2]), 0.)\n            y2 = max(float(box_part[3]), 0.)\n\n            # Box data checks\n            if x2 < x1:\n                warnings.warn('X2 < X1 value in box. Swap them.')\n                x1, x2 = x2, x1\n            if y2 < y1:\n                warnings.warn('Y2 < Y1 value in box. Swap them.')\n                y1, y2 = y2, y1\n            if x1 > 1:\n                warnings.warn('X1 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                x1 = 1\n            if x2 > 1:\n                warnings.warn('X2 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                x2 = 1\n            if y1 > 1:\n                warnings.warn('Y1 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                y1 = 1\n            if y2 > 1:\n                warnings.warn('Y2 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                y2 = 1\n            if (x2 - x1) * (y2 - y1) == 0.0:\n                warnings.warn(\"Zero area box skipped: {}.\".format(box_part))\n                continue\n\n            # [label, score, weight, model index, x1, y1, x2, y2]\n            b = [int(label), float(score) * weights[t], weights[t], t, x1, y1, x2, y2]\n            if label not in new_boxes:\n                new_boxes[label] = []\n            new_boxes[label].append(b)\n\n    # Sort each list in dict by score and transform it to numpy array\n    for k in new_boxes:\n        current_boxes = np.array(new_boxes[k])\n        new_boxes[k] = current_boxes[current_boxes[:, 1].argsort()[::-1]]\n\n    return new_boxes","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.334822Z","iopub.execute_input":"2023-12-09T02:38:22.335252Z","iopub.status.idle":"2023-12-09T02:38:22.354901Z","shell.execute_reply.started":"2023-12-09T02:38:22.335217Z","shell.execute_reply":"2023-12-09T02:38:22.353856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_weighted_box(boxes, conf_type='avg'):\n    \"\"\"\n    Create weighted box for set of boxes\n    :param boxes: set of boxes to fuse\n    :param conf_type: type of confidence one of 'avg' or 'max'\n    :return: weighted box (label, score, weight, x1, y1, x2, y2)\n    \"\"\"\n\n    box = np.zeros(8, dtype=np.float32)\n    conf = 0\n    conf_list = []\n    w = 0\n    for b in boxes:\n        box[4:] += (b[1] * b[4:])\n        conf += b[1]\n        conf_list.append(b[1])\n        w += b[2]\n    box[0] = boxes[0][0]\n    if conf_type == 'avg':\n        box[1] = conf / len(boxes)\n    elif conf_type == 'max':\n        box[1] = np.array(conf_list).max()\n    elif conf_type in ['box_and_model_avg', 'absent_model_aware_avg']:\n        box[1] = conf / len(boxes)\n    box[2] = w\n    box[3] = -1 # model index field is retained for consistensy but is not used.\n    box[4:] /= conf\n    return box","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.358642Z","iopub.execute_input":"2023-12-09T02:38:22.358972Z","iopub.status.idle":"2023-12-09T02:38:22.370857Z","shell.execute_reply.started":"2023-12-09T02:38:22.358939Z","shell.execute_reply":"2023-12-09T02:38:22.369949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def find_matching_box(boxes_list, new_box, match_iou):\n    best_iou = match_iou\n    best_index = -1\n    for i in range(len(boxes_list)):\n        box = boxes_list[i]\n        if box[0] != new_box[0]:\n            continue\n        iou = bb_intersection_over_union(box[4:], new_box[4:])\n        if iou > best_iou:\n            best_index = i\n            best_iou = iou\n\n    return best_index, best_iou","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.371945Z","iopub.execute_input":"2023-12-09T02:38:22.372269Z","iopub.status.idle":"2023-12-09T02:38:22.381932Z","shell.execute_reply.started":"2023-12-09T02:38:22.372238Z","shell.execute_reply":"2023-12-09T02:38:22.381082Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def weighted_boxes_fusion_tracking(boxes_list, scores_list, labels_list, weights=None, iou_thr=0.55, skip_box_thr=0.0, conf_type='avg', allows_overflow=False):\n    '''\n    :param boxes_list: list of boxes predictions from each model, each box is 4 numbers.\n    It has 3 dimensions (models_number, model_preds, 4)\n    Order of boxes: x1, y1, x2, y2. We expect float normalized coordinates [0; 1]\n    :param scores_list: list of scores for each model\n    :param labels_list: list of labels for each model\n    :param weights: list of weights for each model. Default: None, which means weight == 1 for each model\n    :param iou_thr: IoU value for boxes to be a match\n    :param skip_box_thr: exclude boxes with score lower than this variable\n    :param conf_type: how to calculate confidence in weighted boxes. 'avg': average value, 'max': maximum value, 'box_and_model_avg': box and model wise hybrid weighted average, 'absent_model_aware_avg': weighted average that takes into account the absent model.\n    :param allows_overflow: false if we want confidence score not exceed 1.0\n\n    :return: boxes: boxes coordinates (Order of boxes: x1, y1, x2, y2).\n    :return: scores: confidence scores\n    :return: labels: boxes labels\n    :return: wbfo: original boxes coordinates for each fused box\n    '''\n\n    if weights is None:\n        weights = np.ones(len(boxes_list))\n    if len(weights) != len(boxes_list):\n        print('Warning: incorrect number of weights {}. Must be: {}. Set weights equal to 1.'.format(len(weights), len(boxes_list)))\n        weights = np.ones(len(boxes_list))\n    weights = np.array(weights)\n\n    if conf_type not in ['avg', 'max', 'box_and_model_avg', 'absent_model_aware_avg']:\n        print('Unknown conf_type: {}. Must be \"avg\", \"max\" or \"box_and_model_avg\", or \"absent_model_aware_avg\"'.format(conf_type))\n        sys.exit()\n\n    filtered_boxes = prefilter_boxes(boxes_list, scores_list, labels_list, weights, skip_box_thr)\n\n    if len(filtered_boxes) == 0:\n        return np.zeros((0, 4)), np.zeros((0,)), np.zeros((0,)), np.zeros((0, 4))\n    \n    overall_boxes = []\n    original_boxes = []\n    for label in filtered_boxes:\n        boxes = filtered_boxes[label]\n        new_boxes = []\n        weighted_boxes = []\n        # Clusterize boxes\n        for j in range(0, len(boxes)):\n            index, best_iou = find_matching_box(weighted_boxes, boxes[j], iou_thr)\n            if index != -1:\n                new_boxes[index].append(boxes[j])\n                weighted_boxes[index] = get_weighted_box(new_boxes[index], conf_type)\n            else:\n                new_boxes.append([boxes[j].copy()])\n                weighted_boxes.append(boxes[j].copy())\n        # Rescale confidence based on number of models and boxes\n        original_boxes.append(new_boxes)\n        for i in range(len(new_boxes)):\n            clustered_boxes = np.array(new_boxes[i])\n            if conf_type == 'box_and_model_avg':\n                # weighted average for boxes\n                weighted_boxes[i][1] = weighted_boxes[i][1] * len(clustered_boxes) / weighted_boxes[i][2]\n                # identify unique model index by model index column\n                _, idx = np.unique(clustered_boxes[:, 3], return_index=True)\n                # rescale by unique model weights\n                weighted_boxes[i][1] = weighted_boxes[i][1] *  clustered_boxes[idx, 2].sum() / weights.sum()\n            elif conf_type == 'absent_model_aware_avg':\n                # get unique model index in the cluster\n                models = np.unique(clustered_boxes[:, 3]).astype(int)\n                # create a mask to get unused model weights\n                mask = np.ones(len(weights), dtype=bool)\n                mask[models] = False\n                # absent model aware weighted average\n                weighted_boxes[i][1] = weighted_boxes[i][1] * len(clustered_boxes) / (weighted_boxes[i][2] + weights[mask].sum())\n            elif conf_type == 'max':\n                weighted_boxes[i][1] = weighted_boxes[i][1] / weights.max()\n            elif not allows_overflow:\n                weighted_boxes[i][1] = weighted_boxes[i][1] * min(len(weights), len(clustered_boxes)) / weights.sum()\n            else:\n                weighted_boxes[i][1] = weighted_boxes[i][1] * len(clustered_boxes) / weights.sum()\n        overall_boxes.append(np.array(weighted_boxes))\n    overall_boxes = np.concatenate(overall_boxes, axis=0)\n    sidx = overall_boxes[:, 1].argsort()\n    overall_boxes = overall_boxes[sidx[::-1]]\n    boxes = overall_boxes[:, 4:]\n    scores = overall_boxes[:, 1]\n    labels = overall_boxes[:, 0]\n    # sort originals accoring to wbf\n    original_boxes = original_boxes[0]\n    wbfo = [original_boxes[i] for i in sidx[::-1]]\n    return boxes, scores, labels, wbfo","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.383107Z","iopub.execute_input":"2023-12-09T02:38:22.383777Z","iopub.status.idle":"2023-12-09T02:38:22.405665Z","shell.execute_reply.started":"2023-12-09T02:38:22.383744Z","shell.execute_reply":"2023-12-09T02:38:22.404723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def combine_results_nmms(masks, boxes, scores, iou_nms=0.5):\n    # assert list(np.argsort(boxes[:, 4])[::-1]) == list(range(len(boxes)))\n    order = np.argsort(scores)[::-1]\n    masks = masks[order]\n    boxes = boxes[order]\n\n    rle_pred = [pycocotools.mask.encode(np.asarray(m, order='F')) for m in masks]\n    ious = pycocotools.mask.iou(rle_pred, rle_pred, [0] * len(rle_pred))\n\n    picks = []\n    idxs = list(range(len(ious)))\n    # removed = []\n\n    while len(idxs) > 0:\n        idx = idxs[0]\n        overlapping = np.where(ious[idx] > iou_nms)[0]\n\n        # removed += [v for v in overlapping if v > idx]\n\n        if len(overlapping):\n            picks.append(idx)\n            idxs = [i for i in idxs if i not in overlapping]\n        else:\n            idxs = idxs[1:]\n\n    masks = masks[picks]\n    boxes = boxes[picks]\n    return masks, boxes, scores[picks]","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.406895Z","iopub.execute_input":"2023-12-09T02:38:22.407202Z","iopub.status.idle":"2023-12-09T02:38:22.422123Z","shell.execute_reply.started":"2023-12-09T02:38:22.407177Z","shell.execute_reply":"2023-12-09T02:38:22.421217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def bbox_to_key(bbox):\n    return ','.join(map(str, np.round(bbox, 4)))","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.423308Z","iopub.execute_input":"2023-12-09T02:38:22.423684Z","iopub.status.idle":"2023-12-09T02:38:22.432359Z","shell.execute_reply.started":"2023-12-09T02:38:22.423640Z","shell.execute_reply":"2023-12-09T02:38:22.431418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def combine_results_wbf(masks, boxes, scores, iou_thr=0.55, skip_box_thr=0.0, label_thr=0, min_votes=2, cut_by_box=False, weights=None):\n    bbox_to_idx = {}\n    h, w = masks.shape[-2:]\n    \n    for i, (mask, box) in enumerate(zip(masks, boxes)):\n        bbox_to_idx[bbox_to_key(box)] = i\n    \n    labels = np.ones(len(boxes))  \n    wbf_boxes, _, _, wbf_ogs = weighted_boxes_fusion_tracking(\n        [boxes],\n        [scores],\n        labels_list=[labels],\n        iou_thr=iou_thr,\n        skip_box_thr=skip_box_thr,\n        weights=weights,\n    )\n\n    wbf_masks, wbf_scores = [], []\n    for i, wbf_box in enumerate(wbf_boxes):\n        \n        if cut_by_box:\n            filter_box = np.zeros((h, w), dtype=np.uint8)\n\n            x1 = max(0, int(h * wbf_box[0]))\n            y1 = max(0, int(w * wbf_box[1]))\n            x2 = min(h, int(h * wbf_box[2]))\n            y2 = min(w, int(w * wbf_box[3]))\n            filter_box[y1:y2, x1:x2] = 1\n        \n        keep = []\n        skiped = 0\n        for og_box in wbf_ogs[i]:\n            key = bbox_to_key(og_box[4:])\n            if key in bbox_to_idx:\n                keep.append(bbox_to_idx[key])\n            else:\n                skiped += 1\n                continue\n        if len(keep) < min_votes-skiped:\n            wbf_mask = np.zeros((h, w), dtype=np.uint8)\n            wbf_score = 0\n        else:\n            wbf_mask = (np.mean(masks[keep], axis=0) > label_thr).astype(bool)\n            if cut_by_box:\n                wbf_mask = wbf_mask & filter_box # remove pixels outside wbf\n            wbf_score = np.mean(scores[keep])\n\n        wbf_masks.append(wbf_mask)\n        wbf_scores.append(wbf_score)\n    \n    wbf_masks = np.stack(wbf_masks)\n    wbf_scores = np.array(wbf_scores) \n    return wbf_masks, wbf_boxes, wbf_scores","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.433610Z","iopub.execute_input":"2023-12-09T02:38:22.435782Z","iopub.status.idle":"2023-12-09T02:38:22.447974Z","shell.execute_reply.started":"2023-12-09T02:38:22.435730Z","shell.execute_reply":"2023-12-09T02:38:22.447153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def remove_overlap_naive(masks, ious=None):\n    if ious is None:\n        rles = [pycocotools.mask.encode(np.asarray(m, order='F')) for m in masks]\n        ious = pycocotools.mask.iou(rles, rles, [0] * len(rles))\n\n    if not len(ious):\n        return masks\n    \n    for i in range(len(ious)):\n        ious[i, i] = 0\n    to_process = np.where(ious.sum(0) > 0)[0]\n\n    if not len(to_process):\n        return masks\n\n    masks = torch.from_numpy(masks).cuda('cuda:0')\n    overlapping_masks = masks[to_process]\n\n    for idx, i in enumerate(to_process):\n        if idx == 0:\n            continue\n        others = overlapping_masks[:idx].max(0)[0]\n        masks[i] *= ~others\n\n    return masks.cpu().numpy()","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.449375Z","iopub.execute_input":"2023-12-09T02:38:22.450332Z","iopub.status.idle":"2023-12-09T02:38:22.463874Z","shell.execute_reply.started":"2023-12-09T02:38:22.450298Z","shell.execute_reply":"2023-12-09T02:38:22.462851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def remove_small_masks(masks, boxes, min_size=0):\n    if min_size == 0 or len(masks) == 0:\n        return masks, boxes\n\n    sizes = masks.sum(-1).sum(-1) / 512**2\n    to_keep = sizes > min_size\n\n    if to_keep.min() == 1:\n        return masks, boxes\n\n    smallest = sizes.min()\n    to_keep = sizes > smallest\n\n    return masks[to_keep], boxes[to_keep]","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.465002Z","iopub.execute_input":"2023-12-09T02:38:22.465393Z","iopub.status.idle":"2023-12-09T02:38:22.478662Z","shell.execute_reply.started":"2023-12-09T02:38:22.465366Z","shell.execute_reply":"2023-12-09T02:38:22.477872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def degrade_mask(mask):\n    cont, hier = cv2.findContours(mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n\n    img_cont = np.zeros((mask.shape[0], mask.shape[1], 3), dtype=np.uint8)\n    img_cont = cv2.drawContours(img_cont, cont, -1, (255, 255, 255), 1)\n    img_cont = img_cont[:, :, 0]\n\n    conv_mask = np.zeros((mask.shape[0], mask.shape[1], 3), dtype=np.uint8)\n\n    for c in cont:\n        conv_mask = cv2.fillConvexPoly(conv_mask, points=c, color=(1, 1, 1))\n    conv_mask = conv_mask[:, :, 0].astype(mask.dtype)\n\n    return conv_mask, img_cont","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.479925Z","iopub.execute_input":"2023-12-09T02:38:22.480293Z","iopub.status.idle":"2023-12-09T02:38:22.489070Z","shell.execute_reply.started":"2023-12-09T02:38:22.480261Z","shell.execute_reply":"2023-12-09T02:38:22.488178Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def postprocess_masks(masks, boxes,\n                      iou_nms=0.6,\n                      conf_thresh=0,\n                      min_size=0,\n                      dilation_n_iter=1,\n                      remove_overlap=True,\n                      corrupt=False,\n                     ):\n\n    # Sort by decreasing conf\n    order = np.argsort(boxes[:, 4])[::-1]\n    masks = masks[order]\n    boxes = boxes[order]\n\n    # Remove low confidence\n    last = (\n        np.argmax(boxes[:, 4] < conf_thresh) if np.min(boxes[:, 4]) < conf_thresh\n        else len(boxes)\n    )\n    masks = masks[:last]\n    boxes = boxes[:last]\n\n    # Remove small masks\n    if min_size:\n        masks, boxes = remove_small_masks(masks, boxes, min_size=min_size)\n       \n    # Corrupt\n    if corrupt:\n        masks = np.array([degrade_mask(mask)[0] for mask in masks])\n\n    # Remove overlap\n    if remove_overlap:\n        masks = remove_overlap_naive(masks)\n    \n    # Dilate\n    if dilation_n_iter != 0:\n        for i in range(len(masks)):\n            for _ in range(dilation_n_iter):\n                masks[i] = binary_dilation(masks[i])\n\n    return masks, boxes","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.490292Z","iopub.execute_input":"2023-12-09T02:38:22.490585Z","iopub.status.idle":"2023-12-09T02:38:22.500628Z","shell.execute_reply.started":"2023-12-09T02:38:22.490560Z","shell.execute_reply":"2023-12-09T02:38:22.499804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from dataclasses import dataclass\nimport albumentations as A\n\n@dataclass\nclass CFG:\n    # inference\n    # conf: float = 0.01\n    imgsz: int = (512, 512)\n    # retina_masks: bool = True\n    # iou_nms: float = 0.5\n#     transforms: tuple = (None, A.HorizontalFlip(p=1.0), A.VerticalFlip(p=1.0), A.Rotate(limit=(180,180), p=1.0))\n    transforms: tuple = (None, A.HorizontalFlip(p=1.0), A.VerticalFlip(p=1.0))\n        \n    \n    # ensemble\n    method: str = 'wbf' # 'nms' or 'wbf'\n    iou_ensemble: float = 0.5\n\n    wbf_min_votes: int = 2\n    wbf_label_thr: float = 0.6\n    wbf_cut_by_box: bool = 1\n    # postproccess\n    conf_thresh: float = 0.01\n    min_size: float = 0.001\n    dilation_n_iter: int = 0\n    remove_overlap: bool = True\n    corrupt: bool = False\n        \n    # models\n    models = [\n        dict(\n            config_file = '/kaggle/input/cascade-mask-rcnn-convnext-lsj/custom_config_exp-cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco-lsj.py',\n            checkpoint_file = '/kaggle/input/cascade-mask-rcnn-convnext-lsj/best_coco_bbox_mAP_epoch_30.pth',\n            weight = 0.3\n        ),\n        dict(\n            config_file = '/kaggle/input/cascade-mask-rcnn-convnext-baseline-3x/custom_config_exp-cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco-baseline-3x.py',\n            checkpoint_file = '/kaggle/input/cascade-mask-rcnn-convnext-baseline-3x/best_coco_bbox_mAP_epoch_31.pth',\n            weight = 0.3\n        ),\n        dict(\n            config_file = '/kaggle/input/cascade-mask-rcnn-convnext-pretrain-fold2/custom_config_exp-cascade-mask-rcnn_convnext-s-p4-w7_fpn_4conv1fc-giou_amp-ms-crop-3x_coco-pretrain-fold2.py',\n            checkpoint_file = '/kaggle/input/cascade-mask-rcnn-convnext-pretrain-fold2/best_coco_bbox_mAP_epoch_28.pth',\n            weight = 0.3\n        ),\n        #dict(\n        #    config_file = '/kaggle/input/davidz-yolov5-hubmap-pretrain/config.py',\n        #    checkpoint_file = '/kaggle/input/davidz-yolov5-hubmap-pretrain/weight.pth',\n        #    weight = 0.03\n        #),\n        dict(\n            config_file = '/kaggle/input/ms-rcnn-1-2/ms-rcnn-1-2/custom_config_ms-rcnn_r50_fpn_1x_coco.py',\n            checkpoint_file = '/kaggle/input/ms-rcnn-1-2/ms-rcnn-1-2/best_coco_segm_mAP_epoch_20.pth',\n            weight = 0.04\n        ),\n        dict(\n            config_file = '/kaggle/input/scnet-fold3/custom_config_exp-scnet_x101-64x4d_fpn_20e_coco-fold3.py',\n            checkpoint_file = '/kaggle/input/scnet-fold3/best_coco_bbox_mAP_epoch_5.pth',\n            weight = 0.06\n        )\n    ]\n\n    def __repr__(self):\n        params = '\\n'.join(f'{k}={v}' for k, v in self.__dict__.items())\n        return params","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.502230Z","iopub.execute_input":"2023-12-09T02:38:22.502531Z","iopub.status.idle":"2023-12-09T02:38:22.517683Z","shell.execute_reply.started":"2023-12-09T02:38:22.502507Z","shell.execute_reply":"2023-12-09T02:38:22.516854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def normalize_bbox(bbox, rows: int, cols: int):\n    \"\"\"Normalize coordinates of a bounding box. Divide x-coordinates by image width and y-coordinates\n    by image height.\n\n    Args:\n        bbox: Denormalized bounding box `(x_min, y_min, x_max, y_max)`.\n        rows: Image height.\n        cols: Image width.\n\n    Returns:\n        Normalized bounding box `(x_min, y_min, x_max, y_max)`.\n\n    Raises:\n        ValueError: If rows or cols is less or equal zero\n\n    \"\"\"\n\n    if rows <= 0:\n        raise ValueError(\"Argument rows must be positive integer\")\n    if cols <= 0:\n        raise ValueError(\"Argument cols must be positive integer\")\n\n    (x_min, y_min, x_max, y_max), tail = bbox[:4], tuple(bbox[4:])\n\n    x_min, x_max = x_min / cols, x_max / cols\n    y_min, y_max = y_min / rows, y_max / rows\n\n    return (x_min, y_min, x_max, y_max) + tail # type: ignore","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.518848Z","iopub.execute_input":"2023-12-09T02:38:22.519163Z","iopub.status.idle":"2023-12-09T02:38:22.532842Z","shell.execute_reply.started":"2023-12-09T02:38:22.519137Z","shell.execute_reply":"2023-12-09T02:38:22.532055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(\n    image, # image in bgr\n    models,\n    transforms=[None],\n    imgsz=512,\n    conf=0.01,\n    iou_nms=0.5,\n    retina_masks=True,\n):\n    boxes, masks, scores = [], [], []\n\n    og_img = np.array(image.copy()).astype(np.uint8)\n    h, w = og_img.shape[:2]\n\n    for model in models:\n        for transform in transforms:\n            if transform is not None:\n                img = transform(image=og_img)[\"image\"]\n            else:\n                img = og_img.copy()\n\n            # pred = model.predict(\n            #     img, imgsz=imgsz, conf=conf, iou=iou_nms, retina_masks=retina_masks, verbose=False, \n            # )[0]\n            pred = inference_detector(model, img)\n\n            # if pred.masks is None:\n            #     continue\n            if pred.pred_instances['masks'] is None:\n                continue\n\n            # pred_masks = pred.masks.data[pred.boxes.cls == 0].detach().cpu().numpy()\n            pred_masks = pred.pred_instances['masks'][pred.pred_instances['labels'] == 0].detach().cpu().numpy()\n\n            if len(pred_masks) == 0:\n                continue\n            # pred_confs = pred.boxes.conf[pred.boxes.cls == 0].detach().cpu().numpy()\n            # pred_boxes = pred.boxes.xyxyn[pred.boxes.cls == 0].detach().cpu().numpy()\n            pred_confs = pred.pred_instances['scores'][pred.pred_instances['labels'] == 0].detach().cpu().numpy()\n            pred_boxes = pred.pred_instances['bboxes'][pred.pred_instances['labels'] == 0].detach().cpu().numpy()\n            pred_boxes = [normalize_bbox(box, h, w) for box in pred_boxes]\n\n            if transform is not None:\n                out = transform(image=img, bboxes=pred_boxes, masks=pred_masks)\n                pred_boxes = out[\"bboxes\"]\n                pred_masks  = out[\"masks\"]\n\n            boxes.append(np.array(pred_boxes))\n            masks.append(pred_masks)\n            scores.append(np.array(pred_confs))\n            del pred, pred_boxes, pred_masks, img\n    del og_img\n    if len(boxes) == 0:\n        return np.array([]), np.array([]), np.array([])\n    boxes, masks, scores = np.concatenate(boxes), np.concatenate(masks).astype(np.uint8), np.concatenate(scores)\n    \n    return masks, boxes, scores","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.534273Z","iopub.execute_input":"2023-12-09T02:38:22.534849Z","iopub.status.idle":"2023-12-09T02:38:22.549888Z","shell.execute_reply.started":"2023-12-09T02:38:22.534816Z","shell.execute_reply":"2023-12-09T02:38:22.548939Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from mmdet.apis import init_detector, inference_detector\n\nmodels = [init_detector(model['config_file'], model['checkpoint_file'], device='cuda:0') for model in CFG.models]\nweights = [model['weight'] for model in CFG.models]","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:22.551146Z","iopub.execute_input":"2023-12-09T02:38:22.552284Z","iopub.status.idle":"2023-12-09T02:38:31.050328Z","shell.execute_reply.started":"2023-12-09T02:38:22.552255Z","shell.execute_reply":"2023-12-09T02:38:31.049471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_rows = []\nresults_csv = pd.DataFrame([], columns=[\"id\",\"height\",\"width\",\"prediction_string\"])\nresults_csv.set_index(\"id\")\n\nfor dirname, _, filenames in os.walk('/kaggle/input/hubmap-hacking-the-human-vasculature/test'):\n    for filename in filenames:\n\n        image = cv2.imread(os.path.join(dirname, filename))\n        width, height = image.shape[:2]\n\n        row = dict()\n        row[\"id\"] = filename[:-4]\n        row[\"height\"] = height\n        row[\"width\"] = width\n        row[\"prediction_string\"] = \"\"\n\n        raw_masks, raw_boxes, raw_scores = predict(\n            image=image,\n            models=models,\n            transforms=CFG.transforms,\n            imgsz=CFG.imgsz,\n            # conf=CFG.conf,\n            # iou_nms=CFG.iou_nms,\n            # retina_masks=CFG.retina_masks\n        )\n\n        if len(raw_boxes):\n            if CFG.method == 'wbf':\n                masks, boxes, scores = combine_results_wbf(\n                    raw_masks, raw_boxes, raw_scores,\n                    iou_thr=CFG.iou_ensemble,\n                    min_votes=CFG.wbf_min_votes, \n                    label_thr=CFG.wbf_label_thr,\n                    cut_by_box=CFG.wbf_cut_by_box,\n                    weights=weights\n                )\n            elif CFG.method == 'nms':\n                masks, boxes, scores = combine_results_nmms(\n                    raw_masks, raw_boxes,\n                    raw_scores, iou_nms=CFG.iou_ensemble\n                )\n            else:\n                masks, boxes, scores = raw_masks, raw_boxes, raw_scores\n        else:\n            masks, boxes, scores = raw_masks, raw_boxes, raw_scores\n\n        if len(boxes):\n            boxes = np.concatenate((boxes, scores[:, None]), axis=1)  # add conf to boxes\n            masks, boxes = postprocess_masks(\n                masks,\n                boxes,\n                conf_thresh=CFG.conf_thresh,\n                min_size=CFG.min_size,\n                dilation_n_iter=CFG.dilation_n_iter,\n                remove_overlap=CFG.remove_overlap,\n                corrupt=CFG.corrupt\n            )\n            scores = boxes[:, 4]\n        else:\n            masks = np.zeros((1, 512, 512), dtype=np.uint8)\n            scores = np.array([0])\n\n        non_zero_masks = []\n        for i, (mask, score) in enumerate(sorted(zip(masks, scores), key=lambda x: x[1], reverse=True)):\n            if score == 0:\n                continue\n            non_zero_masks.append(mask)\n            coded_len = encode_binary_mask(mask.astype(bool)).decode('utf-8')\n            row[\"prediction_string\"] += '0 ' + str(score)+' '+ coded_len+' '\n        \n        new_row = pd.DataFrame(row, index=[0])\n        results_csv = pd.concat([new_row, results_csv.loc[:]]).reset_index(drop=True)\n\nresults_csv.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:31.051605Z","iopub.execute_input":"2023-12-09T02:38:31.051902Z","iopub.status.idle":"2023-12-09T02:38:35.682008Z","shell.execute_reply.started":"2023-12-09T02:38:31.051876Z","shell.execute_reply":"2023-12-09T02:38:35.680937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_csv.head()","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:35.683399Z","iopub.execute_input":"2023-12-09T02:38:35.683702Z","iopub.status.idle":"2023-12-09T02:38:35.694211Z","shell.execute_reply.started":"2023-12-09T02:38:35.683676Z","shell.execute_reply":"2023-12-09T02:38:35.693226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generate Image","metadata":{}},{"cell_type":"code","source":"import json\nimport matplotlib.pyplot as plt\n\n# # cell type specific thresholds\ncell_type_dict = {'blood_vessel': 1, 'glomerulus': 2, 'unsure': 3}\n\nmask_threshold_dict = {0: 0.4, 1: 0.80, 2:  0.80}\nmin_score_dict = {0: 0.4, 1: 0.80, 2: 0.80}\n\ndef remove_overlapping_pixels(mask, other_masks):\n    for other_mask in other_masks:\n        if np.sum(np.logical_and(mask, other_mask)) > 0:\n            mask[np.logical_and(mask, other_mask)] = 0\n    return mask\n\nindex = 100\n\nwith open('/kaggle/input/hubmap-hacking-the-human-vasculature/polygons.jsonl', 'r') as json_file:\n    json_list = list(json_file)\n\ndef get_cartesian_coords(coords, img_height):\n    coords_array = np.array(coords).squeeze()\n    xs = coords_array[:, 0]\n    ys = coords_array[:, 1] # + img_height\n    \n    return xs, ys\n    \ntiles_dicts = []\nfor json_str in json_list:\n    tiles_dicts.append(json.loads(json_str))\n\nannotations = tiles_dicts[index][\"annotations\"]\nimg = f\"/kaggle/input/hubmap-hacking-the-human-vasculature/train/{tiles_dicts[index]['id']}.tif\"\n\nimage = cv2.imread(str(img))\nimage = np.array(image.copy()).astype(np.uint8)\n    \n\n    \ncolors = [ 'Set1', 'Set3'] \nlegend = {0: 'blood_vessel',1: 'glomerulus'} \nfrom skimage import io\nimport matplotlib.patches as mpatches\nfig, axs = plt.subplots(1, 3, figsize=(15, 5))\n\n# axs[0].set_axis_off()\n# axs[1].set_axis_off()\n\nI = io.imread(str(img))\n# axs[0].imshow(I)\naxs[0].set_title('Ground Truth')\naxs[0].imshow(I)\n\n# Add polygons\nfor annotation in annotations:\n    if annotation[\"type\"] != \"blood_vessel\":\n        continue\n\n    xs, ys = get_cartesian_coords(annotation[\"coordinates\"], 512)\n    polygon = plt.Polygon(list(zip(xs, ys)), closed=True, edgecolor='red', fill=True, facecolor='red', alpha=0.4)\n    axs[0].add_patch(polygon)  # Add the polygon with face color and opacity to the plot\n    # ax.plot(xs, ys, label=annotation[\"type\"])  # You can adjust the plot style as needed\n\n    \n    \naxs[1].imshow(I)\npred = inference_detector(models[0], image)\nprevious_masks = []\nfor i, mask in enumerate(pred.pred_instances[\"masks\"]):\n    # Filter-out low-scoring results.\n    score = pred.pred_instances[\"scores\"][i].cpu().item()\n    label = pred.pred_instances[\"labels\"][i].cpu().item()\n    if score > min_score_dict[label]:\n        mk = mask.cpu().numpy()\n        # Keep only highly likely pixels\n        binary_mask = mk > mask_threshold_dict[label]\n        binary_mask = remove_overlapping_pixels(binary_mask, previous_masks)\n        previous_masks.append(binary_mask)\n        color = colors[label]\n        mask = np.ma.masked_where(mk == 0, mk)\n        axs[1].imshow(mask, cmap=color, alpha=0.6)\n        axs[1].set_title('Baseline: Predicted Masks')\n        # Add score text on each segment\n        y, x = np.where(mk > 0)\n        text_x, text_y = np.min(x), np.min(y)\n        axs[1].text(text_x, text_y, f\"{score:.2f}\", color='white', fontsize=8)\n        handles = []\n        for cl in legend:\n            color = colors[cl]\n            handles.append(mpatches.Patch(color=plt.get_cmap(color)(0)))\n        # axs[1].legend(handles, legend.values(), bbox_to_anchor=(1.05, 1), loc='upper left')\n\n\n# --------------------------------------------\n\nwidth, height = image.shape[:2]\n\nrow = dict()\nrow[\"id\"] = filename[:-4]\nrow[\"height\"] = height\nrow[\"width\"] = width\nrow[\"prediction_string\"] = \"\"\n\nraw_masks, raw_boxes, raw_scores = predict(\n    image=image,\n    models=models,\n    transforms=CFG.transforms,\n    imgsz=CFG.imgsz,\n    # conf=CFG.conf,\n    # iou_nms=CFG.iou_nms,\n    # retina_masks=CFG.retina_masks\n)\n\nif len(raw_boxes):\n    if CFG.method == 'wbf':\n        masks, boxes, scores = combine_results_wbf(\n            raw_masks, raw_boxes, raw_scores,\n            iou_thr=CFG.iou_ensemble,\n            min_votes=CFG.wbf_min_votes, \n            label_thr=CFG.wbf_label_thr,\n            cut_by_box=CFG.wbf_cut_by_box,\n            weights=weights\n        )\n    elif CFG.method == 'nms':\n        masks, boxes, scores = combine_results_nmms(\n            raw_masks, raw_boxes,\n            raw_scores, iou_nms=CFG.iou_ensemble\n        )\n    else:\n        masks, boxes, scores = raw_masks, raw_boxes, raw_scores\nelse:\n    masks, boxes, scores = raw_masks, raw_boxes, raw_scores\n\nif len(boxes):\n    boxes = np.concatenate((boxes, scores[:, None]), axis=1)  # add conf to boxes\n    masks, boxes = postprocess_masks(\n        masks,\n        boxes,\n        conf_thresh=CFG.conf_thresh,\n        min_size=CFG.min_size,\n        dilation_n_iter=CFG.dilation_n_iter,\n        remove_overlap=CFG.remove_overlap,\n        corrupt=CFG.corrupt\n    )\n    scores = boxes[:, 4]\nelse:\n    masks = np.zeros((1, 512, 512), dtype=np.uint8)\n    scores = np.array([0])        \n\n# --------------------------------------------\n    \naxs[2].imshow(I)\nprevious_masks = []\nfor i, mask in enumerate(masks):\n    # Filter-out low-scoring results.\n    score = scores[i]\n    label = 0\n    if score > min_score_dict[label]:\n        mk = mask\n        # Keep only highly likely pixels\n        binary_mask = mk > mask_threshold_dict[label]\n        binary_mask = remove_overlapping_pixels(binary_mask, previous_masks)\n        previous_masks.append(binary_mask)\n        color = colors[label]\n        mask = np.ma.masked_where(mk == 0, mk)\n        axs[2].imshow(mask, cmap=color, alpha=0.6)\n        axs[2].set_title('Ensemble Model: Predicted Masks')\n        # Add score text on each segment\n        y, x = np.where(mk > 0)\n        text_x, text_y = np.min(x), np.min(y)\n        axs[2].text(text_x, text_y, f\"{score:.2f}\", color='white', fontsize=8)\n        handles = []\n        for cl in legend:\n            color = colors[cl]\n            handles.append(mpatches.Patch(color=plt.get_cmap(color)(0)))\n        # axs[1].legend(handles, legend.values(), bbox_to_anchor=(1.05, 1), loc='upper left')\n\nplt.savefig('output.png')      ","metadata":{"execution":{"iopub.status.busy":"2023-12-09T02:38:35.695661Z","iopub.execute_input":"2023-12-09T02:38:35.695965Z","iopub.status.idle":"2023-12-09T02:38:47.031187Z","shell.execute_reply.started":"2023-12-09T02:38:35.695940Z","shell.execute_reply":"2023-12-09T02:38:47.030295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}