{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.13"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"},{"sourceId":11958008,"sourceType":"datasetVersion","datasetId":7518651}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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":{"_cell_guid":"99b40487-2edf-40e5-b92e-744663bf3b02","_uuid":"6eaede2b-02ad-4f6e-b1fb-bbad68d3f585","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:39:54.12603Z","iopub.execute_input":"2025-11-04T08:39:54.126285Z","iopub.status.idle":"2025-11-04T08:40:01.9837Z","shell.execute_reply.started":"2025-11-04T08:39:54.126266Z","shell.execute_reply":"2025-11-04T08:40:01.982815Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install kaggle","metadata":{"_cell_guid":"3a7fd481-b855-48d5-bbe7-059ad857e05b","_uuid":"05a415f0-bf34-42ed-9616-b563f4e6b4aa","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:01.985144Z","iopub.execute_input":"2025-11-04T08:40:01.985558Z","iopub.status.idle":"2025-11-04T08:40:06.013061Z","shell.execute_reply.started":"2025-11-04T08:40:01.985539Z","shell.execute_reply":"2025-11-04T08:40:06.012287Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install nibabel matplotlib scikit-learn tensorflow keras","metadata":{"_cell_guid":"69397875-f957-4d19-9952-6c03f05e0925","_uuid":"3531bbba-c9e0-423d-b247-ce0a28614aed","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:06.014126Z","iopub.execute_input":"2025-11-04T08:40:06.014496Z","iopub.status.idle":"2025-11-04T08:40:09.447447Z","shell.execute_reply.started":"2025-11-04T08:40:06.014464Z","shell.execute_reply":"2025-11-04T08:40:09.446688Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install opencv-python-headless nibabel scikit-image matplotlib pandas Pillow tensorflow keras","metadata":{"_cell_guid":"b7df1234-169b-4a06-8807-f16b5acd7712","_uuid":"09087ac7-e2c6-4db1-8dad-70921ec74439","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:09.448529Z","iopub.execute_input":"2025-11-04T08:40:09.448853Z","iopub.status.idle":"2025-11-04T08:40:16.910112Z","shell.execute_reply.started":"2025-11-04T08:40:09.448821Z","shell.execute_reply":"2025-11-04T08:40:16.909292Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install --upgrade pip","metadata":{"_cell_guid":"808c25cf-d362-4da8-b207-bf2532ad5fbb","_uuid":"d04afe36-5d97-4bac-b11a-3a6dceabd6cd","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:16.912552Z","iopub.execute_input":"2025-11-04T08:40:16.913139Z","iopub.status.idle":"2025-11-04T08:40:21.979368Z","shell.execute_reply.started":"2025-11-04T08:40:16.913116Z","shell.execute_reply":"2025-11-04T08:40:21.978431Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"dataset path","metadata":{"_cell_guid":"6f6e3cb3-f94c-46c6-88a1-71056edb1cd8","_uuid":"f8cfc209-5d21-4b28-9b88-a7a7658fef13","collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"TRAIN_DATASET_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"","metadata":{"_cell_guid":"5f684948-01ff-4c80-a319-8dd0043e208f","_uuid":"8780746d-0d6d-4d28-9567-f1f510f2b263","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:21.980634Z","iopub.execute_input":"2025-11-04T08:40:21.980948Z","iopub.status.idle":"2025-11-04T08:40:21.985168Z","shell.execute_reply.started":"2025-11-04T08:40:21.980922Z","shell.execute_reply":"2025-11-04T08:40:21.984529Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib  \ntest_image_flair = nib.load(\"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00000-000/BraTS-GLI-00000-000-seg.nii\").get_fdata()\nprint(\"Shape: \", test_image_flair.shape)\nprint(\"Dtype: \", test_image_flair.dtype)","metadata":{"_cell_guid":"58d49a29-346d-470d-a93a-001937fde8de","_uuid":"ce473731-014a-49b5-ac7b-68f91b40f3bb","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:21.986006Z","iopub.execute_input":"2025-11-04T08:40:21.986281Z","iopub.status.idle":"2025-11-04T08:40:22.969504Z","shell.execute_reply.started":"2025-11-04T08:40:21.98626Z","shell.execute_reply":"2025-11-04T08:40:22.968841Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Min: \", test_image_flair.min())\nprint(\"Max: \", test_image_flair.max())","metadata":{"_cell_guid":"d4fa6fd1-8cc4-4096-9bbf-1dd4fd0c95b2","_uuid":"4784313f-0312-41a0-8efd-46122df55227","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:22.970206Z","iopub.execute_input":"2025-11-04T08:40:22.970632Z","iopub.status.idle":"2025-11-04T08:40:22.991954Z","shell.execute_reply.started":"2025-11-04T08:40:22.970611Z","shell.execute_reply":"2025-11-04T08:40:22.991261Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import MinMaxScaler\n\nscaler = MinMaxScaler()","metadata":{"_cell_guid":"e8756f27-7f4f-4210-9b44-f7246e7fbcba","_uuid":"9e7c6b3f-8a65-4221-8532-612989046a7a","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:22.992666Z","iopub.execute_input":"2025-11-04T08:40:22.992956Z","iopub.status.idle":"2025-11-04T08:40:23.499635Z","shell.execute_reply.started":"2025-11-04T08:40:22.992939Z","shell.execute_reply":"2025-11-04T08:40:23.499022Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_image_flair = scaler.fit_transform(test_image_flair.reshape(-1, test_image_flair.shape[-1])).reshape(test_image_flair.shape)","metadata":{"_cell_guid":"67338dc8-0c00-408a-9337-9af3f4066991","_uuid":"65e4153c-f703-4cd2-aa87-1e0729fed3fc","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:23.500353Z","iopub.execute_input":"2025-11-04T08:40:23.500711Z","iopub.status.idle":"2025-11-04T08:40:23.680556Z","shell.execute_reply.started":"2025-11-04T08:40:23.500694Z","shell.execute_reply":"2025-11-04T08:40:23.679686Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Min: \", test_image_flair.min())\nprint(\"Max: \", test_image_flair.max())","metadata":{"_cell_guid":"d23fcc9b-6607-4bd0-87ee-910a689c3dc5","_uuid":"d88cfe1d-1f7f-497f-862a-2050ff164c17","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:23.681455Z","iopub.execute_input":"2025-11-04T08:40:23.681741Z","iopub.status.idle":"2025-11-04T08:40:23.704733Z","shell.execute_reply.started":"2025-11-04T08:40:23.681713Z","shell.execute_reply":"2025-11-04T08:40:23.704136Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport nibabel as nib\nimport numpy as np\nimport pandas as pd\nfrom sklearn.preprocessing import MinMaxScaler\n\n# ✅ Path to your BRATS dataset\nDATASET_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"\nscaler = MinMaxScaler()\n\n# --- Helper: Explore dataset structure ---\ndef explore_brats_dataset(path=DATASET_PATH):\n    patients = sorted([p for p in os.listdir(path) if p.startswith(\"BraTS-GLI\")])\n    print(f\"Total Patients Found: {len(patients)}\\n\")\n    \n    # check modality availability\n    records = []\n    for pid in patients[:5]:  # explore first 5\n        ppath = os.path.join(path, pid)\n        files = os.listdir(ppath)\n        records.append({\n            \"Patient_ID\": pid,\n            \"Files\": files\n        })\n    df = pd.DataFrame(records)\n    print(df)\n    return patients\n\npatients = explore_brats_dataset()","metadata":{"_cell_guid":"bf8b7ab5-85e8-4df3-a784-ed16b74a4354","_uuid":"9d7e6f57-ea93-4cc6-b702-2b787dce1247","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:23.705502Z","iopub.execute_input":"2025-11-04T08:40:23.705773Z","iopub.status.idle":"2025-11-04T08:40:23.726252Z","shell.execute_reply.started":"2025-11-04T08:40:23.70575Z","shell.execute_reply":"2025-11-04T08:40:23.725613Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_and_rescale_modalities(patient_id, base_path=DATASET_PATH):\n    \"\"\"\n    Load t1n, t1c, t2f, t2w, seg from a BraTS 2023 patient folder.\n    Rescale each image to [0,1] using MinMaxScaler.\n    Returns: 4 modalities + segmentation (numpy arrays)\n    \"\"\"\n    pdir = os.path.join(base_path, patient_id)\n\n    def rescale_volume(img_path):\n        img = nib.load(img_path).get_fdata()\n        flat = img.reshape(-1, 1)\n        scaled = scaler.fit_transform(flat).reshape(img.shape)\n        return scaled.astype(np.float32)\n\n    # load modalities according to new naming\n    t1n = rescale_volume(os.path.join(pdir, f\"{patient_id}-t1n.nii\"))\n    t1c = rescale_volume(os.path.join(pdir, f\"{patient_id}-t1c.nii\"))\n    t2f = rescale_volume(os.path.join(pdir, f\"{patient_id}-t2f.nii\"))\n    t2w = rescale_volume(os.path.join(pdir, f\"{patient_id}-t2w.nii\"))\n    seg = nib.load(os.path.join(pdir, f\"{patient_id}-seg.nii\")).get_fdata().astype(np.uint8)\n\n    return t1n, t1c, t2f, t2w, seg","metadata":{"_cell_guid":"72d489ff-3e3d-4eb1-a184-fc1fa98e2c31","_uuid":"271b461f-f4fa-4974-899f-ff08bce09bdf","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:23.726942Z","iopub.execute_input":"2025-11-04T08:40:23.727119Z","iopub.status.idle":"2025-11-04T08:40:23.732723Z","shell.execute_reply.started":"2025-11-04T08:40:23.727106Z","shell.execute_reply":"2025-11-04T08:40:23.732059Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pick one case for inspection\npid = patients[0]\nprint(f\"\\nInspecting patient: {pid}\")\n\nt1n, t1c, t2f, t2w, seg = load_and_rescale_modalities(pid)\n\nprint(\"Shapes:\")\nprint(\"T1n:\", t1n.shape)\nprint(\"T1c:\", t1c.shape)\nprint(\"T2f:\", t2f.shape)\nprint(\"T2w:\", t2w.shape)\nprint(\"Seg:\", seg.shape)\n\nprint(\"\\nValue ranges:\")\nprint(\"T1n:\", (np.min(t1n), np.max(t1n)))\nprint(\"Seg unique labels:\", np.unique(seg))","metadata":{"_cell_guid":"8dbe18c5-70bc-4d4b-abb5-9e4ab0cec267","_uuid":"29372642-19e2-4c53-900a-92aef80b38de","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:23.735715Z","iopub.execute_input":"2025-11-04T08:40:23.735961Z","iopub.status.idle":"2025-11-04T08:40:25.108193Z","shell.execute_reply.started":"2025-11-04T08:40:23.735939Z","shell.execute_reply":"2025-11-04T08:40:25.107475Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Stack modalities into one 4-channel 3D array (C, H, W, D)\nimage_4ch = np.stack([t1n, t1c, t2f, t2w], axis=0)\nprint(\"\\nStacked image shape:\", image_4ch.shape)\n\n# save temporarily as npy for faster reuse\nos.makedirs(\"/content/preprocessed\", exist_ok=True)\nnp.save(f\"/content/preprocessed/{pid}_img.npy\", image_4ch)\nnp.save(f\"/content/preprocessed/{pid}_mask.npy\", seg)","metadata":{"_cell_guid":"0102566d-f70e-4bcb-95d2-1b3a89323df8","_uuid":"03a7b689-8037-4581-99c4-79d5d1b32d24","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:25.108809Z","iopub.execute_input":"2025-11-04T08:40:25.109008Z","iopub.status.idle":"2025-11-04T08:40:25.264231Z","shell.execute_reply.started":"2025-11-04T08:40:25.108994Z","shell.execute_reply":"2025-11-04T08:40:25.263517Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport matplotlib.pyplot as plt\n\n# ✅ Set slice number\nslice_num = 95\nprint(f\"Slice Number: {slice_num}\")\n\n# ✅ Define patient folder (adjust if needed)\npatient_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00011-000\"\n\n# ✅ Load NIfTI files (convert to numpy arrays)\nt1n  = nib.load(f\"{patient_path}/BraTS-GLI-00011-000-t1n.nii\").get_fdata()\nt1c  = nib.load(f\"{patient_path}/BraTS-GLI-00011-000-t1c.nii\").get_fdata()\nt2f  = nib.load(f\"{patient_path}/BraTS-GLI-00011-000-t2f.nii\").get_fdata()\nt2w  = nib.load(f\"{patient_path}/BraTS-GLI-00011-000-t2w.nii\").get_fdata()\nmask = nib.load(f\"{patient_path}/BraTS-GLI-00011-000-seg.nii\").get_fdata()\n\n# ✅ Plot each modality and segmentation mask\nplt.figure(figsize=(12, 8))\n\nplt.subplot(2, 3, 1)\nplt.imshow(t1n[:, :, slice_num], cmap='gray')\nplt.title('T1n (Native)')\n\nplt.subplot(2, 3, 2)\nplt.imshow(t1c[:, :, slice_num], cmap='gray')\nplt.title('T1c (Contrast Enhanced)')\n\nplt.subplot(2, 3, 3)\nplt.imshow(t2f[:, :, slice_num], cmap='gray')\nplt.title('T2-FLAIR')\n\nplt.subplot(2, 3, 4)\nplt.imshow(t2w[:, :, slice_num], cmap='gray')\nplt.title('T2-weighted')\n\nplt.subplot(2, 3, 5)\nplt.imshow(mask[:, :, slice_num])\nplt.title('Segmentation Mask')\n\nplt.tight_layout()\nplt.show()","metadata":{"_cell_guid":"2be89129-df1d-4eb0-b570-cf1d75ff380e","_uuid":"b3bbb67e-0924-4633-b75e-ef54c33f7f27","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:25.264981Z","iopub.execute_input":"2025-11-04T08:40:25.265161Z","iopub.status.idle":"2025-11-04T08:40:27.248259Z","shell.execute_reply.started":"2025-11-04T08:40:25.265147Z","shell.execute_reply":"2025-11-04T08:40:27.247357Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\n\n# Define paths\nmodality_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00011-000/BraTS-GLI-00011-000-t1n.nii\"\nseg_path      = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00011-000/BraTS-GLI-00011-000-seg.nii\"\n\n# Load NIfTI files\nmodality_img = nib.load(modality_path).get_fdata()\nseg_img = nib.load(seg_path).get_fdata()\n\n# Print shapes\nprint(\"Modality shape:\", modality_img.shape)\nprint(\"Segmentation shape:\", seg_img.shape)","metadata":{"_cell_guid":"ee1d7ce2-f6fb-44de-ace0-b1f179f95bec","_uuid":"4adfe3ea-0e19-4da0-8ac3-d193f84e83f3","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:27.249091Z","iopub.execute_input":"2025-11-04T08:40:27.249321Z","iopub.status.idle":"2025-11-04T08:40:27.309382Z","shell.execute_reply.started":"2025-11-04T08:40:27.249304Z","shell.execute_reply":"2025-11-04T08:40:27.308687Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport matplotlib.pyplot as plt\nfrom scipy.ndimage import rotate\n\n# Load a T1CE example\nt1ce_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00000-000/BraTS-GLI-00000-000-t1c.nii\"\ntest_image_t1ce = nib.load(t1ce_path).get_fdata()\n\n# Choose a slice index\nslice_num = 95\nprint(f\"Slice number: {slice_num}\")\n\nplt.figure(figsize=(15, 5))\n\n# --- 1️⃣ Axial (Transverse) View ---\nplt.subplot(1, 3, 1)\nplt.imshow(test_image_t1ce[:, :, slice_num], cmap='gray')\nplt.title('Axial (Transverse) View')\nplt.axis('off')\n\n# --- 2️⃣ Coronal (Frontal) View ---\nplt.subplot(1, 3, 2)\nplt.imshow(rotate(test_image_t1ce[:, slice_num, :], 90, reshape=True), cmap='gray')\nplt.title('Coronal (Frontal) View')\nplt.axis('off')\n\n# --- 3️⃣ Sagittal View ---\nplt.subplot(1, 3, 3)\nplt.imshow(rotate(test_image_t1ce[slice_num, :, :], 90, reshape=True), cmap='gray')\nplt.title('Sagittal View')\nplt.axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_cell_guid":"4cc501c8-b42b-4381-8d10-8d466d9aaf01","_uuid":"4782e854-0126-4b16-b3c8-871fc0d49bae","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:27.31016Z","iopub.execute_input":"2025-11-04T08:40:27.310426Z","iopub.status.idle":"2025-11-04T08:40:27.675658Z","shell.execute_reply.started":"2025-11-04T08:40:27.310409Z","shell.execute_reply":"2025-11-04T08:40:27.674892Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport matplotlib.pyplot as plt\nfrom scipy.ndimage import rotate\nfrom skimage.util import montage\n\n# Load a T1CE volume (example)\nt1ce_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00000-000/BraTS-GLI-00000-000-t1c.nii\"\ntest_image_t1ce = nib.load(t1ce_path).get_fdata()\n\n# Create montage from all slices\nmontage_img = montage(test_image_t1ce, grid_shape=None, padding_width=2)\n\n# Plot rotated montage\nplt.figure(figsize=(10, 10))\nplt.imshow(rotate(montage_img, 90, reshape=True), cmap='gray')\nplt.title('Montage of T1CE Slices (All Slices)')\nplt.axis('off')\nplt.show()","metadata":{"_cell_guid":"403f0d3c-e2ff-401e-979f-57dc880e59a5","_uuid":"c019191f-d72f-4784-b066-4bc5c776bbc2","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:27.676535Z","iopub.execute_input":"2025-11-04T08:40:27.676804Z","iopub.status.idle":"2025-11-04T08:40:30.445599Z","shell.execute_reply.started":"2025-11-04T08:40:27.676783Z","shell.execute_reply":"2025-11-04T08:40:30.44483Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nplt.imshow(rotate(montage(test_image_t1ce[50:-50,:,:]), 90, reshape=True), cmap='gray')\nplt.title('Cropped Montage (Slices 50–105)')\nplt.axis('off')\nplt.show()","metadata":{"_cell_guid":"e27bf4a9-ef7b-461e-a5d2-9bf1fef99d9d","_uuid":"21cf564b-d211-4fc5-9c43-00eaa2114cb4","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:30.446529Z","iopub.execute_input":"2025-11-04T08:40:30.447144Z","iopub.status.idle":"2025-11-04T08:40:31.983502Z","shell.execute_reply.started":"2025-11-04T08:40:30.447117Z","shell.execute_reply":"2025-11-04T08:40:31.982481Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install matplotlib\n!pip install scipy\n!pip install scikit-image","metadata":{"_cell_guid":"3b8292e5-b309-4834-8d8d-a0531158cfd5","_uuid":"36eef743-5493-4c63-9c79-724f0ce25dfc","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:31.984493Z","iopub.execute_input":"2025-11-04T08:40:31.984755Z","iopub.status.idle":"2025-11-04T08:40:37.971694Z","shell.execute_reply.started":"2025-11-04T08:40:31.984736Z","shell.execute_reply":"2025-11-04T08:40:37.970924Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport matplotlib.pyplot as plt\nfrom scipy.ndimage import rotate\nfrom skimage.util import montage\n\n# --- Load the segmentation volume ---\nseg_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00000-000/BraTS-GLI-00000-000-seg.nii\"\ntest_image_seg = nib.load(seg_path).get_fdata()\n\n# --- Create and visualize montage (skip empty slices) ---\nplt.figure(figsize=(10, 10))\nplt.imshow(rotate(montage(test_image_seg[50:-50, :, :]), 90, reshape=True), cmap='gray')\nplt.title('Segmentation Mask Montage (Slices 50–105)')\nplt.axis('off')\nplt.show()","metadata":{"_cell_guid":"d3d36cc6-e61a-449e-a4d8-6b44baeb94dd","_uuid":"4974b22d-3004-4c40-8b23-6df63d3abdfa","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:37.972796Z","iopub.execute_input":"2025-11-04T08:40:37.973087Z","iopub.status.idle":"2025-11-04T08:40:39.699952Z","shell.execute_reply.started":"2025-11-04T08:40:37.973056Z","shell.execute_reply":"2025-11-04T08:40:39.699118Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nibabel as nib\nimport matplotlib.pyplot as plt\nimport matplotlib.colors as mcolors\n\n# Load segmentation if not already loaded\nseg_path = \"/kaggle/input/brats2023-full/BraTS2023/BraTS-GLI-00011-000/BraTS-GLI-00011-000-seg.nii\"\ntest_image_seg = nib.load(seg_path).get_fdata()\n\n# Custom colormap for BraTS segmentation labels\n# Labels: 0=background, 1=necrotic, 2=edema, 4=enhancing tumor\ncmap = mcolors.ListedColormap(['#440054', '#3b528b', '#18b880', '#e6d74f'])\nbounds = [-0.5, 0.5, 1.5, 2.5, 4.5]\nnorm = mcolors.BoundaryNorm(bounds, cmap.N)\n\n# Select slice\nslice_num = 95\n\nplt.figure(figsize=(6, 6))\nplt.imshow(test_image_seg[:, :, slice_num], cmap=cmap, norm=norm)\ncbar = plt.colorbar(ticks=[0, 1, 2, 4])\ncbar.ax.set_yticklabels(['Background', 'Necrotic', 'Edema', 'Enhancing'])\nplt.title(f'Segmentation Mask (Slice {slice_num})')\nplt.axis('off')\nplt.show()","metadata":{"_cell_guid":"b85bb8c1-1664-4d48-a3a8-ccdca653e730","_uuid":"5592a737-0566-4c13-9aa9-50dcb5722a55","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:39.700839Z","iopub.execute_input":"2025-11-04T08:40:39.701167Z","iopub.status.idle":"2025-11-04T08:40:39.884702Z","shell.execute_reply.started":"2025-11-04T08:40:39.701142Z","shell.execute_reply":"2025-11-04T08:40:39.883889Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Choose a slice index\nslice_num = 95\n\n# --- Isolate each segmentation class ---\nseg_0 = test_image_seg.copy()\nseg_0[seg_0 != 0] = np.nan\n\nseg_1 = test_image_seg.copy()\nseg_1[seg_1 != 1] = np.nan\n\nseg_2 = test_image_seg.copy()\nseg_2[seg_2 != 2] = np.nan\n\nseg_4 = test_image_seg.copy()\nseg_4[seg_4 != 4] = np.nan\n\n# --- Define legend ---\nclass_names = ['Background (0)', 'Non-Enhancing (1)', 'Edema (2)', 'Enhancing (4)']\nlegend = [plt.Rectangle((0, 0), 1, 1, color=cmap(i), label=class_names[i]) for i in range(len(class_names))]\n\n# --- Plot ---\nfig, ax = plt.subplots(1, 5, figsize=(20, 5))\n\nax[0].imshow(test_image_seg[:, :, slice_num], cmap=cmap, norm=norm)\nax[0].set_title('Original Segmentation')\nax[0].legend(handles=legend, loc='lower left', fontsize=8)\n\nax[1].imshow(seg_0[:, :, slice_num], cmap=cmap, norm=norm)\nax[1].set_title('Background (Class 0)')\n\nax[2].imshow(seg_1[:, :, slice_num], cmap=cmap, norm=norm)\nax[2].set_title('Non-Enhancing (Class 1)')\n\nax[3].imshow(seg_2[:, :, slice_num], cmap=cmap, norm=norm)\nax[3].set_title('Edema (Class 2)')\n\nax[4].imshow(seg_4[:, :, slice_num], cmap=cmap, norm=norm)\nax[4].set_title('Enhancing (Class 4)')\n\nfor a in ax:\n    a.axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_cell_guid":"b02fc823-8c0b-4eed-a59c-d80bb2be64cd","_uuid":"a97ed132-17f4-4d12-89c6-56e64e9fdfa3","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:39.885578Z","iopub.execute_input":"2025-11-04T08:40:39.88585Z","iopub.status.idle":"2025-11-04T08:40:40.671452Z","shell.execute_reply.started":"2025-11-04T08:40:39.885832Z","shell.execute_reply":"2025-11-04T08:40:40.670693Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\n\n# Path to your dataset\nTRAIN_DATASET_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"\n\n# --- List of all patient directories ---\ntrain_and_val_directories = [f.path for f in os.scandir(TRAIN_DATASET_PATH) if f.is_dir()]\n\n# --- Helper: Extract patient IDs from folder paths ---\ndef pathListIntoIds(dirList):\n    return [os.path.basename(path) for path in dirList]\n\ntrain_and_test_ids = pathListIntoIds(train_and_val_directories)\n\n# --- Split into Train / Validation / Test ---\ntrain_test_ids, val_ids = train_test_split(train_and_test_ids, test_size=0.2, random_state=42)\ntrain_ids, test_ids = train_test_split(train_test_ids, test_size=0.15, random_state=42)\n\n# --- Print summary ---\nprint(f\"Train length: {len(train_ids)}\")\nprint(f\"Validation length: {len(val_ids)}\")\nprint(f\"Test length: {len(test_ids)}\")\n\n# --- Visualize data distribution ---\nplt.figure(figsize=(6, 4))\nplt.bar([\"Train\", \"Valid\", \"Test\"],\n        [len(train_ids), len(val_ids), len(test_ids)],\n        color=['green', 'red', 'blue'])\nplt.ylabel('Number of Patients')\nplt.title('Data Distribution across Sets')\nplt.grid(axis='y', linestyle='--', alpha=0.6)\nplt.show()","metadata":{"_cell_guid":"078027de-5e59-4e2c-b478-6bd141f1cad0","_uuid":"288a0891-5503-42fc-a87f-74ea6f20487a","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:40.672211Z","iopub.execute_input":"2025-11-04T08:40:40.672478Z","iopub.status.idle":"2025-11-04T08:40:41.312161Z","shell.execute_reply.started":"2025-11-04T08:40:40.67246Z","shell.execute_reply":"2025-11-04T08:40:41.311432Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport nibabel as nib\nimport tensorflow as tf\nimport cv2\nfrom tensorflow import keras\n\n# --- Define segmentation classes ---\nSEGMENT_CLASSES = {\n    0: 'NOT tumor',\n    1: 'NECROTIC/CORE',    # Non-enhancing core\n    2: 'EDEMA',\n    3: 'ENHANCING'         # Original label 4 -> converted to 3\n}\n\n# --- Config ---\nVOLUME_SLICES = 100\nVOLUME_START_AT = 22\nIMG_SIZE = 128\nTRAIN_DATASET_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"\n\n# --- Data Generator ---\nclass DataGenerator(keras.utils.Sequence):\n    'Generates data for Keras (2-channel: FLAIR + T1CE)'\n    def __init__(self, list_IDs, dim=(IMG_SIZE, IMG_SIZE), batch_size=1, n_channels=2, shuffle=True):\n        self.dim = dim\n        self.batch_size = batch_size\n        self.list_IDs = list_IDs\n        self.n_channels = n_channels\n        self.shuffle = shuffle\n        self.on_epoch_end()\n\n    def __len__(self):\n        'Denotes the number of batches per epoch'\n        return int(np.floor(len(self.list_IDs) / self.batch_size))\n\n    def __getitem__(self, index):\n        'Generate one batch of data'\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n        Batch_ids = [self.list_IDs[k] for k in indexes]\n        X, Y = self.__data_generation(Batch_ids)\n        return X, Y\n\n    def on_epoch_end(self):\n        'Shuffle at the end of each epoch'\n        self.indexes = np.arange(len(self.list_IDs))\n        if self.shuffle:\n            np.random.shuffle(self.indexes)\n\n    def __data_generation(self, Batch_ids):\n        'Generates data containing batch_size samples'\n        X = np.zeros((self.batch_size * VOLUME_SLICES, *self.dim, self.n_channels), dtype=np.float32)\n        y = np.zeros((self.batch_size * VOLUME_SLICES, 240, 240), dtype=np.uint8)\n\n        for c, pid in enumerate(Batch_ids):\n            case_path = os.path.join(TRAIN_DATASET_PATH, pid)\n\n            # BraTS 2023 modality file names\n            flair_path = os.path.join(case_path, f\"{pid}-t2f.nii\")\n            t1ce_path  = os.path.join(case_path, f\"{pid}-t1c.nii\")\n            seg_path   = os.path.join(case_path, f\"{pid}-seg.nii\")\n\n            # Load modalities\n            flair = nib.load(flair_path).get_fdata()\n            t1ce  = nib.load(t1ce_path).get_fdata()\n            seg   = nib.load(seg_path).get_fdata()\n\n            for j in range(VOLUME_SLICES):\n                slice_idx = j + VOLUME_START_AT\n                if slice_idx >= flair.shape[2]:\n                    break\n                X[j + VOLUME_SLICES*c, :, :, 0] = cv2.resize(flair[:, :, slice_idx], self.dim)\n                X[j + VOLUME_SLICES*c, :, :, 1] = cv2.resize(t1ce[:, :, slice_idx], self.dim)\n                y[j + VOLUME_SLICES*c] = seg[:, :, slice_idx]\n\n        # Normalize X\n        X /= np.max(X)\n\n        # Fix label 4 -> 3 and one-hot encode masks\n        y[y == 4] = 3\n        mask = tf.one_hot(y, depth=4)\n        Y = tf.image.resize(mask, self.dim)\n        return X, Y\n\n\n# --- Create Generators ---\ntraining_generator = DataGenerator(train_ids)\nvalid_generator = DataGenerator(val_ids)\ntest_generator  = DataGenerator(test_ids)\n\nprint(f\"✅ Data Generators ready: {len(train_ids)} train, {len(val_ids)} val, {len(test_ids)} test\")","metadata":{"_cell_guid":"bee241c3-838f-4198-9fb9-5753a4bb53da","_uuid":"8159fadc-d403-419c-a819-17e21f9fc0b5","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:41.312917Z","iopub.execute_input":"2025-11-04T08:40:41.313101Z","iopub.status.idle":"2025-11-04T08:40:54.331913Z","shell.execute_reply.started":"2025-11-04T08:40:41.313086Z","shell.execute_reply":"2025-11-04T08:40:54.331177Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# --- Visualization Function ---\ndef display_slice_and_segmentation(flair, t1ce, segmentation):\n    fig, axes = plt.subplots(1, 3, figsize=(12, 5))\n\n    axes[0].imshow(flair, cmap='gray')\n    axes[0].set_title('FLAIR')\n    axes[0].axis('off')\n\n    axes[1].imshow(t1ce, cmap='gray')\n    axes[1].set_title('T1CE')\n    axes[1].axis('off')\n\n    # segmentation colormap for BraTS labels (0,1,2,3)\n    axes[2].imshow(segmentation, cmap='nipy_spectral')\n    axes[2].set_title('Segmentation')\n    axes[2].axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n\n# --- Retrieve one batch from generator ---\nbatch_index = 8  # change freely\nX_batch, Y_batch = training_generator[batch_index]\n\n# --- Extract modalities and segmentation ---\nflair_batch = X_batch[:, :, :, 0]\nt1ce_batch = X_batch[:, :, :, 1]\nsegmentation_batch = np.argmax(Y_batch, axis=-1)  # categorical mask\n\n# --- Select a slice index within this batch ---\nslice_index = 60  # 0 ≤ slice_index < VOLUME_SLICES (default 100)\n\n# --- Safety check ---\nif slice_index >= flair_batch.shape[0]:\n    raise IndexError(f\"slice_index {slice_index} exceeds batch size {flair_batch.shape[0]}\")\n\n# --- Extract slice ---\nslice_flair = flair_batch[slice_index]\nslice_t1ce = t1ce_batch[slice_index]\nslice_segmentation = segmentation_batch[slice_index]\n\n# --- Display slice and segmentation ---\ndisplay_slice_and_segmentation(slice_flair, slice_t1ce, slice_segmentation)","metadata":{"_cell_guid":"7525a7b2-9729-44e7-8d4f-409093b6da87","_uuid":"ca7a2b30-2d23-450e-b581-b98f47542018","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:54.332776Z","iopub.execute_input":"2025-11-04T08:40:54.333402Z","iopub.status.idle":"2025-11-04T08:40:56.306294Z","shell.execute_reply.started":"2025-11-04T08:40:54.333383Z","shell.execute_reply":"2025-11-04T08:40:56.305434Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras import layers, models, optimizers\nimport numpy as np\n\ndef dice_coef(y_true, y_pred, smooth=1.0):\n    class_num = 4\n    dice_total = 0\n    for i in range(class_num):\n        y_true_f = K.flatten(y_true[:,:,:,i])\n        y_pred_f = K.flatten(y_pred[:,:,:,i])\n        intersection = K.sum(y_true_f * y_pred_f)\n        dice = (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n        dice_total += dice\n    return dice_total / class_num\n\ndef dice_loss(y_true, y_pred):\n    return 1 - dice_coef(y_true, y_pred)\n\ndef focal_loss(gamma=2., alpha=0.25):\n    def focal_loss_fixed(y_true, y_pred):\n        epsilon = K.epsilon()\n        y_pred = K.clip(y_pred, epsilon, 1. - epsilon)\n        cross_entropy = -y_true * K.log(y_pred)\n        weight = alpha * K.pow(1 - y_pred, gamma)\n        loss = weight * cross_entropy\n        return K.sum(loss, axis=-1)\n    return focal_loss_fixed\n\ndef tversky_loss(y_true, y_pred, alpha=0.7, beta=0.3, smooth=1e-6):\n    y_true_pos = K.flatten(y_true)\n    y_pred_pos = K.flatten(y_pred)\n    true_pos = K.sum(y_true_pos * y_pred_pos)\n    false_neg = K.sum(y_true_pos * (1 - y_pred_pos))\n    false_pos = K.sum((1 - y_true_pos) * y_pred_pos)\n    return 1 - ((true_pos + smooth) / (true_pos + alpha * false_neg + beta * false_pos + smooth))\n\ndef combined_loss(y_true, y_pred):\n    dl = dice_loss(y_true, y_pred)\n    fl = focal_loss()(y_true, y_pred)\n    tl = tversky_loss(y_true, y_pred)\n    return 0.4 * dl + 0.3 * fl + 0.3 * tl","metadata":{"_cell_guid":"23922803-ead4-4a60-af11-b7b11d26680e","_uuid":"9442dea6-3aba-4ec9-8903-a5fff81e7737","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:56.307175Z","iopub.execute_input":"2025-11-04T08:40:56.307438Z","iopub.status.idle":"2025-11-04T08:40:56.316318Z","shell.execute_reply.started":"2025-11-04T08:40:56.307412Z","shell.execute_reply":"2025-11-04T08:40:56.315734Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def dice_coef_necrotic(y_true, y_pred, epsilon=1e-6):\n    intersection = K.sum(K.abs(y_true[:,:,:,1] * y_pred[:,:,:,1]))\n    return (2. * intersection) / (K.sum(K.square(y_true[:,:,:,1])) + K.sum(K.square(y_pred[:,:,:,1])) + epsilon)\n\ndef dice_coef_edema(y_true, y_pred, epsilon=1e-6):\n    intersection = K.sum(K.abs(y_true[:,:,:,2] * y_pred[:,:,:,2]))\n    return (2. * intersection) / (K.sum(K.square(y_true[:,:,:,2])) + K.sum(K.square(y_pred[:,:,:,2])) + epsilon)\n\ndef dice_coef_enhancing(y_true, y_pred, epsilon=1e-6):\n    intersection = K.sum(K.abs(y_true[:,:,:,3] * y_pred[:,:,:,3]))\n    return (2. * intersection) / (K.sum(K.square(y_true[:,:,:,3])) + K.sum(K.square(y_pred[:,:,:,3])) + epsilon)","metadata":{"_cell_guid":"d2cd108c-97e2-497c-8b29-90c3f7f1177d","_uuid":"d4126625-e0d3-4f76-9aab-a3b7e1a845aa","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:56.317082Z","iopub.execute_input":"2025-11-04T08:40:56.317378Z","iopub.status.idle":"2025-11-04T08:40:56.337048Z","shell.execute_reply.started":"2025-11-04T08:40:56.317353Z","shell.execute_reply":"2025-11-04T08:40:56.336278Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def precision(y_true, y_pred):\n    true_pos = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n    pred_pos = K.sum(K.round(K.clip(y_pred, 0, 1)))\n    return true_pos / (pred_pos + K.epsilon())\n\ndef sensitivity(y_true, y_pred):\n    true_pos = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n    possible_pos = K.sum(K.round(K.clip(y_true, 0, 1)))\n    return true_pos / (possible_pos + K.epsilon())\n\ndef specificity(y_true, y_pred):\n    true_neg = K.sum(K.round(K.clip((1 - y_true) * (1 - y_pred), 0, 1)))\n    possible_neg = K.sum(K.round(K.clip(1 - y_true, 0, 1)))\n    return true_neg / (possible_neg + K.epsilon())","metadata":{"_cell_guid":"7d3e4457-7889-4182-8d98-82eb4c2917c0","_uuid":"fdb93e3e-3175-4a8f-8513-b6f850ab1111","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:56.337839Z","iopub.execute_input":"2025-11-04T08:40:56.338101Z","iopub.status.idle":"2025-11-04T08:40:56.355243Z","shell.execute_reply.started":"2025-11-04T08:40:56.338079Z","shell.execute_reply":"2025-11-04T08:40:56.354348Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_unet(input_shape=(128,128,2)):\n    inputs = layers.Input(shape=input_shape)\n    \n    # Encoder\n    c1 = layers.Conv2D(32, 3, activation='relu', padding='same')(inputs)\n    c1 = layers.Conv2D(32, 3, activation='relu', padding='same')(c1)\n    p1 = layers.MaxPooling2D((2, 2))(c1)\n\n    c2 = layers.Conv2D(64, 3, activation='relu', padding='same')(p1)\n    c2 = layers.Conv2D(64, 3, activation='relu', padding='same')(c2)\n    p2 = layers.MaxPooling2D((2, 2))(c2)\n\n    c3 = layers.Conv2D(128, 3, activation='relu', padding='same')(p2)\n    c3 = layers.Conv2D(128, 3, activation='relu', padding='same')(c3)\n    p3 = layers.MaxPooling2D((2, 2))(c3)\n\n    # Bottleneck\n    bn = layers.Conv2D(256, 3, activation='relu', padding='same')(p3)\n    bn = layers.Conv2D(256, 3, activation='relu', padding='same')(bn)\n\n    # Decoder\n    u3 = layers.Conv2DTranspose(128, 2, strides=(2, 2), padding='same')(bn)\n    u3 = layers.concatenate([u3, c3])\n    c4 = layers.Conv2D(128, 3, activation='relu', padding='same')(u3)\n    c4 = layers.Conv2D(128, 3, activation='relu', padding='same')(c4)\n\n    u2 = layers.Conv2DTranspose(64, 2, strides=(2, 2), padding='same')(c4)\n    u2 = layers.concatenate([u2, c2])\n    c5 = layers.Conv2D(64, 3, activation='relu', padding='same')(u2)\n    c5 = layers.Conv2D(64, 3, activation='relu', padding='same')(c5)\n\n    u1 = layers.Conv2DTranspose(32, 2, strides=(2, 2), padding='same')(c5)\n    u1 = layers.concatenate([u1, c1])\n    c6 = layers.Conv2D(32, 3, activation='relu', padding='same')(u1)\n    c6 = layers.Conv2D(32, 3, activation='relu', padding='same')(c6)\n\n    outputs = layers.Conv2D(4, (1, 1), activation='softmax')(c6)\n    model = models.Model(inputs=[inputs], outputs=[outputs])\n    return model\n\nmodel = build_unet((128, 128, 2))\nmodel.compile(optimizer='adam',\n              loss=combined_loss,\n              metrics=[dice_coef, dice_coef_necrotic, dice_coef_edema, dice_coef_enhancing,\n                       precision, sensitivity, specificity])\nmodel.summary()","metadata":{"_cell_guid":"15f81482-37c7-4177-a5dc-42d8214cd632","_uuid":"484546fd-6c9d-4219-9c1d-1f95f226f059","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:56.356153Z","iopub.execute_input":"2025-11-04T08:40:56.356812Z","iopub.status.idle":"2025-11-04T08:40:57.685666Z","shell.execute_reply.started":"2025-11-04T08:40:56.356776Z","shell.execute_reply":"2025-11-04T08:40:57.685085Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!apt-get install graphviz -y\n!pip install --upgrade pydot graphviz\n!pip install --upgrade tensorflow pydotplus\n!pip install pydot","metadata":{"_cell_guid":"18e89fa2-4278-4368-8fee-5a7ec0c8f397","_uuid":"1b5fa323-577d-49c8-ac03-cc800e3d39c1","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:40:57.686365Z","iopub.execute_input":"2025-11-04T08:40:57.686571Z","iopub.status.idle":"2025-11-04T08:42:11.33453Z","shell.execute_reply.started":"2025-11-04T08:40:57.686556Z","shell.execute_reply":"2025-11-04T08:42:11.333685Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.utils import plot_model\n!pip install pydot\n\nimport pydot\nimport graphviz\n\n\nplot_model(\n    model,\n    to_file='unet_architecture.png',\n    show_shapes=True,\n    show_layer_names=True,\n    rankdir='TB',\n    dpi=70\n)","metadata":{"_cell_guid":"f75c5832-411c-4043-8df6-2345a2aaf325","_uuid":"4ec6f731-7cee-4909-bee8-aa90ca6da9d9","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:42:11.335653Z","iopub.execute_input":"2025-11-04T08:42:11.335945Z","iopub.status.idle":"2025-11-04T08:42:13.674708Z","shell.execute_reply.started":"2025-11-04T08:42:11.335921Z","shell.execute_reply":"2025-11-04T08:42:13.673677Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q pydot graphviz pydotplus\nimport pydot\nimport graphviz\nimport tensorflow as tf\nfrom tensorflow.keras.utils import plot_model\nimport os, shutil\n\n# --- Ensure Graphviz 'dot' is in PATH ---\nif not shutil.which(\"dot\"):\n    os.environ[\"PATH\"] += os.pathsep + '/usr/bin'\nprint(\"✅ Graphviz path:\", shutil.which(\"dot\"))\n\n# --- Verify pydot & graphviz ---\nprint(\"✅ pydot version:\", pydot.__version__)\nprint(\"✅ TensorFlow version:\", tf.__version__)","metadata":{"_cell_guid":"b057da90-e041-4471-ab76-877c7492739d","_uuid":"1f2e2a3a-4ff0-4e63-b38a-b28806cab97b","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:42:13.675856Z","iopub.execute_input":"2025-11-04T08:42:13.676206Z","iopub.status.idle":"2025-11-04T08:42:15.682583Z","shell.execute_reply.started":"2025-11-04T08:42:13.676182Z","shell.execute_reply":"2025-11-04T08:42:15.681661Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom tensorflow import keras\nfrom tensorflow.keras.callbacks import CSVLogger\n\n# --- Base directory for all outputs ---\nBASE_DIR = \"/kaggle/working/Radiogenomics_BraTS2023\"\nos.makedirs(BASE_DIR, exist_ok=True)\n\n# --- Callback paths ---\nMODEL_PATH = os.path.join(BASE_DIR, \"model_epoch{epoch:02d}_valloss{val_loss:.6f}.weights.h5\")\nLOG_PATH   = os.path.join(BASE_DIR, \"training_log.csv\")\n\n# --- Define callbacks ---\ncallbacks = [\n    keras.callbacks.ReduceLROnPlateau(\n        monitor='val_loss',\n        factor=0.2,\n        patience=2,\n        min_lr=1e-6,\n        verbose=1\n    ),\n    \n    keras.callbacks.ModelCheckpoint(\n        filepath=MODEL_PATH,\n        monitor='val_loss',\n        verbose=1,\n        save_best_only=True,\n        save_weights_only=True\n    ),\n    \n    CSVLogger(\n        filename=LOG_PATH,\n        separator=',',\n        append=False\n    )\n]\n\nprint(\"✅ Callbacks configured successfully.\")\nprint(f\"📁 Models will be saved to: {MODEL_PATH}\")\nprint(f\"🧾 Training log will be saved to: {LOG_PATH}\")","metadata":{"_cell_guid":"e3813299-2808-403c-99ae-9e22443adbbc","_uuid":"93ae5a0e-9bea-4ca2-8f99-7602726386a2","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:42:15.683794Z","iopub.execute_input":"2025-11-04T08:42:15.684132Z","iopub.status.idle":"2025-11-04T08:42:15.692677Z","shell.execute_reply.started":"2025-11-04T08:42:15.68411Z","shell.execute_reply":"2025-11-04T08:42:15.691755Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import KFold\n\nkf = KFold(n_splits=5, shuffle=True, random_state=42)\nall_ids = np.array(all_patient_ids)\n\ntrain_ids_folds, val_ids_folds = [], []\nfor train_idx, val_idx in kf.split(all_ids):\n    train_ids_folds.append(all_ids[train_idx])\n    val_ids_folds.append(all_ids[val_idx])","metadata":{"_cell_guid":"94b28244-9262-4330-929b-d072d3d6bfaf","_uuid":"f7624db3-2cfd-433e-b23c-93ec740cff36","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:42:15.693557Z","iopub.execute_input":"2025-11-04T08:42:15.693925Z","iopub.status.idle":"2025-11-04T08:42:16.017818Z","shell.execute_reply.started":"2025-11-04T08:42:15.693897Z","shell.execute_reply":"2025-11-04T08:42:16.016336Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Custom MeanIoU that works with one-hot encoded outputs ---\nclass MeanIoUOneHot(tf.keras.metrics.MeanIoU):\n    def __init__(self, num_classes=4, name=\"mean_iou_onehot\", **kwargs):\n        super().__init__(num_classes=num_classes, name=name, **kwargs)\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_true = tf.argmax(y_true, axis=-1)\n        y_pred = tf.argmax(y_pred, axis=-1)\n        return super().update_state(y_true, y_pred, sample_weight)","metadata":{"_cell_guid":"17be7588-fcfc-4289-acb2-214fb76b251a","_uuid":"89736d9b-a978-4e71-8f59-154554d39723","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:43:04.817745Z","iopub.execute_input":"2025-11-04T08:43:04.818374Z","iopub.status.idle":"2025-11-04T08:43:04.823647Z","shell.execute_reply.started":"2025-11-04T08:43:04.818349Z","shell.execute_reply":"2025-11-04T08:43:04.822818Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.layers import Conv2D, MaxPooling2D, UpSampling2D, Dropout, concatenate, Input\nfrom tensorflow.keras.models import Model\n\ndef build_unet(input_shape=(128, 128, 2), \n               ker_init='he_normal', \n               dropout=0.3):\n    \"\"\"\n    U-Net architecture for multi-class tumor segmentation.\n    Compatible with BraTS2023 preprocessing (Flair + T1CE as input).\n    \"\"\"\n\n    inputs = Input(shape=input_shape)\n\n    # ---- Encoder ----\n    conv1 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer=ker_init)(inputs)\n    conv1 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv1)\n    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n\n    conv2 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer=ker_init)(pool1)\n    conv2 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv2)\n    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n\n    conv3 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer=ker_init)(pool2)\n    conv3 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv3)\n    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n\n    conv4 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer=ker_init)(pool3)\n    conv4 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv4)\n    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)\n\n    # ---- Bottleneck ----\n    conv5 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer=ker_init)(pool4)\n    conv5 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv5)\n    drop5 = Dropout(dropout)(conv5)\n\n    # ---- Decoder ----\n    up6 = UpSampling2D(size=(2, 2))(drop5)\n    up6 = Conv2D(256, 2, activation='relu', padding='same', kernel_initializer=ker_init)(up6)\n    merge6 = concatenate([conv4, up6])\n    conv6 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer=ker_init)(merge6)\n    conv6 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv6)\n\n    up7 = UpSampling2D(size=(2, 2))(conv6)\n    up7 = Conv2D(128, 2, activation='relu', padding='same', kernel_initializer=ker_init)(up7)\n    merge7 = concatenate([conv3, up7])\n    conv7 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer=ker_init)(merge7)\n    conv7 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv7)\n\n    up8 = UpSampling2D(size=(2, 2))(conv7)\n    up8 = Conv2D(64, 2, activation='relu', padding='same', kernel_initializer=ker_init)(up8)\n    merge8 = concatenate([conv2, up8])\n    conv8 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer=ker_init)(merge8)\n    conv8 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv8)\n\n    up9 = UpSampling2D(size=(2, 2))(conv8)\n    up9 = Conv2D(32, 2, activation='relu', padding='same', kernel_initializer=ker_init)(up9)\n    merge9 = concatenate([conv1, up9])\n    conv9 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer=ker_init)(merge9)\n    conv9 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer=ker_init)(conv9)\n\n    # ---- Output ----\n    outputs = Conv2D(4, (1, 1), activation='softmax')(conv9)\n\n    model = Model(inputs=inputs, outputs=outputs)\n    return model","metadata":{"_cell_guid":"94120529-9ddd-4722-bf4d-66ad77addf5a","_uuid":"343db841-043f-41fc-b970-d711e3cc2f3d","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:43:09.864659Z","iopub.execute_input":"2025-11-04T08:43:09.864973Z","iopub.status.idle":"2025-11-04T08:43:09.884979Z","shell.execute_reply.started":"2025-11-04T08:43:09.864951Z","shell.execute_reply":"2025-11-04T08:43:09.884182Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ======================================================\n# 🧠 Radiogenomics U-Net Training — Fold 1 (100 folders)\n# ======================================================\nimport os\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import backend as K\nfrom sklearn.model_selection import KFold\nfrom tensorflow.keras.callbacks import CSVLogger\n\n# --- Custom MeanIoU that supports one-hot encoded masks ---\nclass MeanIoUOneHot(tf.keras.metrics.MeanIoU):\n    def __init__(self, num_classes=4, name=\"mean_iou_onehot\", **kwargs):\n        super().__init__(num_classes=num_classes, name=name, **kwargs)\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_true = tf.argmax(y_true, axis=-1)\n        y_pred = tf.argmax(y_pred, axis=-1)\n        return super().update_state(y_true, y_pred, sample_weight)\n\n\n# ======================================================\n# 🔹 0. Paths & Config\n# ======================================================\nBASE_DIR = \"/kaggle/working/Radiogenomics_BraTS2023\"\nTRAIN_DATASET_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"\nos.makedirs(BASE_DIR, exist_ok=True)\n\nEPOCHS = 10             # quick test\nIMG_SIZE = 128\nBATCH_SIZE = 1\nSUBSET_SIZE = 100       # Only 100 folders for fast evaluation\nVOLUME_SLICES = 100\nVOLUME_START_AT = 22\nNUM_FOLDS = 5\n\n\n# ======================================================\n# 🔹 1. Select Subset and Fold Split\n# ======================================================\nall_ids = np.array(train_and_test_ids[:SUBSET_SIZE])\nprint(f\"✅ Using subset of {len(all_ids)} folders\")\n\nkf = KFold(n_splits=NUM_FOLDS, shuffle=True, random_state=42)\nfolds = list(kf.split(all_ids))\ntrain_idx, val_idx = folds[0]\n\ntrain_ids_fold1 = all_ids[train_idx]\nval_ids_fold1 = all_ids[val_idx]\n\nprint(f\"📊 Fold 1 -> Train: {len(train_ids_fold1)}, Val: {len(val_ids_fold1)}\")\n\n\n# ======================================================\n# 🔹 2. Create Data Generators\n# ======================================================\ntraining_generator = DataGenerator(train_ids_fold1)\nvalid_generator = DataGenerator(val_ids_fold1)\n\n\n# ======================================================\n# 🔹 3. Build Model\n# ======================================================\ninput_layer = keras.layers.Input((IMG_SIZE, IMG_SIZE, 2))\nmodel = build_unet(input_shape=(IMG_SIZE, IMG_SIZE, 2), ker_init='he_normal', dropout=0.3)\n\n\n# ======================================================\n# 🔹 4. Compile Model\n# ======================================================\nmodel.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=0.001),\n    loss=combined_loss,\n    metrics=[\n        'accuracy',\n        MeanIoUOneHot(num_classes=4),\n        dice_coef,\n        precision,\n        sensitivity,\n        specificity,\n        dice_coef_necrotic,\n        dice_coef_edema,\n        dice_coef_enhancing\n    ]\n)\n\n\n# ======================================================\n# 🔹 5. Setup Callbacks\n# ======================================================\nfold_dir = os.path.join(BASE_DIR, \"fold_1_fasttrain\")\nos.makedirs(fold_dir, exist_ok=True)\n\ncallbacks = [\n    keras.callbacks.ReduceLROnPlateau(\n        monitor='val_loss', factor=0.3, patience=2, min_lr=1e-6, verbose=1),\n    keras.callbacks.ModelCheckpoint(\n        filepath=os.path.join(fold_dir, 'unet_fold1_best.weights.h5'),\n        monitor='val_loss', save_best_only=True, save_weights_only=True, verbose=1),\n    CSVLogger(os.path.join(fold_dir, 'fold1_training_log.csv'))\n]\n\n\n# ======================================================\n# 🔹 6. Train Model (Fold 1)\n# ======================================================\nK.clear_session()\n\nprint(\"\\n🚀 Starting Fold 1 training (subset mode)...\\n\")\nhistory = model.fit(\n    training_generator,\n    validation_data=valid_generator,\n    epochs=EPOCHS,\n    steps_per_epoch=len(training_generator),\n    callbacks=callbacks,\n    verbose=1\n)\n\n\n# ======================================================\n# 🔹 7. Save Final Model\n# ======================================================\nfinal_model_path = os.path.join(fold_dir, \"unet_fold1_final.keras\")\nmodel.save(final_model_path)\n\nprint(f\"\\n✅ Training complete!\")\nprint(f\"📁 Model saved to: {final_model_path}\")\nprint(f\"🧾 Logs saved to: {os.path.join(fold_dir, 'fold1_training_log.csv')}\")","metadata":{"_cell_guid":"68bc34e0-7c98-4b71-8de3-e571740c7709","_uuid":"eecadd3e-78f6-4206-85f4-a81066174b52","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:43:09.886268Z","iopub.execute_input":"2025-11-04T08:43:09.886558Z","iopub.status.idle":"2025-11-04T08:49:21.441373Z","shell.execute_reply.started":"2025-11-04T08:43:09.886534Z","shell.execute_reply":"2025-11-04T08:49:21.440654Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_results = model.evaluate(test_generator)\nprint(dict(zip(model.metrics_names, test_results)))","metadata":{"_cell_guid":"8066c642-970a-4446-a804-6f915cb7fd61","_uuid":"de17e96e-1af7-43e5-b3a0-60a5aa511673","collapsed":false,"execution":{"iopub.status.busy":"2025-11-04T08:49:21.442219Z","iopub.execute_input":"2025-11-04T08:49:21.442483Z","iopub.status.idle":"2025-11-04T08:51:00.532275Z","shell.execute_reply.started":"2025-11-04T08:49:21.442465Z","shell.execute_reply":"2025-11-04T08:51:00.531547Z"},"jupyter":{"outputs_hidden":false},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Multitask: Segmentation + Genomic Prediction (MGMT)\n\nThis section maps RSNA labels to BraTS2023 subjects, creates `radiogenomics.csv`, and implements a multi-task U-Net that outputs both segmentation masks and a genomic label (MGMT).","metadata":{}},{"cell_type":"code","source":"\n# --- 1) Map RSNA labels (MGMT) to BraTS2023 subjects and create radiogenomics.csv ---\nimport os, glob, pandas as pd, numpy as np\n\nRSNA_PATH = \"/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification\"\nBRATS_PATH = \"/kaggle/input/brats2023-full/BraTS2023\"\n\n# Load RSNA labels\nrsna_csv = os.path.join(RSNA_PATH, \"train_labels.csv\")\nrsna_df = pd.read_csv(rsna_csv)\n# RSNA uses BraTS21ID as numeric string like \"00000\"\nrsna_df[\"numeric_id\"] = rsna_df[\"BraTS21ID\"].astype(str).str.zfill(5)\n\n# Collect BraTS folders (may be named differently; take basenames)\nbrats_folders = sorted([os.path.basename(p) for p in glob.glob(os.path.join(BRATS_PATH, \"*\")) if os.path.isdir(p)])\ndef extract_numeric_from_brats(name):\n    nums = \"\".join([c for c in name if c.isdigit()])\n    return nums[-5:] if len(nums)>=5 else nums\n\nbrats_df = pd.DataFrame({\"brats_folder\": brats_folders})\nbrats_df[\"numeric_id\"] = brats_df[\"brats_folder\"].apply(extract_numeric_from_brats)\n\n# Merge on numeric_id\nmerged = pd.merge(brats_df, rsna_df, on=\"numeric_id\", how=\"inner\")\nprint(f\"Found {len(merged)} matching subjects between BRATS and RSNA datasets.\")\n\n# Create radiogenomics.csv with columns: patient_id, MGMT_value\nout_df = merged[[\"brats_folder\", \"MGMT_value\"]].rename(columns={\"brats_folder\":\"patient_id\"})\nout_csv = \"/kaggle/working/radiogenomics_mapped.csv\"\nout_df.to_csv(out_csv, index=False)\nprint(f\"Saved mapped radiogenomics CSV to: {out_csv}\")\nout_df.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T08:51:00.533748Z","iopub.execute_input":"2025-11-04T08:51:00.53405Z","iopub.status.idle":"2025-11-04T08:51:01.257204Z","shell.execute_reply.started":"2025-11-04T08:51:00.534031Z","shell.execute_reply":"2025-11-04T08:51:01.256334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# --- 2) Helpers: load NIfTI volumes from BraTS and extract slices with labels ---\nimport nibabel as nib\nimport numpy as np\nimport tensorflow as tf\nfrom glob import glob\nimport os\n\ndef load_nifti(path):\n    img = nib.load(path)\n    return img.get_fdata().astype(np.float32)\n\ndef get_case_paths(brats_root, patient_folder):\n    folder = os.path.join(brats_root, patient_folder)\n    files = glob(os.path.join(folder, \"*\"))\n    # find modalities and seg by common keywords\n    case = {}\n    for f in files:\n        fn = os.path.basename(f).lower()\n        if \"flair\" in fn:\n            case[\"flair\"] = f\n        elif (\"t1ce\" in fn) or (\"t1gd\" in fn) or (\"t1ce\" in os.path.basename(f).lower()):\n            case[\"t1ce\"] = f\n        elif (\"t1\" in fn) and (\"t1ce\" not in fn):\n            case[\"t1\"] = f\n        elif \"t2\" in fn:\n            case[\"t2\"] = f\n        elif \"seg\" in fn or \"segmentation\" in fn:\n            case[\"seg\"] = f\n    return case\n\ndef stack_modalities_brats(case_paths):\n    flair = load_nifti(case_paths[\"flair\"])\n    t1   = load_nifti(case_paths[\"t1\"])\n    t1ce = load_nifti(case_paths[\"t1ce\"])\n    t2   = load_nifti(case_paths[\"t2\"])\n    imgs = np.stack([flair, t1, t1ce, t2], axis=-1)  # H,W,D,4\n    # normalize per-modality\n    for c in range(imgs.shape[-1]):\n        vol = imgs[..., c]\n        imgs[..., c] = (vol - vol.mean()) / (vol.std()+1e-6)\n    seg = load_nifti(case_paths[\"seg\"]).astype(np.uint8)\n    return imgs, seg\n\ndef extract_slices_with_mask(imgs, seg, min_nonzero=100):\n    H,W,D,_ = imgs.shape\n    out = []\n    for z in range(D):\n        mask = seg[..., z]\n        if np.sum(mask>0) < min_nonzero:\n            continue\n        im = imgs[..., z, :]\n        m = mask[..., None]\n        out.append((im, m))\n    return out\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T08:51:01.257928Z","iopub.execute_input":"2025-11-04T08:51:01.258123Z","iopub.status.idle":"2025-11-04T08:51:01.267949Z","shell.execute_reply.started":"2025-11-04T08:51:01.258108Z","shell.execute_reply":"2025-11-04T08:51:01.267236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# --- 3) Create tf.data.Dataset yielding (image_slice, mask), label ---\nimport pandas as pd, os, random, tensorflow as tf\nfrom functools import partial\n\nradiogenomics_csv = \"/kaggle/working/radiogenomics_mapped.csv\"\nrg_df = pd.read_csv(radiogenomics_csv).set_index(\"patient_id\")\nprint(\"Radiogenomics mapping loaded:\", rg_df.shape)\n\n# Build a list of samples (patient_id, slice_idx, image, mask) preloaded for prototyping\nsamples = []\nMAX_SUBJECTS = 50  # limit for quick runs, adjust as needed\ncnt = 0\nfor pid in rg_df.index.tolist():\n    case = get_case_paths(\"/kaggle/input/brats2023-full\", pid)\n    if len(case) < 5:\n        continue\n    try:\n        imgs, seg = stack_modalities_brats(case)\n    except Exception as e:\n        print(\"skip\", pid, \"error\", e)\n        continue\n    slices = extract_slices_with_mask(imgs, seg, min_nonzero=200)\n    for im, m in slices:\n        samples.append((pid, im.astype(np.float32), m.astype(np.uint8)))\n    cnt += 1\n    if cnt >= MAX_SUBJECTS:\n        break\n\nprint(\"Total loaded slices:\", len(samples))\n\n# Create tf.data.Dataset from samples\nIMG_SIZE = 128\ndef generator():\n    for pid, im, m in samples:\n        # resize\n        im_r = tf.image.resize(im, (IMG_SIZE, IMG_SIZE)).numpy()\n        m_r  = tf.image.resize(m, (IMG_SIZE, IMG_SIZE), method='nearest').numpy()\n        label = rg_df.loc[pid, \"MGMT_value\"]\n        yield (im_r, label.astype(np.float32)), m_r\n\noutput_types = ((tf.float32, tf.float32), tf.uint8)\noutput_shapes = (((IMG_SIZE, IMG_SIZE, 4), ()), (IMG_SIZE, IMG_SIZE, 1))\nds = tf.data.Dataset.from_generator(generator, output_types=output_types, output_shapes=output_shapes)\nds = ds.shuffle(512).batch(4).prefetch(tf.data.AUTOTUNE)\n\n# ds yields ((image_batch, label_batch), mask_batch)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T08:51:01.268782Z","iopub.execute_input":"2025-11-04T08:51:01.269156Z","iopub.status.idle":"2025-11-04T08:51:01.349786Z","shell.execute_reply.started":"2025-11-04T08:51:01.269123Z","shell.execute_reply":"2025-11-04T08:51:01.349147Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# --- 4) Build multi-task U-Net: segmentation + genomic prediction head ---\nimport tensorflow as tf\nfrom tensorflow.keras import layers, Model, backend as K\n\ndef conv_block(x, filters):\n    x = layers.Conv2D(filters, 3, padding='same', activation='relu')(x)\n    x = layers.Conv2D(filters, 3, padding='same', activation='relu')(x)\n    return x\n\ndef encoder_block(x, filters):\n    c = conv_block(x, filters)\n    p = layers.MaxPooling2D(2)(c)\n    return c, p\n\ndef decoder_block(x, skip, filters):\n    x = layers.UpSampling2D(2)(x)\n    x = layers.Concatenate()([x, skip])\n    x = conv_block(x, filters)\n    return x\n\ndef build_multitask_unet(input_shape=(128,128,4), base_filters=32):\n    img_in = layers.Input(shape=input_shape, name='image_input')\n    # encoder\n    c1, p1 = encoder_block(img_in, base_filters)\n    c2, p2 = encoder_block(p1, base_filters*2)\n    c3, p3 = encoder_block(p2, base_filters*4)\n    c4, p4 = encoder_block(c3 if False else p3, base_filters*8)  # safety if shapes vary\n    bn = conv_block(p4, base_filters*16)\n    # decoder for segmentation\n    d4 = decoder_block(bn, c4, base_filters*8)\n    d3 = decoder_block(d4, c3, base_filters*4)\n    d2 = decoder_block(d3, c2, base_filters*2)\n    d1 = decoder_block(d2, c1, base_filters)\n    seg_out = layers.Conv2D(1, 1, activation='sigmoid', name='seg_output')(d1)\n    # genomic prediction head: global pooling + dense\n    gp = layers.GlobalAveragePooling2D()(bn)\n    g = layers.Dense(128, activation='relu')(gp)\n    g = layers.Dense(64, activation='relu')(g)\n    geno_out = layers.Dense(1, activation='sigmoid', name='geno_output')(g)\n    model = Model(inputs=img_in, outputs=[seg_out, geno_out])\n    return model\n\nmodel = build_multitask_unet(input_shape=(128,128,4))\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T08:51:01.35051Z","iopub.execute_input":"2025-11-04T08:51:01.350786Z","iopub.status.idle":"2025-11-04T08:51:01.574599Z","shell.execute_reply.started":"2025-11-04T08:51:01.350762Z","shell.execute_reply":"2025-11-04T08:51:01.573998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip uninstall keras -y\n!pip install tensorflow==2.15.0\n!pip uninstall -y keras tensorflow keras-core tf-keras\n!pip install tensorflow==2.15.0\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:06:01.070616Z","iopub.execute_input":"2025-11-04T09:06:01.071178Z","iopub.status.idle":"2025-11-04T09:06:30.112042Z","shell.execute_reply.started":"2025-11-04T09:06:01.071153Z","shell.execute_reply":"2025-11-04T09:06:30.111167Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# --- 5) Compile and train multi-task model (TF 2.15–compatible) ---\nimport tensorflow as tf\nfrom tensorflow.keras.losses import BinaryCrossentropy\nfrom tensorflow.keras.optimizers import Adam\n\n# ----- Custom loss functions -----\ndef dice_loss(y_true, y_pred, smooth=1e-6):\n    y_true_f = tf.reshape(y_true, (-1,))\n    y_pred_f = tf.reshape(y_pred, (-1,))\n    intersection = tf.reduce_sum(y_true_f * y_pred_f)\n    return 1 - (2. * intersection + smooth) / (\n        tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) + smooth\n    )\n\ndef seg_loss(y_true, y_pred):\n    bce = BinaryCrossentropy()(y_true, y_pred)\n    return 0.5 * bce + 0.5 * dice_loss(y_true, y_pred)\n\n# ----- Compile model -----\nmodel.compile(\n    optimizer=Adam(1e-4),\n    loss={\n        'seg_output': seg_loss,\n        'geno_output': BinaryCrossentropy(),\n    },\n    loss_weights={'seg_output': 1.0, 'geno_output': 0.5},\n    metrics={'seg_output': [], 'geno_output': ['accuracy']},\n)\n\n# ----- Dataset mapping -----\ndef map_fn(data, mask):\n    img, label = data\n    img = tf.cast(img, tf.float32)\n    mask = tf.cast(mask, tf.float32)\n    label = tf.expand_dims(tf.cast(label, tf.float32), axis=-1)\n    return img, {'seg_output': mask, 'geno_output': label}\n\nds_mapped = ds.map(map_fn)\n\n# ----- Train (quick test run) -----\nmodel.fit(ds_mapped, epochs=3, steps_per_epoch=10)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T09:07:02.871147Z","iopub.execute_input":"2025-11-04T09:07:02.871821Z","iopub.status.idle":"2025-11-04T09:07:02.910746Z","shell.execute_reply.started":"2025-11-04T09:07:02.871795Z","shell.execute_reply":"2025-11-04T09:07:02.909636Z"}},"outputs":[],"execution_count":null}]}