{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":117682,"databundleVersionId":15062069},{"sourceType":"datasetVersion","sourceId":13744909,"datasetId":8745866,"databundleVersionId":14494777},{"sourceType":"datasetVersion","sourceId":12192573,"datasetId":7679902,"databundleVersionId":12731246},{"sourceType":"datasetVersion","sourceId":12112738,"datasetId":7626297,"databundleVersionId":12643106},{"sourceType":"datasetVersion","sourceId":13890663,"datasetId":8741359,"databundleVersionId":14655903},{"sourceType":"datasetVersion","sourceId":13733466,"datasetId":8737966,"databundleVersionId":14481813},{"sourceType":"datasetVersion","sourceId":13745180,"datasetId":8745691,"databundleVersionId":14495087},{"sourceType":"datasetVersion","sourceId":12908673,"datasetId":8167951,"databundleVersionId":13559740},{"sourceType":"datasetVersion","sourceId":13041191,"datasetId":8056419,"databundleVersionId":13718004},{"sourceType":"datasetVersion","sourceId":14848532,"datasetId":8751217,"databundleVersionId":15708547},{"sourceType":"datasetVersion","sourceId":13733258,"datasetId":8737808,"databundleVersionId":14481588},{"sourceType":"datasetVersion","sourceId":13793191,"datasetId":8781413,"databundleVersionId":14548367},{"sourceType":"datasetVersion","sourceId":14780135,"datasetId":9448048,"databundleVersionId":15633150},{"sourceType":"datasetVersion","sourceId":14790425,"datasetId":9416721,"databundleVersionId":15644504,"isSourceIdPinned":true},{"sourceType":"datasetVersion","sourceId":14884440,"datasetId":8751134,"databundleVersionId":15747756},{"sourceType":"kernelVersion","sourceId":277844088},{"sourceType":"kernelVersion","sourceId":290917305}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/111test-whl/pip-21.0.1-py3-none-any.whl","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:00.923231Z","iopub.execute_input":"2026-02-27T07:24:00.923498Z","iopub.status.idle":"2026-02-27T07:24:08.836228Z","shell.execute_reply.started":"2026-02-27T07:24:00.923475Z","shell.execute_reply":"2026-02-27T07:24:08.835498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install --no-deps /kaggle/input/mamba-zzz-whl/causal_conv1d-1.5.3.post1cu12torch2.6cxx11abiFALSE-cp311-cp311-linux_x86_64.whl\n!pip install --no-deps /kaggle/input/mamba-zzz-whl/mamba_ssm-2.2.4cu12torch2.6cxx11abiFALSE-cp311-cp311-linux_x86_64.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:08.837930Z","iopub.execute_input":"2026-02-27T07:24:08.838204Z","iopub.status.idle":"2026-02-27T07:24:18.370498Z","shell.execute_reply.started":"2026-02-27T07:24:08.838173Z","shell.execute_reply":"2026-02-27T07:24:18.369610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install --no-index --no-deps --find-links='/kaggle/input/torch-topological-1119/whl' 'torch_topological' 'pot' 'gudhi' 'cycler' 'networkx' 'sympy' 'giotto_ph'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:18.371571Z","iopub.execute_input":"2026-02-27T07:24:18.371806Z","iopub.status.idle":"2026-02-27T07:24:20.010306Z","shell.execute_reply.started":"2026-02-27T07:24:18.371782Z","shell.execute_reply":"2026-02-27T07:24:20.009380Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install --no-index --find-links='/kaggle/input/imagecodecs-nnunet' 'imagecodecs'\n!pip install --no-index --no-deps --find-links='/kaggle/input/2nd-place-byu-challenge-packages' 'fft_conv_pytorch' 'connected_components_3d'\n!pip install --no-index --no-deps --find-links='/kaggle/input/monaiwheel' 'monai'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:20.012141Z","iopub.execute_input":"2026-02-27T07:24:20.012343Z","iopub.status.idle":"2026-02-27T07:24:32.542504Z","shell.execute_reply.started":"2026-02-27T07:24:20.012322Z","shell.execute_reply":"2026-02-27T07:24:32.541817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install /kaggle/input/notebooks/ipythonx/vsdetection-packages-offline-installer-only/whls/medicai-0.0.3-py3-none-any.whl\n!pip install --no-deps /kaggle/input/datasets/arunodhayan/moonai/timm_3d-1.0.1-py3-none-any.whl\n!pip install --no-deps /kaggle/input/datasets/deniztopal01/all-wheels/wheels_min/efficientnet_pytorch-0.7.1-py3-none-any.whl\n!pip install --no-deps /kaggle/input/datasets/deniztopal01/all-wheels/wheels_min/pretrainedmodels-0.7.4-py3-none-any.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:32.543450Z","iopub.execute_input":"2026-02-27T07:24:32.543691Z","iopub.status.idle":"2026-02-27T07:24:45.058065Z","shell.execute_reply.started":"2026-02-27T07:24:32.543658Z","shell.execute_reply":"2026-02-27T07:24:45.057308Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# prepare data","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport nibabel as nib\nimport tifffile\nfrom tqdm import tqdm\nimport os\n\nos.environ['nnUNet_raw'] = \"/kaggle/input/tmp-data-raw/nnUNet_raw\"\nos.environ['nnUNet_preprocessed'] = \"/kaggle/input/tmp-data-raw/nnUNet_preprocessed\"\nos.environ['nnUNet_results'] = \"/kaggle/input/zzz-nnunet-1116/nnUNet_results\"\n# os.environ['nnUNet_compile'] = \"true\"","metadata":{"trusted":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2026-02-27T07:24:45.059060Z","iopub.execute_input":"2026-02-27T07:24:45.059306Z","iopub.status.idle":"2026-02-27T07:24:46.420959Z","shell.execute_reply.started":"2026-02-27T07:24:45.059275Z","shell.execute_reply":"2026-02-27T07:24:46.420314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom tqdm import tqdm\nimport shutil\n\ndef copy_test_tif_renamed():\n    test_csv_path = \"/kaggle/input/vesuvius-challenge-surface-detection/test.csv\"\n    test_images_dir = \"/kaggle/input/vesuvius-challenge-surface-detection/test_images\"\n\n    output_dir = \"/kaggle/working/test_tmp\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    test_df = pd.read_csv(test_csv_path)\n    test_ids = test_df['id'].tolist()\n    \n    print(f\"找到 {len(test_ids)} 个测试样本\")\n    print(f\"输出目录: {output_dir}\")\n    \n    success_count = 0\n    for test_id in tqdm(test_ids, desc=\"复制测试集\"):\n        src_path = os.path.join(test_images_dir, f\"{test_id}.tif\")\n        dst_path = os.path.join(output_dir, f\"{test_id}_0000.tif\")\n        \n        if not os.path.exists(src_path):\n            print(f\"❌ 文件不存在: {src_path}\")\n            continue\n        \n        try:\n            shutil.copy2(src_path, dst_path)\n            success_count += 1\n            \n        except Exception as e:\n            print(f\"❌ 复制失败 {test_id}: {e}\")\n    \n    print(f\"\\n🎉 复制完成!\")\n    print(f\"成功复制: {success_count}/{len(test_ids)} 个文件\")\n    print(f\"文件保存在: {output_dir}\")\n\n    tif_files = [f for f in os.listdir(output_dir) if f.endswith('.tif')]\n    print(f\"\\n生成的TIFF文件:\")\n    for file in tif_files:\n        file_path = os.path.join(output_dir, file)\n        file_size = os.path.getsize(file_path) / (1024 * 1024)  # MB\n        print(f\"  📄 {file} ({file_size:.1f} MB)\")\n    \n    return output_dir\n\n\nif __name__ == \"__main__\":\n    output_dir = copy_test_tif_renamed()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:46.421728Z","iopub.execute_input":"2026-02-27T07:24:46.422124Z","iopub.status.idle":"2026-02-27T07:24:46.898917Z","shell.execute_reply.started":"2026-02-27T07:24:46.422105Z","shell.execute_reply":"2026-02-27T07:24:46.898072Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# infer","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport shutil\nfrom tqdm import tqdm\n\nW_A = 0.30   # 802Segmamba3_1000\nW_B = 0.20   # 801Segmamba3_1500\nW_C = 0.20    # 801NNunetRes3_1000\nW_D = 0.05    # 802NNunetLowres2_1000\nW_lhw = 0.25\n\nDIR_A = \"/kaggle/tmp/test_pred_802Segmamba3_1000\"\nDIR_B = \"/kaggle/tmp/test_pred_801Segmamba3_1500\"\nDIR_C = \"/kaggle/tmp/test_pred_801NNunetRes3_1000\"\nDIR_D = \"/kaggle/tmp/test_pred_802NNunetLowres2_1000\"\nDIR_lhw = \"/kaggle/tmp/test_pred_lhwsmpmitb5\"\n\nPART_SUM_DIR = \"/kaggle/tmp/partial_probs\"\nos.makedirs(PART_SUM_DIR, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:46.899811Z","iopub.execute_input":"2026-02-27T07:24:46.900660Z","iopub.status.idle":"2026-02-27T07:24:46.905980Z","shell.execute_reply.started":"2026-02-27T07:24:46.900623Z","shell.execute_reply":"2026-02-27T07:24:46.905226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_802Segmamba3_1000.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\ndef nnunetv2_predict_with_predictor_802Segmamba3_1000():\n    DATASET_ID = 802\n    CONFIGURATION = '3d_fullres'\n    TRAINER_CLASS = 'nnUNetTrainerThreestageLossSegmamba'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/test_pred_802Segmamba3_1000'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_Tiffdata', f'{TRAINER_CLASS}__nnUNetPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_final.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_802Segmamba3_1000()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:46.906792Z","iopub.execute_input":"2026-02-27T07:24:46.907020Z","iopub.status.idle":"2026-02-27T07:24:46.922872Z","shell.execute_reply.started":"2026-02-27T07:24:46.906996Z","shell.execute_reply":"2026-02-27T07:24:46.922316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_801Segmamba3_1500.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\n\ndef nnunetv2_predict_with_predictor_801Segmamba3_1500():\n    DATASET_ID = 801\n    CONFIGURATION = '3d_fullres'\n    TRAINER_CLASS = 'nnUNetTrainerMoreThreestageLossSegmamba'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/test_pred_801Segmamba3_1500'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_Extra', f'{TRAINER_CLASS}__nnUNetPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_final.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_801Segmamba3_1500()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:46.925819Z","iopub.execute_input":"2026-02-27T07:24:46.926090Z","iopub.status.idle":"2026-02-27T07:24:46.940440Z","shell.execute_reply.started":"2026-02-27T07:24:46.926075Z","shell.execute_reply":"2026-02-27T07:24:46.939753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%bash\nCUDA_VISIBLE_DEVICES=0 python inference_802Segmamba3_1000.py > log_A.txt 2>&1 &\nCUDA_VISIBLE_DEVICES=1 python inference_801Segmamba3_1500.py > log_B.txt 2>&1 &\nwait\necho \"Stage 1: A & B Inference Done.\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:24:46.941308Z","iopub.execute_input":"2026-02-27T07:24:46.941572Z","iopub.status.idle":"2026-02-27T07:28:02.175641Z","shell.execute_reply.started":"2026-02-27T07:24:46.941527Z","shell.execute_reply":"2026-02-27T07:28:02.174853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_A.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.176464Z","iopub.execute_input":"2026-02-27T07:28:02.176704Z","iopub.status.idle":"2026-02-27T07:28:02.300725Z","shell.execute_reply.started":"2026-02-27T07:28:02.176676Z","shell.execute_reply":"2026-02-27T07:28:02.300046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_B.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.301637Z","iopub.execute_input":"2026-02-27T07:28:02.301904Z","iopub.status.idle":"2026-02-27T07:28:02.421122Z","shell.execute_reply.started":"2026-02-27T07:28:02.301878Z","shell.execute_reply":"2026-02-27T07:28:02.420284Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_801NNunetRes3_1000.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\n\ndef nnunetv2_predict_with_predictor_801NNunetRes3_1000():\n    DATASET_ID = 801\n    CONFIGURATION = '3d_fullres'\n    TRAINER_CLASS = 'nnUNetTrainerThreestageLoss'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/test_pred_801NNunetRes3_1000'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_Extra', f'{TRAINER_CLASS}__nnUNetResEncUNetLPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_final.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_801NNunetRes3_1000()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.422329Z","iopub.execute_input":"2026-02-27T07:28:02.422621Z","iopub.status.idle":"2026-02-27T07:28:02.430766Z","shell.execute_reply.started":"2026-02-27T07:28:02.422589Z","shell.execute_reply":"2026-02-27T07:28:02.429991Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_802NNunetLowres2_1000.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\n\ndef nnunetv2_predict_with_predictor_802NNunetLowres2_1000():\n    DATASET_ID = 802\n    CONFIGURATION = '3d_lowres'\n    TRAINER_CLASS = 'nnUNetTrainerTwostageLoss'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/test_pred_802NNunetLowres2_1000'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_Tiffdata', f'{TRAINER_CLASS}__nnUNetPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_final.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_802NNunetLowres2_1000()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.431673Z","iopub.execute_input":"2026-02-27T07:28:02.432016Z","iopub.status.idle":"2026-02-27T07:28:02.454523Z","shell.execute_reply.started":"2026-02-27T07:28:02.431994Z","shell.execute_reply":"2026-02-27T07:28:02.453847Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_lhwsmpmitb5.py\nimport os\nimport sys\nimport numpy as np\nimport pandas as pd\nimport torch\nimport tifffile\nimport torch.nn.functional as F\nfrom tqdm import tqdm\n\nsys.path.append('/kaggle/input/datasets/lihaoweicvch/vesuvius2025-3rd-pkg')\nfrom medicai.transforms import Compose, NormalizeIntensity\nimport segmentation_models_pytorch_3d as smp\nfrom monai.inferers import sliding_window_inference\n\nROOT_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection\"\nTEST_DIR = f\"{ROOT_DIR}/test_images\"\nOUTPUT_DIR = \"/kaggle/tmp/test_pred_lhwsmpmitb5\"\nMODEL_PATH = '/kaggle/input/vesuvius2025-models/0210_tta_flip_last.pth'\n\ndef val_transformation(image):\n    data = {\"image\": image}\n    pipeline = Compose([\n        NormalizeIntensity(\n            keys=[\"image\"], \n            nonzero=True,\n            channel_wise=False\n        ),\n    ])\n    result = pipeline(data)\n    return result[\"image\"]\n\n\nclass Model1(torch.nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.model = smp.Unet(\n            encoder_name=\"mit_b5\",\n            in_channels=3,\n            classes=2,\n            encoder_weights=None\n        )\n    def forward(self, image):\n        roi_size = (160, 160, 160)\n        sw_batch_size = 1\n        overlap = 0.5\n \n        with torch.no_grad(),torch.autocast(device_type='cuda', dtype=torch.float16):\n            out = sliding_window_inference(\n                inputs=image,\n                roi_size=roi_size,\n                sw_batch_size=sw_batch_size,\n                predictor=self.model,\n                overlap=overlap,\n                mode='gaussian',\n            )\n            return out.cpu().numpy()\n            \n\ndef predict_with_tta(inputs, model):\n    inputs = inputs.unsqueeze(0).repeat(1, 3, 1, 1, 1)\n    print('inputs: ', inputs.shape)\n    logits = []\n    logits.append(model(inputs))\n    # Flips (spatial only)\n    for axis in [2,3,4]:\n        img_f = torch.flip(inputs, dims=[axis])\n        p = model(img_f)\n        p = np.flip(p, axis=axis)\n        logits.append(p)\n    logits = np.array(logits)\n    mean_logits = np.mean(logits, axis=0)\n    mean_logits = torch.from_numpy(mean_logits)\n    mean_logits = torch.softmax(mean_logits, dim=1)\n    print('mean_logits: ', mean_logits.shape)\n    return mean_logits.squeeze().numpy()\n    \ndef load_volume(path):\n    vol = tifffile.imread(path)\n    vol = vol.astype(np.float32)\n    vol = vol[None, ...]\n    return vol\n\ndef lhw_main():\n    os.makedirs(OUTPUT_DIR, exist_ok=True)\n    test_df = pd.read_csv(f\"{ROOT_DIR}/test.csv\")\n\n    print(\"Loading SMP MIT-B5 Model...\")\n    model = Model1()\n    state_dict = torch.load(MODEL_PATH, map_location='cpu')\n    model.load_state_dict(state_dict, strict=False)\n    model = model.cuda().eval()\n\n    for image_id in tqdm(test_df[\"id\"], desc=\"SMP Inference\"):\n        tif_path = f\"{TEST_DIR}/{image_id}.tif\"\n\n        volume = load_volume(tif_path)\n        volume = val_transformation(volume).numpy()\n        volume = torch.from_numpy(volume).cuda()\n\n        probs = predict_with_tta(volume, model)\n\n        save_path = os.path.join(OUTPUT_DIR, f\"{image_id}.npz\")\n        np.savez_compressed(save_path, probabilities=probs.astype(np.float16))\n        print(f'Saved: {save_path}, shape: {probs.shape}, dtype: float16')\n\nif __name__ == '__main__':\n    lhw_main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.455325Z","iopub.execute_input":"2026-02-27T07:28:02.455656Z","iopub.status.idle":"2026-02-27T07:28:02.476903Z","shell.execute_reply.started":"2026-02-27T07:28:02.455633Z","shell.execute_reply":"2026-02-27T07:28:02.476227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%bash\nCUDA_VISIBLE_DEVICES=0 python inference_801NNunetRes3_1000.py > log_C.txt 2>&1 &\n\n\n(CUDA_VISIBLE_DEVICES=1 python inference_802NNunetLowres2_1000.py > log_D.txt 2>&1 ; \\\n CUDA_VISIBLE_DEVICES=1 python inference_lhwsmpmitb5.py > log_LHW.txt 2>&1) &\n\nwait\necho \"Stage 1: (nnU-Net C, D + SMP) Inference Done.\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:28:02.498005Z","iopub.execute_input":"2026-02-27T07:28:02.498290Z","iopub.status.idle":"2026-02-27T07:30:10.107430Z","shell.execute_reply.started":"2026-02-27T07:28:02.498274Z","shell.execute_reply":"2026-02-27T07:30:10.106521Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_C.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:10.108218Z","iopub.execute_input":"2026-02-27T07:30:10.108389Z","iopub.status.idle":"2026-02-27T07:30:10.228129Z","shell.execute_reply.started":"2026-02-27T07:30:10.108374Z","shell.execute_reply":"2026-02-27T07:30:10.227361Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_D.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:10.229213Z","iopub.execute_input":"2026-02-27T07:30:10.229438Z","iopub.status.idle":"2026-02-27T07:30:10.349306Z","shell.execute_reply.started":"2026-02-27T07:30:10.229415Z","shell.execute_reply":"2026-02-27T07:30:10.348532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_LHW.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:10.350353Z","iopub.execute_input":"2026-02-27T07:30:10.350592Z","iopub.status.idle":"2026-02-27T07:30:10.468796Z","shell.execute_reply.started":"2026-02-27T07:30:10.350557Z","shell.execute_reply":"2026-02-27T07:30:10.468126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%matplotlib inline\nimport os\nimport numpy as np\nimport pandas as pd\nimport tifffile\nimport scipy.ndimage as ndi\nfrom skimage.morphology import remove_small_objects\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\n\ndef build_anisotropic_struct(z_radius: int, xy_radius: int):\n    \"\"\"构建 3D 各向异性结构元素\"\"\"\n    z, r = int(z_radius), int(xy_radius)\n    if z == 0 and r == 0: return None\n    depth, size = 2*z + 1, 2*r + 1\n    struct = np.zeros((depth, size, size), dtype=bool)\n    cz, cy, cx = z, r, r\n    for dz in range(-z, z+1):\n        for dy in range(-r, r+1):\n            for dx in range(-r, r+1):\n                if dy*dy + dx*dx <= r*r:\n                    struct[cz+dz, cy+dy, cx+dx] = True\n    return struct\n\n\ndef seeded_hysteresis_with_topology(prob, T_low=0.40, T_high=0.85, z_radius=3, xy_radius=2, dust_min_size=150):\n    prob = np.asarray(prob, dtype=np.float32)\n    \n    # 定义强弱区域\n    strong = prob >= float(T_high)\n    # 没有第二个模型的 pub_fg_bool，weak 就只靠 T_low 支撑\n    weak = prob >= float(T_low) \n    if not strong.any():\n        return np.zeros_like(prob, dtype=np.uint8)\n\n    # 3D 传播\n    struct_hyst = ndi.generate_binary_structure(3, 3) # 定义 26 连通\n    mask = ndi.binary_propagation(strong, mask=weak, structure=struct_hyst)\n    if not mask.any():\n        return np.zeros_like(prob, dtype=np.uint8)\n\n    # 3D 闭合\n    struct_close = build_anisotropic_struct(z_radius, xy_radius)\n    if struct_close is not None:\n        mask = ndi.binary_closing(mask, structure=struct_close)\n\n    # 清理\n    if int(dust_min_size) > 0:\n        mask = remove_small_objects(mask.astype(bool), min_size=int(dust_min_size))\n\n    return mask.astype(np.uint8)\n\ndef read_tiff_volume(tiff_path):\n    img = Image.open(tiff_path)\n    slices = []\n    for i in range(img.n_frames):\n        img.seek(i)\n        slices.append(np.array(img))\n    return np.stack(slices, axis=0)\n\ndef visualize_comparison(pred_slices, gt_slices, test_id, slice_indices):\n    fig, axes = plt.subplots(2, len(slice_indices), figsize=(15, 8))\n    \n    if len(slice_indices) == 1:\n        axes = axes.reshape(2, 1)\n    \n    for i, slice_idx in enumerate(slice_indices):\n        axes[0, i].imshow(pred_slices[i], cmap='gray', vmin=0, vmax=1)\n        axes[0, i].set_title(f'Pred Slice {slice_idx}')\n        axes[0, i].axis('off')\n\n        axes[1, i].imshow(gt_slices[i], cmap='gray', vmin=0, vmax=2)\n        axes[1, i].set_title(f'GT Slice {slice_idx}')\n        axes[1, i].axis('off')\n    \n    plt.suptitle(f'Comparison for {test_id}', fontsize=16)\n    plt.tight_layout()\n    plt.show()\n\n\n# ----------------------------\nTRAIN_LABELS_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_labels\"\nTEST_IMAGES_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/test_images\"\nOUTPUT_DIR = \"/kaggle/working\"\nos.makedirs(OUTPUT_DIR, exist_ok=True)\nDEBUG_MODE = len(os.listdir(TEST_IMAGES_DIR)) <= 1\n\nCFG = dict(\n    T_low=0.40, \n    T_high=0.90, \n    z_radius=3, \n    xy_radius=2, \n    dust_min_size=150\n)\n\nprint(f\"Debug Mode: {DEBUG_MODE}\")\n\n\nfiles_A = set(os.listdir(DIR_A))\nfiles_B = set(os.listdir(DIR_B))\nfiles_C = set(os.listdir(DIR_C))\nfiles_D = set(os.listdir(DIR_D))\nfiles_lhw = set(os.listdir(DIR_lhw))\ncommon_files = sorted([f for f in files_A.intersection(files_B, files_C, files_D, files_lhw) if f.endswith('.npz')])\n\nfor f in tqdm(common_files, desc=\"Ensemble + Post-processing\"):\n    tid = f.replace('.npz', '')\n    data_a = np.load(os.path.join(DIR_A, f), mmap_mode='r')\n    data_b = np.load(os.path.join(DIR_B, f), mmap_mode='r')\n    data_c = np.load(os.path.join(DIR_C, f), mmap_mode='r')\n    data_d = np.load(os.path.join(DIR_D, f), mmap_mode='r')\n    data_lhw = np.load(os.path.join(DIR_lhw, f), mmap_mode='r')\n    prob_a = np.array(data_a['probabilities'][1], dtype=np.float32)\n    prob_b = np.array(data_b['probabilities'][1], dtype=np.float32)\n    prob_c = np.array(data_c['probabilities'][1], dtype=np.float32)\n    prob_d = np.array(data_d['probabilities'][1], dtype=np.float32)\n    prob_lhw = np.array(data_lhw['probabilities'][1], dtype=np.float32)\n    # print(prob_a.shape, prob_a.max())\n    # print(prob_b.shape, prob_b.max())\n    # print(prob_c.shape, prob_c.max())\n    # print(prob_d.shape, prob_d.max())\n    # print(prob_lhw.shape, prob_lhw.max())\n    fg_prob = (W_A * prob_a + W_B * prob_b + W_C * prob_c + W_D * prob_d + W_lhw * prob_lhw)\n    del prob_a, prob_b, prob_c, prob_d, prob_lhw\n    mask = seeded_hysteresis_with_topology(fg_prob, **CFG)\n    out_path_stage2 = os.path.join(\"/kaggle/working/test_tmp\", f\"{tid}_0001.tif\")\n    tifffile.imwrite(out_path_stage2, mask, compression='zlib')\n\n    if DEBUG_MODE:\n        print(f\"\\n🔍 Debug模式: {tid}\")\n        print(f\"预测mask形状: {mask.shape}\")\n        \n        train_label_path = os.path.join(TRAIN_LABELS_DIR, f\"{tid}.tif\")\n        if os.path.exists(train_label_path):\n            print(\"找到对应的训练标签，进行可视化对比...\")\n            gt_mask = read_tiff_volume(train_label_path)\n            print(f\"真实标签形状: {gt_mask.shape}\")\n        \n            depth = mask.shape[0]\n            slice_indices = [depth // 2, 0, depth - 1]\n            \n            pred_slices = [mask[i] for i in slice_indices]\n            gt_slices = [gt_mask[i] for i in slice_indices]\n            \n            visualize_comparison(pred_slices, gt_slices, tid, slice_indices)\n\n            print(f\"预测mask数值范围: [{mask.min()}, {mask.max()}]\")\n            print(f\"真实标签数值范围: [{gt_mask.min()}, {gt_mask.max()}]\")\n            print(f\"预测mask唯一值: {np.unique(mask)}\")\n            print(f\"真实标签唯一值: {np.unique(gt_mask)}\")\n        else:\n            print(\"未找到对应的训练标签...\")\n            depth = mask.shape[0]\n            slice_indices = [depth // 2, 0, depth - 1]\n            pred_slices = [mask[i] for i in slice_indices]\n            visualize_comparison(pred_slices, pred_slices, tid, slice_indices)\n            print(f\"预测mask数值范围: [{mask.min()}, {mask.max()}]\")\n            print(f\"预测mask唯一值: {np.unique(mask)}\")\n\nprint(f\"🎉 Stage 1 结果已转换为 _0001.tif 并存入 /kaggle/working/test_tmp\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:34:26.944957Z","iopub.execute_input":"2026-02-27T07:34:26.945353Z","iopub.status.idle":"2026-02-27T07:34:36.939198Z","shell.execute_reply.started":"2026-02-27T07:34:26.945327Z","shell.execute_reply":"2026-02-27T07:34:36.938595Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# stage2","metadata":{}},{"cell_type":"code","source":"%%writefile inference_stage2_nnunetbase.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\n\ndef nnunetv2_predict_with_predictor_stage2nnunetbase():\n    DATASET_ID = 803\n    CONFIGURATION = '3d_fullres'\n    TRAINER_CLASS = 'nnUNetTrainerThreestageLossNNunetbase2stage'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/stage2_nnunetbase'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_CascadeTif', f'{TRAINER_CLASS}__nnUNetPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_latest.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_stage2nnunetbase()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:20.673634Z","iopub.execute_input":"2026-02-27T07:30:20.674098Z","iopub.status.idle":"2026-02-27T07:30:20.680064Z","shell.execute_reply.started":"2026-02-27T07:30:20.674080Z","shell.execute_reply":"2026-02-27T07:30:20.679344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile inference_stage2_segmambav2.py\n\nimport torch\nimport sys\n\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/acvl_utils-0.2.5/acvl_utils-0.2.5')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgenerators-0.25.1/batchgenerators-0.25.1')\nsys.path.insert(0, '/kaggle/input/2nd-place-byu-challenge-packages/batchgeneratorsv2-0.3.0/batchgeneratorsv2-0.3.0')\nsys.path.insert(0, '/kaggle/input/dynamic-package/dynamic_network_architectures-0.4.2')\nsys.path.insert(0, '/kaggle/input/nnunet-master-1116/nnUNet-master')\n\nfrom nnunetv2.inference.predict_from_raw_data import nnUNetPredictor\nfrom nnunetv2.paths import nnUNet_results, nnUNet_raw\nimport os\nfrom batchgenerators.utilities.file_and_folder_operations import join\nimport glob\n\n\ndef nnunetv2_predict_with_predictor_stage2segmamba():\n    DATASET_ID = 803\n    CONFIGURATION = '3d_fullres'\n    TRAINER_CLASS = 'nnUNetTrainerThreestageLossSegmamba2stage'\n    FOLDS = (\"all\",) \n\n    RAW_DATA_FOLDER = '/kaggle/working'\n    INPUT_FOLDER = join(RAW_DATA_FOLDER, 'test_tmp')  \n    OUTPUT_FOLDER = '/kaggle/tmp/stage2_segmamba'\n\n    model_folder = join(nnUNet_results, f'Dataset{DATASET_ID}_CascadeTif', f'{TRAINER_CLASS}__nnUNetPlans__{CONFIGURATION}')\n    \n    print(\"=\" * 60)\n    print(\"nnUNetv2 Predictor 推理\")\n    print(\"=\" * 60)\n    print(f\"模型文件夹: {model_folder}\")\n    print(f\"输入目录: {INPUT_FOLDER}\")\n    print(f\"输出目录: {OUTPUT_FOLDER}\")\n    \n    if not os.path.exists(INPUT_FOLDER):\n        print(f\"❌ 输入文件夹不存在: {INPUT_FOLDER}\")\n        print(\"正在查找可用的输入文件...\")\n        \n        possible_paths = [\n            '/kaggle/working/test_tmp'\n        ]\n        \n        for path in possible_paths:\n            if os.path.exists(path):\n                INPUT_FOLDER = path\n                print(f\"✓ 找到输入文件夹: {INPUT_FOLDER}\")\n                break\n        else:\n            print(\"未找到输入文件夹，请手动指定正确的路径\")\n            return\n    \n    os.makedirs(OUTPUT_FOLDER, exist_ok=True)\n    \n    try:\n        predictor = nnUNetPredictor(\n            tile_step_size=0.5,\n            use_gaussian=True,\n            use_mirroring=True,\n            perform_everything_on_device=True,\n            device=torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n            verbose=True,\n            verbose_preprocessing=False,\n            enable_deep_supervision=False,\n            allow_tqdm=True\n        )\n        \n        print(\"\\n1. 初始化模型...\")\n        predictor.initialize_from_trained_model_folder(\n            model_folder,\n            use_folds=FOLDS,\n            checkpoint_name='checkpoint_latest.pth',\n        )\n        print(\"✓ 模型初始化完成\")\n \n        nii_files = glob.glob(join(INPUT_FOLDER, \"*.tif\"))\n        if not nii_files:\n            print(f\"❌ 在 {INPUT_FOLDER} 中没有找到.tif文件\")\n            print(\"请确保文件存在且格式正确\")\n            return\n        \n        print(f\"找到 {len(nii_files)} 个输入文件\")\n        for f in nii_files[:3]:  \n            print(f\"  - {os.path.basename(f)}\")\n        if len(nii_files) > 3:\n            print(f\"  - ... 还有 {len(nii_files) - 3} 个文件\")\n        \n        print(\"\\n2. 开始推理...\")\n        predictor.predict_from_files(\n            INPUT_FOLDER,\n            OUTPUT_FOLDER,\n            save_probabilities=True,\n            overwrite=True,\n            num_processes_preprocessing=2,\n            num_processes_segmentation_export=2,\n            folder_with_segs_from_prev_stage=None,\n            num_parts=1,\n            part_id=0\n        )\n        \n        print(\"\\n✓ 推理完成!\")\n        \n        output_files = os.listdir(OUTPUT_FOLDER)\n        print(f\"生成 {len(output_files)} 个预测文件:\")\n        for f in output_files:\n            print(f\"  - {f}\")\n            \n    except Exception as e:\n        print(f\"\\n❌ 推理失败: {e}\")\n        import traceback\n        traceback.print_exc()\n\n\nif __name__ == '__main__':\n    nnunetv2_predict_with_predictor_stage2segmamba()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:20.681014Z","iopub.execute_input":"2026-02-27T07:30:20.681260Z","iopub.status.idle":"2026-02-27T07:30:20.703231Z","shell.execute_reply.started":"2026-02-27T07:30:20.681240Z","shell.execute_reply":"2026-02-27T07:30:20.702591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%bash\nCUDA_VISIBLE_DEVICES=0 python inference_stage2_nnunetbase.py > log_2A.txt 2>&1 &\nCUDA_VISIBLE_DEVICES=1 python inference_stage2_segmambav2.py > log_2B.txt 2>&1 &\nwait\necho \"Stage 2: 2A & 2B Inference Done.\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:30:20.704027Z","iopub.execute_input":"2026-02-27T07:30:20.704250Z","iopub.status.idle":"2026-02-27T07:32:57.950678Z","shell.execute_reply.started":"2026-02-27T07:30:20.704225Z","shell.execute_reply":"2026-02-27T07:32:57.949975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_2A.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:32:57.951678Z","iopub.execute_input":"2026-02-27T07:32:57.951936Z","iopub.status.idle":"2026-02-27T07:32:58.078442Z","shell.execute_reply.started":"2026-02-27T07:32:57.951917Z","shell.execute_reply":"2026-02-27T07:32:58.077743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tail -n 1000 log_2B.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:32:58.079587Z","iopub.execute_input":"2026-02-27T07:32:58.079866Z","iopub.status.idle":"2026-02-27T07:32:58.203579Z","shell.execute_reply.started":"2026-02-27T07:32:58.079832Z","shell.execute_reply":"2026-02-27T07:32:58.202695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%matplotlib inline\nimport os\nimport numpy as np\nimport pandas as pd\nimport tifffile\nimport scipy.ndimage as ndi\nfrom skimage.morphology import remove_small_objects\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\n\ndef build_anisotropic_struct(z_radius: int, xy_radius: int):\n    \"\"\"构建 3D 各向异性结构元素\"\"\"\n    z, r = int(z_radius), int(xy_radius)\n    if z == 0 and r == 0: return None\n    depth, size = 2*z + 1, 2*r + 1\n    struct = np.zeros((depth, size, size), dtype=bool)\n    cz, cy, cx = z, r, r\n    for dz in range(-z, z+1):\n        for dy in range(-r, r+1):\n            for dx in range(-r, r+1):\n                if dy*dy + dx*dx <= r*r:\n                    struct[cz+dz, cy+dy, cx+dx] = True\n    return struct\n\n\ndef seeded_hysteresis_with_topology(prob, T_low=0.40, T_high=0.85, z_radius=3, xy_radius=2, dust_min_size=150):\n    prob = np.asarray(prob, dtype=np.float32)\n    \n    # 定义强弱区域\n    strong = prob >= float(T_high)\n    # 没有第二个模型的 pub_fg_bool，weak 就只靠 T_low 支撑\n    weak = prob >= float(T_low) \n    if not strong.any():\n        return np.zeros_like(prob, dtype=np.uint8)\n\n    # 3D 传播\n    struct_hyst = ndi.generate_binary_structure(3, 3) # 定义 26 连通\n    mask = ndi.binary_propagation(strong, mask=weak, structure=struct_hyst)\n    if not mask.any():\n        return np.zeros_like(prob, dtype=np.uint8)\n\n    # 3D 闭合\n    struct_close = build_anisotropic_struct(z_radius, xy_radius)\n    if struct_close is not None:\n        mask = ndi.binary_closing(mask, structure=struct_close)\n\n    # 清理\n    if int(dust_min_size) > 0:\n        mask = remove_small_objects(mask.astype(bool), min_size=int(dust_min_size))\n\n    return mask.astype(np.uint8)\n\ndef read_tiff_volume(tiff_path):\n    img = Image.open(tiff_path)\n    slices = []\n    for i in range(img.n_frames):\n        img.seek(i)\n        slices.append(np.array(img))\n    return np.stack(slices, axis=0)\n\ndef visualize_comparison(pred_slices, gt_slices, test_id, slice_indices):\n    fig, axes = plt.subplots(2, len(slice_indices), figsize=(15, 8))\n    \n    if len(slice_indices) == 1:\n        axes = axes.reshape(2, 1)\n    \n    for i, slice_idx in enumerate(slice_indices):\n        axes[0, i].imshow(pred_slices[i], cmap='gray', vmin=0, vmax=1)\n        axes[0, i].set_title(f'Pred Slice {slice_idx}')\n        axes[0, i].axis('off')\n\n        axes[1, i].imshow(gt_slices[i], cmap='gray', vmin=0, vmax=2)\n        axes[1, i].set_title(f'GT Slice {slice_idx}')\n        axes[1, i].axis('off')\n    \n    plt.suptitle(f'Comparison for {test_id}', fontsize=16)\n    plt.tight_layout()\n    plt.show()\n\n\n# ----------------------------\nLOGITS_DIR = \"/kaggle/tmp/stage2_output\"   \nTRAIN_LABELS_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_labels\"\nTEST_IMAGES_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/test_images\"\nOUTPUT_DIR = \"/kaggle/working\"\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\n\nDEBUG_MODE = len(os.listdir(TEST_IMAGES_DIR)) <= 1\nCFG = dict(\n    T_low=0.40, \n    T_high=0.85, \n    z_radius=2, \n    xy_radius=1, \n    dust_min_size=400\n)\n\nprint(f\"Debug Mode: {DEBUG_MODE}\")\n\n\nDIR_2A = '/kaggle/tmp/stage2_nnunetbase'\nDIR_2B = '/kaggle/tmp/stage2_segmamba'\nW_2A = 0.3\nW_2B = 0.7\n\nfiles_2A = set(os.listdir(DIR_2A))\nfiles_2B = set(os.listdir(DIR_2B))\ncommon_files = sorted([f for f in files_2A.intersection(files_2B) if f.endswith('.npz')])\n\nfor f in tqdm(common_files, desc=\"Ensemble + Post-processing\"):\n    test_id = f.replace('.npz', '')\n    data_2a = np.load(os.path.join(DIR_2A, f), mmap_mode='r')\n    data_2b = np.load(os.path.join(DIR_2B, f), mmap_mode='r')\n\n    prob_2a = np.array(data_2a['probabilities'][1], dtype=np.float32)\n    prob_2b = np.array(data_2b['probabilities'][1], dtype=np.float32)\n    fg_prob = (W_2A * prob_2a + W_2B * prob_2b)\n    del prob_2a, prob_2b\n    mask = seeded_hysteresis_with_topology(fg_prob, **CFG)\n    out_path = os.path.join(OUTPUT_DIR, f\"{test_id}.tif\")\n    tifffile.imwrite(out_path, mask, compression='zlib')\n    \n    if DEBUG_MODE:\n        print(f\"\\n🔍 Debug模式: {test_id}\")\n        print(f\"预测mask形状: {mask.shape}\")\n        \n        train_label_path = os.path.join(TRAIN_LABELS_DIR, f\"{test_id}.tif\")\n        if os.path.exists(train_label_path):\n            print(\"找到对应的训练标签，进行可视化对比...\")\n            gt_mask = read_tiff_volume(train_label_path)\n            print(f\"真实标签形状: {gt_mask.shape}\")\n        \n            depth = mask.shape[0]\n            slice_indices = [depth // 2, 0, depth - 1]\n            \n            pred_slices = [mask[i] for i in slice_indices]\n            gt_slices = [gt_mask[i] for i in slice_indices]\n            \n            visualize_comparison(pred_slices, gt_slices, test_id, slice_indices)\n\n            print(f\"预测mask数值范围: [{mask.min()}, {mask.max()}]\")\n            print(f\"真实标签数值范围: [{gt_mask.min()}, {gt_mask.max()}]\")\n            print(f\"预测mask唯一值: {np.unique(mask)}\")\n            print(f\"真实标签唯一值: {np.unique(gt_mask)}\")\n        else:\n            print(\"未找到对应的训练标签...\")\n            depth = mask.shape[0]\n            slice_indices = [depth // 2, 0, depth - 1]\n            \n            pred_slices = [mask[i] for i in slice_indices]\n            \n            visualize_comparison(pred_slices, pred_slices, test_id, slice_indices)\n\n            print(f\"预测mask数值范围: [{mask.min()}, {mask.max()}]\")\n            print(f\"预测mask唯一值: {np.unique(mask)}\")\n            \n    \n   \nprint(f\"🎉 处理完成！最终 Mask 已保存至: {OUTPUT_DIR}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:32:58.210318Z","iopub.execute_input":"2026-02-27T07:32:58.210892Z","iopub.status.idle":"2026-02-27T07:33:03.725732Z","shell.execute_reply.started":"2026-02-27T07:32:58.210866Z","shell.execute_reply":"2026-02-27T07:33:03.724921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\nimport numpy as np\nimport pandas as pd\n\ntest_csv_path = \"/kaggle/input/vesuvius-challenge-surface-detection/test.csv\"\ntest_df = pd.read_csv(test_csv_path)\ntest_ids = test_df['id'].tolist()\n\n\nprint(\"\\n📦 创建提交文件...\")\nwith zipfile.ZipFile('submission.zip', 'w') as zipf:\n    for test_id in test_ids:\n        tif_file = f\"{test_id}.tif\"\n        if os.path.exists(tif_file):\n            zipf.write(tif_file)\n            print(f\"✓ 添加: {tif_file}\")\n        else:\n            print(f\"❌ 文件不存在: {tif_file}\")\n\nprint(\"🎉 提交文件创建完成!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T07:33:03.727284Z","iopub.execute_input":"2026-02-27T07:33:03.727583Z","iopub.status.idle":"2026-02-27T07:33:03.742744Z","shell.execute_reply.started":"2026-02-27T07:33:03.727539Z","shell.execute_reply":"2026-02-27T07:33:03.741984Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}