{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":99552,"databundleVersionId":13851420,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip -q install nnunetv2 nibabel pydicom tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:01.074716Z","iopub.execute_input":"2025-10-04T22:49:01.075041Z","iopub.status.idle":"2025-10-04T22:49:04.662634Z","shell.execute_reply.started":"2025-10-04T22:49:01.075009Z","shell.execute_reply":"2025-10-04T22:49:04.661864Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!nvidia-smi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:48:58.813344Z","iopub.execute_input":"2025-10-04T22:48:58.813612Z","iopub.status.idle":"2025-10-04T22:48:59.032931Z","shell.execute_reply.started":"2025-10-04T22:48:58.813584Z","shell.execute_reply":"2025-10-04T22:48:59.032281Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, re, json, sys, shutil, zipfile, glob\nfrom pathlib import Path\nimport numpy as np\nimport nibabel as nib\nimport pydicom\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:08.883497Z","iopub.execute_input":"2025-10-04T22:49:08.883773Z","iopub.status.idle":"2025-10-04T22:49:09.602354Z","shell.execute_reply.started":"2025-10-04T22:49:08.883745Z","shell.execute_reply":"2025-10-04T22:49:09.601791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"INPUT_ROOT = Path(\"/kaggle/input/rsna-intracranial-aneurysm-detection\")\nRAW_ROOT   = Path(\"/kaggle/working/nnunet_raw\")\nPREP_ROOT  = Path(\"/kaggle/working/nnunet_preprocessed\")\nRES_ROOT   = Path(\"/kaggle/working/nnunet_results\")\n\nos.environ[\"nnUNet_raw\"]         = str(RAW_ROOT)\nos.environ[\"nnUNet_preprocessed\"] = str(PREP_ROOT)\nos.environ[\"nnUNet_results\"]      = str(RES_ROOT)\n\nDATASET_ID  = 601\nDATASET_TAG = f\"Dataset{DATASET_ID:03d}_RSNAIA\"\nDS_ROOT = RAW_ROOT / DATASET_TAG\nIMAGES_TR = DS_ROOT / \"imagesTr\"\nLABELS_TR = DS_ROOT / \"labelsTr\"\nfor p in [IMAGES_TR, LABELS_TR, PREP_ROOT, RES_ROOT]:\n    p.mkdir(parents=True, exist_ok=True)\n\nprint(\"INPUT_ROOT exists:\", INPUT_ROOT.exists())\nprint(\"Will write nnU-Net raw data to:\", DS_ROOT)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:10.86373Z","iopub.execute_input":"2025-10-04T22:49:10.864209Z","iopub.status.idle":"2025-10-04T22:49:10.870411Z","shell.execute_reply.started":"2025-10-04T22:49:10.864185Z","shell.execute_reply":"2025-10-04T22:49:10.869838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport nibabel as nib\nimport numpy as np\n\n# === Define your dataset paths ===\nTRAIN_SERIES = Path(\"/kaggle/input/rsna-intracranial-aneurysm-detection/series\")\nTRAIN_SEGS   = Path(\"/kaggle/input/rsna-intracranial-aneurysm-detection/segmentations\")\n\n# === Collect all series folders ===\nseries_candidates = [p for p in TRAIN_SERIES.iterdir() if p.is_dir()]\nprint(f\"Found {len(series_candidates)} DICOM series folders\")\n\n# === Collect all segmentation NIfTI files ===\nseg_map = {}\nfor seg_path in sorted(TRAIN_SEGS.glob(\"*.nii*\")):\n    uid = seg_path.stem.replace(\".nii\",\"\").replace(\".gz\",\"\")\n    seg_map[uid] = seg_path\nprint(f\"Found {len(seg_map)} segmentation masks\")\n\n# === Match series folders to segmentation files by UID ===\npairs = []\nfor s in series_candidates:\n    uid = s.name  # folder name is the UID\n    if uid in seg_map:\n        pairs.append((uid, s, seg_map[uid]))\nprint(f\"Matched {len(pairs)} series+mask pairs\")\n\n# Quick sanity check on one pair\nif pairs:\n    uid, sdir, smask = pairs[0]\n    print(f\"Example:\\nUID: {uid}\\nSeries folder: {sdir}\\nMask: {smask}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:12.82526Z","iopub.execute_input":"2025-10-04T22:49:12.825775Z","iopub.status.idle":"2025-10-04T22:49:15.742088Z","shell.execute_reply.started":"2025-10-04T22:49:12.825753Z","shell.execute_reply":"2025-10-04T22:49:15.741486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pydicom, numpy as np, nibabel as nib\n\ndef _read_dicom_series_from_dir(series_dir):\n    \"\"\"Read a DICOM series (folder of .dcm) and return sorted list of slices.\"\"\"\n    dcm_files = [p for p in Path(series_dir).glob(\"**/*\") if p.is_file()]\n    ds_list = []\n    for p in dcm_files:\n        try:\n            ds = pydicom.dcmread(str(p), stop_before_pixels=False, force=True)\n            if hasattr(ds, \"PixelData\"):\n                ds_list.append(ds)\n        except Exception:\n            pass\n    if not ds_list:\n        raise RuntimeError(f\"No readable DICOMs in {series_dir}\")\n\n    # sort by slice location (or instance number fallback)\n    def slice_key(ds):\n        if hasattr(ds, \"ImagePositionPatient\") and hasattr(ds, \"ImageOrientationPatient\"):\n            ipp = np.array(ds.ImagePositionPatient, dtype=float)\n            iop = np.array(ds.ImageOrientationPatient, dtype=float)\n            row, col = iop[:3], iop[3:]\n            normal = np.cross(row, col)\n            return float(np.dot(ipp, normal))\n        return float(getattr(ds, \"InstanceNumber\", 0))\n    ds_list.sort(key=slice_key)\n    return ds_list\n\n\ndef _dicom_list_to_nifti(ds_list, out_path):\n    \"\"\"Stack DICOM slices and save as .nii.gz (in HU if CT).\"\"\"\n    imgs = []\n    for ds in ds_list:\n        arr = ds.pixel_array.astype(np.float32)\n        slope = float(getattr(ds, \"RescaleSlope\", 1.0))\n        inter = float(getattr(ds, \"RescaleIntercept\", 0.0))\n        imgs.append(arr * slope + inter)\n    vol = np.stack(imgs, axis=-1)\n\n    # build simple affine\n    ds0 = ds_list[0]\n    ps = np.array(getattr(ds0, \"PixelSpacing\", [1.0, 1.0]), dtype=float)\n    try:\n        st = float(getattr(ds0, \"SliceThickness\"))\n    except Exception:\n        st = 1.0\n    iop = np.array(getattr(ds0, \"ImageOrientationPatient\", [1,0,0,0,1,0]), dtype=float)\n    row, col = iop[:3], iop[3:]\n    nor = np.cross(row, col)\n    origin = np.array(getattr(ds0, \"ImagePositionPatient\", [0,0,0]), dtype=float)\n\n    affine = np.eye(4)\n    affine[:3,0] = row * ps[1]\n    affine[:3,1] = col * ps[0]\n    affine[:3,2] = nor * st\n    affine[:3,3] = origin\n\n    vol = np.clip(vol, -1024, 3071)\n    img = nib.Nifti1Image(vol.astype(np.int16), affine)\n    nib.save(img, str(out_path))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:20.108837Z","iopub.execute_input":"2025-10-04T22:49:20.109515Z","iopub.status.idle":"2025-10-04T22:49:20.119156Z","shell.execute_reply.started":"2025-10-04T22:49:20.109489Z","shell.execute_reply":"2025-10-04T22:49:20.118497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport shutil, nibabel as nib, numpy as np\n\nTMP = Path(\"/kaggle/working/tmp_series\")\nTMP.mkdir(exist_ok=True, parents=True)\n\nfail_count, done = 0, 0\n\nfor uid, series_src, seg_path in tqdm(pairs, desc=\"Converting to nnU-Net raw\"):\n    img_out = IMAGES_TR / f\"{uid}_0000.nii.gz\"\n    lab_out = LABELS_TR / f\"{uid}.nii.gz\"\n    if img_out.exists() and lab_out.exists():\n        continue\n    try:\n        # --- convert DICOM folder -> NIfTI image ---\n        ds_list = _read_dicom_series_from_dir(series_src)\n        _dicom_list_to_nifti(ds_list, img_out)\n\n        # --- load segmentation, ensure binary mask ---\n        seg_img = nib.load(str(seg_path))\n        seg_arr = seg_img.get_fdata()\n        seg_bin = (seg_arr > 0).astype(np.uint8)\n        nib.save(nib.Nifti1Image(seg_bin, affine=seg_img.affine), str(lab_out))\n        done += 1\n    except Exception as e:\n        fail_count += 1\n        print(f\"[WARN] Failed {uid}: {e}\")\n\nprint(f\"✅ Converted {done} cases. ❌ Failed: {fail_count}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T22:49:23.039877Z","iopub.execute_input":"2025-10-04T22:49:23.040179Z","iopub.status.idle":"2025-10-04T23:14:07.352486Z","shell.execute_reply.started":"2025-10-04T22:49:23.040156Z","shell.execute_reply":"2025-10-04T23:14:07.351773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport json\n\n# list of all converted cases\nall_cases = sorted([p.stem.replace(\"_0000\",\"\") for p in IMAGES_TR.glob(\"*_0000.nii.gz\")])\nprint(\"Total usable cases:\", len(all_cases))\n\n# simple 80/20 split\ntrain_cases, val_cases = train_test_split(all_cases, test_size=0.2, random_state=42)\n\n# build dataset.json\ndataset_json = {\n    \"name\": \"RSNAIA\",\n    \"description\": \"RSNA Intracranial Aneurysm Segmentation Dataset\",\n    \"tensorImageSize\": \"3D\",\n    \"reference\": \"Kaggle RSNA Intracranial Aneurysm Detection 2024\",\n    \"licence\": \"Challenge rules apply\",\n    \"release\": \"1.0\",\n    \"modality\": {\"0\": \"CT\"},\n    \"labels\": {\"0\": \"background\", \"1\": \"aneurysm\"},\n    \"numTraining\": len(all_cases),\n    \"file_ending\": \".nii.gz\",\n    \"training\": [\n        {\"image\": f\"./imagesTr/{c}_0000.nii.gz\", \"label\": f\"./labelsTr/{c}.nii.gz\"}\n        for c in all_cases\n    ],\n    \"test\": []\n}\n\n# save to Dataset601_RSNAIA\nwith open(DS_ROOT / \"dataset.json\", \"w\") as f:\n    json.dump(dataset_json, f, indent=2)\n\n# save split lists (optional, but helpful later)\nwith open(DS_ROOT / \"split_train.txt\", \"w\") as f: f.write(\"\\n\".join(train_cases))\nwith open(DS_ROOT / \"split_val.txt\", \"w\") as f: f.write(\"\\n\".join(val_cases))\n\nprint(\"✅ dataset.json created at:\", DS_ROOT / \"dataset.json\")\nprint(\"Train cases:\", len(train_cases), \"Val cases:\", len(val_cases))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T23:40:35.757451Z","iopub.execute_input":"2025-10-04T23:40:35.758193Z","iopub.status.idle":"2025-10-04T23:40:36.710982Z","shell.execute_reply.started":"2025-10-04T23:40:35.758166Z","shell.execute_reply":"2025-10-04T23:40:36.710201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, sys, subprocess, glob\n\n# Locate where nnunetv2 is actually installed\nnnunet_path = subprocess.check_output(\n    [\"python3\", \"-c\", \"import nnunetv2, os; print(os.path.dirname(nnunetv2.__file__))\"]\n).decode().strip()\nprint(\"nnUNetv2 package path:\", nnunet_path)\n\n# Look for the verify and plan scripts\nverify_script = glob.glob(os.path.join(nnunet_path, \"**/verify_dataset*.py\"), recursive=True)\nplan_script = glob.glob(os.path.join(nnunet_path, \"**/plan_and_preprocess*.py\"), recursive=True)\n\nprint(\"Found verify scripts:\", verify_script)\nprint(\"Found plan scripts:\", plan_script)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T23:40:53.657262Z","iopub.execute_input":"2025-10-04T23:40:53.658051Z","iopub.status.idle":"2025-10-04T23:40:53.784401Z","shell.execute_reply.started":"2025-10-04T23:40:53.658019Z","shell.execute_reply":"2025-10-04T23:40:53.78378Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos.environ[\"nnUNet_raw\"] = \"/kaggle/working/nnunet_raw\"\nos.environ[\"nnUNet_preprocessed\"] = \"/kaggle/working/nnunet_preprocessed\"\nos.environ[\"nnUNet_results\"] = \"/kaggle/working/nnunet_results\"\n\n# confirm the correct dataset exists\n!ls /kaggle/working/nnunet_raw/Dataset601_RSNAIA","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-04T23:40:56.166439Z","iopub.execute_input":"2025-10-04T23:40:56.167085Z","iopub.status.idle":"2025-10-04T23:40:56.318498Z","shell.execute_reply.started":"2025-10-04T23:40:56.167058Z","shell.execute_reply":"2025-10-04T23:40:56.317675Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import json\n\njson_path = \"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/dataset.json\"\n\nwith open(json_path, \"r\") as f:\n    data = json.load(f)\n\n# Add required \"channel_names\" field if missing\nif \"channel_names\" not in data:\n    data[\"channel_names\"] = {\"0\": \"CT\"}\n\n# (Optional) keep consistent order & re-save\nwith open(json_path, \"w\") as f:\n    json.dump(data, f, indent=2)\n\nprint(\"✅ Fixed dataset.json; added channel_names = {'0': 'CT'}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-05T00:02:49.9296Z","iopub.execute_input":"2025-10-05T00:02:49.930283Z","iopub.status.idle":"2025-10-05T00:02:49.937796Z","shell.execute_reply.started":"2025-10-05T00:02:49.930254Z","shell.execute_reply":"2025-10-05T00:02:49.93709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import json\n\njson_path = \"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/dataset.json\"\n\nwith open(json_path, \"r\") as f:\n    data = json.load(f)\n\n# Fix label structure (keys should be names, values are integers)\ndata[\"labels\"] = {\"background\": 0, \"aneurysm\": 1}\n\n# Ensure channel_names still present\nif \"channel_names\" not in data:\n    data[\"channel_names\"] = {\"0\": \"CT\"}\n\n# Save back\nwith open(json_path, \"w\") as f:\n    json.dump(data, f, indent=2)\n\nprint(\"✅ Fixed dataset.json — labels now use correct format (background:0, aneurysm:1)\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-05T00:04:47.355372Z","iopub.execute_input":"2025-10-05T00:04:47.356311Z","iopub.status.idle":"2025-10-05T00:04:47.365397Z","shell.execute_reply.started":"2025-10-05T00:04:47.356264Z","shell.execute_reply":"2025-10-05T00:04:47.364695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport nibabel as nib\n\nIMAGES = Path(\"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/imagesTr\")\nLABELS = Path(\"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/labelsTr\")\n\nbad = []\nfor img_path in IMAGES.glob(\"*_0000.nii.gz\"):\n    uid = img_path.stem.replace(\"_0000\",\"\")\n    lab_path = LABELS / f\"{uid}.nii.gz\"\n    if not lab_path.exists():\n        continue\n    try:\n        img = nib.load(str(img_path))\n        lab = nib.load(str(lab_path))\n        if img.shape != lab.shape:\n            bad.append(uid)\n    except Exception as e:\n        print(f\"{uid}: {e}\")\n        bad.append(uid)\n\nprint(\"❌ Problematic cases:\", bad)\nprint(\"Count:\", len(bad))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-05T00:47:12.356487Z","iopub.execute_input":"2025-10-05T00:47:12.357106Z","iopub.status.idle":"2025-10-05T00:47:12.366547Z","shell.execute_reply.started":"2025-10-05T00:47:12.357076Z","shell.execute_reply":"2025-10-05T00:47:12.3659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import SimpleITK as sitk\nfrom pathlib import Path\n\nIMAGES = Path(\"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/imagesTr\")\nLABELS = Path(\"/kaggle/working/nnunet_raw/Dataset601_RSNAIA/labelsTr\")\n\nfixed = 0\nfor img_path in IMAGES.glob(\"*_0000.nii.gz\"):\n    uid = img_path.stem.replace(\"_0000\",\"\")\n    lab_path = LABELS / f\"{uid}.nii.gz\"\n    if not lab_path.exists():\n        continue\n    try:\n        img = sitk.ReadImage(str(img_path))\n        seg = sitk.ReadImage(str(lab_path))\n        seg = sitk.Resample(seg, img, sitk.Transform(), sitk.sitkNearestNeighbor, 0.0, seg.GetPixelID())\n        sitk.WriteImage(seg, str(lab_path))\n        fixed += 1\n    except Exception as e:\n        print(\"⚠️ Failed to fix\", uid, e)\n\nprint(f\"✅ Realigned {fixed} masks to match image orientation/spacing.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-05T01:25:47.130987Z","iopub.execute_input":"2025-10-05T01:25:47.131319Z","iopub.status.idle":"2025-10-05T01:25:47.14186Z","shell.execute_reply.started":"2025-10-05T01:25:47.131297Z","shell.execute_reply":"2025-10-05T01:25:47.141031Z"}},"outputs":[],"execution_count":null}]}