{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":52279,"databundleVersionId":5822112},{"sourceType":"datasetVersion","sourceId":6085957,"datasetId":3484793,"databundleVersionId":6164397},{"sourceType":"datasetVersion","sourceId":16503331,"datasetId":10552926,"databundleVersionId":17505834},{"sourceType":"datasetVersion","sourceId":6225623,"datasetId":3575833,"databundleVersionId":6305027},{"sourceType":"datasetVersion","sourceId":6225706,"datasetId":3575870,"databundleVersionId":6305110},{"sourceType":"datasetVersion","sourceId":6226761,"datasetId":3575901,"databundleVersionId":6306180},{"sourceType":"datasetVersion","sourceId":6056210,"datasetId":3465237,"databundleVersionId":6134497},{"sourceType":"datasetVersion","sourceId":5905549,"datasetId":3391298,"databundleVersionId":5982881},{"sourceType":"datasetVersion","sourceId":16468837,"datasetId":10553278,"databundleVersionId":17468929},{"sourceType":"datasetVersion","sourceId":6225321,"datasetId":3575473,"databundleVersionId":6304718}],"dockerImageVersionId":30528,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q --no-index /kaggle/input/mmdetv3-env/archive/addict-2.4.0-py3-none-any.whl\n# !pip install -q --no-index /kaggle/input/mmdetv3-env/archive/mmengine-0.7.4-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/mmengine-0.8.3-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/mmdetv3-env/archive/mmcv-2.0.0-cp310-cp310-linux_x86_64.whl\n!pip install -q --no-index /kaggle/input/mmdetv3-env/archive/terminaltables-3.1.10-py2.py3-none-any.whl\n!pip install -q --no-index /kaggle/input/pycocotools-206/wheels/pycocotools-2.0.6-cp310-cp310-linux_x86_64.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/ensemble_boxes-1.0.9-py3-none-any.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:53:54.879092Z","iopub.execute_input":"2026-05-30T18:53:54.879408Z","iopub.status.idle":"2026-05-30T18:54:44.023777Z","shell.execute_reply.started":"2026-05-30T18:53:54.879378Z","shell.execute_reply":"2026-05-30T18:54:44.022579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q --no-index /kaggle/input/vasculature-packages/ordered_set-4.1.0-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/model_index-0.1.11-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/einops-0.6.1-py3-none-any.whl\n!pip install -q --no-index /kaggle/input/vasculature-packages/mat4py-0.5.0-py2.py3-none-any.whl\n!pip install --no-deps --no-index /kaggle/input/vasculature-packages/mmpretrain-1.0.1-py2.py3-none-any.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:54:44.025366Z","iopub.execute_input":"2026-05-30T18:54:44.025722Z","iopub.status.idle":"2026-05-30T18:55:18.108177Z","shell.execute_reply.started":"2026-05-30T18:54:44.025689Z","shell.execute_reply":"2026-05-30T18:55:18.107109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q --no-index /kaggle/input/vasculature-packages/mmdet-3.1.0-py3-none-any.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:18.110590Z","iopub.execute_input":"2026-05-30T18:55:18.110886Z","iopub.status.idle":"2026-05-30T18:55:27.697613Z","shell.execute_reply.started":"2026-05-30T18:55:18.110863Z","shell.execute_reply":"2026-05-30T18:55:27.696509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport os\n\nimport mmengine\n\n\ndef prepare_dataset():\n    coco = {\n        'info': {},\n        'categories': [{\n            'id': 0,\n            'name': 'blood_vessel',\n        },{\n            'id': 1,\n            'name': 'glomerulus',\n        },{\n            'id': 2,\n            'name': 'unsure'\n        }],\n        'annotations': []\n    }\n    test_imgs = glob.glob('/kaggle/input/hubmap-hacking-the-human-vasculature/test/*.tif')\n\n    print(f\"================ 找到的測試集圖片總數: {len(test_imgs)} ================\")\n    img_infos = []\n    img_id = 0\n    for path in test_imgs:\n        filename = os.path.basename(path)\n        img_info = dict(\n            id=img_id,\n            width=512,\n            height=512,\n            file_name=filename,\n        )\n        img_infos.append(img_info)\n        img_id += 1\n    coco['images'] = img_infos\n    return coco\n\n\nmmengine.dump(prepare_dataset(), '/kaggle/working/test.json')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:27.698849Z","iopub.execute_input":"2026-05-30T18:55:27.699098Z","iopub.status.idle":"2026-05-30T18:55:27.878102Z","shell.execute_reply.started":"2026-05-30T18:55:27.699077Z","shell.execute_reply":"2026-05-30T18:55:27.877361Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import json\nimport os\n\n# 1. 確保 /kaggle/data 實體資料夾存在 (對應 Config 裡的 ../data/)\nos.makedirs(\"/kaggle/data\", exist_ok=True)\n\n# 2. 建立符合結構的 COCO 假標籤 (把測試集真實影像的名字塞進去)\ntest_img_dir = \"/kaggle/input/hubmap-hacking-the-human-vasculature/test\"\ntest_files = [f for f in os.listdir(test_img_dir) if f.endswith(\".tif\")]\n\ntest_coco = {\n    \"images\": [],\n    \"annotations\": [],\n    \"categories\": [\n        {\"id\": 0, \"name\": \"blood_vessel\"},\n        {\"id\": 1, \"name\": \"glomerulus\"},\n        {\"id\": 2, \"name\": \"unsure\"},\n    ],\n}\n\nfor idx, filename in enumerate(test_files):\n    test_coco[\"images\"].append(\n        {\"id\": idx, \"file_name\": filename, \"height\": 512, \"width\": 512}\n    )\n\n# 3. 🌟 關鍵：強制把檔案寫入 /kaggle/data/ 裡面\nwith open(\"/kaggle/data/dval0i.json\", \"w\") as f:\n    json.dump(test_coco, f)\n\n# 同步生一個訓練用的，防止 MMEngine 初始化別的 Loop 時偷看噴錯\nwith open(\"/kaggle/data/dtrain0i.json\", \"w\") as f:\n    json.dump(test_coco, f)\n\n# 4. 🌟 雙重保險：在當前工作目錄 /kaggle/working/data 也複製一份，防止有些地方寫 ./data\nos.makedirs(\"/kaggle/working/data\", exist_ok=True)\nwith open(\"/kaggle/working/data/dval0i.json\", \"w\") as f:\n    json.dump(test_coco, f)\n\nprint(f\"【成功】已在所有可能的路徑生成包含 {len(test_files)} 張影像的相容性標籤！\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:27.879287Z","iopub.execute_input":"2026-05-30T18:55:27.879586Z","iopub.status.idle":"2026-05-30T18:55:27.889674Z","shell.execute_reply.started":"2026-05-30T18:55:27.879564Z","shell.execute_reply":"2026-05-30T18:55:27.888756Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile test.py\n\n# Copyright (c) OpenMMLab. All rights reserved.\nimport argparse\nimport os\nimport os.path as osp\nimport warnings\nfrom copy import deepcopy\n\nimport importlib # 💡 補上這個 import\nimport importlib.machinery\n\nfrom mmengine import ConfigDict\nfrom mmengine.config import Config, DictAction\nfrom mmengine.runner import Runner\n\nfrom mmdet.engine.hooks.utils import trigger_visualization_hook\nfrom mmdet.evaluation import DumpDetResults\nfrom mmdet.registry import RUNNERS\nfrom mmdet.utils import setup_cache_size_limit_of_dynamo\n\nimport sys\nimport types\nif 'detectron2' not in sys.modules:\n    d2_mock = types.ModuleType('detectron2')\n    \n    # 🌟 重點：幫假模組補上 __spec__ 屬性，防止 transformers 的 find_spec 崩潰\n    d2_mock.__spec__ = importlib.machinery.ModuleSpec('detectron2', None)\n    \n    d2_eval_mock = types.ModuleType('detectron2.evaluation')\n    d2_fast_mock = types.ModuleType('detectron2.evaluation.fast_eval_api')\n    \n    try:\n        from pycocotools.cocoeval import COCOeval\n        d2_fast_mock.COCOeval_opt = COCOeval\n    except:\n        d2_fast_mock.COCOeval_opt = object\n        \n    sys.modules['detectron2'] = d2_mock\n    sys.modules['detectron2.evaluation'] = d2_eval_mock\n    sys.modules['detectron2.evaluation.fast_eval_api'] = d2_fast_mock\n    print(\"成功在離線環境中完美偽裝 detectron2 模組（含 __spec__）！\")\n# ========================================================\n\n# TODO: support fuse_conv_bn and format_only\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description='MMDet test (and eval) a model')\n    parser.add_argument('config', help='test config file path')\n    parser.add_argument('checkpoint', help='checkpoint file')\n    parser.add_argument(\n        '--work-dir',\n        help='the directory to save the file containing evaluation metrics')\n    parser.add_argument(\n        '--out',\n        type=str,\n        help='dump predictions to a pickle file for offline evaluation')\n    parser.add_argument(\n        '--show', action='store_true', help='show prediction results')\n    parser.add_argument(\n        '--show-dir',\n        help='directory where painted images will be saved. '\n        'If specified, it will be automatically saved '\n        'to the work_dir/timestamp/show_dir')\n    parser.add_argument(\n        '--wait-time', type=float, default=2, help='the interval of show (s)')\n    parser.add_argument(\n        '--cfg-options',\n        nargs='+',\n        action=DictAction,\n        help='override some settings in the used config, the key-value pair '\n        'in xxx=yyy format will be merged into config file. If the value to '\n        'be overwritten is a list, it should be like key=\"[a,b]\" or key=a,b '\n        'It also allows nested list/tuple values, e.g. key=\"[(a,b),(c,d)]\" '\n        'Note that the quotation marks are necessary and that no white space '\n        'is allowed.')\n    parser.add_argument(\n        '--launcher',\n        choices=['none', 'pytorch', 'slurm', 'mpi'],\n        default='none',\n        help='job launcher')\n    parser.add_argument('--tta', action='store_true')\n    # When using PyTorch version >= 2.0.0, the `torch.distributed.launch`\n    # will pass the `--local-rank` parameter to `tools/train.py` instead\n    # of `--local_rank`.\n    parser.add_argument('--local_rank', '--local-rank', type=int, default=0)\n    args = parser.parse_args()\n    if 'LOCAL_RANK' not in os.environ:\n        os.environ['LOCAL_RANK'] = str(args.local_rank)\n    return args\n\n\ndef main():\n    args = parse_args()\n\n    # Reduce the number of repeated compilations and improve\n    # testing speed.\n    setup_cache_size_limit_of_dynamo()\n\n    # load config\n    cfg = Config.fromfile(args.config)\n    # =========================================================================\n    # 🌟 離線純推理大招：清空所有訓練與優化排程配置，徹底滿足 MMEngine 檢查\n    # =========================================================================\n    cfg.train_dataloader = None\n    cfg.train_cfg = None\n    cfg.optim_wrapper = None\n    cfg.param_scheduler = None  # 💡 補上這一行，徹底把排程器也關掉！\n    # 2. 💡 砍掉驗證相關（解決本次報錯）\n    cfg.val_dataloader = None\n    cfg.val_evaluator = None\n    cfg.val_cfg = None\n    print(\"【模式對齊】已成功將訓練與排程配置全部設為 None，切換為純 Test 推理模式！\")\n    # =========================================================================\n    \n    cfg.launcher = args.launcher\n    if args.cfg_options is not None:\n        cfg.merge_from_dict(args.cfg_options)\n\n    # work_dir is determined in this priority: CLI > segment in file > filename\n    if args.work_dir is not None:\n        # update configs according to CLI args if args.work_dir is not None\n        cfg.work_dir = args.work_dir\n    elif cfg.get('work_dir', None) is None:\n        # use config filename as default work_dir if cfg.work_dir is None\n        cfg.work_dir = osp.join('./work_dirs',\n                                osp.splitext(osp.basename(args.config))[0])\n\n    if args.checkpoint != 'none':\n        cfg.load_from = args.checkpoint\n\n    if args.show or args.show_dir:\n        cfg = trigger_visualization_hook(cfg, args)\n\n    if args.tta:\n\n        if 'tta_model' not in cfg:\n            warnings.warn('Cannot find ``tta_model`` in config, '\n                          'we will set it as default.')\n            cfg.tta_model = dict(\n                type='DetTTAModel',\n                tta_cfg=dict(\n                    nms=dict(type='nms', iou_threshold=0.5), max_per_img=100))\n        if 'tta_pipeline' not in cfg:\n            warnings.warn('Cannot find ``tta_pipeline`` in config, '\n                          'we will set it as default.')\n            test_data_cfg = cfg.test_dataloader.dataset\n            while 'dataset' in test_data_cfg:\n                test_data_cfg = test_data_cfg['dataset']\n            cfg.tta_pipeline = deepcopy(test_data_cfg.pipeline)\n            flip_tta = dict(\n                type='TestTimeAug',\n                transforms=[\n                    [\n                        dict(type='RandomFlip', prob=1.),\n                        dict(type='RandomFlip', prob=0.)\n                    ],\n                    [\n                        dict(\n                            type='PackDetInputs',\n                            meta_keys=('img_id', 'img_path', 'ori_shape',\n                                       'img_shape', 'scale_factor', 'flip',\n                                       'flip_direction'))\n                    ],\n                ])\n            cfg.tta_pipeline[-1] = flip_tta\n        cfg.model = ConfigDict(**cfg.tta_model, module=cfg.model)\n        cfg.test_dataloader.dataset.pipeline = cfg.tta_pipeline\n\n    # build the runner from config\n    if 'runner_type' not in cfg:\n        # build the default runner\n        runner = Runner.from_cfg(cfg)\n    else:\n        # build customized runner from the registry\n        # if 'runner_type' is set in the cfg\n        runner = RUNNERS.build(cfg)\n\n    # add `DumpResults` dummy metric\n    if args.out is not None:\n        assert args.out.endswith(('.pkl', '.pickle')), \\\n            'The dump file must be a pkl file.'\n        runner.test_evaluator.metrics.append(\n            DumpDetResults(out_file_path=args.out))\n\n    # start testing\n    runner.test()\n\n\nif __name__ == '__main__':\n    main()\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:55:27.891237Z","iopub.execute_input":"2026-05-30T18:55:27.891532Z","iopub.status.idle":"2026-05-30T18:55:27.906036Z","shell.execute_reply.started":"2026-05-30T18:55:27.891511Z","shell.execute_reply":"2026-05-30T18:55:27.905294Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!cp -r /kaggle/input/hubmap-2023-modules /kaggle/working/hubmap_modules","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:55:27.907058Z","iopub.execute_input":"2026-05-30T18:55:27.907388Z","iopub.status.idle":"2026-05-30T18:55:28.900759Z","shell.execute_reply.started":"2026-05-30T18:55:27.907361Z","shell.execute_reply":"2026-05-30T18:55:28.899581Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/working/hubmap_modules/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:28.902359Z","iopub.execute_input":"2026-05-30T18:55:28.902864Z","iopub.status.idle":"2026-05-30T18:55:29.898269Z","shell.execute_reply.started":"2026-05-30T18:55:28.902834Z","shell.execute_reply":"2026-05-30T18:55:29.897213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 刪掉錯誤的 symlink，重新指向正確位置\n!rm /kaggle/working/custom_modules\n!ln -s /kaggle/input/datasets/yyastudent/rtmdet-module/custom_modules /kaggle/working/custom_modules\n!ls /kaggle/working/custom_modules/  # 應該看到 __init__.py 等檔案","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:29.901476Z","iopub.execute_input":"2026-05-30T18:55:29.901829Z","iopub.status.idle":"2026-05-30T18:55:32.858428Z","shell.execute_reply.started":"2026-05-30T18:55:29.901804Z","shell.execute_reply":"2026-05-30T18:55:32.857585Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# !PYTHONPATH=/kaggle/working:$PYTHONPATH \\\n# python test.py \\\n#     /kaggle/input/datasets/yyastudent/convnextv2-rtmdet/r0_kaggle.py \\\n#     /kaggle/input/datasets/yyastudent/convnextv2-rtmdet/iter_16128.pth \\\n#     --out /kaggle/working/r0_16218_rtmdet.pkl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T18:55:32.859667Z","iopub.execute_input":"2026-05-30T18:55:32.859995Z","iopub.status.idle":"2026-05-30T18:55:32.864493Z","shell.execute_reply.started":"2026-05-30T18:55:32.859971Z","shell.execute_reply":"2026-05-30T18:55:32.863561Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python test.py \\\n    /kaggle/input/hubmap-2023-configs/m0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/m0i.pth \\\n    --out /kaggle/working/m0i.pkl\n\n!python test.py \\\n    /kaggle/input/hubmap-2023-configs/m0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/m1i.pth \\\n    --out /kaggle/working/m1i.pkl","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:55:32.865514Z","iopub.execute_input":"2026-05-30T18:55:32.865839Z","iopub.status.idle":"2026-05-30T18:56:18.259775Z","shell.execute_reply.started":"2026-05-30T18:55:32.865809Z","shell.execute_reply":"2026-05-30T18:56:18.258853Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python test.py \\\n    /kaggle/input/hubmap-2023-configs/y0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/y0i.pth \\\n    --out /kaggle/working/y0i.pkl\n\n!python test.py \\\n    /kaggle/input/hubmap-2023-configs/y0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/y1i.pth \\\n    --out /kaggle/working/y1i.pkl","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:56:18.261437Z","iopub.execute_input":"2026-05-30T18:56:18.261797Z","iopub.status.idle":"2026-05-30T18:56:56.494116Z","shell.execute_reply.started":"2026-05-30T18:56:18.261764Z","shell.execute_reply":"2026-05-30T18:56:56.493051Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python test.py \\\n    /kaggle/input/hubmap-2023-configs/r0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/r0i.pth \\\n    --out /kaggle/working/r0i.pkl\n\n!python test.py \\\n    /kaggle/input/hubmap-2023-configs/r0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/r1i.pth \\\n    --out /kaggle/working/r1i.pkl","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:56:56.495543Z","iopub.execute_input":"2026-05-30T18:56:56.495849Z","iopub.status.idle":"2026-05-30T18:57:36.463641Z","shell.execute_reply.started":"2026-05-30T18:56:56.495824Z","shell.execute_reply":"2026-05-30T18:57:36.462664Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python test.py \\\n    /kaggle/input/hubmap-2023-configs/s0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/s0i.pth \\\n    --out /kaggle/working/s0i.pkl\n\n!python test.py \\\n    /kaggle/input/hubmap-2023-configs/s0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/s1i.pth \\\n    --out /kaggle/working/s1i.pkl\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:57:36.464939Z","iopub.execute_input":"2026-05-30T18:57:36.465293Z","iopub.status.idle":"2026-05-30T18:58:24.443117Z","shell.execute_reply.started":"2026-05-30T18:57:36.465267Z","shell.execute_reply":"2026-05-30T18:58:24.442071Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python test.py \\\n    /kaggle/input/hubmap-2023-configs/sb0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/sb0i.pth \\\n    --out /kaggle/working/sb0i.pkl\n\n!python test.py \\\n    /kaggle/input/hubmap-2023-configs/sb0i.py \\\n    /kaggle/input/hubmap-2023-checkpoints/sb1i.pth \\\n    --out /kaggle/working/sb1i.pkl\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T18:58:24.444761Z","iopub.execute_input":"2026-05-30T18:58:24.445027Z","iopub.status.idle":"2026-05-30T18:59:14.675043Z","shell.execute_reply.started":"2026-05-30T18:58:24.445005Z","shell.execute_reply":"2026-05-30T18:59:14.674048Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport mmengine\nfrom ensemble_boxes import weighted_boxes_fusion\n\nresults = [\n    mmengine.load(f'/kaggle/working/{name}.pkl') for name in\n    ['r0i', 'r1i', 's0i', 's1i', 'm0i', 'm1i', 'y0i', 'y1i', 'sb0i', 'sb1i']\n]\nweights = [\n    2, 2, 2, 2, 1, 1, 1, 1, 2, 2\n]\n\nSCALER = 10000\nIOU_THR = 0.7\n\nfor rs in zip(*results):\n    boxes_list = [(r['pred_instances']['bboxes'] / SCALER).tolist() for r in rs]\n    scores_list = [r['pred_instances']['scores'].tolist() for r in rs]\n    labels_list = [r['pred_instances']['labels'].tolist() for r in rs]\n    boxes, scores, labels = weighted_boxes_fusion(boxes_list,\n                                                scores_list,\n                                                labels_list,\n                                                weights=weights,\n                                                iou_thr=IOU_THR,\n                                                conf_type='avg')\n    pred_instances = dict(\n        bboxes=torch.from_numpy(boxes).float() * SCALER,\n        scores=torch.from_numpy(scores).float(),\n        labels=torch.from_numpy(labels).long(),\n    )\n    rs[0]['pred_instances'] = pred_instances\n\nensemble_result = results[0]\n\nmmengine.dump(ensemble_result, 'ensemble.pkl')","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:00:57.538108Z","iopub.execute_input":"2026-05-30T19:00:57.538894Z","iopub.status.idle":"2026-05-30T19:00:57.723280Z","shell.execute_reply.started":"2026-05-30T19:00:57.538839Z","shell.execute_reply":"2026-05-30T19:00:57.722296Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # 不要用ensemble\n# import mmengine\n# import shutil\n\n# # 方法一：用 Python 複製檔案，直接把單模型結果改名為後續程式要用的檔名\n# shutil.copyfile('/kaggle/working/r0_16218_rtmdet.pkl', '/kaggle/working/ensemble.pkl')\n\n# print(\"已成功將單模型 m0i 的結果複製為後續預測所需的結果，跳過 Ensemble。\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:00:58.908376Z","iopub.execute_input":"2026-05-30T19:00:58.908999Z","iopub.status.idle":"2026-05-30T19:00:58.913304Z","shell.execute_reply.started":"2026-05-30T19:00:58.908969Z","shell.execute_reply":"2026-05-30T19:00:58.912315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile predict_mask.py\n\nimport sys\nimport os\nimport types\nimport importlib\nimport importlib.machinery\n\n# ========================================================\n# 💡 離線環境完美偽裝 detectron2，防止載入自定義模組崩潰\n# ========================================================\nif 'detectron2' not in sys.modules:\n    d2_mock = types.ModuleType('detectron2')\n    d2_mock.__spec__ = importlib.machinery.ModuleSpec('detectron2', None)\n    d2_eval_mock = types.ModuleType('detectron2.evaluation')\n    d2_fast_mock = types.ModuleType('detectron2.evaluation.fast_eval_api')\n    try:\n        from pycocotools.cocoeval import COCOeval\n        d2_fast_mock.COCOeval_opt = COCOeval\n    except:\n        d2_fast_mock.COCOeval_opt = object\n    sys.modules['detectron2'] = d2_mock\n    sys.modules['detectron2.evaluation'] = d2_eval_mock\n    sys.modules['detectron2.evaluation.fast_eval_api'] = d2_fast_mock\n\nsolution_path = '/kaggle/working/custom_modules'\nif os.path.exists(solution_path) and solution_path not in sys.path:\n    sys.path.insert(0, solution_path)\n# ========================================================\n\nimport mmcv\nimport mmengine\nimport torch\nfrom mmengine.runner import load_checkpoint\nfrom mmdet.registry import MODELS\nfrom mmdet.structures import DetDataSample\nfrom mmdet.structures.mask import encode_mask_results\nfrom mmdet.utils import register_all_modules\n\nregister_all_modules()\n\n# 載入你最新優化版的 Config 與單模型權重\ncfg = mmengine.Config.fromfile('/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/r0_kaggle.py')\nmodel = MODELS.build(cfg.model)\nload_checkpoint(model, '/kaggle/input/datasets/yyastudent/convnextv2-rtmdet/iter_16128.pth')\nmodel.eval()\nmodel.cuda()\n\n# 讀取最原始、包含真實 img_path 的單模型預測 BBox 檔案\nresults = mmengine.load('/kaggle/working/ensemble.pkl')\n\noutputs = []\nprint(\"開始使用 RTMDetWithMaskHead 一條龍預測 Mask...\")\n\nfor idx, result in enumerate(results):\n    img_path = result['img_path']\n    img = mmcv.imread(img_path)\n    \n    # 建立符合 MMDetection 規範的推理輸入\n    batch_data = dict(\n        inputs=[torch.from_numpy(img).permute(2, 0, 1)],\n        data_samples=[\n            DetDataSample(metainfo=dict(\n                img_id=result['img_id'],\n                ori_shape=img.shape[:2],\n                img_shape=img.shape[:2],\n                img_path=img_path,\n                scale_factor=(1.0, 1.0)\n            ))\n        ]\n    )\n    \n    # 🌟 透過內建 data_preprocessor 把 list 轉化為標準的四維矩陣\n    batch_data = model.data_preprocessor(batch_data, False)\n    \n    with torch.no_grad():\n        predictions = model(batch_data['inputs'], batch_data['data_samples'], mode='predict')\n    \n    # 提取模型預測執行個體 (內含 bboxes, scores, labels, masks)\n    pred_instances = predictions[0].pred_instances.cpu()\n    \n    # 打包成轉換 CSV 程式碼最愛的格式\n    ret = dict(\n        img_id=result['img_id'],\n        img_path=img_path,\n        pred_instances=dict(\n            bboxes=pred_instances.bboxes,\n            labels=pred_instances.labels,\n            scores=pred_instances.scores,\n            masks=encode_mask_results(pred_instances.masks) \n        )\n    )\n    outputs.append(ret)\n    \n    if (idx + 1) % 50 == 0:\n        print(f\"已完成 {idx + 1} / {len(results)} 張影像的遮罩預測\")\n\n# 匯出最終結果\nmmengine.dump(outputs, '/kaggle/working/ensemble_results_rtmdet.pkl')\nprint(\"【大成功】ensemble_results_rtmdet.pkl 檔案已順利生成！\")","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:00.164243Z","iopub.execute_input":"2026-05-30T19:01:00.164862Z","iopub.status.idle":"2026-05-30T19:01:00.172604Z","shell.execute_reply.started":"2026-05-30T19:01:00.164835Z","shell.execute_reply":"2026-05-30T19:01:00.171667Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python predict_mask.py","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:00.400711Z","iopub.execute_input":"2026-05-30T19:01:00.401669Z","iopub.status.idle":"2026-05-30T19:01:28.100096Z","shell.execute_reply.started":"2026-05-30T19:01:00.401640Z","shell.execute_reply":"2026-05-30T19:01:28.099118Z"},"trusted":true},"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\n# 🌟 重新定義一個完全相容新版 NumPy 的二值化遮罩編碼函數\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  # 💡 關鍵修正：將 np.bool 改為 bool，徹底解決新舊版本 NumPy 的相容性大坑\n  if mask.dtype != bool and 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\n\nprint(\"【成功】encode_binary_mask 函數已成功更新為安全相容版本！\")","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:28.102381Z","iopub.execute_input":"2026-05-30T19:01:28.102703Z","iopub.status.idle":"2026-05-30T19:01:28.114371Z","shell.execute_reply.started":"2026-05-30T19:01:28.102677Z","shell.execute_reply":"2026-05-30T19:01:28.113445Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport mmcv\nimport mmengine\nimport pandas as pd\nimport pycocotools.mask as mask_utils\n\nresults = mmengine.load('/kaggle/working/ensemble_results_rtmdet.pkl')\nids = []\nHEIGHT = 512\nWIDTH = 512\nprediction_strings = []\nfor result in results:\n    img_path = result['img_path']\n    filename = os.path.basename(img_path)\n    ids.append(filename[:-4])\n    pred_instances = result['pred_instances']\n    bboxes = pred_instances['bboxes']\n    scores = pred_instances['scores'].tolist()\n    labels = pred_instances['labels'].tolist()\n    masks = pred_instances['masks']\n    instance_strings = []\n    for label, score, mask in zip(labels, scores, masks):\n        if label != 0:\n            continue\n        mask = mask_utils.decode(mask).astype(bool)\n        mask_string = encode_binary_mask(mask).decode('utf-8')\n        \n        instance_string = f'{label} {score} {mask_string}'\n        instance_strings.append(instance_string)\n    prediction_strings.append(' '.join(instance_strings))\n","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:28.115503Z","iopub.execute_input":"2026-05-30T19:01:28.115898Z","iopub.status.idle":"2026-05-30T19:01:28.308583Z","shell.execute_reply.started":"2026-05-30T19:01:28.115875Z","shell.execute_reply":"2026-05-30T19:01:28.307898Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = pd.DataFrame(dict(\n    id=ids,\n    height=[HEIGHT] * len(ids),\n    width=[WIDTH] * len(ids),\n    prediction_string=prediction_strings\n))","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:28.310214Z","iopub.execute_input":"2026-05-30T19:01:28.310786Z","iopub.status.idle":"2026-05-30T19:01:28.318800Z","shell.execute_reply.started":"2026-05-30T19:01:28.310760Z","shell.execute_reply":"2026-05-30T19:01:28.317695Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2026-05-30T19:01:28.319909Z","iopub.execute_input":"2026-05-30T19:01:28.320266Z","iopub.status.idle":"2026-05-30T19:01:28.334791Z","shell.execute_reply.started":"2026-05-30T19:01:28.320234Z","shell.execute_reply":"2026-05-30T19:01:28.333968Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls -lh /kaggle/working/submission.csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-30T19:01:28.336743Z","iopub.execute_input":"2026-05-30T19:01:28.337236Z","iopub.status.idle":"2026-05-30T19:01:29.342476Z","shell.execute_reply.started":"2026-05-30T19:01:28.337213Z","shell.execute_reply":"2026-05-30T19:01:29.341460Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}