{"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":6084483,"datasetId":3483806,"databundleVersionId":6162906},{"sourceType":"datasetVersion","sourceId":6073875,"datasetId":3476711,"databundleVersionId":6152255},{"sourceType":"datasetVersion","sourceId":6057697,"datasetId":3466282,"databundleVersionId":6136002},{"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":"datasetVersion","sourceId":16544157,"datasetId":10594781,"databundleVersionId":17549694},{"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\n# import sys\n# import json\n# from PIL import Image\n# from collections import Counter\n\n# import numpy as np\n# import pandas as pd\n# import plotly.express as px\n# import plotly.graph_objects as go\n# import tifffile as tiff\n# import matplotlib.pyplot as plt\n# from tqdm import tqdm\n# import torch\n# import cv2\n\n# import pandas as pd\n\n# from sklearn.model_selection import KFold\n\n# sys.path.append(\"/kaggle/input/detection-wheel\")","metadata":{"execution":{"iopub.status.busy":"2026-05-31T07:46:34.719203Z","iopub.execute_input":"2026-05-31T07:46:34.720012Z","iopub.status.idle":"2026-05-31T07:46:39.613679Z","shell.execute_reply.started":"2026-05-31T07:46:34.719980Z","shell.execute_reply":"2026-05-31T07:46:39.612923Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch\n# torch.__version__  #torch 2.0\n# #!nvidia-smi   #CUDA Version: 11.4\n# ! ls /usr/local","metadata":{"execution":{"iopub.status.busy":"2026-05-31T07:46:39.615052Z","iopub.execute_input":"2026-05-31T07:46:39.615631Z","iopub.status.idle":"2026-05-31T07:46:40.602439Z","shell.execute_reply.started":"2026-05-31T07:46:39.615608Z","shell.execute_reply":"2026-05-31T07:46:40.601512Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # # Install pycocotools package\n# import os\n# !mkdir /kaggle/working/packages\n# !cp -r /kaggle/input/pycocotools/* /kaggle/working/packages\n# os.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-31T07:46:40.603980Z","iopub.execute_input":"2026-05-31T07:46:40.604514Z","iopub.status.idle":"2026-05-31T07:52:20.037368Z","shell.execute_reply.started":"2026-05-31T07:46:40.604486Z","shell.execute_reply":"2026-05-31T07:52:20.036316Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import 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. 切換到可讀寫的編譯工作目錄\n# os.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 的主工作目錄\n# os.chdir(\"/kaggle/working\")\n\n# # 5. 驗證是否成功安裝並能正常載入\n# try:\n#     import pycocotools\n#     from pycocotools import _mask as coco_mask\n#     print(\"🎉 太棒了！順利繞過唯讀限制，pycocotools 已成功離線安裝並載入！\")\n# except Exception as e:\n#     print(f\"仍然出現錯誤: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T07:52:20.039971Z","iopub.execute_input":"2026-05-31T07:52:20.040329Z","iopub.status.idle":"2026-05-31T07:52:29.801721Z","shell.execute_reply.started":"2026-05-31T07:52:20.040256Z","shell.execute_reply":"2026-05-31T07:52:29.800534Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import sys\n# import os\n\n# # 1. 我們知道剛才在 /kaggle/working/pycocotools_build 已經編譯成功\n# # 編譯好的 Python 模組和 .so 檔會放在 build/lib.linux-x86_64-3.10/ 底下\n# compiled_lib_path = \"/kaggle/working/pycocotools_build/build/lib.linux-x86_64-3.10\"\n\n# # 2. 直接將這個路徑插到 Python 搜尋路徑的最前面！\n# if compiled_lib_path not in sys.path:\n#     sys.path.insert(0, compiled_lib_path)\n\n# # 3. 測試載入\n# try:\n#     import pycocotools\n#     from pycocotools import _mask as coco_mask\n#     print(\"🎉 終於成功啦！我們直接載入了編譯好的檔案，完全繞過系統的依賴檢查！\")\n#     print(f\"目前載入的 pycocotools 路徑為: {pycocotools.__file__}\")\n# except Exception as e:\n#     print(f\"仍然報錯: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T07:52:29.803355Z","iopub.execute_input":"2026-05-31T07:52:29.803724Z","iopub.status.idle":"2026-05-31T07:52:29.810758Z","shell.execute_reply.started":"2026-05-31T07:52:29.803689Z","shell.execute_reply":"2026-05-31T07:52:29.809867Z"}},"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-31T07:53:19.241933Z","iopub.execute_input":"2026-05-31T07:53:19.242202Z","iopub.status.idle":"2026-05-31T07:53:59.529493Z","shell.execute_reply.started":"2026-05-31T07:53:19.242183Z","shell.execute_reply":"2026-05-31T07:53:59.528427Z"},"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-31T07:53:59.532808Z","iopub.execute_input":"2026-05-31T07:53:59.533216Z","iopub.status.idle":"2026-05-31T07:54:08.548079Z","shell.execute_reply.started":"2026-05-31T07:53:59.533187Z","shell.execute_reply":"2026-05-31T07:54:08.547091Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Install","metadata":{}},{"cell_type":"code","source":"import os\nimport sys\n\nprint(\"開始安裝基礎依賴套件...\")\n# 1. 離線安裝必要的基礎 wheel 包\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/addict-2.4.0-py3-none-any.whl -q\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/yapf-0.32.0-py2.py3-none-any.whl -q\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminal-0.4.0-py3-none-any.whl -q\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminaltables-3.1.10-py2.py3-none-any.whl -q\n\n# 2. 離線安裝與當前 Python 3.10 / PyTorch 2.0 匹配的 mmcv-full (1.7.1)\n!pip install /kaggle/input/mmdet3-wheels/mmcv_full-1.7.1-cp310-cp310-linux_x86_64.whl -q\n\n# 3. 唯讀目錄繞過：將 pycocotools 複製到 working 目錄進行本地編譯\n!rm -rf /kaggle/working/pycocotools_build\n!cp -r /kaggle/input/pycocotools-206 /kaggle/working/pycocotools_build\nos.chdir(\"/kaggle/working/pycocotools_build\")\n!python setup.py build_ext --inplace -q\n!python setup.py install -q\n\n# 4. 務必切回 Kaggle 主工作目錄\nos.chdir(\"/kaggle/working\")\nprint(\"🎉 基礎依賴與 pycocotools 編譯完成！\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T11:38:02.572545Z","iopub.execute_input":"2026-05-31T11:38:02.573362Z","iopub.status.idle":"2026-05-31T11:41:32.689711Z","shell.execute_reply.started":"2026-05-31T11:38:02.573334Z","shell.execute_reply":"2026-05-31T11:41:32.688588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nimport os\n\n# 1. 將編譯好的 pycocotools 和 cbnet_repo 優先加到 Python 搜尋路徑最前面\ncompiled_lib_path = \"/kaggle/working/pycocotools_build/build/lib.linux-x86_64-3.10\"\npaths_to_add = [\n    compiled_lib_path,\n    '/opt/conda/lib/python3.10/site-packages',\n    '/kaggle/working/cbnet_repo'\n]\nfor p in paths_to_add:\n    if p not in sys.path:\n        sys.path.insert(0, p)\n\n# 2. 複製並離線安裝相容於 Config 的 CBNetV2 倉庫與 mmdet-2.26.0\n!cp -r /kaggle/input/cbnetv2-repo/cbnet_repo /kaggle/working/\nos.chdir(\"/kaggle/working/cbnet_repo\")\n!pip install -e . --no-deps -q\n\nos.chdir(\"/kaggle/working\")\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmdet-2.26.0-py3-none-any.whl --no-deps -q\n\n# 3. 離線加載 WBF (Ensemble Boxes) 工具包\n!pip install /kaggle/input/enseibleboxes109/ensemble_boxes-1.0.9-py3-none-any.whl -q\n\n# 4. 驗證 mmcv 與 mmdet 是否成功配對\ntry:\n    import mmcv\n    import mmdet\n    print(f\"🎉 離線環境部署大成功！\")\n    print(f\"   ▪️ MMCV 版本: {mmcv.__version__}\")\n    print(f\"   ▪️ MMDet 版本: {mmdet.__version__}\")\nexcept Exception as e:\n    print(f\"❌ 載入時發生錯誤: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T11:41:32.691658Z","iopub.execute_input":"2026-05-31T11:41:32.692106Z","iopub.status.idle":"2026-05-31T11:43:57.478567Z","shell.execute_reply.started":"2026-05-31T11:41:32.692080Z","shell.execute_reply":"2026-05-31T11:43:57.477539Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\n\n# 1. 複製並將 ViT-Adapter / PAFPN 核心套件路徑導通\n!cp -r /kaggle/input/vitadadapter/hubmap/vitadap/vitadapzip/ ./\nsys.path.insert(1, '/kaggle/working/vitadapzip')\n\n# 2. 引入其餘需要的外部套件搜尋路徑（SMP、Einops 等）\nsys.path.append('/kaggle/input/einops/einops-master')\nsys.path.append(\"../input/pretrained-models-pytorch\")\nsys.path.append(\"../input/efficientnet-pytorch\")\nsys.path.append(\"/kaggle/input/smp-github/segmentation_models.pytorch-master\")\n\n# 3. 複製其餘評估與訓練需要的 wheel 輔助指令腳本\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\nprint(\"🎉 擴充模組與輔助腳本配置完畢！\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T11:43:57.479862Z","iopub.execute_input":"2026-05-31T11:43:57.480331Z","iopub.status.idle":"2026-05-31T11:44:23.874919Z","shell.execute_reply.started":"2026-05-31T11:43:57.480307Z","shell.execute_reply":"2026-05-31T11:44:23.873706Z"}},"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-31T11:44:54.324825Z","iopub.execute_input":"2026-05-31T11:44:54.325727Z","iopub.status.idle":"2026-05-31T11:44:54.335224Z","shell.execute_reply.started":"2026-05-31T11:44:54.325696Z","shell.execute_reply":"2026-05-31T11:44:54.334263Z"},"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-31T11:44:54.514007Z","iopub.execute_input":"2026-05-31T11:44:54.514726Z","iopub.status.idle":"2026-05-31T11:44:54.520977Z","shell.execute_reply.started":"2026-05-31T11:44:54.514697Z","shell.execute_reply":"2026-05-31T11:44:54.519971Z"},"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-31T11:44:54.691019Z","iopub.execute_input":"2026-05-31T11:44:54.691368Z","iopub.status.idle":"2026-05-31T11:44:54.701716Z","shell.execute_reply.started":"2026-05-31T11:44:54.691341Z","shell.execute_reply":"2026-05-31T11:44:54.700652Z"},"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-31T11:44:54.836771Z","iopub.execute_input":"2026-05-31T11:44:54.837220Z","iopub.status.idle":"2026-05-31T11:44:55.117979Z","shell.execute_reply.started":"2026-05-31T11:44:54.837192Z","shell.execute_reply":"2026-05-31T11:44:55.117252Z"},"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-31T11:44:55.119352Z","iopub.execute_input":"2026-05-31T11:44:55.119698Z","iopub.status.idle":"2026-05-31T11:44:55.141507Z","shell.execute_reply.started":"2026-05-31T11:44:55.119676Z","shell.execute_reply":"2026-05-31T11:44:55.140845Z"},"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-31T11:44:55.207832Z","iopub.execute_input":"2026-05-31T11:44:55.208685Z","iopub.status.idle":"2026-05-31T11:44:55.212809Z","shell.execute_reply.started":"2026-05-31T11:44:55.208656Z","shell.execute_reply":"2026-05-31T11:44:55.211849Z"},"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-31T11:44:55.482996Z","iopub.execute_input":"2026-05-31T11:44:55.483451Z","iopub.status.idle":"2026-05-31T11:44:55.488175Z","shell.execute_reply.started":"2026-05-31T11:44:55.483422Z","shell.execute_reply":"2026-05-31T11:44:55.487178Z"},"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-31T11:44:55.671748Z","iopub.execute_input":"2026-05-31T11:44:55.672564Z","iopub.status.idle":"2026-05-31T11:45:00.764163Z","shell.execute_reply.started":"2026-05-31T11:44:55.672531Z","shell.execute_reply":"2026-05-31T11:45:00.763158Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import sys\n# import os\n\n# # 1. 將 Kaggle 的標準套件庫路徑與先前編譯的路徑強制加回最前面\n# # 這樣可以確保 Python 能夠順利偵測到剛剛安裝的 mmcv-full 內容\n# paths = [\n#     '/opt/conda/lib/python3.10/site-packages',\n#     '/kaggle/working/cbnet_repo'\n# ]\n\n# for p in paths:\n#     if p not in sys.path:\n#         sys.path.insert(0, p)\n\n# # 2. 測試是否能正常 import mmcv\n# try:\n#     import mmcv\n#     print(f\"🎉 成功！mmcv 已經被正確載入，版本為: {mmcv.__version__}\")\n# except 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-31T11:45:00.766041Z","iopub.execute_input":"2026-05-31T11:45:00.766382Z","iopub.status.idle":"2026-05-31T11:45:00.771313Z","shell.execute_reply.started":"2026-05-31T11:45:00.766344Z","shell.execute_reply":"2026-05-31T11:45:00.770395Z"}},"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# # =====================================================================\n# import sys\n# # 確保最乾淨的路徑優先級\n# if '/opt/conda/lib/python3.10/site-packages' not in sys.path:\n#     sys.path.insert(0, '/opt/conda/lib/python3.10/site-packages')\n\n# try:\n#     import mmcv\n#     from mmcv import Config\n#     print(f\"🎉 太棒了！mmcv 及其所有依賴項已完全恢復，版本: {mmcv.__version__}\")\n# except Exception as e:\n#     print(f\"仍然缺少其他依賴，錯誤為: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T11:45:00.772580Z","iopub.execute_input":"2026-05-31T11:45:00.772896Z","iopub.status.idle":"2026-05-31T11:45:00.786191Z","shell.execute_reply.started":"2026-05-31T11:45:00.772868Z","shell.execute_reply":"2026-05-31T11:45:00.785459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import segmentation_models_pytorch as smp\nimport mmcv_custom\nimport mmdet_custom\n","metadata":{"execution":{"iopub.status.busy":"2026-05-31T11:45:00.787848Z","iopub.execute_input":"2026-05-31T11:45:00.788435Z","iopub.status.idle":"2026-05-31T11:45:05.877165Z","shell.execute_reply.started":"2026-05-31T11:45:00.788399Z","shell.execute_reply":"2026-05-31T11:45:05.876373Z"},"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-31T11:45:05.878440Z","iopub.execute_input":"2026-05-31T11:45:05.878758Z","iopub.status.idle":"2026-05-31T11:45:06.597316Z","shell.execute_reply.started":"2026-05-31T11:45:05.878735Z","shell.execute_reply":"2026-05-31T11:45:06.596629Z"},"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-31T11:45:06.598391Z","iopub.execute_input":"2026-05-31T11:45:06.598708Z","iopub.status.idle":"2026-05-31T11:45:06.605118Z","shell.execute_reply.started":"2026-05-31T11:45:06.598685Z","shell.execute_reply":"2026-05-31T11:45:06.604448Z"},"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-31T11:45:06.606221Z","iopub.execute_input":"2026-05-31T11:45:06.606534Z","iopub.status.idle":"2026-05-31T11:45:06.619164Z","shell.execute_reply.started":"2026-05-31T11:45:06.606504Z","shell.execute_reply":"2026-05-31T11:45:06.618245Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Inference\n","metadata":{}},{"cell_type":"code","source":"from mmdet.apis import init_detector, inference_detector, show_result_pyplot, set_random_seed\nfrom mmcv import Config\n\n# ==========================================\n# 所有確定存在且可用的 Config 與 權重檔案\n# ==========================================\n\nconfigs_path = [\n    # 組合 1: 你最後自己新增的 ViT-Adapter\n    '/kaggle/input/datasets/yyastudent/vitadapter-pretrain-2nddaatset-weight/kaggle_config.py',\n    \n    # 組合 2: HTC 50 實驗系列\n    '/kaggle/input/ds1pretexp1moreaug-htc50-2048-cv408ps/h50psexp1.py',\n    \n    # 組合 3: CBNetV2 Swin-B 實驗系列\n    '/kaggle/input/pretexp1cbnetv2-base-2048-basic-exp1-f5cv437/exp1_cbnet_swinb2.py'\n]\n\nckpt_paths = [\n    # 組合 1 權重: ViT-Adapter Epoch 8\n    '/kaggle/input/datasets/yyastudent/vitadapter-pretrain-2nddaatset-weight/epoch_8.pth',\n    \n    # 組合 2 權重: HTC 50 Best Epoch 21\n    '/kaggle/input/ds1pretexp1moreaug-htc50-2048-cv408ps/best_segm_mAP_epoch_21.pth',\n    \n    # 組合 3 權重: CBNetV2 Swin-B Best Epoch 21\n    '/kaggle/input/pretexp1cbnetv2-base-2048-basic-exp1-f5cv437/best_segm_mAP_epoch_21.pth'\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-31T11:45:06.620168Z","iopub.execute_input":"2026-05-31T11:45:06.620459Z","iopub.status.idle":"2026-05-31T11:45:06.630781Z","shell.execute_reply.started":"2026-05-31T11:45:06.620439Z","shell.execute_reply":"2026-05-31T11:45:06.630083Z"}},"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-31T11:45:06.631818Z","iopub.execute_input":"2026-05-31T11:45:06.632053Z","iopub.status.idle":"2026-05-31T11:46:30.388236Z","shell.execute_reply.started":"2026-05-31T11:45:06.632034Z","shell.execute_reply":"2026-05-31T11:46:30.387447Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob  # 👈 補上這一行就解套了！\nimport os\nimport torch\n\nall_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-31T11:46:30.390521Z","iopub.execute_input":"2026-05-31T11:46:30.390814Z","iopub.status.idle":"2026-05-31T11:46:30.408829Z","shell.execute_reply.started":"2026-05-31T11:46:30.390791Z","shell.execute_reply":"2026-05-31T11:46:30.408124Z"},"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-31T11:46:30.409913Z","iopub.execute_input":"2026-05-31T11:46:30.410339Z","iopub.status.idle":"2026-05-31T11:46:30.418326Z","shell.execute_reply.started":"2026-05-31T11:46:30.410317Z","shell.execute_reply":"2026-05-31T11:46:30.417423Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ids = []\nheights = []\nwidths = []\nprediction_strings = []","metadata":{"execution":{"iopub.status.busy":"2026-05-31T11:46:30.419883Z","iopub.execute_input":"2026-05-31T11:46:30.420630Z","iopub.status.idle":"2026-05-31T11:46:30.430464Z","shell.execute_reply.started":"2026-05-31T11:46:30.420600Z","shell.execute_reply":"2026-05-31T11:46:30.429815Z"},"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-31T11:46:30.431713Z","iopub.execute_input":"2026-05-31T11:46:30.432543Z","iopub.status.idle":"2026-05-31T11:46:30.965456Z","shell.execute_reply.started":"2026-05-31T11:46:30.432511Z","shell.execute_reply":"2026-05-31T11:46:30.964494Z"},"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, 4))  # 👈 從 6 改成 4，讓容錯率變高\n\n\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            # 如果因為極微小的精度抖動找不到 key，就默默跳過，交給其他姿態的 mask 融合即可\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-31T11:48:28.553234Z","iopub.execute_input":"2026-05-31T11:48:28.553932Z","iopub.status.idle":"2026-05-31T11:48:28.561710Z","shell.execute_reply.started":"2026-05-31T11:48:28.553901Z","shell.execute_reply":"2026-05-31T11:48:28.560836Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subm_ids, subm_masks = [], []\nsample = None\nimport mmcv\nimport numpy as np\nimport cv2  # 👈 記得確保有 import cv2\n\nconfidence_thresholds = {0: 0.5, 1: 0.5, 2: 0.8}\n\n# 確保輸出容器乾淨\nids = []\nheights = []\nwidths = []\nprediction_strings = []\n\n# ====== 🔍 偵錯區：在進入迴圈前，先印出 models 列表的狀況 ======\nprint(\"=\" * 50)\nprint(f\"📊 目前偵測到全域 models 變數中共有 {len(models)} 個模型：\")\nfor idx, m in enumerate(models):\n    print(f\"    -> 模型 [{idx}]: {m.__class__.__name__}\")\nprint(\"=\" * 50)\n# ============================================================\n\nfor img_idx, img in enumerate(all_imgs):\n    pred_string = ''\n\n    img_array = mmcv.imread(img, channel_order='rgb')\n    [h, w, c_dim] = img_array.shape \n    \n    masks_nms_list = []\n    pred_dict_list = []\n    score_nms_list = []\n    box_nms_list = []\n    class_nms_list = []\n    \n    # 印出目前處理的影像進度\n    if img_idx % 10 == 0 or img_idx == len(all_imgs) - 1:\n        print(f\"📸 正在處理第 {img_idx + 1}/{len(all_imgs)} 張影像: {os.path.basename(img)}\")\n    \n    for model_idx, modely in enumerate(models):\n        \n        # 💡 【TTA 修正】這些容器要收集「該模型所有 TTA 姿態」的總和，所以要放在 TTA 迴圈外\n        pred_dict = {}\n        previous_masks = []\n        classes_nms = []\n        scoresb_nms = []\n        bboxesb_nms = []\n        count = 0  # 用來給對照表當 index 的計數器\n        \n        # 💡 【TTA 定義】定義 4 種手動翻轉 TTA 的姿態姿態：(影像矩陣, 是否水平翻轉, 是否垂直翻轉)\n        tta_variants = [\n            (img_array, False, False),                             # 1. 原圖\n            (cv2.flip(img_array, 1), True, False),                 # 2. 水平翻轉\n            (cv2.flip(img_array, 0), False, True),                 # 3. 垂直翻轉\n            (cv2.flip(img_array, -1), True, True)                  # 4. 水平+垂直翻轉 (等同旋轉180度)\n        ]\n        \n        for tta_idx, (img_tta, flip_h, flip_v) in enumerate(tta_variants):\n            # 餵入翻轉後的影像矩陣進行推論\n            result = inference_detector(modely, img_tta)\n\n            c = []\n            for i, classe in enumerate(result[0]):\n                c.append(classe.shape[0])\n            \n            if len(c) == 0 or np.max(c) == 0:\n                continue  # 如果這個姿態沒偵測到任何東西就跳過\n                \n            maxclass = np.argwhere(np.array(c) == np.max(c))[0][0]\n            \n            for i, classe in enumerate(result[0]):\n                if i == maxclass: \n                    bbs = classe\n                    sgs = result[1][i]\n\n                    for bb, sg in zip(bbs, sgs):\n                        box = bb[:4]\n                        cnf = bb[4]\n                        \n                        if cnf >= 0.00001:\n                            # 1. 轉為 0~1 的歸一化座標\n                            x1, y1, x2, y2 = box[0] / 512, box[1] / 512, box[2] / 512, box[3] / 512\n                            \n                            # 2. 核心：將 BBox 座標幾何反轉回原圖視角\n                            if flip_h:\n                                x1, x2 = 1.0 - x2, 1.0 - x1\n                            if flip_v:\n                                y1, y2 = 1.0 - y2, 1.0 - y1\n                                \n                            final_box = [x1, y1, x2, y2]\n                            \n                            # 3. 核心：將 Mask 矩陣反轉回原圖視角\n                            mask = np.array(sg, dtype=np.uint8)  \n                            if flip_h:\n                                mask = cv2.flip(mask, 1)\n                            if flip_v:\n                                mask = cv2.flip(mask, 0)\n                                \n                            # 4. 塞入該模型的總容器中\n                            previous_masks.append(mask)\n                            scoresb_nms.append(cnf)\n                            bboxesb_nms.append(final_box)\n                            \n                            pred_dict[bbox_to_key(final_box)] = count\n                            count += 1\n\n        # 💡 【TTA 修正】當某個模型跑完 4 種 TTA 姿態後，再一次 append 進大列表\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([0] * len(previous_masks), dtype=np.int32))\n        pred_dict_list.append(pred_dict)\n            \n    # 🔍 偵錯區：丟進 WBF 之前，檢查收集到的模型預測清單長度是否等於模型總數\n    if len(box_nms_list) != len(models):\n        print(f\"⚠️ 警告：收集到的預測資料量 ({len(box_nms_list)}) 與模型總數 ({len(models)}) 不一致！\")\n\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=[1.5, 1.0, 1.5], # 這裡可以維持你原本對三個模型的權重分配\n        iou_thr=0.6, \n        skip_box_thr=0.01\n    )\n    \n    fin_masks = []\n    for i in range(len(wbf_boxes)):\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    if len(fin_masks) > 0:\n        fin_masks = ensemble_pred_masks(fin_masks) \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        if m == 0:\n            pred_string += f\"0 {scory} {encoded.decode('utf-8')}\"\n            m = m + 1\n        else:\n            pred_string += f\" 0 {scory} {encoded.decode('utf-8')}\"\n\n    ids.append(os.path.basename(img).split('.')[0])\n    heights.append(h)\n    widths.append(w)\n    prediction_strings.append(pred_string)\n\nprint(\"\\n🎉 所有影像包含 TTA 的 Ensemble 推論完成！\")","metadata":{"execution":{"iopub.status.busy":"2026-05-31T11:48:30.551680Z","iopub.execute_input":"2026-05-31T11:48:30.551990Z","iopub.status.idle":"2026-05-31T11:49:22.116016Z","shell.execute_reply.started":"2026-05-31T11:48:30.551966Z","shell.execute_reply":"2026-05-31T11:49:22.115122Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\nsubmission = 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-31T11:49:22.117629Z","iopub.execute_input":"2026-05-31T11:49:22.117925Z","iopub.status.idle":"2026-05-31T11:49:22.149061Z","shell.execute_reply.started":"2026-05-31T11:49:22.117902Z","shell.execute_reply":"2026-05-31T11:49:22.148275Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# submission.loc['72e40acccadf','prediction_string']","metadata":{"execution":{"iopub.status.busy":"2026-05-31T11:49:22.149998Z","iopub.execute_input":"2026-05-31T11:49:22.150560Z","iopub.status.idle":"2026-05-31T11:49:22.154443Z","shell.execute_reply.started":"2026-05-31T11:49:22.150525Z","shell.execute_reply":"2026-05-31T11:49:22.153461Z"},"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-31T11:51:06.870038Z","iopub.execute_input":"2026-05-31T11:51:06.871034Z","iopub.status.idle":"2026-05-31T11:51:10.976364Z","shell.execute_reply.started":"2026-05-31T11:51:06.871000Z","shell.execute_reply":"2026-05-31T11:51:10.975278Z"},"trusted":true},"outputs":[],"execution_count":null}]}