{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":36363,"databundleVersionId":4050810},{"sourceType":"datasetVersion","sourceId":1421668,"datasetId":832340,"databundleVersionId":1454967},{"sourceType":"kernelVersion","sourceId":63372526},{"sourceType":"kernelVersion","sourceId":103711756},{"sourceType":"kernelVersion","sourceId":103714622}],"dockerImageVersionId":30213,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Baseline 3D classification\n\n#### 📔 [Data preparation notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-dicom-to-numpy-3d)\n#### 📔 [Training notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-train)\n#### 📔 [Inference notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-inference)","metadata":{}},{"cell_type":"code","source":"#install pydicom requirements\n\n%load_ext autoreload\n%autoreload 2\n!conda install '/kaggle/input/pydicom-conda-helper/libjpeg-turbo-2.1.0-h7f98852_0.tar.bz2' --offline -y\n!conda install '/kaggle/input/pydicom-conda-helper/libgcc-ng-9.3.0-h2828fa1_19.tar.bz2' --offline -y\n#!conda install '/kaggle/input/pydicom-conda-helper/gdcm-2.8.9-py37h500ead1_1.Truetar.bz2' --offline -y \n!cp ../input/gdcm-conda-install/gdcm.tar .\n!tar -xvzf gdcm.tar\n!conda install --offline ./gdcm/gdcm-2.8.9-py37h71b2a6d_0.tar.bz2\n!conda install '/kaggle/input/pydicom-conda-helper/conda-4.10.1-py37h89c1867_0.tar.bz2' --offline -y\n!conda install '/kaggle/input/pydicom-conda-helper/certifi-2020.12.5-py37h89c1867_1.tar.bz2' --offline -y\n!conda install '/kaggle/input/pydicom-conda-helper/openssl-1.1.1k-h7f98852_0.tar.bz2' --offline -y\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2026-06-12T16:51:23.434176Z","iopub.execute_input":"2026-06-12T16:51:23.434594Z","iopub.status.idle":"2026-06-12T16:52:15.854048Z","shell.execute_reply.started":"2026-06-12T16:51:23.434569Z","shell.execute_reply":"2026-06-12T16:52:15.852922Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install pylibjpeg pylibjpeg-libjpeg","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:52:15.85629Z","iopub.execute_input":"2026-06-12T16:52:15.857194Z","iopub.status.idle":"2026-06-12T16:52:22.504047Z","shell.execute_reply.started":"2026-06-12T16:52:15.857141Z","shell.execute_reply":"2026-06-12T16:52:22.502947Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# RSNA 2022 Cervical Spine Fracture Detection\n# Baseline 3D Classification\n# Refs:\n#   Data prep:  https://www.kaggle.com/code/vmuzhichenko/rsna-22-dicom-to-numpy-3d\n#   Training:   https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-train\n#   Inference:  https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-inference\n# ============================================================\n\n# ── Cell 1: Install pydicom / GDCM (Kaggle-specific, comment out locally) ──────\n# %load_ext autoreload\n# %autoreload 2\n# !conda install '/kaggle/input/pydicom-conda-helper/libjpeg-turbo-2.1.0-h7f98852_0.tar.bz2' --offline -y\n# !conda install '/kaggle/input/pydicom-conda-helper/libgcc-ng-9.3.0-h2828fa1_19.tar.bz2' --offline -y\n# !cp ../input/gdcm-conda-install/gdcm.tar .\n# !tar -xvzf gdcm.tar\n# !conda install --offline ./gdcm/gdcm-2.8.9-py37h71b2a6d_0.tar.bz2\n# !conda install '/kaggle/input/pydicom-conda-helper/conda-4.10.1-py37h89c1867_0.tar.bz2' --offline -y\n# !conda install '/kaggle/input/pydicom-conda-helper/certifi-2020.12.5-py37h89c1867_1.tar.bz2' --offline -y\n# !conda install '/kaggle/input/pydicom-conda-helper/openssl-1.1.1k-h7f98852_0.tar.bz2' --offline -y\n\n# ── Cell 2: Imports ────────────────────────────────────────────────────────────\nimport os\nimport glob\nimport random\nimport collections\nimport gc\nimport math\n\nimport numpy as np\nimport pandas as pd\n\nfrom joblib import Parallel, delayed\nfrom tqdm.notebook import tqdm\n\nimport pydicom\nimport matplotlib.pyplot as plt\nimport cv2\nimport scipy\nimport tensorflow as tf\nimport tensorflow_addons as tfa\nfrom tensorflow.keras import backend as K\nfrom tensorflow import keras\nfrom tensorflow.keras import layers as L\n\nfrom sklearn.model_selection import KFold, StratifiedKFold\n\n# ── Cell 3: Config & Paths ─────────────────────────────────────────────────────\n# Set desired image size and depth (number of slices to load per patient)\nclass Config:\n    img_size = 256\n    depth = 128\n    train_one_fold = True\n\n\nIMG_PATH_TRAIN = '../input/rsna-2022-cervical-spine-fracture-detection/train_images/'\nIMG_PATH_TEST  = '../input/rsna-2022-cervical-spine-fracture-detection/test_images/'\nTRAIN_CSV_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/train.csv'\nTEST_CSV_PATH  = '../input/rsna-2022-cervical-spine-fracture-detection/test.csv'\n\ntrain_images = os.listdir(IMG_PATH_TRAIN)\ntest_images  = os.listdir(IMG_PATH_TEST)\n\ntrain = pd.read_csv(TRAIN_CSV_PATH)\ntest  = pd.read_csv(TEST_CSV_PATH)\n\n# ── Cell 4: DICOM loaders ──────────────────────────────────────────────────────\ndef load_dicom(path):\n    dicom = pydicom.read_file(path)\n    data  = dicom.pixel_array\n    data  = data - np.min(data)\n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    data = cv2.resize(data, (Config.img_size, Config.img_size),\n                      interpolation=cv2.INTER_AREA)\n    return data\n\n\ndef load_dicom_line_par(path, indices: list = None):\n    t_paths = sorted(\n        glob.glob(os.path.join(path, \"*\")),\n        key=lambda x: int(x.split('/')[-1].split(\".\")[0])\n    )\n    if indices is not None:\n        t_paths = [t_paths[i] for i in indices]\n    images = Parallel(n_jobs=-1)(\n        delayed(load_dicom)(filename) for filename in t_paths\n    )\n    return np.array(images)\n\n# ── Cell 5: Convert test DICOMs to .npy voxels ────────────────────────────────\ntrain_output_path = './train_arrays/'\ntest_output_path  = './test_arrays/'\n\nif not os.path.exists(train_output_path): os.mkdir(train_output_path)\nif not os.path.exists(test_output_path):  os.mkdir(test_output_path)\n\ntest_patients = sorted(os.listdir(IMG_PATH_TEST))\n\n\ndef save_3d_voxels(dicom_path, output_path):\n    n_scans = len(os.listdir(dicom_path))\n    # Sample evenly spaced slices between 10th and 90th percentile\n    ind   = np.quantile(list(range(n_scans)),\n                        np.linspace(0.1, 0.9, Config.depth)).astype(int)\n    image = load_dicom_line_par(dicom_path, indices=ind)\n    if image.ndim < 4:\n        image = np.expand_dims(image, -1)\n    np.save(f\"{output_path}{dicom_path.split('/')[-1]}.npy\", image)\n    del image\n    return None\n\n\nfor i in tqdm(range(len(test_patients))):\n    case = IMG_PATH_TEST + test_patients[i]\n    save_3d_voxels(case, test_output_path)\n\ngc.collect()\n\n# ── Cell 6: Build test DataFrame ──────────────────────────────────────────────\ntest = pd.DataFrame({'StudyInstanceUID': test_patients})\ntest['StudyInstanceUID'] = test_patients\ntest['numpy_path'] = test['StudyInstanceUID'].apply(\n    lambda x: f'{test_output_path}{x}.npy'\n)\nprint(test)\n\n# ── Cell 7: Data generator ────────────────────────────────────────────────────\nclass SampleGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df: pd.DataFrame, batch_size,\n                 resample_rate: float = None,\n                 steps_per_epoch: int = 10000,\n                 is_train=True, shuffle=True):\n        self.is_train        = is_train\n        self.numpy_path      = df.numpy_path\n        self.df              = df\n        self.batch_size      = batch_size\n        self.length          = len(df)\n        self.resample        = resample_rate\n        self.shuffle         = shuffle\n        self.steps_per_epoch = steps_per_epoch\n\n    def __len__(self):\n        return min(int(np.ceil(self.length / float(self.batch_size))),\n                   self.steps_per_epoch)\n\n    def on_epoch_end(self):\n        if self.shuffle:\n            self.df         = self.df.sample(frac=1).reset_index(drop=True)\n            self.numpy_path = self.df.numpy_path\n\n    def __getitem__(self, index):\n        if self.is_train:\n            batch_x = []\n            batch_y = []\n            targets = self.df[['patient_overall',\n                                'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']]\n            for i in range(self.batch_size):\n                cur_ind = self.batch_size * index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n                    batch_y.append(targets.iloc[cur_ind])\n            if self.resample is not None:\n                n_images = batch_x[0].shape[0]\n                im_ids   = sorted(np.random.choice(\n                    list(range(n_images)),\n                    int(n_images * self.resample), replace=False\n                ))\n                batch_x = np.array(batch_x)[:, im_ids]\n            return np.array(batch_x), np.array(batch_y).astype(np.float32)\n        else:\n            batch_x = []\n            for i in range(self.batch_size):\n                cur_ind = self.batch_size * index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n            return np.array(batch_x)\n\n# ── Cell 8: Competition loss ───────────────────────────────────────────────────\n# https://www.kaggle.com/competitions/rsna-2022-cervical-spine-fracture-detection/discussion/341854#1884562\ndef competiton_loss(y_true, y_pred):\n    competition_weights = {\n        '-': tf.constant([7,  1, 1, 1, 1, 1, 1, 1], dtype=tf.float32),\n        '+': tf.constant([14, 2, 2, 2, 2, 2, 2, 2], dtype=tf.float32),\n    }\n    loss    = tf.keras.losses.BinaryCrossentropy(\n        reduction=tf.keras.losses.Reduction.NONE\n    )(tf.expand_dims(y_true, -1), tf.expand_dims(y_pred, -1))\n    weights = (y_true * competition_weights['+']\n               + (1 - y_true) * competition_weights['-'])\n    loss    = tf.reduce_mean(tf.reduce_sum(loss * weights, axis=1)) \\\n              / tf.reduce_sum(weights)\n    return loss\n\n# ── Cell 9: Load pre-trained model ────────────────────────────────────────────\nmodel = tf.keras.models.load_model(\n    '../input/rsna-22-resnet-50-3d-train/resnet50_end_fold_1/',\n    custom_objects={'competiton_loss': competiton_loss}\n)\n\n# ── Cell 10: Build test generator ─────────────────────────────────────────────\ndata_test = SampleGenerator(test, 4, shuffle=False, is_train=False)\n\n# ── Cell 11: Run inference ────────────────────────────────────────────────────\npred = model.predict(data_test)\n\n# ── Cell 12: Post-process predictions ─────────────────────────────────────────\ndef proccess_test(df, preds):\n    cols     = ['patient_overall', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']\n    patients = df.StudyInstanceUID.to_list()\n    df_sub   = pd.DataFrame()\n    for i, p in enumerate(patients):\n        scores = list(preds[i])\n        if len(scores) < 8:\n            scores.append(preds[i].max() + preds[i].mean())\n        df_temp = pd.DataFrame({\n            'StudyInstanceUID': [p] * len(cols),\n            'prediction_type':  cols,\n            'fractured':        scores,\n        })\n        df_sub = pd.concat([df_sub, df_temp])\n        del df_temp\n    df_sub['row_id'] = (df_sub['StudyInstanceUID'] + '_'\n                        + df_sub['prediction_type'])\n    return df_sub[['row_id', 'fractured']].reset_index(drop=True)\n\n# ── Cell 13–14: Save submission ───────────────────────────────────────────────\ndf_sub = proccess_test(test, pred)\ndf_sub.to_csv('submission.csv', index=False)\nprint(df_sub)\n\n# ── Cell 15: List kaggle input datasets ───────────────────────────────────────\nprint(os.listdir('/kaggle/input'))\n\n# ── Cell 16: Peek at one DICOM path (intentional early exit — exploration only)\n# NOTE: raise SystemExit is deliberate here; it's just a quick path check.\n# Remove or comment out if you don't want the script to stop here.\nfor root, dirs, files in os.walk(\n        '/kaggle/input/rsna-2022-cervical-spine-fracture-detection'):\n    for f in files:\n        if f.endswith('.dcm'):\n            print(os.path.join(root, f))\n            break   # FIX: replaced `raise SystemExit` with `break`\n    else:\n        continue\n    break\n\n# ── Cell 17: Inspect model layer names ────────────────────────────────────────\nfor layer in model.layers:\n    print(layer.name, layer.output_shape)\n\n# ── Cell 18: Feature map extraction ───────────────────────────────────────────\nfrom tensorflow.keras.models import Model\n\n# TODO: run Cell 17 first and replace 'conv3d_1' with an actual layer name\n# printed above (e.g. 'conv3d', 'conv3d_2', etc.)\nlayer_name = 'conv3d_1'\n\nfeature_extractor = Model(\n    inputs=model.input,\n    outputs=model.get_layer(layer_name).output\n)\n\n# FIX: `tensor` was undefined. Load one test sample and add the batch dimension.\nsample = np.load(test['numpy_path'].iloc[0])          # (128, 256, 256, 1)\ntensor = np.expand_dims(sample, axis=0)               # (1, 128, 256, 256, 1)\n\nfeature_maps = feature_extractor.predict(tensor)\nprint(\"feature_maps shape:\", feature_maps.shape)\n\n# ── Cell 19: Inspect feature maps ─────────────────────────────────────────────\nfm = feature_maps[0]\nprint(\"fm shape:\", fm.shape)\n\n# ── Cell 20: Average across depth + channels ──────────────────────────────────\nfeature_map = fm.mean(axis=0).mean(axis=-1)\nprint(\"2D feature map shape:\", feature_map.shape)\n\n# ── Cell 21: Visualise feature map ────────────────────────────────────────────\nsmall_map = cv2.resize(feature_map, (32, 32))\n\nplt.figure(figsize=(8, 8))\nplt.imshow(small_map, cmap='inferno', interpolation='nearest')\nplt.title(\"3D ResNet Feature Map\")\nplt.axis('off')\nplt.show()\n\n# ── Cell 22: OpenCV AI-style enhancement ──────────────────────────────────────\n# NOTE: `preprocessed` must be a 2D grayscale slice (H, W) uint8.\n# Example: take the middle slice of the first test volume.\n# preprocessed = np.load(test['numpy_path'].iloc[0])[64, :, :, 0]  # shape (256,256)\n\n# Uncomment and adapt once `preprocessed` is defined:\n# enhanced = (preprocessed * 255).astype(np.uint8)\n# enhanced = cv2.equalizeHist(enhanced)\n# kernel   = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])\n# enhanced = cv2.filter2D(enhanced, -1, kernel)\n\n# ── Cell 23: Original vs AI-Enhanced plot ─────────────────────────────────────\n# NOTE: `original_norm` and `enhanced` must be defined (see Cell 22 above).\n# Uncomment once those variables exist:\n#\n# fig, ax = plt.subplots(1, 2, figsize=(14, 6))\n# ax[0].imshow(original_norm, cmap='gray')\n# ax[0].set_title(\"Original DICOM\")\n# ax[0].axis('off')\n# ax[1].imshow(enhanced, cmap='inferno')\n# ax[1].set_title(\"AI-Enhanced Output\")\n# ax[1].axis('off')\n# plt.tight_layout()\n# plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:52:22.505663Z","iopub.execute_input":"2026-06-12T16:52:22.505947Z","iopub.status.idle":"2026-06-12T16:52:47.19525Z","shell.execute_reply.started":"2026-06-12T16:52:22.505921Z","shell.execute_reply":"2026-06-12T16:52:47.194197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ── CV Enhancement + DICOM Visualisation ──────────────────────────────────────\n# Pull middle slice from the first test volume\nvolume = np.load(test['numpy_path'].iloc[0])   # (128, 256, 256, 1)\npreprocessed = volume[64, :, :, 0]             # (256, 256) float, 0–255 range\n\noriginal_norm = preprocessed.astype(np.uint8)\n\n# CV enhancements\nenhanced = cv2.equalizeHist(original_norm)\nkernel   = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])\nenhanced = cv2.filter2D(enhanced, -1, kernel)\n\n# Plot\nfig, ax = plt.subplots(1, 2, figsize=(14, 6))\nax[0].imshow(original_norm, cmap='gray')\nax[0].set_title(\"Original DICOM\")\nax[0].axis('off')\nax[1].imshow(enhanced, cmap='inferno')\nax[1].set_title(\"CV Enhanced\")\nax[1].axis('off')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:52:47.197613Z","iopub.execute_input":"2026-06-12T16:52:47.197887Z","iopub.status.idle":"2026-06-12T16:52:47.495402Z","shell.execute_reply.started":"2026-06-12T16:52:47.197864Z","shell.execute_reply":"2026-06-12T16:52:47.494063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport glob\nimport random\nimport collections\nimport gc\nimport math\n\nimport numpy as np\nimport pandas as pd\n\nfrom joblib import Parallel, delayed\nfrom tqdm.notebook import tqdm\n\nimport pydicom\nimport matplotlib.pyplot as plt\nimport cv2\nimport scipy\nimport tensorflow as tf\nimport tensorflow_addons as tfa\nfrom tensorflow.keras import backend as K\nfrom tensorflow import keras\nfrom tensorflow.keras import layers as L\n\nfrom sklearn.model_selection import KFold, StratifiedKFold\n\n","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:47.496557Z","iopub.execute_input":"2026-06-12T16:52:47.496839Z","iopub.status.idle":"2026-06-12T16:52:47.552221Z","shell.execute_reply.started":"2026-06-12T16:52:47.496817Z","shell.execute_reply":"2026-06-12T16:52:47.550921Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#set desired image size and depth (number of patient's images to load)\nclass Config:\n    img_size = 256\n    depth = 128\n    train_one_fold = True\n\n\n\nIMG_PATH_TRAIN = '../input/rsna-2022-cervical-spine-fracture-detection/train_images/'\nIMG_PATH_TEST = '../input/rsna-2022-cervical-spine-fracture-detection/test_images/'\nTRAIN_CSV_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/train.csv'\nTEST_CSV_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/test.csv'\n\ntrain_images = os.listdir(IMG_PATH_TRAIN)\ntest_images = os.listdir(IMG_PATH_TEST)\n\ntrain=pd.read_csv(TRAIN_CSV_PATH)\ntest=pd.read_csv(TEST_CSV_PATH)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:47.553709Z","iopub.execute_input":"2026-06-12T16:52:47.554114Z","iopub.status.idle":"2026-06-12T16:52:47.620722Z","shell.execute_reply.started":"2026-06-12T16:52:47.554059Z","shell.execute_reply":"2026-06-12T16:52:47.619238Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_dicom(path):\n    dicom = pydicom.read_file(path)\n    data = dicom.pixel_array\n    data = data - np.min(data)\n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    data = cv2.resize(data, (Config.img_size,Config.img_size), interpolation = cv2.INTER_AREA)\n    return data\n     \n\ndef load_dicom_line_par(path, indices:list = None):\n    t_paths = sorted(glob.glob(os.path.join(path, \"*\")),\n       key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n    \n    if indices is not None:\n        t_paths = [t_paths[i] for i in indices]\n        \n    images = Parallel(n_jobs=-1)(delayed(load_dicom)(filename) for filename in t_paths)\n    \n    return np.array(images)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:47.623257Z","iopub.execute_input":"2026-06-12T16:52:47.623725Z","iopub.status.idle":"2026-06-12T16:52:47.681568Z","shell.execute_reply.started":"2026-06-12T16:52:47.623682Z","shell.execute_reply":"2026-06-12T16:52:47.680778Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_output_path = './train_arrays/'\ntest_output_path = './test_arrays/'\n\nif not os.path.exists(train_output_path): os.mkdir(train_output_path)\nif not os.path.exists(test_output_path): os.mkdir(test_output_path)    \n    \ntest_patients = sorted(os.listdir(IMG_PATH_TEST))\n\ndef save_3d_voxels(dicom_path, output_path):\n    \n    n_scans=len(os.listdir(dicom_path))\n    \n    #instead of zooming whole dicom series, load only part of the images\n    ind = np.quantile(list(range(n_scans)), np.linspace(0.1, 0.9, Config.depth)).astype(int)\n    image = load_dicom_line_par(dicom_path, indices = ind)\n    \n    if image.ndim <4:\n        image = np.expand_dims(image, -1)\n    \n    np.save(f\"{output_path}{dicom_path.split('/')[-1]}.npy\", image)\n    \n    del image\n    return None\n \n\nfor i in tqdm(range(len(test_patients))):\n    case = IMG_PATH_TEST + test_patients[i]\n    save_3d_voxels(case, test_output_path)\n    \ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:47.682577Z","iopub.execute_input":"2026-06-12T16:52:47.682807Z","iopub.status.idle":"2026-06-12T16:52:51.092261Z","shell.execute_reply.started":"2026-06-12T16:52:47.682787Z","shell.execute_reply":"2026-06-12T16:52:51.091259Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = pd.DataFrame({'StudyInstanceUID': test_patients})\n\ntest['StudyInstanceUID'] = test_patients\ntest['numpy_path'] = test['StudyInstanceUID'].apply(lambda x: f'{test_output_path}{x}.npy')\ntest","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:51.093222Z","iopub.execute_input":"2026-06-12T16:52:51.093459Z","iopub.status.idle":"2026-06-12T16:52:51.154341Z","shell.execute_reply.started":"2026-06-12T16:52:51.093437Z","shell.execute_reply":"2026-06-12T16:52:51.153287Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SampleGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df: pd.DataFrame, batch_size, resample_rate: float = None, steps_per_epoch: int = 10000, is_train=True, shuffle=True):\n        self.is_train      = is_train\n        self.numpy_path  = df.numpy_path\n        self.df  = df\n        self.batch_size = batch_size\n        self.length = len(df)\n        self.resample = resample_rate\n        self.shuffle = shuffle\n        self.steps_per_epoch= steps_per_epoch\n        \n    def __len__(self):\n        return  min(int(np.ceil(self.length / float(self.batch_size))), self.steps_per_epoch)\n    \n    def on_epoch_end(self):\n        if self.shuffle:\n            self.df = self.df.sample(frac=1).reset_index(drop=True)\n            self.numpy_path  = self.df.numpy_path\n    \n    def __getitem__(self, index):\n                  \n        if self.is_train:         \n            \n            batch_x = []\n            batch_y = []\n            \n            targets = self.df[['patient_overall', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']]\n            \n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n                    batch_y.append(targets.iloc[cur_ind])\n              \n            if self.resample is not None:\n                n_images = batch_x[0].shape[0]\n                im_ids = sorted(np.random.choice(list(range(n_images)), int(n_images * self.resample), replace=False))\n                batch_x = np.array(batch_x)[:,im_ids]\n                   \n            #return np.array(batch_x), np.expand_dims(np.array(batch_y), -1).astype(np.float32)\n            return np.array(batch_x), np.array(batch_y).astype(np.float32)\n\n        else:\n            batch_x = []\n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n            \n            return np.array(batch_x)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:51.157728Z","iopub.execute_input":"2026-06-12T16:52:51.15803Z","iopub.status.idle":"2026-06-12T16:52:51.217561Z","shell.execute_reply.started":"2026-06-12T16:52:51.157999Z","shell.execute_reply":"2026-06-12T16:52:51.216429Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n#https://www.kaggle.com/competitions/rsna-2022-cervical-spine-fracture-detection/discussion/341854#1884562\ndef competiton_loss(y_true, y_pred):\n\n    competition_weights = {\n        '-' : tf.constant([7, 1, 1, 1, 1, 1, 1, 1], dtype=tf.float32),\n        '+' : tf.constant([14, 2, 2, 2, 2, 2, 2, 2], dtype=tf.float32)\n    }\n    \n    loss = tf.keras.losses.BinaryCrossentropy(reduction=tf.keras.losses.Reduction.NONE)(tf.expand_dims(y_true, -1),tf.expand_dims(y_pred,-1))\n    weights  = y_true*competition_weights['+'] + (1-y_true)*competition_weights['-'] \n    \n    loss = tf.reduce_mean(tf.reduce_sum(loss * weights, axis=1)) / tf.reduce_sum(weights)\n    return loss\n    \n    \n\n","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:51.218741Z","iopub.execute_input":"2026-06-12T16:52:51.219042Z","iopub.status.idle":"2026-06-12T16:52:51.273764Z","shell.execute_reply.started":"2026-06-12T16:52:51.219016Z","shell.execute_reply":"2026-06-12T16:52:51.272609Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = tf.keras.models.load_model('../input/rsna-22-resnet-50-3d-train/resnet50_end_fold_1/', custom_objects = {'competiton_loss': competiton_loss})","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:51.274976Z","iopub.execute_input":"2026-06-12T16:52:51.275257Z","iopub.status.idle":"2026-06-12T16:52:59.459187Z","shell.execute_reply.started":"2026-06-12T16:52:51.275235Z","shell.execute_reply":"2026-06-12T16:52:59.458159Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_test = SampleGenerator(test, 4, shuffle = False, is_train = False)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:59.461495Z","iopub.execute_input":"2026-06-12T16:52:59.461875Z","iopub.status.idle":"2026-06-12T16:52:59.518418Z","shell.execute_reply.started":"2026-06-12T16:52:59.461835Z","shell.execute_reply":"2026-06-12T16:52:59.517384Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred = model.predict(data_test)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:52:59.519612Z","iopub.execute_input":"2026-06-12T16:52:59.519886Z","iopub.status.idle":"2026-06-12T16:53:10.936485Z","shell.execute_reply.started":"2026-06-12T16:52:59.519863Z","shell.execute_reply":"2026-06-12T16:53:10.935373Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def proccess_test(df, preds):\n    cols = ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']\n    cols = ['patient_overall', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']\n    patients = df.StudyInstanceUID.to_list()\n    \n    df_sub = pd.DataFrame()\n    \n    for i, p in enumerate(patients):\n        scores = list(preds[i])\n        if len(scores) < 8:\n            scores.append(preds[i].max() + preds[i].mean())\n        \n        df_temp = pd.DataFrame({'StudyInstanceUID': [p]*len(cols), 'prediction_type': cols, 'fractured': scores})\n        df_sub = pd.concat([df_sub, df_temp])\n        \n        del df_temp\n    \n    df_sub['row_id'] = df_sub['StudyInstanceUID'] + '_' + df_sub['prediction_type']\n    \n    return df_sub[['row_id', 'fractured']].reset_index(drop = True)\n    ","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:53:10.93773Z","iopub.execute_input":"2026-06-12T16:53:10.93806Z","iopub.status.idle":"2026-06-12T16:53:10.994386Z","shell.execute_reply.started":"2026-06-12T16:53:10.938026Z","shell.execute_reply":"2026-06-12T16:53:10.993014Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_sub = proccess_test(test, pred)","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:53:10.995454Z","iopub.execute_input":"2026-06-12T16:53:10.99579Z","iopub.status.idle":"2026-06-12T16:53:11.056289Z","shell.execute_reply.started":"2026-06-12T16:53:10.995764Z","shell.execute_reply":"2026-06-12T16:53:11.055302Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_sub.to_csv('submission.csv', index=False)\ndf_sub","metadata":{"execution":{"iopub.status.busy":"2026-06-12T16:53:11.057546Z","iopub.execute_input":"2026-06-12T16:53:11.057828Z","iopub.status.idle":"2026-06-12T16:53:11.119235Z","shell.execute_reply.started":"2026-06-12T16:53:11.057805Z","shell.execute_reply":"2026-06-12T16:53:11.11784Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(os.listdir('/kaggle/input'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.120505Z","iopub.execute_input":"2026-06-12T16:53:11.120818Z","iopub.status.idle":"2026-06-12T16:53:11.176532Z","shell.execute_reply.started":"2026-06-12T16:53:11.12079Z","shell.execute_reply":"2026-06-12T16:53:11.175403Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfor root, dirs, files in os.walk('/kaggle/input/rsna-2022-cervical-spine-fracture-detection'):\n    for f in files:\n        if f.endswith('.dcm'):\n            print(os.path.join(root, f))\n            raise SystemExit","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.177554Z","iopub.execute_input":"2026-06-12T16:53:11.177847Z","iopub.status.idle":"2026-06-12T16:53:11.396211Z","shell.execute_reply.started":"2026-06-12T16:53:11.177816Z","shell.execute_reply":"2026-06-12T16:53:11.395126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for layer in model.layers:\n    print(layer.name, layer.output_shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.396785Z","iopub.status.idle":"2026-06-12T16:53:11.397033Z","shell.execute_reply.started":"2026-06-12T16:53:11.396911Z","shell.execute_reply":"2026-06-12T16:53:11.396923Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import Model\n\nlayer_name = 'conv3d_1'\n\nfeature_extractor = Model(\n    inputs=model.input,\n    outputs=model.get_layer(layer_name).output\n)\n\nfeature_maps = feature_extractor.predict(tensor)\n\nprint(feature_maps.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.398112Z","iopub.status.idle":"2026-06-12T16:53:11.398397Z","shell.execute_reply.started":"2026-06-12T16:53:11.398278Z","shell.execute_reply":"2026-06-12T16:53:11.398289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fm = feature_maps[0]\n\nprint(fm.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.399443Z","iopub.status.idle":"2026-06-12T16:53:11.399681Z","shell.execute_reply.started":"2026-06-12T16:53:11.399562Z","shell.execute_reply":"2026-06-12T16:53:11.399573Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# average across depth + channels\nfeature_map = fm.mean(axis=0).mean(axis=-1)\n\nprint(feature_map.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.400751Z","iopub.status.idle":"2026-06-12T16:53:11.400994Z","shell.execute_reply.started":"2026-06-12T16:53:11.400874Z","shell.execute_reply":"2026-06-12T16:53:11.400886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"small_map = cv2.resize(feature_map, (32, 32))\n\nplt.figure(figsize=(8,8))\n\nplt.imshow(\n    small_map,\n    cmap='inferno',\n    interpolation='nearest'\n)\n\nplt.title(\"3D ResNet Feature Map\")\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.401868Z","iopub.status.idle":"2026-06-12T16:53:11.402128Z","shell.execute_reply.started":"2026-06-12T16:53:11.401987Z","shell.execute_reply":"2026-06-12T16:53:11.401997Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# OPENCV AI-STYLE ENHANCEMENT\n# ============================================================\n\nenhanced = (preprocessed * 255).astype(np.uint8)\n\n# histogram equalization\nenhanced = cv2.equalizeHist(enhanced)\n\n# sharpening\nkernel = np.array([\n    [0, -1, 0],\n    [-1, 5,-1],\n    [0, -1, 0]\n])\n\nenhanced = cv2.filter2D(enhanced, -1, kernel)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.403859Z","iopub.status.idle":"2026-06-12T16:53:11.404445Z","shell.execute_reply.started":"2026-06-12T16:53:11.404162Z","shell.execute_reply":"2026-06-12T16:53:11.404195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# ORIGINAL vs AI-ENHANCED (OpenCV)\n# ============================================================\n\nfig, ax = plt.subplots(1, 2, figsize=(14,6))\n\n# ORIGINAL\nax[0].imshow(original_norm, cmap='gray')\nax[0].set_title(\"Original DICOM\")\nax[0].axis('off')\n\n# AI ENHANCED (OpenCV)\nax[1].imshow(enhanced, cmap='inferno')\nax[1].set_title(\"AI-Enhanced Output\")\nax[1].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-12T16:53:11.406206Z","iopub.status.idle":"2026-06-12T16:53:11.406614Z","shell.execute_reply.started":"2026-06-12T16:53:11.406416Z","shell.execute_reply":"2026-06-12T16:53:11.406434Z"}},"outputs":[],"execution_count":null}]}