{"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":"gpu","dataSources":[{"sourceId":117682,"databundleVersionId":15062069,"sourceType":"competition"},{"sourceId":290917305,"sourceType":"kernelVersion"},{"sourceId":672178,"sourceType":"modelInstanceVersion","modelInstanceId":495238,"modelId":510647},{"sourceId":673516,"sourceType":"modelInstanceVersion","modelInstanceId":499479,"modelId":510647},{"sourceId":674747,"sourceType":"modelInstanceVersion","modelInstanceId":503784,"modelId":510647},{"sourceId":681152,"sourceType":"modelInstanceVersion","modelInstanceId":516822,"modelId":510647},{"sourceId":732880,"sourceType":"modelInstanceVersion","modelInstanceId":516822,"modelId":510647}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ============================================================\n# Vesuvius — Z-FLIP DYNAMIC DENSITY (0.57+ Strategy)\n# Hardware: Optimized for P100 / T4 (Single or Dual)\n# Strategy:\n#   1. Drop redundant \"Public\" stream to save time.\n#   2. Add Z-Flip (Depth) Augmentation to catch hidden ink.\n#   3. Use \"Dynamic Density\": High overlap for Z-views, Fast overlap for spatial TTA.\n# ============================================================\n\nfrom IPython.display import clear_output\nimport os\n\n# --- 1. PROTOBUF PATCH (Fixes your log error) ---\ntry:\n    from google.protobuf import message_factory as _message_factory\n    if not hasattr(_message_factory.MessageFactory, \"GetPrototype\"):\n        from google.protobuf.message_factory import GetMessageClass\n        def _GetPrototype(self, descriptor): return GetMessageClass(descriptor)\n        _message_factory.MessageFactory.GetPrototype = _GetPrototype\nexcept Exception: pass\n\nos.environ[\"KERAS_BACKEND\"] = \"jax\"\nos.environ[\"XLA_FLAGS\"] = \"--xla_gpu_strict_conv_algorithm_picker=false\"\n\n# --- 2. INSTALLS ---\nvar=\"/kaggle/input/vsdetection-packages-offline-installer-only/whls\"\n!pip install \\\n  \"$var\"/keras_nightly-*.whl \\\n  \"$var\"/tifffile-*.whl \\\n  \"$var\"/imagecodecs-*.whl \\\n  \"$var\"/medicai-*.whl \\\n  --no-index \\\n  --find-links \"$var\"\nclear_output()\n\nimport time, zipfile\nimport numpy as np\nimport pandas as pd\nimport tifffile\nimport scipy.ndimage as ndi\nfrom skimage.morphology import remove_small_objects\nimport keras\nfrom medicai.transforms import Compose, NormalizeIntensity\nfrom medicai.models import TransUNet\nfrom medicai.utils.inference import SlidingWindowInference\nimport gc\n\nprint(f\"JAX Backend: {keras.config.backend()}\")\n\n# --- 3. CONFIGURATION ---\nCFG = dict(\n    # Model\n    model_path=\"/kaggle/input/vsd-model/keras/transunet/3/transunet.seresnext50.160px.comboloss.weights.h5\",\n    \n    # Dynamic Overlaps\n    overlap_dense=0.60, # For Original & Z-Flip (High Detail)\n    overlap_fast=0.25,  # For Spatial TTA (Fast Context)\n    \n    # Post-Processing\n    T_high=0.85,        # Lowered from 0.90 to catch z-flip signals\n    T_low=0.40,         # Aggressive expansion\n    z_radius=2,         # Tuned for Z-flip consistency\n    xy_radius=2,\n    dust_min=150,\n)\n\n# --- 4. DATA & MODEL ---\nroot_dir = \"/kaggle/input/vesuvius-challenge-surface-detection\"\ntest_df = pd.read_csv(f\"{root_dir}/test.csv\")\nids = test_df[\"id\"].tolist()\n\n# Transform\n_val_pipeline = Compose([NormalizeIntensity(keys=[\"image\"], nonzero=True, channel_wise=False)])\ndef val_transformation(image):\n    return _val_pipeline({\"image\": image})[\"image\"]\n\ndef load_volume(path):\n    vol = tifffile.imread(path).astype(np.float32)\n    return vol[None, ..., None]\n\n# Model Factory\ndef get_model():\n    return TransUNet(\n        input_shape=(160, 160, 160, 1),\n        encoder_name='seresnext50',\n        classifier_activation=None, # Logits\n        num_classes=3,\n    )\n\nprint(f\"Loading Weights: {os.path.basename(CFG['model_path'])}\")\nmodel = get_model()\nmodel.load_weights(CFG['model_path'])\n\n# --- 5. DUAL INFERENCE RUNNERS ---\n# Runner 1: High Density (Slow, Accurate)\nrunner_dense = SlidingWindowInference(\n    model, num_classes=3, roi_size=(160,160,160), \n    sw_batch_size=1, mode='gaussian', overlap=CFG['overlap_dense']\n)\n\n# Runner 2: Fast Context (Fast, Robustness)\nrunner_fast = SlidingWindowInference(\n    model, num_classes=3, roi_size=(160,160,160), \n    sw_batch_size=1, mode='gaussian', overlap=CFG['overlap_fast']\n)\n\n# --- 6. NUMERICS ---\ndef sigmoid(x):\n    return 1.0 / (1.0 + np.exp(-x))\n\ndef to_binary_prob(logits_5d):\n    # Convert Multiclass Logits (D,H,W,3) -> Binary Prob (D,H,W)\n    # Using FG12 strategy: (LogSumExp(L1, L2) - L0) -> Sigmoid\n    x = np.asarray(logits_5d, dtype=np.float32)[0]\n    l0, l1, l2 = x[...,0], x[...,1], x[...,2]\n    \n    # Stable LogSumExp(l1, l2)\n    m = np.maximum(l1, l2)\n    l12 = m + np.log(np.exp(l1 - m) + np.exp(l2 - m) + 1e-7)\n    \n    return sigmoid(l12 - l0)\n\n# --- 7. Z-FLIP PREDICTION ENGINE ---\ndef predict_z_enhanced(volume):\n    \"\"\"\n    Combines:\n    1. High-Density Original\n    2. High-Density Z-Flip (The Secret Weapon)\n    3. Fast-Density Spatial TTA\n    \"\"\"\n    probs = []\n    \n    # --- A. HIGH DENSITY PASSES (The Anchors) ---\n    \n    # A1. Original (Overlap 0.60)\n    logits = runner_dense(volume)\n    probs.append(to_binary_prob(logits))\n    gc.collect()\n    \n    # A2. Z-Flip (Overlap 0.60) - Catches bottom-up ink\n    vol_z = np.flip(volume, axis=1) # Axis 1 is Depth in (B, D, H, W, C)\n    logits_z = runner_dense(vol_z)\n    prob_z = to_binary_prob(logits_z)\n    probs.append(np.flip(prob_z, axis=0)) # Unflip D (Prob is D,H,W)\n    gc.collect()\n    \n    # --- B. FAST SPATIAL TTA (The Stabilizers) ---\n    # We use lower overlap (0.25) here to save time\n    \n    # B1. Spatial Flips (Axes 2, 3 -> H, W)\n    for axis in [2, 3]:\n        vol_f = np.flip(volume, axis=axis)\n        logits = runner_fast(vol_f)\n        p = to_binary_prob(logits)\n        probs.append(np.flip(p, axis=axis-1)) # Adjust axis for 3D array\n        gc.collect()\n        \n    # B2. Spatial Rotations (Axes 2, 3)\n    for k in [1, 2, 3]:\n        vol_r = np.rot90(volume, k=k, axes=(2, 3))\n        logits = runner_fast(vol_r)\n        p = to_binary_prob(logits)\n        probs.append(np.rot90(p, k=-k, axes=(1, 2))) # Un-rotate H,W\n        gc.collect()\n\n    # Average all 8 views\n    return np.mean(probs, axis=0)\n\n# --- 8. HYSTERESIS ---\ndef build_struct(z, r):\n    if z==0 and r==0: return None\n    s = np.zeros((2*z+1, 2*r+1, 2*r+1), 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**2 + dx**2 <= r**2: s[cz+dz, cy+dy, cx+dx] = True\n    return s\n\ndef process_mask(prob):\n    # Thresholds\n    strong = prob >= CFG['T_high']\n    weak   = prob >= CFG['T_low']\n    \n    if not strong.any(): return np.zeros_like(prob, dtype=np.uint8)\n    \n    # Hysteresis\n    # Note: 26-connectivity (3x3x3)\n    mask = ndi.binary_propagation(strong, mask=weak, structure=np.ones((3,3,3)))\n    \n    # Closing\n    s = build_struct(CFG['z_radius'], CFG['xy_radius'])\n    if s is not None: mask = ndi.binary_closing(mask, structure=s)\n        \n    # Dust\n    if CFG['dust_min'] > 0:\n        mask = remove_small_objects(mask, min_size=CFG['dust_min'])\n        \n    return mask.astype(np.uint8)\n\n# --- 9. SUBMISSION LOOP ---\nzip_path = \"/kaggle/working/submission.zip\"\noutput_dir = \"/kaggle/working/submission_masks\"\nos.makedirs(output_dir, exist_ok=True)\n\nprint(f\"Starting Z-Flip Inference on {len(ids)} volumes...\")\nt0 = time.time()\n\nwith zipfile.ZipFile(zip_path, \"w\", compression=zipfile.ZIP_DEFLATED) as zf:\n    for i, image_id in enumerate(ids):\n        print(f\"[{i+1}/{len(ids)}] Processing {image_id}...\")\n        \n        # Load\n        tif_path = f\"{root_dir}/test_images/{image_id}.tif\"\n        vol = load_volume(tif_path)\n        vol = val_transformation(vol)\n        \n        # Predict (Z-Enhanced)\n        prob_map = predict_z_enhanced(vol)\n        \n        # Post-Process\n        mask = process_mask(prob_map)\n        \n        # Save\n        out_name = f\"{image_id}.tif\"\n        out_path = f\"{output_dir}/{out_name}\"\n        tifffile.imwrite(out_path, mask)\n        zf.write(out_path, arcname=out_name)\n        os.remove(out_path) # Save disk space\n        \n        # Stat check\n        print(f\"   -> Positives: {mask.sum()} | Time: {(time.time()-t0)/60:.1f}m\")\n\nprint(f\"✅ Done. Saved to {zip_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-11T03:43:05.784065Z","iopub.execute_input":"2026-02-11T03:43:05.784250Z","iopub.status.idle":"2026-02-11T03:46:23.009489Z","shell.execute_reply.started":"2026-02-11T03:43:05.784233Z","shell.execute_reply":"2026-02-11T03:46:23.008634Z"}},"outputs":[],"execution_count":null}]}