{"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":"%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","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:47:19.627756Z","iopub.execute_input":"2023-05-17T18:47:19.628479Z","iopub.status.idle":"2023-05-17T18:48:16.40451Z","shell.execute_reply.started":"2023-05-17T18:47:19.628423Z","shell.execute_reply":"2023-05-17T18:48:16.403045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport glob\nimport gc\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\nfrom matplotlib import animation, rc\nimport cv2\n\nimport nibabel as nib\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nimport random\nimport collections\nimport math\n\nimport plotly.offline as py\nimport plotly.graph_objs as go\n\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\nfrom skimage.transform import resize\nfrom classification_models_3D.tfkeras import Classifiers\n\n","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.407979Z","iopub.execute_input":"2023-05-17T18:48:16.409063Z","iopub.status.idle":"2023-05-17T18:48:16.542919Z","shell.execute_reply.started":"2023-05-17T18:48:16.408998Z","shell.execute_reply":"2023-05-17T18:48:16.539022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config:\n    img_size = 256\n    depth = 128\n    train_one_fold = False\n    epochs = 10\n    data_augmentation = True  # Add a new configuration for data augmentation\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","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.544377Z","iopub.status.idle":"2023-05-17T18:48:16.545211Z","shell.execute_reply.started":"2023-05-17T18:48:16.544956Z","shell.execute_reply":"2023-05-17T18:48:16.544982Z"},"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\ndef load_dicom_line_par(path, indices=None):\n    t_paths = sorted(glob.glob(os.path.join(path, \"*\")), key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n    if indices is not None:\n        t_paths = [t_paths[i] for i in indices]\n    images = Parallel(n_jobs=-1)(delayed(load_dicom)(filename) for filename in t_paths)\n    return np.array(images)","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.546834Z","iopub.status.idle":"2023-05-17T18:48:16.547364Z","shell.execute_reply.started":"2023-05-17T18:48:16.547121Z","shell.execute_reply":"2023-05-17T18:48:16.547144Z"},"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):\n    os.mkdir(train_output_path)\nif not os.path.exists(test_output_path):\n    os.mkdir(test_output_path)\n\ntrain_patients = train.StudyInstanceUID.to_list()\n\ndatagen = ImageDataGenerator(\n    rotation_range=10,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    vertical_flip=True,\n    fill_mode='constant',\n    cval=0\n)\n\ndef save_3d_voxels(dicom_path, output_path):\n    n_scans = len(os.listdir(dicom_path))\n    ind = np.quantile(list(range(n_scans)), np.linspace(0., 1., Config.depth)).round().astype(int)\n    images = load_dicom_line_par(dicom_path, indices=ind)\n\n    # Create augmented images and save\n    augmented_images = []\n    for image in images:\n        image = np.expand_dims(image, axis=-1)  # Expand dimensions to (height, width, channels)\n        augmented_image = datagen.random_transform(image)\n        augmented_images.append(augmented_image.squeeze())\n\n    augmented_images = np.array(augmented_images)\n    if augmented_images.ndim < 4:\n        augmented_images = np.expand_dims(augmented_images, -1)\n\n    np.save(f\"{output_path}{dicom_path.split('/')[-1]}.npy\", augmented_images)\n    \n    del images, augmented_images\n    return None\n\nfor i in tqdm(range(len(train_patients[:10]))):  # Process only the first 10 patients as an example\n    case = IMG_PATH_TRAIN + train_patients[i]\n    save_3d_voxels(case, train_output_path)\n\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.549211Z","iopub.status.idle":"2023-05-17T18:48:16.549735Z","shell.execute_reply.started":"2023-05-17T18:48:16.5495Z","shell.execute_reply":"2023-05-17T18:48:16.549522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nrc('animation', html='jshtml')\n\n\ndef display_images(original_image, augmented_images):\n    num_images = len(augmented_images) + 1\n    num_images_per_row = 4  # Set the number of images per row\n    num_rows = int(np.ceil(num_images / num_images_per_row))\n    fig, axes = plt.subplots(num_rows, num_images_per_row, figsize=(10 * num_images_per_row, 8 * num_rows))\n\n    axes = axes.flatten()  # Flatten the axes array for easy indexing\n\n    axes[0].imshow(original_image, cmap='gray')\n    axes[0].set_title('Original')\n    axes[0].axis('off')\n\n    for i, augmented_image in enumerate(augmented_images):\n        axes[i + 1].imshow(augmented_image, cmap='gray')\n        axes[i + 1].set_title(f'Augmented {i+1}')\n        axes[i + 1].axis('off')\n\n    # Hide any remaining empty subplots\n    for j in range(num_images, num_rows * num_images_per_row):\n        axes[j].axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n\n# Show saved example array\ntrain_arrays = os.listdir(train_output_path)\narray_path = train_output_path + train_arrays[np.random.randint(len(train_arrays))]\narray = np.load(array_path)\ncase_id = array_path.split('/')[-1][:-4]\noriginal_image = array[0]\naugmented_images = array[1:]\n\ndisplay_images(original_image, augmented_images)","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.551292Z","iopub.status.idle":"2023-05-17T18:48:16.551742Z","shell.execute_reply.started":"2023-05-17T18:48:16.551518Z","shell.execute_reply":"2023-05-17T18:48:16.551537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['numpy_path'] = train['StudyInstanceUID'].apply(lambda x: f'{IMG_PATH_TRAIN}{x}.npy')\n","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.553167Z","iopub.status.idle":"2023-05-17T18:48:16.553645Z","shell.execute_reply.started":"2023-05-17T18:48:16.553415Z","shell.execute_reply":"2023-05-17T18:48:16.553436Z"},"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        # Define the ImageDataGenerator for data augmentation\n        self.datagen = ImageDataGenerator(\n            rotation_range=10,\n            width_shift_range=0.1,\n            height_shift_range=0.1,\n            shear_range=0.2,\n            zoom_range=0.2,\n            horizontal_flip=True,\n            vertical_flip=True,\n            fill_mode='constant',\n            cval=0\n        )\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        if self.is_train:\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                    image = np.load(self.numpy_path.iloc[cur_ind])\n\n                    # Apply data augmentation using ImageDataGenerator\n                    augmented_images = []\n                    for channel in range(image.shape[0]):\n                        augmented_channel = self.datagen.random_transform(image[channel])\n                        augmented_images.append(augmented_channel)\n                    augmented_image = np.stack(augmented_images, axis=0)\n\n                    batch_x.append(augmented_image)\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.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)\n","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.55537Z","iopub.status.idle":"2023-05-17T18:48:16.555828Z","shell.execute_reply.started":"2023-05-17T18:48:16.555606Z","shell.execute_reply":"2023-05-17T18:48:16.555627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install classification-models-3D\n!pip install keras_applications\n","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.558949Z","iopub.status.idle":"2023-05-17T18:48:16.560579Z","shell.execute_reply.started":"2023-05-17T18:48:16.560192Z","shell.execute_reply":"2023-05-17T18:48:16.56023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#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([1, 1, 1, 1, 1, 1, 1], dtype=tf.float32),\n        '+' : tf.constant([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    ","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.56244Z","iopub.status.idle":"2023-05-17T18:48:16.562917Z","shell.execute_reply.started":"2023-05-17T18:48:16.562692Z","shell.execute_reply":"2023-05-17T18:48:16.562714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#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","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.564776Z","iopub.status.idle":"2023-05-17T18:48:16.566092Z","shell.execute_reply.started":"2023-05-17T18:48:16.565653Z","shell.execute_reply":"2023-05-17T18:48:16.56571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from classification_models_3D.tfkeras import Classifiers\n\nmodel_arch = 'resnet50' \n#model_arch = 'resnet34'\n\ndef concat_max_avg(tensor):\n    _avr = L.GlobalAveragePooling3D()(tensor)\n    _max = L.GlobalMaxPooling3D()(tensor)\n    return L.Concatenate()([_avr, _max])\n\ndef create_model(input_shape, num_classes):\n    inputs = L.Input((*input_shape, 1), name='inputs')\n    x = L.Rescaling(1/255.)(inputs)\n    x = L.Conv3D(3, (1, 1, 1), padding='same')(x)\n    \n    net, preprocess_input = Classifiers.get(model_arch)\n    #x = preprocess_input(x)\n    x = net(input_shape=(*input_shape, 3), include_top=False, weights='imagenet')(x)\n    \n    x = concat_max_avg(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(512, activation='relu')(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(128, activation='relu')(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(32, activation='relu')(x)\n    \n    outputs = L.Dense(num_classes, activation='sigmoid')(x)\n    model  = tf.keras.Model(inputs, outputs)\n   \n    model.compile(loss= competiton_loss, #'binary_crossentropy',\n                      optimizer=keras.optimizers.Adam(learning_rate=1e-3),\n                       metrics=[tf.keras.metrics.AUC(name='mAP'),tfa.metrics.F1Score(num_classes=num_classes, threshold=0.25, average='macro')])\n    return model\n    return model\n\ncreate_model((None, Config.img_size, Config.img_size), num_classes = 7).summary()","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.568265Z","iopub.status.idle":"2023-05-17T18:48:16.568906Z","shell.execute_reply.started":"2023-05-17T18:48:16.568587Z","shell.execute_reply":"2023-05-17T18:48:16.568617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LR_MAX = 1e-3\nLR_DECAY = 0.75\n\ndef lr_func(epoch):   \n    lr = np.pi/2 * LR_DECAY**epoch \n    lr = np.sin(lr) * LR_MAX\n    return lr\n    \nlr_list = [lr_func(x) for x in list(range(Config.epochs))]\nplt.scatter(range(Config.epochs), lr_list)\nplt.show()\n\nLR = tf.keras.callbacks.LearningRateScheduler(lr_func, verbose = True)","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.571804Z","iopub.status.idle":"2023-05-17T18:48:16.572426Z","shell.execute_reply.started":"2023-05-17T18:48:16.57212Z","shell.execute_reply":"2023-05-17T18:48:16.572159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_folds = 5\npred_columns = ['patient_overall_pred', 'C1_pred', 'C2_pred', 'C3_pred', 'C4_pred', 'C5_pred', 'C6_pred', 'C7_pred']\n#pred_columns = ['C1_pred', 'C2_pred', 'C3_pred', 'C4_pred', 'C5_pred', 'C6_pred', 'C7_pred']\ntrain[pred_columns] = 0\n\nestop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=0, mode='min',restore_best_weights=True)\nkf_split = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=0)\n\nfor fold,(tr_idx, val_idx) in enumerate(kf_split.split(train, train.patient_overall)):\n    \n    X_train = train.iloc[tr_idx]\n    X_test = train.iloc[val_idx]\n    \n    train_data = SampleGenerator(X_train, 4, steps_per_epoch = 200, resample_rate = 0.8, shuffle = True)\n    test_data = SampleGenerator(X_test, 2, resample_rate = 0.8, shuffle = False)\n    \n    path_to_checkpoints = f\"{model_arch}_best_fold_{fold+1}.hdf5\"\n    checkpointer = tf.keras.callbacks.ModelCheckpoint(filepath=path_to_checkpoints, monitor=\"val_loss\", mode='min', verbose=1, save_best_only=True)\n\n    #model = get_model()\n    model = create_model((None, Config.img_size, Config.img_size), num_classes = 8)\n\n    print(f'Training Model Fold {fold+1}...')\n    \n    history = model.fit(\n        train_data,\n        epochs = Config.epochs,\n        callbacks = [estop, LR, checkpointer],\n        validation_data = (test_data),\n    )   \n    \n    del X_train, train_data\n    \n    model.save(f\"{model_arch}_end_fold_{fold+1}\")\n    \n    test_preds = model.predict(test_data)\n    train.loc[val_idx, pred_columns] = test_preds\n    \n    del X_test,test_preds, test_data\n    K.clear_session()\n    gc.collect()\n    \n    if Config.train_one_fold: break","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.573887Z","iopub.status.idle":"2023-05-17T18:48:16.574403Z","shell.execute_reply.started":"2023-05-17T18:48:16.57417Z","shell.execute_reply":"2023-05-17T18:48:16.574193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model(test_data.__getitem__(1)[0])","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.578607Z","iopub.status.idle":"2023-05-17T18:48:16.579867Z","shell.execute_reply.started":"2023-05-17T18:48:16.57956Z","shell.execute_reply":"2023-05-17T18:48:16.579589Z"},"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":"2023-05-17T18:48:16.581342Z","iopub.status.idle":"2023-05-17T18:48:16.582153Z","shell.execute_reply.started":"2023-05-17T18:48:16.581866Z","shell.execute_reply":"2023-05-17T18:48:16.58189Z"},"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":"2023-05-17T18:48:16.583642Z","iopub.status.idle":"2023-05-17T18:48:16.584176Z","shell.execute_reply.started":"2023-05-17T18:48:16.583892Z","shell.execute_reply":"2023-05-17T18:48:16.583913Z"},"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":"2023-05-17T18:48:16.585948Z","iopub.status.idle":"2023-05-17T18:48:16.58642Z","shell.execute_reply.started":"2023-05-17T18:48:16.586201Z","shell.execute_reply":"2023-05-17T18:48:16.586223Z"},"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":"2023-05-17T18:48:16.588073Z","iopub.status.idle":"2023-05-17T18:48:16.588532Z","shell.execute_reply.started":"2023-05-17T18:48:16.588314Z","shell.execute_reply":"2023-05-17T18:48:16.588335Z"},"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":"2023-05-17T18:48:16.590248Z","iopub.status.idle":"2023-05-17T18:48:16.590698Z","shell.execute_reply.started":"2023-05-17T18:48:16.590481Z","shell.execute_reply":"2023-05-17T18:48:16.5905Z"},"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":"2023-05-17T18:48:16.592357Z","iopub.status.idle":"2023-05-17T18:48:16.592814Z","shell.execute_reply.started":"2023-05-17T18:48:16.592587Z","shell.execute_reply":"2023-05-17T18:48:16.592607Z"},"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":"2023-05-17T18:48:16.594159Z","iopub.status.idle":"2023-05-17T18:48:16.594588Z","shell.execute_reply.started":"2023-05-17T18:48:16.594376Z","shell.execute_reply":"2023-05-17T18:48:16.594395Z"},"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":"2023-05-17T18:48:16.596225Z","iopub.status.idle":"2023-05-17T18:48:16.596664Z","shell.execute_reply.started":"2023-05-17T18:48:16.59645Z","shell.execute_reply":"2023-05-17T18:48:16.59647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = model.predict(data_test)","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.598496Z","iopub.status.idle":"2023-05-17T18:48:16.598953Z","shell.execute_reply.started":"2023-05-17T18:48:16.598714Z","shell.execute_reply":"2023-05-17T18:48:16.598741Z"},"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":"2023-05-17T18:48:16.600064Z","iopub.status.idle":"2023-05-17T18:48:16.600865Z","shell.execute_reply.started":"2023-05-17T18:48:16.600287Z","shell.execute_reply":"2023-05-17T18:48:16.600308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = proccess_test(test, pred)","metadata":{"execution":{"iopub.status.busy":"2023-05-17T18:48:16.602772Z","iopub.status.idle":"2023-05-17T18:48:16.603395Z","shell.execute_reply.started":"2023-05-17T18:48:16.603074Z","shell.execute_reply":"2023-05-17T18:48:16.603101Z"},"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":"2023-05-17T18:48:16.605619Z","iopub.status.idle":"2023-05-17T18:48:16.60621Z","shell.execute_reply.started":"2023-05-17T18:48:16.605962Z","shell.execute_reply":"2023-05-17T18:48:16.60599Z"},"trusted":true},"execution_count":null,"outputs":[]}]}