{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":117682,"databundleVersionId":15062069,"sourceType":"competition"},{"sourceId":14358680,"sourceType":"datasetVersion","datasetId":9168730},{"sourceId":14360638,"sourceType":"datasetVersion","datasetId":9169910},{"sourceId":14361281,"sourceType":"datasetVersion","datasetId":9170370},{"sourceId":14695027,"sourceType":"datasetVersion","datasetId":9162843},{"sourceId":14701016,"sourceType":"datasetVersion","datasetId":9163984}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport sys\n\n# --find-links で、データセット内のすべての .whl を参照してインストール\n!pip install -q --no-index --find-links=/kaggle/input/monai-wheels-for-vesuvius monai\n# !pip install -q --no-index --find-links=/kaggle/input/imagecodecs-wheels imagecodecs\n\n!pip install --no-deps /kaggle/input/my-imagecodecs/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:48.868683Z","iopub.execute_input":"2026-02-01T08:18:48.868997Z","iopub.status.idle":"2026-02-01T08:18:53.353502Z","shell.execute_reply.started":"2026-02-01T08:18:48.868971Z","shell.execute_reply":"2026-02-01T08:18:53.352695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from monai.transforms import (\n    Compose,\n    LoadImaged,\n    EnsureChannelFirstd,\n    Resized,              # A.Resize の代わり\n    RandFlipd,            # A.HorizontalFlip/VerticalFlip の代わり\n    RandScaleIntensityd,  # A.MultiplicativeNoise / Contrast の代わり\n    RandShiftIntensityd,  # Brightness の代わり\n    NormalizeIntensityd,  # A.Normalize の代わり\n    ToTensord,            # ToTensorV2 の代わり\n)\nfrom monai.networks.nets import DynUNet\nfrom pytorch_lightning.callbacks import ModelCheckpoint, LearningRateMonitor\nfrom pytorch_lightning.loggers import CSVLogger\nimport torch\nimport torch.nn.functional as F\nimport gc\nimport numpy as np\nimport tifffile as tiff\nimport monai\n\nimport sys\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.355410Z","iopub.execute_input":"2026-02-01T08:18:53.355903Z","iopub.status.idle":"2026-02-01T08:18:53.361035Z","shell.execute_reply.started":"2026-02-01T08:18:53.355871Z","shell.execute_reply":"2026-02-01T08:18:53.360353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nif not os.path.exists('/kaggle/working/core'):\n    os.symlink('/kaggle/input/vesuvius-core-lib', '/kaggle/working/core')\n\nif '/kaggle/working' not in sys.path:\n    sys.path.insert(0, '/kaggle/working')\n\n\ntry:\n    from core.model  import (\n        MyVesConfig, MyVesDataset, MyVesDataModule, \n        MyVesTask, load_simple_model,\n        predict_fragment_ensemble\n    )\n    # from core.io import data_loader\n    from core.utils import plot_training_metrics\n    print(\"全モジュールのインポートに成功しました！\")\nexcept Exception as e:\n    print(f\"【索敵継続】エラー内容: {e}\")\n    # 万が一失敗した時のために、現在のパスの中身を表示\n    !ls -ld /kaggle/working/core","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.362372Z","iopub.execute_input":"2026-02-01T08:18:53.362603Z","iopub.status.idle":"2026-02-01T08:18:53.384550Z","shell.execute_reply.started":"2026-02-01T08:18:53.362580Z","shell.execute_reply":"2026-02-01T08:18:53.384008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub_folder = \"test_20260201\"\nsuffix = \"v2\"\n\nconfig = MyVesConfig()\n\nconfig.setup(is_kaggle = True)\nconfig.update_output(sub_folder, suffix)\nconfig.update_checkpoint_from_output(monitor=\"val_dice\", mode=\"max\")\nconfig","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.385369Z","iopub.execute_input":"2026-02-01T08:18:53.386004Z","iopub.status.idle":"2026-02-01T08:18:53.401822Z","shell.execute_reply.started":"2026-02-01T08:18:53.385980Z","shell.execute_reply":"2026-02-01T08:18:53.401264Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 準備","metadata":{}},{"cell_type":"code","source":"import pathlib\n\ntemp = pathlib.WindowsPath\n\n# 2. WindowsPath を PosixPath に強制的に置き換える\npathlib.WindowsPath = pathlib.PosixPath\n\ntry:\n    # 3. これでロードが可能になります\n    # MONAIのTraceKeys設定も維持\n    torch.serialization.add_safe_globals([monai.utils.enums.TraceKeys])\n    checkpoint = torch.load(config.CHECKPOINT_PATH, map_location=\"cpu\", weights_only=False)\n    print(\"Checkpoint loaded successfully!\")\n\nexcept Exception as e:\n    print(f\"Error loading checkpoint: {e}\")\n    raise e\n\n# finally:\n    # 4. 処理が終わったら（またはエラーでも）元に戻す\n    # pathlib.WindowsPath = temp\n\nconfig2 = config.load_checkpoint()\n# config.MODEL_SIZE = config2.MODEL_SIZE\n# config.VAL_TRANSFORM = config2.VAL_TRANSFORM\nconfig2.setup(is_kaggle = True)\nconfig2.update_output(sub_folder, suffix)\n\nconfig2\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.403495Z","iopub.execute_input":"2026-02-01T08:18:53.404032Z","iopub.status.idle":"2026-02-01T08:18:53.490968Z","shell.execute_reply.started":"2026-02-01T08:18:53.404004Z","shell.execute_reply":"2026-02-01T08:18:53.490278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"config2.TEST_TRANSFORM.transforms","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.492005Z","iopub.execute_input":"2026-02-01T08:18:53.492496Z","iopub.status.idle":"2026-02-01T08:18:53.496797Z","shell.execute_reply.started":"2026-02-01T08:18:53.492471Z","shell.execute_reply":"2026-02-01T08:18:53.496186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torch\n# import monai\n# # MONAIのTraceKeysを「安全なリスト」扱い\n# torch.serialization.add_safe_globals([monai.utils.enums.TraceKeys])\n# checkpoint = torch.load(config.CHECKPOINT_PATH, map_location=\"cpu\", weights_only=False)\n\n# val_config = checkpoint[\"hyper_parameters\"].get(\"config\")\n# MyVesConfig.MODEL_SIZE=val_config.MODEL_SIZE","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.497895Z","iopub.execute_input":"2026-02-01T08:18:53.498366Z","iopub.status.idle":"2026-02-01T08:18:53.510276Z","shell.execute_reply.started":"2026-02-01T08:18:53.498339Z","shell.execute_reply":"2026-02-01T08:18:53.509600Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_image_paths = config2.get_tif_paths(\"test\", \"image\")\n\n\n\n# net = DynUNet(\n#     spatial_dims=3,             # 1. 空間次元を3Dへ（Z, Y, X）\n#     in_channels=1,              # 入力チャンネル（1チャンネルの3Dボリューム）\n#     out_channels=1,             # 2. 出力はインクの有無（二値なら1、DiceLossに適合）\n#     kernel_size=[3, 3, 3, 3],   # 3. 3D畳み込みのサイズ（[D, H, W]のカーネル）\n#     strides=[1, 2, 2, 2],       # 4. 解像度を落とす歩幅（深すぎるとZ方向が消えるので注意）\n#     upsample_kernel_size=[2, 2, 2], # 逆畳み込みのサイズ\n#     filters=[32, 64, 128, 256], # フィルタ数（メモリと相談して調整）\n#     dropout=0.2,\n#     # res_block=True,\n#     deep_supervision=False,    # 深層監督を有効化\n\n# )\nfrom monai.networks.nets import UNet\nunet = UNet(\n    spatial_dims=3,\n    in_channels=1,\n    out_channels=1,\n    channels=(16, 32, 64, 128),\n    strides=(2, 2, 2, 2),\n    num_res_units=3,\n    norm='batch'\n)\n\ndyn_unet = DynUNet(\n    spatial_dims=3,             # 1. 空間次元を3Dへ（Z, Y, X）\n    in_channels=1,              # 入力チャンネル（1チャンネルの3Dボリューム）\n    out_channels=1,             # 2. 出力はインクの有無（二値なら1、DiceLossに適合）\n    kernel_size=[3, 3, 3, 3],   # 3. 3D畳み込みのサイズ（[D, H, W]のカーネル）\n    strides=[1, 2, 2, 2],       # 4. 解像度を落とす歩幅（深すぎるとZ方向が消えるので注意）\n    upsample_kernel_size=[2, 2, 2], # 逆畳み込みのサイズ\n    filters=[32, 64, 128, 256], # フィルタ数（メモリと相談して調整）\n    dropout=0.2,\n\n    deep_supervision=False,    # 深層監督を有効化（推奨）\n)\nnet = dyn_unet\n\nconfig2.setup(is_kaggle = True)\ncpt_path_model_tpl = [\n    (config2.CHECKPOINT_PATH, net)\n]\nprint(config2.TEST_TRANSFORM.transforms)\ntest_image_paths","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.511293Z","iopub.execute_input":"2026-02-01T08:18:53.511874Z","iopub.status.idle":"2026-02-01T08:18:53.549731Z","shell.execute_reply.started":"2026-02-01T08:18:53.511850Z","shell.execute_reply":"2026-02-01T08:18:53.549195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport monai\n# MONAIの列挙型などを「安全なクラス」として登録\ntorch.serialization.add_safe_globals([\n    monai.utils.enums.TraceKeys,\n    # もし他にもエラーが出たら、ここに追加していく\n])\n\nprint(\"【安全宣言完了】MONAIの独自クラスを許可リストに追加しました\")\n\nfrom pathlib import Path\nimport tifffile as tiff\n# Configの定義を上書き、あるいは直接パスを指定\nsubmission_base = Path(\"/kaggle/working/submission\")\nsubmission_base.mkdir(parents=True, exist_ok=True)\n\n# 自作クラスとMONAIクラスを「安全なグローバル」に一括登録\ntorch.serialization.add_safe_globals([\n    MyVesConfig, \n    monai.utils.enums.TraceKeys\n])\n#ここで調整・保存も行う\nfor test_image_path in test_image_paths:\n    test_scroll_name = test_image_path.stem\n    tif_dir = config2.SUBMISSION_DIR / test_scroll_name \n    tif_path = f\"{tif_dir}.tif\"\n    \n    ensemble_preds, cpt_path_model_tpl, test_scroll_name, test_act_size = predict_fragment_ensemble(\n            test_image_path,\n            config2,\n            cpt_path_model_tpl)\n    try:\n    \n        final_predictions = ensemble_preds / len(cpt_path_model_tpl)\n        print(f\"形状: {final_predictions.shape}\")\n        binary_mask = (final_predictions > config2.THRESHOLD).astype(np.uint8)\n        # メモリ解放\n        del ensemble_preds, final_predictions\n        gc.collect()    \n        \n        tiff.imwrite(tif_path, binary_mask, compression=None, bigtiff=True)\n        print( config2.SUBMISSION_DIR)\n    \n        # ループごとの掃除\n        gc.collect()\n        torch.cuda.empty_cache()\n    except Exception as e:\n        print(f\"!!! CRITICAL ERROR on {test_scroll_name}: {e}\")\n        # エラー時はダミー(0埋め)を保存しておく（第二部でZIPするときにエラーにならないように）\n        # ※サイズ取得が難しければ適当なサイズで逃げる\n        dummy_mask = np.zeros(test_act_size, dtype=np.uint8) \n        tiff.imwrite(save_path, dummy_mask, compression=None, bigtiff=True)\n        print(\"  -> Saved DUMMY mask due to error.\")\n      ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:18:53.550476Z","iopub.execute_input":"2026-02-01T08:18:53.550719Z","iopub.status.idle":"2026-02-01T08:19:01.774943Z","shell.execute_reply.started":"2026-02-01T08:18:53.550682Z","shell.execute_reply":"2026-02-01T08:19:01.774277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\n\ntif_files = sorted(config2.SUBMISSION_DIR.glob(\"*.tif\"))\nzip_path = Path(\"/kaggle/working/submission.zip\")\n\nwith zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:\n    for tif_file in tif_files:\n        # 1. ZIPへ書き込み\n        # ★ arcnameを指定し、ディレクトリ階層を含ませない（超重要）\n        zipf.write(tif_file, arcname = tif_file.name)\n        # 2. 即座に元ファイルを削除（ディスク容量確保）\n        os.remove(tif_file)\n\nprint(\"\\n=== Submission Created ===\")\nprint(f\"Files: {len(tif_files)} .tif files\")\nprint(f\"Zip: {zip_path}\")\nprint(f\"Threshold: {config2.THRESHOLD}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:19:01.776023Z","iopub.execute_input":"2026-02-01T08:19:01.776942Z","iopub.status.idle":"2026-02-01T08:19:02.169730Z","shell.execute_reply.started":"2026-02-01T08:19:01.776902Z","shell.execute_reply":"2026-02-01T08:19:02.169082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"# if False:\n#     a = tiff.imread(f\"{config2.TEST_IMAGES_DIR}/1407735.tif\")\n    \n#     print(a.shape)\n#     print(a[0, :4, :4])\n#     print(np.max(a[0, :, :]))\n#     print(np.mean(a[0, :, :]))\n#     print(type(a))\n#     print(type(a[0,0,0]))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:19:02.170652Z","iopub.execute_input":"2026-02-01T08:19:02.171050Z","iopub.status.idle":"2026-02-01T08:19:02.174046Z","shell.execute_reply.started":"2026-02-01T08:19:02.171022Z","shell.execute_reply":"2026-02-01T08:19:02.173427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# if True:\n#     binary_mask\n#     print(np.mean(binary_mask[0, :, :]))\n#     print(binary_mask[0, :2, :2])\n#     print(binary_mask.shape)\n#     print(binary_mask.dtype)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:19:02.174909Z","iopub.execute_input":"2026-02-01T08:19:02.175162Z","iopub.status.idle":"2026-02-01T08:19:02.188523Z","shell.execute_reply.started":"2026-02-01T08:19:02.175131Z","shell.execute_reply":"2026-02-01T08:19:02.187973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# tif_dir = config2.SUBMISSION_DIR\n# tif_files = sorted(tif_dir.glob(\"*.tif\"))[:3]  # Show first 3 files\n\n# import matplotlib.pyplot as plt\n\n# if tif_files:\n#     print(\"\\n=== Binary Mask Previews ===\\n\")\n    \n#     for tif_file in tif_files:\n#         print(tif_file)\n#         # Load the binary mask\n#         binary_mask = tiff.imread(tif_file)\n#         scroll_id = tif_file.stem\n        \n#         # Show 2 slices\n#         mid_slice = binary_mask.shape[0] // 2\n#         fig, axes = plt.subplots(1, 2, figsize=(12, 6))\n        \n#         axes[0].imshow(binary_mask[mid_slice], cmap='gray')\n#         axes[0].set_title(f'{scroll_id} - Slice {mid_slice}')\n#         axes[0].axis('off')\n        \n#         axes[1].imshow(binary_mask[mid_slice -140], cmap='gray')\n#         axes[1].set_title(f'{scroll_id} - Slice {mid_slice + binary_mask.shape[0]//4}')\n#         axes[1].axis('off')\n        \n#         plt.tight_layout()\n#         plt.show()\n            ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-01T08:20:35.285977Z","iopub.execute_input":"2026-02-01T08:20:35.286555Z","iopub.status.idle":"2026-02-01T08:20:35.292960Z","shell.execute_reply.started":"2026-02-01T08:20:35.286523Z","shell.execute_reply":"2026-02-01T08:20:35.292330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}