{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"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\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport gc\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport pydicom\n\nfrom tqdm.auto import tqdm\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:12:44.422511Z","iopub.execute_input":"2026-08-26T17:12:44.422746Z","iopub.status.idle":"2026-08-26T17:12:48.459763Z","shell.execute_reply.started":"2026-08-26T17:12:44.422727Z","shell.execute_reply":"2026-08-26T17:12:48.458977Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = Path(\"//kaggle/input/competitions/rsna-knee-abnormality-detection\")\n\nTRAIN_CSV = DATA_DIR / \"train.csv\"\nTRAIN_SERIES_CSV = DATA_DIR / \"train_series.csv\"\nTRAIN_DICOM_DIR = DATA_DIR / \"train_series\"\n\nprint(DATA_DIR)\nprint(TRAIN_CSV.exists())\nprint(TRAIN_SERIES_CSV.exists())\nprint(TRAIN_DICOM_DIR.exists())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:13:32.985865Z","iopub.execute_input":"2026-08-26T17:13:32.986599Z","iopub.status.idle":"2026-08-26T17:13:32.995207Z","shell.execute_reply.started":"2026-08-26T17:13:32.986556Z","shell.execute_reply":"2026-08-26T17:13:32.9944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(TRAIN_CSV)\nseries_df = pd.read_csv(TRAIN_SERIES_CSV)\n\nprint(\"train.csv shape:\", train_df.shape)\nprint(\"train_series.csv shape:\", series_df.shape)\n\ndisplay(train_df.head())\ndisplay(series_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:13:34.722783Z","iopub.execute_input":"2026-08-26T17:13:34.723064Z","iopub.status.idle":"2026-08-26T17:13:34.967896Z","shell.execute_reply.started":"2026-08-26T17:13:34.723041Z","shell.execute_reply":"2026-08-26T17:13:34.967275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TARGETS = [\n    \"ACL\",\n    \"MCL\",\n    \"Medial Meniscus\",\n    \"Lateral Meniscus\",\n    \"Medial OA\",\n    \"Lateral OA\",\n    \"PF OA\",\n    \"Effusion\",\n    \"Synovitis\",\n    \"Baker's\",\n    \"Contusion\",\n    \"Fracture\"\n]\n\nprint(train_df[TARGETS].head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:13:58.999927Z","iopub.execute_input":"2026-08-26T17:13:59.000256Z","iopub.status.idle":"2026-08-26T17:13:59.011508Z","shell.execute_reply.started":"2026-08-26T17:13:59.000231Z","shell.execute_reply":"2026-08-26T17:13:59.010586Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labelled_df = train_df[\n    train_df[TARGETS].notna().all(axis=1)\n].copy()\n\nprint(\"Total training studies:\", len(train_df))\nprint(\"Labelled studies:\", len(labelled_df))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:14:09.332541Z","iopub.execute_input":"2026-08-26T17:14:09.332802Z","iopub.status.idle":"2026-08-26T17:14:09.340542Z","shell.execute_reply.started":"2026-08-26T17:14:09.332779Z","shell.execute_reply":"2026-08-26T17:14:09.339647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(series_df[\"Anatomical_Plane\"].value_counts(dropna=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:14:18.08558Z","iopub.execute_input":"2026-08-26T17:14:18.085825Z","iopub.status.idle":"2026-08-26T17:14:18.09306Z","shell.execute_reply.started":"2026-08-26T17:14:18.085804Z","shell.execute_reply":"2026-08-26T17:14:18.092194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\n    series_df[\n        [\"Fluid_Sensitive\", \"Fat_Suppression\"]\n    ].value_counts()\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:14:24.281091Z","iopub.execute_input":"2026-08-26T17:14:24.281369Z","iopub.status.idle":"2026-08-26T17:14:24.29087Z","shell.execute_reply.started":"2026-08-26T17:14:24.281348Z","shell.execute_reply":"2026-08-26T17:14:24.29006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(\n    series_df[\n        [\n            \"StudyInstanceUID\",\n            \"SeriesInstanceUID\",\n            \"Fluid_Sensitive\",\n            \"Fat_Suppression\",\n            \"Anatomical_Plane\"\n        ]\n    ].head(20)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:14:29.553299Z","iopub.execute_input":"2026-08-26T17:14:29.553608Z","iopub.status.idle":"2026-08-26T17:14:29.566181Z","shell.execute_reply.started":"2026-08-26T17:14:29.553581Z","shell.execute_reply":"2026-08-26T17:14:29.565407Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_dicom(path):\n    \n    dcm = pydicom.dcmread(path)\n    \n    image = dcm.pixel_array.astype(np.float32)\n    \n    # Apply rescale if present\n    slope = float(\n        getattr(dcm, \"RescaleSlope\", 1.0)\n    )\n    \n    intercept = float(\n        getattr(dcm, \"RescaleIntercept\", 0.0)\n    )\n    \n    image = image * slope + intercept\n    \n    return image, dcm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:15:28.76521Z","iopub.execute_input":"2026-08-26T17:15:28.765488Z","iopub.status.idle":"2026-08-26T17:15:28.77039Z","shell.execute_reply.started":"2026-08-26T17:15:28.765446Z","shell.execute_reply":"2026-08-26T17:15:28.769609Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def normalize_mri(image):\n    \n    image = image.astype(np.float32)\n    \n    low = np.percentile(image, 1)\n    high = np.percentile(image, 99)\n    \n    image = np.clip(\n        image,\n        low,\n        high\n    )\n    \n    image -= image.min()\n    \n    if image.max() > 0:\n        image /= image.max()\n    \n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:15:39.393298Z","iopub.execute_input":"2026-08-26T17:15:39.393549Z","iopub.status.idle":"2026-08-26T17:15:39.398167Z","shell.execute_reply.started":"2026-08-26T17:15:39.39353Z","shell.execute_reply":"2026-08-26T17:15:39.397593Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_series_files(study_id, series_id):\n    \n    series_path = (\n        TRAIN_DICOM_DIR\n        / str(study_id)\n        / str(series_id)\n    )\n    \n    return list(series_path.glob(\"*.dcm\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:15:50.715447Z","iopub.execute_input":"2026-08-26T17:15:50.715708Z","iopub.status.idle":"2026-08-26T17:15:50.719913Z","shell.execute_reply.started":"2026-08-26T17:15:50.715687Z","shell.execute_reply":"2026-08-26T17:15:50.719262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def sort_dicom_files(files):\n    \n    dicom_info = []\n    \n    for file in files:\n        \n        try:\n            dcm = pydicom.dcmread(\n                file,\n                stop_before_pixels=True\n            )\n            \n            if hasattr(\n                dcm,\n                \"ImagePositionPatient\"\n            ):\n                \n                position = np.asarray(\n                    dcm.ImagePositionPatient,\n                    dtype=np.float32\n                )\n                \n                dicom_info.append(\n                    (file, position)\n                )\n        \n        except Exception:\n            continue\n    \n    # Spatial sorting\n    if len(dicom_info) == len(files):\n        \n        dicom_info.sort(\n            key=lambda x: x[1][2]\n        )\n        \n        return [\n            item[0]\n            for item in dicom_info\n        ]\n    \n    # Fallback\n    return sorted(files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:15:59.021757Z","iopub.execute_input":"2026-08-26T17:15:59.022513Z","iopub.status.idle":"2026-08-26T17:15:59.027664Z","shell.execute_reply.started":"2026-08-26T17:15:59.02249Z","shell.execute_reply":"2026-08-26T17:15:59.026931Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NUM_SLICES = 32\n\ndef select_slice_files(files, num_slices=NUM_SLICES):\n    \n    if len(files) == 0:\n        return []\n    \n    if len(files) == num_slices:\n        return files\n    \n    indices = np.linspace(\n        0,\n        len(files) - 1,\n        num_slices\n    ).astype(int)\n    \n    return [\n        files[i]\n        for i in indices\n    ]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:16:06.960906Z","iopub.execute_input":"2026-08-26T17:16:06.961192Z","iopub.status.idle":"2026-08-26T17:16:06.96619Z","shell.execute_reply.started":"2026-08-26T17:16:06.961166Z","shell.execute_reply":"2026-08-26T17:16:06.965332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_series(\n    study_id,\n    series_id,\n    num_slices=NUM_SLICES\n):\n    \n    files = get_series_files(\n        study_id,\n        series_id\n    )\n    \n    if len(files) == 0:\n        return None\n    \n    files = sort_dicom_files(files)\n    \n    files = select_slice_files(\n        files,\n        num_slices\n    )\n    \n    volume = []\n    \n    for file in files:\n        \n        image, _ = load_dicom(file)\n        \n        image = normalize_mri(image)\n        \n        image = resize_mri(image)\n        \n        volume.append(image)\n    \n    volume = np.stack(\n        volume,\n        axis=0\n    )\n    \n    return volume.astype(np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:16:24.488643Z","iopub.execute_input":"2026-08-26T17:16:24.488897Z","iopub.status.idle":"2026-08-26T17:16:24.494056Z","shell.execute_reply.started":"2026-08-26T17:16:24.488877Z","shell.execute_reply":"2026-08-26T17:16:24.493262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def select_series(study_id):\n    \n    study_series = series_df[\n        series_df[\"StudyInstanceUID\"] == study_id\n    ].copy()\n    \n    selected = []\n    \n    for plane in [\"Sagittal\", \"Coronal\", \"Axial\"]:\n        \n        plane_series = study_series[\n            study_series[\"Anatomical_Plane\"] == plane\n        ]\n        \n        if len(plane_series) == 0:\n            continue\n        \n        # Priority 1\n        preferred = plane_series[\n            (plane_series[\"Fluid_Sensitive\"] == 1) &\n            (plane_series[\"Fat_Suppression\"] == 1)\n        ]\n        \n        if len(preferred) > 0:\n            selected.append(preferred.iloc[0])\n            continue\n        \n        # Priority 2\n        preferred = plane_series[\n            plane_series[\"Fluid_Sensitive\"] == 1\n        ]\n        \n        if len(preferred) > 0:\n            selected.append(preferred.iloc[0])\n            continue\n        \n        # Priority 3\n        selected.append(plane_series.iloc[0])\n    \n    return pd.DataFrame(selected)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:17:56.629022Z","iopub.execute_input":"2026-08-26T17:17:56.62994Z","iopub.status.idle":"2026-08-26T17:17:56.634862Z","shell.execute_reply.started":"2026-08-26T17:17:56.629901Z","shell.execute_reply":"2026-08-26T17:17:56.634184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = 224\n\ndef resize_mri(image):\n    \n    image = cv2.resize(\n        image,\n        (IMG_SIZE, IMG_SIZE),\n        interpolation=cv2.INTER_AREA\n    )\n    \n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:19:56.185386Z","iopub.execute_input":"2026-08-26T17:19:56.185626Z","iopub.status.idle":"2026-08-26T17:19:56.189225Z","shell.execute_reply.started":"2026-08-26T17:19:56.185607Z","shell.execute_reply":"2026-08-26T17:19:56.188468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def select_series(study_id):\n    \n    study_series = series_df[\n        series_df[\"StudyInstanceUID\"] == study_id\n    ].copy()\n    \n    selected = []\n    \n    for plane in [\"Sagittal\", \"Coronal\", \"Axial\"]:\n        \n        plane_series = study_series[\n            study_series[\"Anatomical_Plane\"] == plane\n        ]\n        \n        if len(plane_series) == 0:\n            continue\n        \n        # Priority 1: Fluid-sensitive + Fat-suppressed\n        preferred = plane_series[\n            (plane_series[\"Fluid_Sensitive\"] == 1) &\n            (plane_series[\"Fat_Suppression\"] == 1)\n        ]\n        \n        if len(preferred) > 0:\n            selected.append(preferred.iloc[0])\n            continue\n        \n        # Priority 2: Fluid-sensitive\n        preferred = plane_series[\n            plane_series[\"Fluid_Sensitive\"] == 1\n        ]\n        \n        if len(preferred) > 0:\n            selected.append(preferred.iloc[0])\n            continue\n        \n        # Priority 3: Any available series\n        selected.append(plane_series.iloc[0])\n    \n    return pd.DataFrame(selected)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:19:56.382687Z","iopub.execute_input":"2026-08-26T17:19:56.38293Z","iopub.status.idle":"2026-08-26T17:19:56.388059Z","shell.execute_reply.started":"2026-08-26T17:19:56.382911Z","shell.execute_reply":"2026-08-26T17:19:56.387564Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_study(study_id):\n    \n    selected = select_series(study_id)\n    \n    study_data = {}\n    \n    for _, row in selected.iterrows():\n        \n        series_id = row[\"SeriesInstanceUID\"]\n        plane = row[\"Anatomical_Plane\"]\n        \n        volume = preprocess_series(\n            study_id,\n            series_id\n        )\n        \n        if volume is None:\n            continue\n        \n        study_data[plane] = volume\n    \n    return study_data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:19:56.606507Z","iopub.execute_input":"2026-08-26T17:19:56.606763Z","iopub.status.idle":"2026-08-26T17:19:56.61147Z","shell.execute_reply.started":"2026-08-26T17:19:56.606741Z","shell.execute_reply":"2026-08-26T17:19:56.610831Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_study = labelled_df.iloc[0][\"StudyInstanceUID\"]\n\nprint(\"Study ID:\")\nprint(test_study)\n\ntest_data = preprocess_study(test_study)\n\nfor plane, volume in test_data.items():\n    \n    print(\n        plane,\n        \"Shape:\", volume.shape,\n        \"Dtype:\", volume.dtype,\n        \"Min:\", volume.min(),\n        \"Max:\", volume.max()\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:19:56.824412Z","iopub.execute_input":"2026-08-26T17:19:56.82465Z","iopub.status.idle":"2026-08-26T17:19:57.638581Z","shell.execute_reply.started":"2026-08-26T17:19:56.824632Z","shell.execute_reply":"2026-08-26T17:19:57.637935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplane = list(test_data.keys())[0]\nvolume = test_data[plane]\n\nindices = [0, 8, 16, 24, 31]\n\nplt.figure(figsize=(15, 3))\n\nfor i, idx in enumerate(indices):\n    \n    plt.subplot(1, 5, i + 1)\n    \n    plt.imshow(\n        volume[idx],\n        cmap=\"gray\"\n    )\n    \n    plt.title(f\"{plane} - Slice {idx}\")\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-26T17:20:11.677031Z","iopub.execute_input":"2026-08-26T17:20:11.677343Z","iopub.status.idle":"2026-08-26T17:20:12.110841Z","shell.execute_reply.started":"2026-08-26T17:20:11.67732Z","shell.execute_reply":"2026-08-26T17:20:12.109989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}