{"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":"none","dataSources":[{"sourceId":117682,"databundleVersionId":15062069,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:06.545686Z","iopub.execute_input":"2025-12-23T15:18:06.546033Z","iopub.status.idle":"2025-12-23T15:18:08.807225Z","shell.execute_reply.started":"2025-12-23T15:18:06.545985Z","shell.execute_reply":"2025-12-23T15:18:08.805807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nDATASET_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection\"\n\nfor root, dirs, files in os.walk(DATASET_DIR):\n    if any(f.endswith(\".tif\") for f in files):\n        print(root)\n        print(files[:5])\n        break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:26.494238Z","iopub.execute_input":"2025-12-23T15:18:26.494708Z","iopub.status.idle":"2025-12-23T15:18:27.022301Z","shell.execute_reply.started":"2025-12-23T15:18:26.494673Z","shell.execute_reply":"2025-12-23T15:18:27.021040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfor root, dirs, files in os.walk(\"/kaggle/input\"):\n    print(root)\n    print(\"  dirs:\", dirs)\n    print(\"  files:\", files[:5])\n    print(\"-\" * 50)\n    break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:30.289692Z","iopub.execute_input":"2025-12-23T15:18:30.290084Z","iopub.status.idle":"2025-12-23T15:18:30.297847Z","shell.execute_reply.started":"2025-12-23T15:18:30.290052Z","shell.execute_reply":"2025-12-23T15:18:30.296140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATASET_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection\"\n\nprint(\"Exists:\", os.path.exists(DATASET_DIR))\n\nif os.path.exists(DATASET_DIR):\n    print(\"Contents:\", os.listdir(DATASET_DIR))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:34.238106Z","iopub.execute_input":"2025-12-23T15:18:34.238421Z","iopub.status.idle":"2025-12-23T15:18:34.246548Z","shell.execute_reply.started":"2025-12-23T15:18:34.238394Z","shell.execute_reply":"2025-12-23T15:18:34.245255Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nimport numpy as np\nimport cv2\nimport os\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:37.968803Z","iopub.execute_input":"2025-12-23T15:18:37.969187Z","iopub.status.idle":"2025-12-23T15:18:43.499201Z","shell.execute_reply.started":"2025-12-23T15:18:37.969152Z","shell.execute_reply":"2025-12-23T15:18:43.497726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ScrollDataset25D(Dataset):\n    def __init__(self, image_dir, mask_dir, size=256):\n        self.image_dir = image_dir\n        self.mask_dir = mask_dir\n        self.size = size\n        self.images = sorted(os.listdir(image_dir))\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        name = self.images[idx]\n\n        img = cv2.imread(\n            os.path.join(self.image_dir, name),\n            cv2.IMREAD_GRAYSCALE\n        )\n        mask = cv2.imread(\n            os.path.join(self.mask_dir, name),\n            cv2.IMREAD_GRAYSCALE\n        )\n\n        # Resize\n        img = cv2.resize(img, (self.size, self.size))\n        mask = cv2.resize(mask, (self.size, self.size), interpolation=cv2.INTER_NEAREST)\n\n        img = img.astype(\"float32\") / 255.0\n        mask = (mask > 0).astype(\"float32\")\n\n        # -------- PSEUDO-2.5D CHANNELS --------\n        img_small = cv2.resize(img, (self.size//2, self.size//2))\n        img_small = cv2.resize(img_small, (self.size, self.size))\n\n        img_blur = cv2.GaussianBlur(img, (5,5), 0)\n\n        x = np.stack([img, img_small, img_blur], axis=0)  # (3, H, W)\n        y = mask[np.newaxis, :, :]\n\n        return torch.from_numpy(x), torch.from_numpy(y)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:18:47.599159Z","iopub.execute_input":"2025-12-23T15:18:47.599679Z","iopub.status.idle":"2025-12-23T15:18:47.609453Z","shell.execute_reply.started":"2025-12-23T15:18:47.599645Z","shell.execute_reply":"2025-12-23T15:18:47.608086Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nDATA_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection\"\n\nTRAIN_IMG_DIR = os.path.join(DATA_DIR, \"train_images\")\nTRAIN_MASK_DIR = os.path.join(DATA_DIR, \"train_labels\")\nTEST_IMG_DIR  = os.path.join(DATA_DIR, \"test_images\")\n\nprint(\"Train images:\", len(os.listdir(TRAIN_IMG_DIR)))\nprint(\"Train masks :\", len(os.listdir(TRAIN_MASK_DIR)))\nprint(\"Test images :\", len(os.listdir(TEST_IMG_DIR)))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:02.004216Z","iopub.execute_input":"2025-12-23T15:19:02.004592Z","iopub.status.idle":"2025-12-23T15:19:02.015232Z","shell.execute_reply.started":"2025-12-23T15:19:02.004560Z","shell.execute_reply":"2025-12-23T15:19:02.013927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = ScrollDataset25D(\n    TRAIN_IMG_DIR,\n    TRAIN_MASK_DIR,\n    size=256\n)\n\ntrain_loader = DataLoader(\n    train_ds,\n    batch_size=2,\n    shuffle=True,\n    num_workers=0\n)\n\nx, y = next(iter(train_loader))\nprint(\"X:\", x.shape)\nprint(\"Y:\", y.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:05.300963Z","iopub.execute_input":"2025-12-23T15:19:05.301295Z","iopub.status.idle":"2025-12-23T15:19:07.059142Z","shell.execute_reply.started":"2025-12-23T15:19:05.301267Z","shell.execute_reply":"2025-12-23T15:19:07.058129Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass SimpleUNet(nn.Module):\n    def __init__(self, in_ch=3, out_ch=1):\n        super().__init__()\n\n        self.enc1 = nn.Sequential(\n            nn.Conv2d(in_ch, 32, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(32, 32, 3, padding=1),\n            nn.ReLU(inplace=True),\n        )\n\n        self.pool1 = nn.MaxPool2d(2)\n\n        self.enc2 = nn.Sequential(\n            nn.Conv2d(32, 64, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(64, 64, 3, padding=1),\n            nn.ReLU(inplace=True),\n        )\n\n        self.pool2 = nn.MaxPool2d(2)\n\n        self.bottleneck = nn.Sequential(\n            nn.Conv2d(64, 128, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(128, 128, 3, padding=1),\n            nn.ReLU(inplace=True),\n        )\n\n        self.up2 = nn.ConvTranspose2d(128, 64, 2, stride=2)\n        self.dec2 = nn.Sequential(\n            nn.Conv2d(128, 64, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(64, 64, 3, padding=1),\n            nn.ReLU(inplace=True),\n        )\n\n        self.up1 = nn.ConvTranspose2d(64, 32, 2, stride=2)\n        self.dec1 = nn.Sequential(\n            nn.Conv2d(64, 32, 3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(32, 32, 3, padding=1),\n            nn.ReLU(inplace=True),\n        )\n\n        self.out = nn.Conv2d(32, out_ch, 1)\n\n    def forward(self, x):\n        e1 = self.enc1(x)\n        e2 = self.enc2(self.pool1(e1))\n        b  = self.bottleneck(self.pool2(e2))\n\n        d2 = self.up2(b)\n        d2 = self.dec2(torch.cat([d2, e2], dim=1))\n\n        d1 = self.up1(d2)\n        d1 = self.dec1(torch.cat([d1, e1], dim=1))\n\n        return self.out(d1)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:09.934286Z","iopub.execute_input":"2025-12-23T15:19:09.934642Z","iopub.status.idle":"2025-12-23T15:19:09.946946Z","shell.execute_reply.started":"2025-12-23T15:19:09.934610Z","shell.execute_reply":"2025-12-23T15:19:09.945963Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nmodel = SimpleUNet(in_ch=3, out_ch=1).to(DEVICE)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:18.799551Z","iopub.execute_input":"2025-12-23T15:19:18.799902Z","iopub.status.idle":"2025-12-23T15:19:18.814130Z","shell.execute_reply.started":"2025-12-23T15:19:18.799871Z","shell.execute_reply":"2025-12-23T15:19:18.812896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x, y = next(iter(train_loader))\nx = x.to(DEVICE)\n\nwith torch.no_grad():\n    out = model(x)\n\nprint(\"Input :\", x.shape)\nprint(\"Output:\", out.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:21.954369Z","iopub.execute_input":"2025-12-23T15:19:21.954686Z","iopub.status.idle":"2025-12-23T15:19:24.216839Z","shell.execute_reply.started":"2025-12-23T15:19:21.954657Z","shell.execute_reply":"2025-12-23T15:19:24.215804Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ScrollTestDataset(Dataset):\n    def __init__(self, image_dir, size=256):\n        self.image_dir = image_dir\n        self.size = size\n        self.images = sorted(os.listdir(image_dir))\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        name = self.images[idx]\n\n        img = cv2.imread(\n            os.path.join(self.image_dir, name),\n            cv2.IMREAD_GRAYSCALE\n        )\n\n        img = cv2.resize(img, (self.size, self.size))\n        img = img.astype(\"float32\") / 255.0\n\n        # ---- pseudo-2.5D channels ----\n        img_small = cv2.resize(img, (self.size//2, self.size//2))\n        img_small = cv2.resize(img_small, (self.size, self.size))\n        img_blur = cv2.GaussianBlur(img, (5,5), 0)\n\n        x = np.stack([img, img_small, img_blur], axis=0)\n\n        return torch.from_numpy(x), name\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:27.194466Z","iopub.execute_input":"2025-12-23T15:19:27.194834Z","iopub.status.idle":"2025-12-23T15:19:27.203107Z","shell.execute_reply.started":"2025-12-23T15:19:27.194805Z","shell.execute_reply":"2025-12-23T15:19:27.201887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_ds = ScrollTestDataset(TEST_IMG_DIR, size=256)\n\ntest_loader = DataLoader(\n    test_ds,\n    batch_size=1,\n    shuffle=False,\n    num_workers=0\n)\n\nprint(\"Test samples:\", len(test_ds))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:32.923823Z","iopub.execute_input":"2025-12-23T15:19:32.924184Z","iopub.status.idle":"2025-12-23T15:19:32.932826Z","shell.execute_reply.started":"2025-12-23T15:19:32.924151Z","shell.execute_reply":"2025-12-23T15:19:32.931926Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def post_process(pred, thresh=0.5):\n    pred = (pred > thresh).astype(\"uint8\") * 255\n    kernel = np.ones((3,3), np.uint8)\n    pred = cv2.morphologyEx(pred, cv2.MORPH_CLOSE, kernel)\n    return pred\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:36.073795Z","iopub.execute_input":"2025-12-23T15:19:36.074178Z","iopub.status.idle":"2025-12-23T15:19:36.080014Z","shell.execute_reply.started":"2025-12-23T15:19:36.074147Z","shell.execute_reply":"2025-12-23T15:19:36.078813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\nfrom tqdm import tqdm\n\nmodel.eval()\nos.makedirs(\"preds\", exist_ok=True)\n\nwith torch.no_grad():\n    for x, name in tqdm(test_loader):\n        x = x.to(DEVICE)\n\n        pred = torch.sigmoid(model(x))[0,0].cpu().numpy()\n        pred = post_process(pred)\n\n        \n        out_name = name[0].replace(\".tif\", \".png\")\n        cv2.imwrite(f\"preds/{out_name}\", pred)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:41.836034Z","iopub.execute_input":"2025-12-23T15:19:41.837218Z","iopub.status.idle":"2025-12-23T15:19:42.806770Z","shell.execute_reply.started":"2025-12-23T15:19:41.837169Z","shell.execute_reply":"2025-12-23T15:19:42.805810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ntif_path = \"preds/1407735.tif\"\nif os.path.exists(tif_path):\n    os.remove(tif_path)\n\nprint(\"Remaining files:\", os.listdir(\"preds\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:50.251474Z","iopub.execute_input":"2025-12-23T15:19:50.251826Z","iopub.status.idle":"2025-12-23T15:19:50.258772Z","shell.execute_reply.started":"2025-12-23T15:19:50.251795Z","shell.execute_reply":"2025-12-23T15:19:50.257576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\n\nwith zipfile.ZipFile(\"submission.zip\", \"w\") as z:\n    for f in os.listdir(\"preds\"):\n        z.write(os.path.join(\"preds\", f), f)\n\nprint(\"submission.zip rebuilt\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:55.455432Z","iopub.execute_input":"2025-12-23T15:19:55.455752Z","iopub.status.idle":"2025-12-23T15:19:55.463641Z","shell.execute_reply.started":"2025-12-23T15:19:55.455726Z","shell.execute_reply":"2025-12-23T15:19:55.462467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with zipfile.ZipFile(\"submission.zip\") as z:\n    print(\"ZIP contents:\", z.namelist())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:19:59.696284Z","iopub.execute_input":"2025-12-23T15:19:59.696589Z","iopub.status.idle":"2025-12-23T15:19:59.704688Z","shell.execute_reply.started":"2025-12-23T15:19:59.696563Z","shell.execute_reply":"2025-12-23T15:19:59.703757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(\"Final check:\")\nprint(os.path.isfile(\"/kaggle/working/submission.zip\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-23T15:20:26.413301Z","iopub.execute_input":"2025-12-23T15:20:26.413894Z","iopub.status.idle":"2025-12-23T15:20:26.420463Z","shell.execute_reply.started":"2025-12-23T15:20:26.413848Z","shell.execute_reply":"2025-12-23T15:20:26.419537Z"}},"outputs":[],"execution_count":null}]}