{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"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":"2022-08-19T18:47:17.768658Z","iopub.execute_input":"2022-08-19T18:47:17.769753Z","iopub.status.idle":"2022-08-19T18:48:17.988254Z","shell.execute_reply.started":"2022-08-19T18:47:17.769659Z","shell.execute_reply":"2022-08-19T18:48:17.986879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:17.990745Z","iopub.execute_input":"2022-08-19T18:48:17.991155Z","iopub.status.idle":"2022-08-19T18:48:24.02403Z","shell.execute_reply.started":"2022-08-19T18:48:17.991119Z","shell.execute_reply":"2022-08-19T18:48:24.023132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:24.025304Z","iopub.execute_input":"2022-08-19T18:48:24.025914Z","iopub.status.idle":"2022-08-19T18:48:24.195715Z","shell.execute_reply.started":"2022-08-19T18:48:24.025881Z","shell.execute_reply":"2022-08-19T18:48:24.194896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:24.198754Z","iopub.execute_input":"2022-08-19T18:48:24.199595Z","iopub.status.idle":"2022-08-19T18:48:24.26765Z","shell.execute_reply.started":"2022-08-19T18:48:24.199551Z","shell.execute_reply":"2022-08-19T18:48:24.266546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:24.269203Z","iopub.execute_input":"2022-08-19T18:48:24.269777Z","iopub.status.idle":"2022-08-19T18:48:29.188284Z","shell.execute_reply.started":"2022-08-19T18:48:24.269744Z","shell.execute_reply":"2022-08-19T18:48:29.187215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:29.190301Z","iopub.execute_input":"2022-08-19T18:48:29.190726Z","iopub.status.idle":"2022-08-19T18:48:29.270891Z","shell.execute_reply.started":"2022-08-19T18:48:29.190683Z","shell.execute_reply":"2022-08-19T18:48:29.270106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:29.274042Z","iopub.execute_input":"2022-08-19T18:48:29.274361Z","iopub.status.idle":"2022-08-19T18:48:29.347955Z","shell.execute_reply.started":"2022-08-19T18:48:29.274331Z","shell.execute_reply":"2022-08-19T18:48:29.34687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:29.349497Z","iopub.execute_input":"2022-08-19T18:48:29.350131Z","iopub.status.idle":"2022-08-19T18:48:29.414358Z","shell.execute_reply.started":"2022-08-19T18:48:29.3501Z","shell.execute_reply":"2022-08-19T18:48:29.413275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:29.415997Z","iopub.execute_input":"2022-08-19T18:48:29.416414Z","iopub.status.idle":"2022-08-19T18:48:45.342671Z","shell.execute_reply.started":"2022-08-19T18:48:29.416314Z","shell.execute_reply":"2022-08-19T18:48:45.341865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_test = SampleGenerator(test, 4, shuffle = False, is_train = False)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T18:48:45.346529Z","iopub.execute_input":"2022-08-19T18:48:45.347703Z","iopub.status.idle":"2022-08-19T18:48:45.409228Z","shell.execute_reply.started":"2022-08-19T18:48:45.347657Z","shell.execute_reply":"2022-08-19T18:48:45.407974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = model.predict(data_test)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T18:48:45.410947Z","iopub.execute_input":"2022-08-19T18:48:45.411278Z","iopub.status.idle":"2022-08-19T18:48:59.46156Z","shell.execute_reply.started":"2022-08-19T18:48:45.411249Z","shell.execute_reply":"2022-08-19T18:48:59.460529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2022-08-19T18:48:59.462736Z","iopub.execute_input":"2022-08-19T18:48:59.463801Z","iopub.status.idle":"2022-08-19T18:48:59.529111Z","shell.execute_reply.started":"2022-08-19T18:48:59.463756Z","shell.execute_reply":"2022-08-19T18:48:59.528102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = proccess_test(test, pred)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T18:48:59.530422Z","iopub.execute_input":"2022-08-19T18:48:59.530911Z","iopub.status.idle":"2022-08-19T18:48:59.604618Z","shell.execute_reply.started":"2022-08-19T18:48:59.530867Z","shell.execute_reply":"2022-08-19T18:48:59.603395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub.to_csv('submission.csv', index=False)\ndf_sub","metadata":{"execution":{"iopub.status.busy":"2022-08-19T18:48:59.606361Z","iopub.execute_input":"2022-08-19T18:48:59.607723Z","iopub.status.idle":"2022-08-19T18:48:59.682267Z","shell.execute_reply.started":"2022-08-19T18:48:59.607682Z","shell.execute_reply":"2022-08-19T18:48:59.680134Z"},"trusted":true},"execution_count":null,"outputs":[]}]}