{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.10","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":52279,"databundleVersionId":5822112},{"sourceType":"datasetVersion","sourceId":6073875,"datasetId":3476711,"databundleVersionId":6152255},{"sourceType":"datasetVersion","sourceId":6057697,"datasetId":3466282,"databundleVersionId":6136002},{"sourceType":"datasetVersion","sourceId":6084483,"datasetId":3483806,"databundleVersionId":6162906},{"sourceType":"datasetVersion","sourceId":6013715,"datasetId":3442881,"databundleVersionId":6091696},{"sourceType":"datasetVersion","sourceId":6140122,"datasetId":3456413,"databundleVersionId":6218969},{"sourceType":"datasetVersion","sourceId":6044975,"datasetId":3457644,"databundleVersionId":6123162},{"sourceType":"datasetVersion","sourceId":16503563,"datasetId":10571180,"databundleVersionId":17506079},{"sourceType":"datasetVersion","sourceId":6111787,"datasetId":3501748,"databundleVersionId":6190394},{"sourceType":"datasetVersion","sourceId":5872017,"datasetId":3375494,"databundleVersionId":5949206},{"sourceType":"datasetVersion","sourceId":6101554,"datasetId":3495052,"databundleVersionId":6180118},{"sourceType":"datasetVersion","sourceId":6109993,"datasetId":2437951,"databundleVersionId":6188580},{"sourceType":"datasetVersion","sourceId":4165268,"datasetId":2458397,"databundleVersionId":4221946},{"sourceType":"datasetVersion","sourceId":6085416,"datasetId":3484432,"databundleVersionId":6163854},{"sourceType":"datasetVersion","sourceId":1986198,"datasetId":1187413,"databundleVersionId":2025384},{"sourceType":"datasetVersion","sourceId":4575353,"datasetId":2669063,"databundleVersionId":4636641},{"sourceType":"datasetVersion","sourceId":4125181,"datasetId":2437947,"databundleVersionId":4181535},{"sourceType":"datasetVersion","sourceId":6135778,"datasetId":3518283,"databundleVersionId":6214583},{"sourceType":"datasetVersion","sourceId":6160534,"datasetId":3534117,"databundleVersionId":6239514},{"sourceType":"datasetVersion","sourceId":6140593,"datasetId":3521226,"databundleVersionId":6219443},{"sourceType":"datasetVersion","sourceId":5756795,"datasetId":3308073,"databundleVersionId":5833161},{"sourceType":"datasetVersion","sourceId":5875196,"datasetId":3377240,"databundleVersionId":5952396},{"sourceType":"datasetVersion","sourceId":5824224,"datasetId":3347112,"databundleVersionId":5901089},{"sourceType":"datasetVersion","sourceId":6140269,"datasetId":3521028,"databundleVersionId":6219116},{"sourceType":"datasetVersion","sourceId":16528251,"datasetId":10585936,"databundleVersionId":17532621},{"sourceType":"kernelVersion","sourceId":135125982},{"sourceType":"kernelVersion","sourceId":137410741},{"sourceType":"kernelVersion","sourceId":137792569},{"sourceType":"kernelVersion","sourceId":137905093},{"sourceType":"kernelVersion","sourceId":137988356},{"sourceType":"kernelVersion","sourceId":138378528}],"dockerImageVersionId":30512,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os, glob\nimport sys\nimport json\nfrom PIL import Image\nfrom collections import Counter\n\nimport numpy as np\nimport pandas as pd\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport tifffile as tiff\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport torch\nimport cv2\n\nimport pandas as pd\n\nfrom sklearn.model_selection import KFold\n\nsys.path.append(\"/kaggle/input/detection-wheel\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:14:41.910211Z","iopub.execute_input":"2026-05-30T19:14:41.910540Z","iopub.status.idle":"2026-05-30T19:14:46.181428Z","shell.execute_reply.started":"2026-05-30T19:14:41.910511Z","shell.execute_reply":"2026-05-30T19:14:46.180520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\ntorch.__version__  #torch 2.0\n#!nvidia-smi   #CUDA Version: 11.4\n! ls /usr/local","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:14:46.183867Z","iopub.execute_input":"2026-05-30T19:14:46.184872Z","iopub.status.idle":"2026-05-30T19:14:47.207574Z","shell.execute_reply.started":"2026-05-30T19:14:46.184847Z","shell.execute_reply":"2026-05-30T19:14:47.206591Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Install pycocotools package\nimport os\n!mkdir /kaggle/working/packages\n!cp -r /kaggle/input/pycocotools/* /kaggle/working/packages\nos.chdir(\"/kaggle/working/packages/pycocotools-2.0.6/\")\n!python setup.py install -q\n!pip install . --no-index --find-links /kaggle/working/packages/ -q\n# # Install mmcv and mmdet packages\n# #3.0\n# #!pip install mmcv mmdet --no-index --find-links /kaggle/input/mmdetection/ -q\n# #os.chdir(\"/kaggle/working\")\n# #ytt 2140\n# !pip install '/kaggle/input/mmdetectionv2140/addict-2.4.0-py3-none-any.whl' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/yapf-0.31.0-py2.py3-none-any.whl' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/terminal-0.4.0-py3-none-any.whl' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/terminaltables-3.1.0-py3-none-any.whl' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/mmcv_full-1_3_8-cu110-torch1_7_0/mmcv_full-1.3.8-cp37-cp37m-manylinux1_x86_64.whl' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/pycocotools-2.0.2/pycocotools-2.0.2' --no-deps\n# !pip install '/kaggle/input/mmdetectionv2140/mmpycocotools-12.0.3/mmpycocotools-12.0.3' --no-deps\n\n# !rm -rf mmdetection\n\n# !cp -r /kaggle/input/mmdetectionv2140/mmdetection-2.14.0 /kaggle/working/\n# !mv /kaggle/working/mmdetection-2.14.0 /kaggle/working/mmdetection\n# %cd /kaggle/working/mmdetection\n# !pip install -e .\n\n\n# 必要なライブラリのインストール（オフライン用）\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/addict-2.4.0-py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/yapf-0.32.0-py2.py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminal-0.4.0-py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminaltables-3.1.10-py2.py3-none-any.whl\n#ytt\n#!pip install /kaggle/input/2023-hhp-mmdet/mmcv_full-1.7.0-cp310-cp310-manylinux1_x86_64_cu113.whl\n!pip install /kaggle/input/mmdet3-wheels/mmcv_full-1.7.1-cp310-cp310-linux_x86_64.whl\n#!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmcv_full-1.7.0-cp37-cp37m-linux_x86_64.whl\n#!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/pycocotools-2.0.6-cp37-cp37m-linux_x86_64.whl\n#!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmpycocotools-12.0.3-cp37-cp37m-linux_x86_64.whl\n!cp -r /kaggle/input/cbnetv2-repo/cbnet_repo /kaggle/working/\n# !cp -r /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmdetection/ /kaggle/working/\n%cd /kaggle/working/cbnet_repo\n!pip install -e . --no-deps\n%cd /kaggle/working/\n\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmdet-2.26.0-py3-none-any.whl\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:14:47.209222Z","iopub.execute_input":"2026-05-30T19:14:47.209530Z","iopub.status.idle":"2026-05-30T19:20:19.867073Z","shell.execute_reply.started":"2026-05-30T19:14:47.209504Z","shell.execute_reply":"2026-05-30T19:20:19.865724Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport sys\n\n# 1. 確保工作目錄乾淨，並把唯讀的套件源碼複製到可讀寫的 /kaggle/working/\n!rm -rf /kaggle/working/pycocotools_build\n!cp -r /kaggle/input/pycocotools-206 /kaggle/working/pycocotools_build\n\n# 2. 切換到可讀寫的編譯工作目錄\nos.chdir(\"/kaggle/working/pycocotools_build\")\n\n# 3. 在這裡執行本地端編譯與安裝（因為在 working 底下，這次有權限寫入檔案了！）\n!python setup.py build_ext --inplace\n!python setup.py install\n\n# 4. 務必切回 Kaggle 的主工作目錄\nos.chdir(\"/kaggle/working\")\n\n# 5. 驗證是否成功安裝並能正常載入\ntry:\n    import pycocotools\n    from pycocotools import _mask as coco_mask\n    print(\"🎉 太棒了！順利繞過唯讀限制，pycocotools 已成功離線安裝並載入！\")\nexcept Exception as e:\n    print(f\"仍然出現錯誤: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:20:19.869288Z","iopub.execute_input":"2026-05-30T19:20:19.870133Z","iopub.status.idle":"2026-05-30T19:20:29.795888Z","shell.execute_reply.started":"2026-05-30T19:20:19.870100Z","shell.execute_reply":"2026-05-30T19:20:29.795003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nimport os\n\n# 1. 我們知道剛才在 /kaggle/working/pycocotools_build 已經編譯成功\n# 編譯好的 Python 模組和 .so 檔會放在 build/lib.linux-x86_64-3.10/ 底下\ncompiled_lib_path = \"/kaggle/working/pycocotools_build/build/lib.linux-x86_64-3.10\"\n\n# 2. 直接將這個路徑插到 Python 搜尋路徑的最前面！\nif compiled_lib_path not in sys.path:\n    sys.path.insert(0, compiled_lib_path)\n\n# 3. 測試載入\ntry:\n    import pycocotools\n    from pycocotools import _mask as coco_mask\n    print(\"🎉 終於成功啦！我們直接載入了編譯好的檔案，完全繞過系統的依賴檢查！\")\n    print(f\"目前載入的 pycocotools 路徑為: {pycocotools.__file__}\")\nexcept Exception as e:\n    print(f\"仍然報錯: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:20:29.797207Z","iopub.execute_input":"2026-05-30T19:20:29.797561Z","iopub.status.idle":"2026-05-30T19:20:29.804322Z","shell.execute_reply.started":"2026-05-30T19:20:29.797532Z","shell.execute_reply":"2026-05-30T19:20:29.803318Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install /kaggle/input/enseibleboxes109/ensemble_boxes-1.0.9-py3-none-any.whl ","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:20:29.805354Z","iopub.execute_input":"2026-05-30T19:20:29.805717Z","iopub.status.idle":"2026-05-30T19:21:10.166884Z","shell.execute_reply.started":"2026-05-30T19:20:29.805695Z","shell.execute_reply":"2026-05-30T19:21:10.165835Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. 確保所有關鍵模組都精準複製到工作目錄根目錄\n# 這樣 Python 執行 import 時，保證能直接讀到這些檔案\n!cp /kaggle/input/detection-wheel/coco_eval.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/engine.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/presets.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/transforms.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/utils.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/coco_utils.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/group_by_aspect_ratio.py /kaggle/working/\n!cp /kaggle/input/detection-wheel/train.py /kaggle/working/\n\n# 2. 驗證一下這些檔案是否都在這了\n!ls -l /kaggle/working/ | grep -E \"coco|engine|presets|transforms|utils|group|train\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:21:10.171104Z","iopub.execute_input":"2026-05-30T19:21:10.171499Z","iopub.status.idle":"2026-05-30T19:21:19.114281Z","shell.execute_reply.started":"2026-05-30T19:21:10.171470Z","shell.execute_reply":"2026-05-30T19:21:19.113331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import base64\nimport numpy as np\nfrom pycocotools import _mask as coco_mask\nimport typing as t\nimport zlib\n\ndef encode_binary_mask(mask: np.ndarray) -> t.Text:\n  \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n\n  # check input mask --\n  if mask.dtype != np.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":"2026-05-30T19:21:19.115770Z","iopub.execute_input":"2026-05-30T19:21:19.116159Z","iopub.status.idle":"2026-05-30T19:21:19.124367Z","shell.execute_reply.started":"2026-05-30T19:21:19.116133Z","shell.execute_reply":"2026-05-30T19:21:19.123312Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport torch\nfrom PIL import Image\n\n\nclass PennFudanDataset(torch.utils.data.Dataset):\n    def __init__(self, imgs, transforms):\n        self.transforms = transforms\n        # load all image files, sorting them to\n        # ensure that they are aligned\n        self.imgs = imgs\n        self.name_indices = [os.path.splitext(os.path.basename(i))[0] for i in imgs]\n\n    def __getitem__(self, idx):\n        # load images and masks\n        img_path = self.imgs[idx]\n        name = self.name_indices[idx]\n        array = tiff.imread(img_path)\n        img = Image.fromarray(array)\n        \n        img, _ = self.transforms(img, img)\n\n        return img, name\n\n    def __len__(self):\n        return len(self.imgs)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.125526Z","iopub.execute_input":"2026-05-30T19:21:19.125731Z","iopub.status.idle":"2026-05-30T19:21:19.134705Z","shell.execute_reply.started":"2026-05-30T19:21:19.125712Z","shell.execute_reply":"2026-05-30T19:21:19.133956Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile wbf_tracking.py\n\n# coding: utf-8\n\n__author__ = 'ZFTurbo: https://kaggle.com/zfturbo'\n# Modified by Mista G: https://www.kaggle.com/mistag\n\nimport warnings\nimport numpy as np\nfrom numba import jit\n\n@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\n\n\ndef 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\n\n\ndef 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\n\n\ndef 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    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":"2026-05-30T19:21:19.136183Z","iopub.execute_input":"2026-05-30T19:21:19.136563Z","iopub.status.idle":"2026-05-30T19:21:19.151626Z","shell.execute_reply.started":"2026-05-30T19:21:19.136534Z","shell.execute_reply":"2026-05-30T19:21:19.150632Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import transforms as T\n\ndef get_transform(train):\n    transforms = []\n    transforms.append(T.PILToTensor())\n    transforms.append(T.ConvertImageDtype(torch.float))\n    return T.Compose(transforms)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.152605Z","iopub.execute_input":"2026-05-30T19:21:19.152913Z","iopub.status.idle":"2026-05-30T19:21:19.375210Z","shell.execute_reply.started":"2026-05-30T19:21:19.152880Z","shell.execute_reply":"2026-05-30T19:21:19.374483Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from engine import train_one_epoch, evaluate\nimport utils","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.376296Z","iopub.execute_input":"2026-05-30T19:21:19.376603Z","iopub.status.idle":"2026-05-30T19:21:19.392850Z","shell.execute_reply.started":"2026-05-30T19:21:19.376581Z","shell.execute_reply":"2026-05-30T19:21:19.391785Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.394027Z","iopub.execute_input":"2026-05-30T19:21:19.394367Z","iopub.status.idle":"2026-05-30T19:21:19.424829Z","shell.execute_reply.started":"2026-05-30T19:21:19.394335Z","shell.execute_reply":"2026-05-30T19:21:19.423808Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sys.path.append('/kaggle/input/einops/einops-master')\n\nsys.path.append(\"../input/pretrained-models-pytorch\")\nsys.path.append(\"../input/efficientnet-pytorch\")\nsys.path.append(\"/kaggle/input/smp-github/segmentation_models.pytorch-master\")","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.426153Z","iopub.execute_input":"2026-05-30T19:21:19.426511Z","iopub.status.idle":"2026-05-30T19:21:19.434672Z","shell.execute_reply.started":"2026-05-30T19:21:19.426477Z","shell.execute_reply":"2026-05-30T19:21:19.433834Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nprint(sys.path)\n!cp -r /kaggle/input/vitadadapter/hubmap/vitadap/vitadapzip/ ./\nsys.path.insert(1, '/kaggle/working/vitadapzip')\nprint(sys.path)\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:21:19.435978Z","iopub.execute_input":"2026-05-30T19:21:19.436496Z","iopub.status.idle":"2026-05-30T19:21:25.318963Z","shell.execute_reply.started":"2026-05-30T19:21:19.436456Z","shell.execute_reply":"2026-05-30T19:21:25.317880Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nimport os\n\n# 1. 將 Kaggle 的標準套件庫路徑與先前編譯的路徑強制加回最前面\n# 這樣可以確保 Python 能夠順利偵測到剛剛安裝的 mmcv-full 內容\npaths = [\n    '/opt/conda/lib/python3.10/site-packages',\n    '/kaggle/working/cbnet_repo'\n]\n\nfor p in paths:\n    if p not in sys.path:\n        sys.path.insert(0, p)\n\n# 2. 測試是否能正常 import mmcv\ntry:\n    import mmcv\n    print(f\"🎉 成功！mmcv 已經被正確載入，版本為: {mmcv.__version__}\")\nexcept ModuleNotFoundError:\n    print(\"❌ 依舊找不到 mmcv，我們嘗試重新強制連結一次套件...\")\n    # 如果系統路徑真的搞丟了，直接用強迫手段執行一次\n    !pip install /kaggle/input/mmdet3-wheels/mmcv_full-1.7.1-cp310-cp310-linux_x86_64.whl --no-deps --force-reinstall\n    import mmcv\n    print(f\"🎉 強制重新安裝後，mmcv 成功載入！版本: {mmcv.__version__}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:21:25.320554Z","iopub.execute_input":"2026-05-30T19:21:25.320934Z","iopub.status.idle":"2026-05-30T19:21:26.676731Z","shell.execute_reply.started":"2026-05-30T19:21:25.320907Z","shell.execute_reply":"2026-05-30T19:21:26.675842Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =====================================================================\n# 1. 依序強制安裝 mmcv 運作所需的基礎依賴套件（完全離線，且解除 --no-deps 限制）\n# =====================================================================\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/addict-2.4.0-py3-none-any.whl --force-reinstall\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/yapf-0.32.0-py2.py3-none-any.whl --force-reinstall\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminal-0.4.0-py3-none-any.whl --force-reinstall\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminaltables-3.1.10-py2.py3-none-any.whl --force-reinstall\n\n# =====================================================================\n# 2. 測試 mmcv 能不能順利載入\n# =====================================================================\nimport sys\n# 確保最乾淨的路徑優先級\nif '/opt/conda/lib/python3.10/site-packages' not in sys.path:\n    sys.path.insert(0, '/opt/conda/lib/python3.10/site-packages')\n\ntry:\n    import mmcv\n    from mmcv import Config\n    print(f\"🎉 太棒了！mmcv 及其所有依賴項已完全恢復，版本: {mmcv.__version__}\")\nexcept Exception as e:\n    print(f\"仍然缺少其他依賴，錯誤為: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:21:26.678074Z","iopub.execute_input":"2026-05-30T19:21:26.678415Z","iopub.status.idle":"2026-05-30T19:24:08.294231Z","shell.execute_reply.started":"2026-05-30T19:21:26.678385Z","shell.execute_reply":"2026-05-30T19:24:08.293096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import segmentation_models_pytorch as smp\nimport mmcv_custom\nimport mmdet_custom","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:24:08.295700Z","iopub.execute_input":"2026-05-30T19:24:08.295947Z","iopub.status.idle":"2026-05-30T19:24:12.212077Z","shell.execute_reply.started":"2026-05-30T19:24:08.295923Z","shell.execute_reply":"2026-05-30T19:24:12.211323Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import mmdet\nfrom mmdet.apis import init_detector, inference_detector,show_result_pyplot, set_random_seed\nfrom mmdet.models import build_detector\n#print(mmdet.__version__)\n#print(mmcv.__version__)\n#print(mmengine.__version__)\n\nfrom mmdet.models.backbones import *\n# # #check file her\n\nfrom mmcv import Config\n\nfrom mmdet.models.backbones.swin import SwinTransformer\nfrom mmdet.models.backbones import cbnet","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:24:12.213377Z","iopub.execute_input":"2026-05-30T19:24:12.213668Z","iopub.status.idle":"2026-05-30T19:24:12.850674Z","shell.execute_reply.started":"2026-05-30T19:24:12.213646Z","shell.execute_reply":"2026-05-30T19:24:12.849541Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import sys\n# print(sys.path)\n# !cp -r /kaggle/input/cbnetv2-repo ./# \n# sys.path.insert(1, '/kaggle/working/cbnet_repo/')\n# print(sys.path)\n\nimport mmdet\nfrom mmdet.apis import init_detector, inference_detector,show_result_pyplot, set_random_seed\nfrom mmdet.models import build_detector\n#print(mmdet.__version__)\n#print(mmcv.__version__)\n#print(mmengine.__version__)\n\nfrom mmdet.models.backbones import *\n# # #check file her\n\nfrom mmcv import Config\n\nfrom mmdet.models.backbones.swin import SwinTransformer\nfrom mmdet.models.backbones import cbnet","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:24:12.851986Z","iopub.execute_input":"2026-05-30T19:24:12.852862Z","iopub.status.idle":"2026-05-30T19:24:12.858558Z","shell.execute_reply.started":"2026-05-30T19:24:12.852828Z","shell.execute_reply":"2026-05-30T19:24:12.857595Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#import mmdet, mmcv, mmengine\n#from mmengine.config import Config\n#from mmengine.runner import Runner\n#from mmdet.utils import register_all_modules\n#from mmdet.apis import init_detector, inference_detector\n#from mmengine.visualization import Visualizer\n\nfrom mmdet.apis import init_detector, inference_detector,show_result_pyplot, set_random_seed\n\n#print(mmdet.__version__)\n#print(mmcv.__version__)\n#print(mmengine.__version__)\n\n\n# # #check file her\n\nfrom mmcv import Config\n\n\n# configs_path = ['/kaggle/working/test_conf.py','/kaggle/working/test_conf3.py', '/kaggle/working/cbnet_conf.py']\n# ckpt_paths = ['/kaggle/input/ds1pretexp1moreaug-htc50-2048-cv408ps/best_segm_mAP_epoch_21.pth',\n#               '/kaggle/input/ds1pretexp1-htc101-2048-full/detectors_epoch_18.pth',\n#              '/kaggle/input/pretexp1cbnetv2-base-2048-basic-exp1-f5cv437/best_segm_mAP_epoch_21.pth']\n\n\n# configs_path = [\n# #             '/kaggle/input/pretexp3-adaplargehtc-cv411f1/exp1_adaplarge_htc.py',\n#                 # '/kaggle/input/pretexp4-adapbeitv2lhtc-1400-ds2wsiall-ps60-leak/exp4_adapbeitv2l_withps50exp2.py',\n#             '/kaggle/input/pretexp4-adapbeitv2lhtc-1400-ds2wsiall-ps50exp2-lo/exp4_adapbeitv2l_withps50exp2.py',\n# #                 '/kaggle/input/hubmap-adaplargev1-exp5-pretwsiall-leaky/exp5_adaplarge_htc.py',\n#                 # '/kaggle/input/pretwsiallhtc-resnext101-exp3-augv4-maskloss4/detec101next.py',\n#                 # '/kaggle/input/ds1pretexp1moreaug-htc50-2048-cv408ps/h50psexp1.py',\n# #                 '/kaggle/input/exp3-withpret-cblarge-1600-morepretep-ps50exp2/cbnet_large_2048_ps50.py'\n#                 # '/kaggle/input/pretexp1cbnetv2-base-2048-basic-exp1-f5cv437/exp1_cbnet_swinb2.py',\n# ]\n\n# ckpt_paths = [\n# #     '/kaggle/input/pretexp3-adaplargehtc-cv417f1/best_segm_mAP_epoch_21.pth',\n#     # '/kaggle/input/pretexp4-adapbeitv2lhtc-1400-ds2wsiall-ps60-leak/best_segm_mAP_epoch_18.pth',\n#     '/kaggle/input/pretexp4-adapbeitv2lhtc-1400-ds2wsiall-ps50exp2-lo/best_segm_mAP_epoch_20.pth',\n# #     '/kaggle/input/hubmap-adaplargev1-exp5-pretwsiall-leaky/best_segm_mAP_epoch_21.pth',\n#     #         '/kaggle/input/pretwsiallhtc-resnext101-exp3-augv4-maskloss4/best_segm_mAP_epoch_17.pth',\n#     #           '/kaggle/input/ds1pretexp1moreaug-htc50-2048-cv408ps/detectors_epoch_23.pth',\n#     # '/kaggle/input/pretexp1cbnetv2-base-2048-basic-exp1-f5cv437/best_segm_mAP_epoch_21.pth'\n# #     '/kaggle/input/exp3-withpret-cblarge-1600-morepretep-ps50exp2/best_segm_mAP_epoch_19.pth'\n    \n# ]\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:24:12.859843Z","iopub.execute_input":"2026-05-30T19:24:12.860299Z","iopub.status.idle":"2026-05-30T19:24:12.871978Z","shell.execute_reply.started":"2026-05-30T19:24:12.860262Z","shell.execute_reply":"2026-05-30T19:24:12.871061Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"configs_path = ['/kaggle/input/datasets/yyastudent/3rd-vitadpater/exp4_adapbeitv2l_kaggle.py']\nckpt_paths = ['/kaggle/input/datasets/yyastudent/3rd-vitadpater/best_segm_mAP_epoch_19.pth']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T20:00:02.985563Z","iopub.execute_input":"2026-05-30T20:00:02.986242Z","iopub.status.idle":"2026-05-30T20:00:02.993015Z","shell.execute_reply.started":"2026-05-30T20:00:02.986208Z","shell.execute_reply":"2026-05-30T20:00:02.992093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"models = []\nfor cfg_path, ckpt in zip(configs_path,ckpt_paths):\n    cfg = Config.fromfile(cfg_path)\n    if 'cbnet' in cfg_path:\n        print('small image for cbnet')\n        cfg.model.test_cfg.rcnn.score_thr = 0.001\n\n        cfg.model.test_cfg.rcnn.max_per_img = 500\n\n        cfg.model.test_cfg.rcnn.nms.iou_threshold=0.5\n        cfg.model.test_cfg.rcnn.mask_thr_binary=0.55\n        cfg.data.test.pipeline[1].img_scale= [(2048, 2048)]#\n        \n    elif 'adap' in cfg_path:\n        print('small image for adap')\n        cfg.model.test_cfg.rcnn.score_thr = 0.001\n\n        cfg.model.test_cfg.rcnn.max_per_img = 500\n        cfg.model.test_cfg.rcnn.nms.type='nms'\n\n        cfg.model.test_cfg.rcnn.nms.iou_threshold=0.5\n        cfg.model.test_cfg.rcnn.mask_thr_binary=0.55\n        cfg.data.test.pipeline[1].img_scale= [(1600,1600),(1400,1400)]#\n        \n    else:\n        cfg.data.test.pipeline[1].img_scale= [(2048,2048)]#\n        cfg.model.test_cfg.rcnn.score_thr = 0.001\n\n        cfg.model.test_cfg.rcnn.max_per_img = 500\n\n        cfg.model.test_cfg.rcnn.nms.iou_threshold=0.5\n        cfg.model.test_cfg.rcnn.mask_thr_binary=0.55\n    cfg.seed = 69\n    set_random_seed(69, deterministic=False)\n\n    print(f'Config:\\n{cfg.data.test.pipeline}')\n    model = init_detector(cfg, ckpt, device=device)  \n    models.append(model)\n    del cfg","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:03.449669Z","iopub.execute_input":"2026-05-30T20:00:03.450108Z","iopub.status.idle":"2026-05-30T20:00:13.929851Z","shell.execute_reply.started":"2026-05-30T20:00:03.450073Z","shell.execute_reply":"2026-05-30T20:00:13.928739Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_imgs = glob.glob('/kaggle/input/hubmap-hacking-the-human-vasculature/test/*.tif')\ndataset_test = PennFudanDataset(all_imgs, get_transform(train=False))\ntest_dl = torch.utils.data.DataLoader(\n        dataset_test, batch_size=1, shuffle=False, num_workers=os.cpu_count(), pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.931605Z","iopub.execute_input":"2026-05-30T20:00:13.931937Z","iopub.status.idle":"2026-05-30T20:00:13.938241Z","shell.execute_reply.started":"2026-05-30T20:00:13.931907Z","shell.execute_reply":"2026-05-30T20:00:13.937328Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# all_imgs","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.939537Z","iopub.execute_input":"2026-05-30T20:00:13.940044Z","iopub.status.idle":"2026-05-30T20:00:13.953587Z","shell.execute_reply.started":"2026-05-30T20:00:13.940021Z","shell.execute_reply":"2026-05-30T20:00:13.952789Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from skimage.morphology import binary_dilation\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.956039Z","iopub.execute_input":"2026-05-30T20:00:13.956364Z","iopub.status.idle":"2026-05-30T20:00:13.965165Z","shell.execute_reply.started":"2026-05-30T20:00:13.956342Z","shell.execute_reply":"2026-05-30T20:00:13.964131Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nfrom skimage.morphology import binary_erosion, binary_dilation, binary_opening, binary_closing\n\nMIN_PIXELS = 40\ndef nms_predictions(classes, scores, bboxes, masks, \n                    iou_th=.5, shape=(512, 512), weights=[0.5,0.5]):\n    he, wd = shape[0], shape[1]\n    boxes_list = [[x[0] / wd, x[1] / he, x[2] / wd, x[3] / he]\n                  for x in bboxes]\n    scores_list = [x for x in scores]\n    labels_list = [x for x in classes]\n    nms_bboxes, nms_scores, nms_classes = nms(\n        boxes=[boxes_list], \n        scores=[scores_list], \n        labels=[labels_list], \n        weights=weights,\n        iou_thr=iou_th\n    )\n    nms_masks = []\n    for s in nms_scores:\n        nms_masks.append(masks[scores.index(s)])\n    nms_scores, nms_classes, nms_masks = zip(*sorted(zip(nms_scores, nms_classes, nms_masks), reverse=True))\n    return nms_classes, nms_scores, nms_masks\n\ndef ensemble_pred_masks(masks, min_pixels=MIN_PIXELS, shape=(512, 512)):\n    result = []\n    used = np.zeros(shape, dtype=int) \n\n    prev_masks = []\n    new_masks = []\n    for i, mask in enumerate(masks):\n        # cont, hier = cv2.findContours(np.array(mask,dtype=np.uint8),cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n        # if len(cont)>0:\n            # for cnt in cont:\n            #     convex_mask = cv2.fillConvexPoly(np.zeros_like(np.array(mask,dtype=np.uint8)),points=cnt, color=1)\n            #     fillornot = len(pd.Series((convex_mask==mask).flatten()).value_counts())\n            #     if fillornot>1: #fill\n            #         mask = convex_mask\n\n        \n            # before= mask.sum()\n        mask = binary_erosion(binary_dilation(mask))  #post processing \n            # if before!=mask.sum():\n               # print('after',mask.sum(),'before',before)\n        mask = mask * (1-used)\n        if mask.sum() >= 100: # skip predictions with small area\n                #     used += mask \n            new_masks.append(mask)\n        \n    return new_masks\n\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.966320Z","iopub.execute_input":"2026-05-30T20:00:13.966664Z","iopub.status.idle":"2026-05-30T20:00:13.980231Z","shell.execute_reply.started":"2026-05-30T20:00:13.966643Z","shell.execute_reply":"2026-05-30T20:00:13.979301Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ensemble_boxes import *","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.981339Z","iopub.execute_input":"2026-05-30T20:00:13.982179Z","iopub.status.idle":"2026-05-30T20:00:13.988932Z","shell.execute_reply.started":"2026-05-30T20:00:13.982152Z","shell.execute_reply":"2026-05-30T20:00:13.988050Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ids = []\nheights = []\nwidths = []\nprediction_strings = []","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.990050Z","iopub.execute_input":"2026-05-30T20:00:13.990394Z","iopub.status.idle":"2026-05-30T20:00:13.998041Z","shell.execute_reply.started":"2026-05-30T20:00:13.990364Z","shell.execute_reply":"2026-05-30T20:00:13.997364Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from skimage import measure\nfrom wbf_tracking import *","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:13.999146Z","iopub.execute_input":"2026-05-30T20:00:13.999876Z","iopub.status.idle":"2026-05-30T20:00:14.008423Z","shell.execute_reply.started":"2026-05-30T20:00:13.999854Z","shell.execute_reply":"2026-05-30T20:00:14.007719Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ensemble_boxes import *\nMODEL_WEIGHTS = [0.25,0.5,0.25]\ndef bbox_to_key(bbox):\n    return str(np.round(bbox, 6))\n\n\n# Fuse masks that belong to fused boxes\n# 修改後的 get_wsf_mask 函式\ndef get_wsf_mask(wbf_box, wbf_org, pmasks, pmasks_lkup, thres=0.5):\n    w, h = 512, 512\n    mask = np.zeros((w, h), dtype=np.uint8)\n    for i in range(len(wbf_org)):\n        key = bbox_to_key(wbf_org[i][4:])\n        model = int(wbf_org[i][3])\n        \n        # 💡 核心修正：加上 try-except，防止浮點數微幅抖動導致 KeyError 崩潰\n        try:\n            ind = pmasks_lkup[model][key]\n            mask = mask + pmasks[model][ind]\n        except KeyError:\n            # 如果因為精度問題找不到對照，就默默跳過，不讓整個 Notebook 斷掉\n            pass\n            \n    # convert thres to integer based on number of boxes\n    threshold = max(1, int(thres*len(wbf_org)))\n            \n    # remove pixels outside WBF box\n    m2 = np.zeros((w, h), dtype=np.uint8)\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    \n    m2[y1:y2, x1:x2] = 1\n    mask = (mask >= threshold) * m2\n    return mask.astype(np.uint8)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:02:05.563374Z","iopub.execute_input":"2026-05-30T20:02:05.563832Z","iopub.status.idle":"2026-05-30T20:02:05.571788Z","shell.execute_reply.started":"2026-05-30T20:02:05.563802Z","shell.execute_reply":"2026-05-30T20:02:05.570769Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subm_ids, subm_masks = [], []\nsample = None\nimport mmcv\nimport pickle\nimport numpy as np\nimport os\nimport torch\nimport pycocotools.mask as mask_utils  # 確保有載入 MMDetection 內建的解碼套件\n\nconfidence_thresholds = {0: 0.5, 1: 0.5, 2: 0.8}\n\n# =========================================================================\n# 💡 載入並建立 v3 (RTMDet) 離線結果的索引\n# =========================================================================\nv3_pkl_path = '/kaggle/input/notebooks/yyastudent/hubmap-2023-release/ensemble_results.pkl' \n\nprint(\"正在讀取來自 fork-of-hubmap (v3) 的預測結果...\")\nwith open(v3_pkl_path, 'rb') as f:\n    v3_raw_data = pickle.load(f)\n\nv3_results_lookup = {}\nfor item in v3_raw_data:\n    img_name = os.path.basename(item['img_path']).split('.')[0]\n    v3_results_lookup[img_name] = item['pred_instances']\nprint(f\"成功載入 {len(v3_results_lookup)} 張圖片的 v3 預測結果！\")\n\n\n# =========================================================================\n# 💡 開始跑測試集影像推論與 WBF Ensemble 雙模型融合\n# =========================================================================\nfor img in all_imgs:\n    pred_string = ''\n    img_array = mmcv.imread(img, channel_order='rgb')\n    [h, w, c_channels] = img_array.shape \n    \n    # 初始化跨模型的 NMS 與 WBF 總清單\n    masks_nms_list = []\n    score_nms_list = []\n    box_nms_list = []\n    class_nms_list = []\n    pred_dict_list = []\n    \n    # ---------------------------------------------------------------------\n    # 1️⃣ 第一步：執行線上的 Model v2 (ViT-Adapter / CBNet 等) 預測\n    # ---------------------------------------------------------------------\n    for model_idx, modely in enumerate(models):\n        pred_dict = {}\n        previous_masks = []\n        scoresb_nms = []\n        bboxesb_nms = []\n        classes_nms = []\n        \n        result = inference_detector(modely, img_array)\n        \n        if result and len(result) > 0 and len(result[0]) > 0:\n            c_classes = [classe.shape[0] for classe in result[0]]\n            maxclass = np.argmax(c_classes)\n            \n            bbs = result[0][maxclass]\n            sgs = result[1][maxclass]\n            count = 0\n            \n            for bb, sg in zip(bbs, sgs):\n                box = bb[:4]\n                cnf = bb[4]\n                # 將 BBox 座標正規化到 [0, 1] 區間以符合 WBF 規範\n                box_norm = [box[0] / 512, box[1] / 512, box[2] / 512, box[3] / 512]\n                \n                if cnf >= 0.00001:\n                    mask = np.array(sg, dtype=np.uint8)  \n                    previous_masks.append(mask)\n                    scoresb_nms.append(cnf)\n                    bboxesb_nms.append(box_norm)\n                    pred_dict[bbox_to_key(box_norm)] = count\n                    count += 1\n                    \n            classes_nms = [0] * len(previous_masks)\n        \n        # 將線上 Model v2 的數據打包填入\n        masks_nms_list.append(np.array(previous_masks, dtype=np.uint8) if previous_masks else np.zeros((0, 512, 512), dtype=np.uint8))\n        score_nms_list.append(np.array(scoresb_nms, dtype=np.float32))\n        box_nms_list.append(np.array(bboxesb_nms, dtype=np.float32) if bboxesb_nms else np.zeros((0, 4), dtype=np.float32))\n        class_nms_list.append(np.array(classes_nms, dtype=np.int32))\n        pred_dict_list.append(pred_dict)\n            \n    # ---------------------------------------------------------------------\n    # 2️⃣ 第二步：【真正強制接入】將離線 RTMDet (v3) 的結果填入清單當作第二個模型\n    # ---------------------------------------------------------------------\n    img_id = os.path.basename(img).split('.')[0]\n    \n    v3_masks_decoded = []\n    v3_scores_filtered = []\n    v3_boxes_norm = []\n    v3_pred_dict = {}\n    v3_count = 0\n    \n    if img_id in v3_results_lookup:\n        v3_pred = v3_results_lookup[img_id]\n        \n        # 提取數據並進行型態防呆\n        v3_boxes_raw = v3_pred['bboxes'] if isinstance(v3_pred['bboxes'], np.ndarray) else v3_pred['bboxes'].numpy()\n        v3_scores = v3_pred['scores'] if isinstance(v3_pred['scores'], np.ndarray) else v3_pred['scores'].numpy()\n        v3_labels = v3_pred['labels'] if isinstance(v3_pred['labels'], np.ndarray) else v3_pred['labels'].numpy()\n        v3_masks_rle = v3_pred['masks']\n        \n        for i in range(len(v3_labels)):\n            # 鎖定血管目標類別 (0 = blood_vessel)\n            if v3_labels[i] == 0 and v3_scores[i] >= 0.00001:\n                # WBF 要求邊界框需正規化到 [0, 1] 的相對比例 (除以寬高 512)\n                box_norm = [\n                    v3_boxes_raw[i][0] / 512, \n                    v3_boxes_raw[i][1] / 512, \n                    v3_boxes_raw[i][2] / 512, \n                    v3_boxes_raw[i][3] / 512\n                ]\n                v3_boxes_norm.append(box_norm)\n                v3_scores_filtered.append(v3_scores[i])\n                \n                # 將 RLE 壓縮格式的遮罩完美解碼還原成 (512, 512) 的二值化矩陣\n                decoded_mask = mask_utils.decode(v3_masks_rle[i]).astype(np.uint8)\n                v3_masks_decoded.append(decoded_mask)\n                \n                # 建立字典映射，確保 get_wsf_mask 機制可以完美根據座標撈回遮罩\n                v3_pred_dict[bbox_to_key(box_norm)] = v3_count\n                v3_count += 1\n                \n    # 核心保險：即使這張圖沒抓到任何血管，也要推入空的 numpy 陣列，確保清單長度永遠為 2 (對應 weights=[0.5, 0.5])\n    masks_nms_list.append(np.array(v3_masks_decoded, dtype=np.uint8) if v3_masks_decoded else np.zeros((0, 512, 512), dtype=np.uint8))\n    score_nms_list.append(np.array(v3_scores_filtered, dtype=np.float32))\n    box_nms_list.append(np.array(v3_boxes_norm, dtype=np.float32) if v3_boxes_norm else np.zeros((0, 4), dtype=np.float32))\n    class_nms_list.append(np.array([0] * len(v3_boxes_norm), dtype=np.int32))\n    pred_dict_list.append(v3_pred_dict)\n    \n    # ---------------------------------------------------------------------\n    # 3️⃣ 第三步：執行真正的雙模型權重融合 (WBF + Mask Fusion)\n    # ---------------------------------------------------------------------\n    fin_masks = []\n    wbf_scores = []\n\n    total_boxes = sum(len(b) for b in box_nms_list)\n    if total_boxes > 0:\n        # 此時 box_nms_list 長度固定為 2，[0.5, 0.5] 的權重劃分將正式在 WBF 生效！\n        wbf_boxes, wbf_scores, _, wbf_originals = weighted_boxes_fusion_tracking(\n            box_nms_list, \n            score_nms_list, \n            labels_list=class_nms_list, \n            weights=[0.1, 0.9],  \n            iou_thr=0.6, \n            skip_box_thr=0.01\n        )\n        \n        for i in range(len(wbf_boxes)):\n            # 這裡透過 get_wsf_mask 的 try-except 護欄，將 v2 與 v3 的遮罩物理交疊並平均\n            mask = get_wsf_mask(wbf_boxes[i], wbf_originals[i], masks_nms_list, pred_dict_list, thres=0.2)\n            fin_masks.append(mask)\n        \n        # 形態學平滑後處理（侵蝕與膨脹，過濾過小區域）\n        fin_masks = ensemble_pred_masks(fin_masks) \n    else:\n        fin_masks = []\n        wbf_scores = []\n    \n    # ---------------------------------------------------------------------\n    # 4️⃣ 第四步：將雙模型融合結果封裝轉化成 Kaggle 要求的提交字串\n    # ---------------------------------------------------------------------\n    m = 0\n    for masky, scory in zip(fin_masks, tuple(wbf_scores)):\n        masky = masky.astype(bool)       \n        encoded = encode_binary_mask(masky)\n        \n        # 確保編碼文字型態安全\n        enc_str = encoded.decode('utf-8') if isinstance(encoded, bytes) else encoded\n        \n        if m == 0:\n            pred_string += f\"0 {scory} {enc_str}\"\n            m += 1\n        else:\n            pred_string += f\" 0 {scory} {enc_str}\"\n\n    ids.append(os.path.basename(img).split('.')[0])\n    heights.append(h)\n    widths.append(w)\n    prediction_strings.append(pred_string)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:02:07.797451Z","iopub.execute_input":"2026-05-30T20:02:07.797830Z","iopub.status.idle":"2026-05-30T20:02:23.777356Z","shell.execute_reply.started":"2026-05-30T20:02:07.797803Z","shell.execute_reply":"2026-05-30T20:02:23.776513Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# masks_nms","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:27.064936Z","iopub.status.idle":"2026-05-30T20:00:27.065397Z","shell.execute_reply.started":"2026-05-30T20:00:27.065152Z","shell.execute_reply":"2026-05-30T20:00:27.065172Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# len(scoresb_nms)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:27.066930Z","iopub.status.idle":"2026-05-30T20:00:27.067323Z","shell.execute_reply.started":"2026-05-30T20:00:27.067124Z","shell.execute_reply":"2026-05-30T20:00:27.067144Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame()\nsubmission['id'] = ids\nsubmission['height'] = heights\nsubmission['width'] = widths\nsubmission['prediction_string'] = prediction_strings\nsubmission = submission.set_index('id')\nsubmission.to_csv(\"submission.csv\")\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:02:33.783177Z","iopub.execute_input":"2026-05-30T20:02:33.783854Z","iopub.status.idle":"2026-05-30T20:02:33.802838Z","shell.execute_reply.started":"2026-05-30T20:02:33.783825Z","shell.execute_reply":"2026-05-30T20:02:33.801968Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# submission.loc['72e40acccadf','prediction_string']","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:02:42.760217Z","iopub.execute_input":"2026-05-30T20:02:42.761242Z","iopub.status.idle":"2026-05-30T20:02:42.765943Z","shell.execute_reply.started":"2026-05-30T20:02:42.761209Z","shell.execute_reply":"2026-05-30T20:02:42.765002Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf mmdetection\n!rm -rf packages\n# !rm -rf cbnetv2-repo\n!rm -rf cbnet_repo\n!rm -rf /kaggle/working/vitadapzip","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:02:43.096524Z","iopub.execute_input":"2026-05-30T20:02:43.097206Z","iopub.status.idle":"2026-05-30T20:02:47.632375Z","shell.execute_reply.started":"2026-05-30T20:02:43.097176Z","shell.execute_reply":"2026-05-30T20:02:47.631195Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##a","metadata":{"execution":{"iopub.status.busy":"2026-05-30T20:00:27.073637Z","iopub.status.idle":"2026-05-30T20:00:27.074033Z","shell.execute_reply.started":"2026-05-30T20:00:27.073829Z","shell.execute_reply":"2026-05-30T20:00:27.073849Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import glob\n# import mmcv\n# import torch\n# import numpy as np\n# import pandas as pd\n# from mmengine.config import Config\n# from mmengine.runner import load_checkpoint\n# from mmdet.registry import MODELS\n# from mmdet.structures import DetDataSample\n# from skimage.morphology import binary_erosion, binary_dilation\n# from wbf_tracking import weighted_boxes_fusion_tracking\n# import base64\n# import zlib\n# from pycocotools import _mask as coco_mask\n\n# # ==========================================\n# # 1. 定義輔助函數 (從你原本的程式碼改良，加入動態尺寸支援)\n# # ==========================================\n# def bbox_to_key(bbox):\n#     return str(np.round(bbox, 6))\n\n# def get_wsf_mask(wbf_box, wbf_org, pmasks, pmasks_lkup, img_shape, thres=0.5):\n#     h, w = img_shape\n#     mask = np.zeros((h, w), dtype=np.float32)\n#     for i in range(len(wbf_org)):\n#         key = bbox_to_key(wbf_org[i][4:])\n#         model_idx = int(wbf_org[i][3])\n#         try:\n#             ind = pmasks_lkup[model_idx][key]\n#             mask += pmasks[model_idx][ind]\n#         except KeyError:\n#             pass\n            \n#     threshold = max(1, int(thres * len(wbf_org)))\n    \n#     m2 = np.zeros((h, w), dtype=np.uint8)\n#     x1, y1 = max(0, int(w * wbf_box[0])), max(0, int(h * wbf_box[1]))\n#     x2, y2 = min(w, int(w * wbf_box[2])), min(h, int(h * wbf_box[3]))\n#     m2[y1:y2, x1:x2] = 1\n    \n#     mask = (mask >= threshold) * m2\n#     return mask.astype(np.uint8)\n\n# def ensemble_pred_masks(masks, min_pixels=100, shape=(512, 512)):\n#     new_masks = []\n#     used = np.zeros(shape, dtype=int)\n#     for mask in masks:\n#         mask = binary_erosion(binary_dilation(mask))\n#         mask = mask * (1 - used)\n#         if mask.sum() >= min_pixels:\n#             used += mask\n#             new_masks.append(mask)\n#     return new_masks\n\n# def encode_binary_mask(mask: np.ndarray) -> str:\n#     if mask.dtype != bool and mask.dtype != np.bool_:\n#         raise ValueError(f\"encode_binary_mask expects a binary mask, received dtype == {mask.dtype}\")\n#     mask = np.squeeze(mask)\n#     mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1).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#     return base64.b64encode(binary_str).decode('utf-8')\n\n# # ==========================================\n# # 2. 載入兩個模型\n# # ==========================================\n# # TODO: 請在此替換為你實際的 Config 與 Weights 路徑\n# configs = [\n#     '/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/r0_kaggle.py',\n#     '/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/r0_kaggle.py' # 替換為 Model 2 的 Config\n# ]\n# checkpoints = [\n#     '/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/iter_16128.pth',\n#     '/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/iter_16128.pth' # 替換為 Model 2 的 Weight\n# ]\n# model_weights = [1.0, 1.0] # WBF 的模型權重分配\n\n# print(\"正在初始化模型...\")\n# models = []\n# for cfg_path, ckpt_path in zip(configs, checkpoints):\n#     cfg = Config.fromfile(cfg_path)\n#     model = MODELS.build(cfg.model)\n#     load_checkpoint(model, ckpt_path, map_location='cpu')\n#     model.cuda().eval()\n#     models.append(model)\n\n# # ==========================================\n# # 3. 執行 Inference 與 Mask Ensemble\n# # ==========================================\n# all_imgs = glob.glob('/kaggle/input/hubmap-hacking-the-human-vasculature/test/*.tif')\n# submission_data = []\n\n# print(\"開始進行雙模型推論與 WBF Ensemble...\")\n# with torch.no_grad(), torch.autocast(device_type='cuda', dtype=torch.bfloat16):\n#     for img_idx, img_path in enumerate(all_imgs):\n#         img = mmcv.imread(img_path)\n#         h, w = img.shape[:2]\n        \n#         box_nms_list, score_nms_list, class_nms_list = [], [], []\n#         masks_nms_list, pred_dict_list = [], []\n        \n#         # --- A. 分別取得兩個模型的預測結果 ---\n#         for modely in models:\n#             batch_data = dict(\n#                 inputs=[torch.from_numpy(img).permute(2, 0, 1)],\n#                 data_samples=[DetDataSample(metainfo=dict(\n#                     img_id=img_idx, ori_shape=(h, w), img_shape=(h, w),\n#                     img_path=img_path, scale_factor=(1.0, 1.0)\n#                 ))]\n#             )\n#             batch_data = modely.data_preprocessor(batch_data, False)\n#             predictions = modely(batch_data['inputs'], batch_data['data_samples'], mode='predict')\n            \n#             pred_instances = predictions[0].pred_instances.cpu()\n#             bboxes = pred_instances.bboxes.numpy()\n#             scores = pred_instances.scores.numpy()\n#             labels = pred_instances.labels.numpy()\n#             masks = pred_instances.masks.numpy() \n            \n#             # 將 BBox 正規化到 [0, 1] 以符合 WBF 規範\n#             norm_bboxes = bboxes.copy()\n#             if len(norm_bboxes) > 0:\n#                 norm_bboxes[:, [0, 2]] /= w\n#                 norm_bboxes[:, [1, 3]] /= h\n            \n#             # 建立 Mask 對照表 (Key 為 Box 座標字串)\n#             pred_dict = {bbox_to_key(box): i for i, box in enumerate(norm_bboxes)}\n            \n#             box_nms_list.append(norm_bboxes)\n#             score_nms_list.append(scores)\n#             class_nms_list.append(labels)\n#             masks_nms_list.append(masks)\n#             pred_dict_list.append(pred_dict)\n            \n#         # --- B. 執行 WBF 融合 Bounding Boxes ---\n#         wbf_boxes, wbf_scores, wbf_labels, wbf_originals = weighted_boxes_fusion_tracking(\n#             box_nms_list, score_nms_list, class_nms_list,\n#             weights=model_weights, iou_thr=0.6, skip_box_thr=0.01\n#         )\n        \n#         # --- C. 根據融合後的 Box 反向找回 Mask 並平均 ---\n#         fin_masks = []\n#         for i in range(len(wbf_boxes)):\n#             if wbf_labels[i] != 0:  # 只處理 blood_vessel 類別\n#                 continue\n#             mask = get_wsf_mask(wbf_boxes[i], wbf_originals[i], masks_nms_list, pred_dict_list, img_shape=(h, w), thres=0.2)\n#             fin_masks.append(mask)\n            \n#         # 濾除重疊區域與過小的預測\n#         fin_masks = ensemble_pred_masks(fin_masks, shape=(h, w))\n        \n#         # --- D. 轉換為 RLE 格式準備提交 ---\n#         instance_strings = []\n#         for masky, scory in zip(fin_masks, wbf_scores):\n#             masky = masky.astype(bool)\n#             encoded = encode_binary_mask(masky)\n#             instance_strings.append(f\"0 {scory} {encoded}\")\n            \n#         submission_data.append({\n#             'id': os.path.basename(img_path).split('.')[0],\n#             'height': h,\n#             'width': w,\n#             'prediction_string': ' '.join(instance_strings)\n#         })\n\n# # ==========================================\n# # 4. 輸出 submission.csv\n# # ==========================================\n# sub_df = pd.DataFrame(submission_data)\n# sub_df.to_csv('submission.csv', index=False)\n# print(\"🎉 大成功！submission.csv 已生成。\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:24:39.087962Z","iopub.status.idle":"2026-05-30T19:24:39.088475Z","shell.execute_reply.started":"2026-05-30T19:24:39.088326Z","shell.execute_reply":"2026-05-30T19:24:39.088342Z"}},"outputs":[],"execution_count":null}]}