{"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":"none","dataSources":[{"sourceId":117682,"databundleVersionId":14443416,"sourceType":"competition"},{"sourceId":2786089,"sourceType":"datasetVersion","datasetId":1701116},{"sourceId":14031242,"sourceType":"datasetVersion","datasetId":8934971}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nimport os\nimport subprocess\nimport numpy as np\nimport torch\nimport zipfile\nimport math\nfrom PIL import Image\n\n# ==========================================\n# 1. SETUP LIBRARY OFFLINE (WAJIB DULUAN!)\n# ==========================================\nprint(\"⚙️ [1/5] Menginstall library secara offline...\")\nLIB_DIR = \"/kaggle/input/pytorch-segmentation-models-lib\"\n\nif os.path.exists(LIB_DIR):\n    # Install semua file .whl\n    for file in os.listdir(LIB_DIR):\n        if file.endswith(\".whl\"):\n            try:\n                subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", os.path.join(LIB_DIR, file), \"--no-deps\", \"--quiet\"])\n            except:\n                pass\n    \n    # Tambahkan path source code\n    sys.path.append(os.path.join(LIB_DIR, \"efficientnet_pytorch-0.6.3/efficientnet_pytorch-0.6.3\"))\n    sys.path.append(os.path.join(LIB_DIR, \"pretrainedmodels-0.7.4/pretrainedmodels-0.7.4\"))\n    print(\"✅ Setup Library Selesai!\")\nelse:\n    print(\"❌ ERROR: Folder library tidak ditemukan. Pastikan Add Input sudah benar.\")\n\n# ==========================================\n# 2. IMPORT SETELAH INSTALL\n# ==========================================\ntry:\n    import segmentation_models_pytorch as smp\n    print(\"✅ Berhasil import segmentation_models_pytorch\")\nexcept ImportError:\n    print(\"❌ ERROR IMPORT! Library gagal di-load.\")\n    sys.exit(1)\n\n# ==========================================\n# 3. KONFIGURASI\n# ==========================================\nTEST_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/test_images\"\nMODEL_FILE = \"model_v3_final.pth\" \nCROP_SIZE = 256\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Cari lokasi model (otomatis)\nMODEL_PATH = \"\"\nfor root, dirs, files in os.walk(\"/kaggle/input\"):\n    if MODEL_FILE in files:\n        MODEL_PATH = os.path.join(root, MODEL_FILE)\n        break\nif MODEL_PATH == \"\" and os.path.exists(MODEL_FILE): \n    MODEL_PATH = MODEL_FILE\n\nprint(f\"📂 Model Path: {MODEL_PATH}\")\n\n# ==========================================\n# 4. LOAD MODEL (MAGIC CHANNEL FIX)\n# ==========================================\nprint(\"⚙️ [2/5] Loading Model...\")\n# Inisialisasi model 1 Channel\nmodel = smp.Unet(encoder_name=\"resnet34\", encoder_weights=None, in_channels=1, classes=1).to(DEVICE)\n\nif MODEL_PATH:\n    try:\n        state_dict = torch.load(MODEL_PATH, map_location=DEVICE)\n        \n        # --- AUTO-FIX: 3 CHANNEL -> 1 CHANNEL ---\n        if 'encoder.conv1.weight' in state_dict:\n            weight = state_dict['encoder.conv1.weight']\n            if weight.shape[1] == 3: \n                print(\"   ⚠️ Terdeteksi bobot 3-Channel. Mengkonversi ke 1-Channel...\")\n                state_dict['encoder.conv1.weight'] = weight.sum(dim=1, keepdim=True)\n                \n        model.load_state_dict(state_dict)\n        model.eval()\n        print(\"✅ Model Siap Digunakan!\")\n    except Exception as e:\n        print(f\"❌ Error saat load model: {e}\")\nelse:\n    print(\"❌ ERROR: File model tidak ketemu!\")\n\n# ==========================================\n# 5. FUNGSI PREDIKSI & SAVE\n# ==========================================\ndef predict_and_save(image_path, save_name, model):\n    img_pil = Image.open(image_path).convert(\"L\")\n    w_orig, h_orig = img_pil.size\n    \n    # Padding\n    pad_w = math.ceil(w_orig / CROP_SIZE) * CROP_SIZE - w_orig\n    pad_h = math.ceil(h_orig / CROP_SIZE) * CROP_SIZE - h_orig\n    \n    img_padded = Image.new(\"L\", (w_orig + pad_w, h_orig + pad_h), 0)\n    img_padded.paste(img_pil, (0, 0))\n    \n    full_pred = np.zeros((img_padded.size[1], img_padded.size[0]), dtype=np.uint8)\n    \n    # Sliding Window\n    for y in range(0, img_padded.size[1], CROP_SIZE):\n        for x in range(0, img_padded.size[0], CROP_SIZE):\n            chunk = img_padded.crop((x, y, x + CROP_SIZE, y + CROP_SIZE))\n            chunk_tensor = torch.from_numpy(np.array(chunk)/255.0).float().unsqueeze(0).unsqueeze(0).to(DEVICE)\n            \n            with torch.no_grad():\n                preds = (torch.sigmoid(model(chunk_tensor)) > 0.5).float()\n            \n            pred_chunk = preds.squeeze().cpu().numpy()\n            full_pred[y:y+CROP_SIZE, x:x+CROP_SIZE] = (pred_chunk * 255).astype(np.uint8)\n            \n    final_img = Image.fromarray(full_pred[:h_orig, :w_orig])\n    final_img.save(save_name, compression=\"tiff_deflate\")\n    return save_name\n\n# ==========================================\n# 6. EKSEKUSI\n# ==========================================\nsubmission_files = []\n\n# Cek Folder Test\nprint(\"⚙️ [3/5] Scanning Test Folder...\")\nif os.path.exists(TEST_DIR) and len(os.listdir(TEST_DIR)) > 0:\n    test_files = [f for f in os.listdir(TEST_DIR) if f.endswith('.tif')]\n    CURRENT_DIR = TEST_DIR\nelse:\n    print(\"⚠️ Mode Debug (Dummy Train Data)\")\n    DUMMY_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_images\"\n    # Ambil 1 file saja buat tes\n    if os.path.exists(DUMMY_DIR):\n        test_files = [os.listdir(DUMMY_DIR)[0]]\n        CURRENT_DIR = DUMMY_DIR\n    else:\n        test_files = []\n\nprint(f\"🚀 [4/5] Mulai Prediksi ({len(test_files)} files)...\")\n\nfor f in test_files:\n    print(f\"   -> Memproses: {f}\")\n    save_name = predict_and_save(os.path.join(CURRENT_DIR, f), f, model)\n    submission_files.append(save_name)\n\n# Bungkus ZIP\nprint(\"⚙️ [5/5] Membuat ZIP...\")\nzip_name = \"submission.zip\"\nwith zipfile.ZipFile(zip_name, 'w') as zipf:\n    for f in submission_files:\n        zipf.write(f)\n\nprint(f\"\\n🎉 SELESAI! File '{zip_name}' SIAP DISUBMIT!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T00:56:02.063243Z","iopub.execute_input":"2025-12-07T00:56:02.063501Z","iopub.status.idle":"2025-12-07T00:56:25.915386Z","shell.execute_reply.started":"2025-12-07T00:56:02.063481Z","shell.execute_reply":"2025-12-07T00:56:25.914017Z"}},"outputs":[],"execution_count":null}]}