{"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":14245247,"sourceType":"datasetVersion","datasetId":9088503},{"sourceId":14295835,"sourceType":"datasetVersion","datasetId":9125518}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport glob\n\n# Paketlerin olduğu klasörü bul\nwhl_dir = \"/kaggle/input/vesuvius25-packages-offline-installer-v20251226/whls\"\n\n# Kurulması gereken ana paketler\n# Not: Bağımlılıkları (h5py gibi) Kaggle'ın kendi içindekileri kullansın diye --no-deps kullanıyoruz\n!pip install --no-index --find-links={whl_dir} medicai keras-nightly --no-deps\n!pip install --no-index --find-links={whl_dir} tifffile imagecodecs \n\n# Kurulum kontrolü\ntry:\n    import medicai\n    import keras\n    print(\"✅ Başarılı: medicai ve keras-nightly yüklendi!\")\nexcept ImportError as e:\n    print(f\"❌ Hala bir şeyler eksik: {e}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-31T06:09:44.503417Z","iopub.execute_input":"2026-01-31T06:09:44.503808Z","iopub.status.idle":"2026-01-31T06:10:07.643547Z","shell.execute_reply.started":"2026-01-31T06:09:44.503777Z","shell.execute_reply":"2026-01-31T06:10:07.642780Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport gc\nimport numpy as np\nimport pandas as pd\nimport keras\nimport tensorflow as tf\nimport zipfile\nfrom PIL import Image\n\n# --- 1. SİSTEM VE PAKET AYARLARI ---\nos.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\ntf.keras.backend.set_floatx('float16') \nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\n\n# Paketlerin kurulumu (Kaggle Offline)\nwhl_dir = \"/kaggle/input/vesuvius25-packages-offline-installer-v20251226/whls\"\n!pip install --no-index --find-links={whl_dir} medicai keras-nightly --no-deps -q\n!pip install --no-index --find-links={whl_dir} tifffile -q\n\nfrom medicai.models import TransUNet\nfrom medicai.utils.inference import SlidingWindowInference\nfrom skimage.morphology import remove_small_objects\n\n# --- 2. GÜVENLİ YARDIMCI FONKSİYONLAR ---\n\ndef rle_encode(mask):\n    \"\"\"Bellek dostu ve standartlara uygun RLE.\"\"\"\n    pixels = mask.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    res = ' '.join(str(x) for x in runs)\n    # Yarışma bazen tamamen boş RLE kabul etmez, bazen \"\" ister. \n    # \"1 1\" en güvenli 'boş değil' işaretidir.\n    return res if res else \"1 1\"\n\ndef safe_imread(path):\n    \"\"\"Belleği korumak için Pillow üzerinden okuma.\"\"\"\n    img = Image.open(path)\n    # Gizli test setinde görüntüler devasa olabilir! \n    # Eğer görüntü çok büyükse burada hata yönetimini ekliyoruz.\n    pages = []\n    for i in range(img.n_frames):\n        img.seek(i)\n        pages.append(np.array(img))\n    vol = np.stack(pages).astype(np.float32)\n    img.close()\n    return vol\n\ndef load_model_safely(encoder, weights_path):\n    model = TransUNet(\n        input_shape=(160, 160, 160, 1),\n        encoder_name=encoder,\n        num_classes=3,\n        classifier_activation='softmax'\n    )\n    model.load_weights(weights_path)\n    return model\n\n# --- 3. ANA İŞLEME VE ENSEMBLE ---\n\nMODELS_CONFIG = [\n    {'name': 'seresnext50', 'path': '/kaggle/input/train-transunet-baseline-lb-0-537/fine_tuning_epoch_20.weights.h5', 'weight': 0.4},\n    {'name': 'seresnext101', 'path': '/kaggle/input/colab-a-162v4-gpu-transunet-seresnext101-x160/model.weights.h5', 'weight': 0.6}\n]\n\ntest_df = pd.read_csv(\"/kaggle/input/vesuvius-challenge-surface-detection/test.csv\")\nresults = []\n\nprint(f\"✅ Toplam {len(test_df)} test dosyası bulundu.\")\n\nfor idx, row in test_df.iterrows():\n    # ÖNEMLİ: image_id'yi orijinal tipinde tut (id sütunu nasılsa öyle kalsın)\n    image_id = row['id'] \n    vol_path = f\"/kaggle/input/vesuvius-challenge-surface-detection/test_images/{image_id}.tif\"\n    \n    print(f\"\\n[{idx+1}/{len(test_df)}] 🌀 Analiz: {image_id}\")\n    \n    try:\n        if not os.path.exists(vol_path):\n            raise FileNotFoundError(f\"{vol_path} bulunamadı.\")\n\n        vol = safe_imread(vol_path)\n        vol_norm = (vol - np.mean(vol)) / (np.std(vol) + 1e-5)\n        \n        combined_probs = np.zeros(vol.shape, dtype=np.float32)\n        \n        for m_cfg in MODELS_CONFIG:\n            print(f\"  -> {m_cfg['name']} yükleniyor...\")\n            model = load_model_safely(m_cfg['name'], m_cfg['path'])\n            \n            predictor = SlidingWindowInference(\n                model, roi_size=(160, 160, 160), \n                num_classes=3, overlap=0.2, sw_batch_size=1, mode=\"gaussian\"\n            )\n            \n            out = predictor(vol_norm[None, ..., None])\n            probs = np.max(np.asarray(out)[0][..., 1:3], axis=-1).astype(np.float32)\n            combined_probs += probs * m_cfg['weight']\n            \n            del model, predictor, out, probs\n            gc.collect()\n            keras.backend.clear_session()\n\n        # Eşikleme\n        thresh = np.percentile(combined_probs, 99.3)\n        if thresh < 0.001: thresh = 0.5 # Sinyal yoksa varsayılan\n            \n        mask_2d = np.max(combined_probs >= thresh, axis=0).astype(np.uint8)\n        mask_2d = remove_small_objects(mask_2d.astype(bool), min_size=100).astype(np.uint8)\n        \n        results.append({\"id\": image_id, \"rle\": rle_encode(mask_2d)})\n        \n        del vol, vol_norm, combined_probs, mask_2d\n        gc.collect()\n\n    except Exception as e:\n        print(f\"❌ {image_id} işlenirken hata oluştu: {e}\")\n        # HATA DURUMUNDA BİLE SATIRI EKLE (Scoring Error'u önlemek için en kritik adım)\n        results.append({\"id\": image_id, \"rle\": \"1 1\"})\n\n# --- 4. SUBMISSION KAYIT VE KONTROL ---\n\nsubmission_df = pd.DataFrame(results)\n\n# SATIR SAYISI KONTROLÜ: Orijinal test_df ile aynı mı?\nif len(submission_df) != len(test_df):\n    print(\"⚠️ UYARI: Satır sayısı eşleşmiyor! Eksikler tamamlanıyor...\")\n    # Eksik kalan ID'leri doldur\n    missing_ids = set(test_df['id']) - set(submission_df['id'])\n    for m_id in missing_ids:\n        submission_df = pd.concat([submission_df, pd.DataFrame([{\"id\": m_id, \"rle\": \"1 1\"}])])\n\n# Sıralamayı test.csv ile aynı yap (Bazı metrikler buna hassastır)\nsubmission_df = submission_df.set_index('id').reindex(test_df['id']).reset_index()\n\nsubmission_df.to_csv(\"submission.csv\", index=False)\n\nwith zipfile.ZipFile(\"submission.zip\", 'w', zipfile.ZIP_DEFLATED) as zf:\n    zf.write(\"submission.csv\")\n\nprint(\"\\n🚀 BİTTİ! submission.zip hazır.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}