{"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\nThe model used for training is taken from the `classification_models_3D` [repo](https://github.com/ZFTurbo/classification_models_3D).\n\n#### 📔 [Data preparation notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-dicom-to-numpy-3d)\n#### 📔 [Trainnig 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!pip install git+https://github.com/ultralytics/yolov5.git\n","metadata":{},"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\n\nimport nibabel as nib\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{},"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\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_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_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_count":null,"outputs":[]},{"cell_type":"code","source":"from skimage.transform import resize\n\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_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\nimport plotly.offline as py\nimport plotly.graph_objs as go\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\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:30.422589Z","iopub.execute_input":"2022-08-18T19:33:30.423047Z","iopub.status.idle":"2022-08-18T19:33:37.555189Z","shell.execute_reply.started":"2022-08-18T19:33:30.422963Z","shell.execute_reply":"2022-08-18T19:33:37.554015Z"},"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    epochs = 15\n\n\nIMG_PATH_TRAIN = '../input/rsna-22-dicom-to-numpy-3d/train_arrays/'\n\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)\n\ntrain=pd.read_csv(TRAIN_CSV_PATH)\ntest=pd.read_csv(TEST_CSV_PATH)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.560917Z","iopub.execute_input":"2022-08-18T19:33:37.563507Z","iopub.status.idle":"2022-08-18T19:33:37.831724Z","shell.execute_reply.started":"2022-08-18T19:33:37.563449Z","shell.execute_reply":"2022-08-18T19:33:37.830789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['numpy_path'] = train['StudyInstanceUID'].apply(lambda x: f'{IMG_PATH_TRAIN}{x}.npy')","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.836282Z","iopub.execute_input":"2022-08-18T19:33:37.838583Z","iopub.status.idle":"2022-08-18T19:33:37.855521Z","shell.execute_reply.started":"2022-08-18T19:33:37.83855Z","shell.execute_reply":"2022-08-18T19:33:37.854665Z"},"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)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.861046Z","iopub.execute_input":"2022-08-18T19:33:37.863178Z","iopub.status.idle":"2022-08-18T19:33:37.883379Z","shell.execute_reply.started":"2022-08-18T19:33:37.863143Z","shell.execute_reply":"2022-08-18T19:33:37.88238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install classification-models-3D\n!pip install keras_applications","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.889063Z","iopub.execute_input":"2022-08-18T19:33:37.892048Z","iopub.status.idle":"2022-08-18T19:34:01.9542Z","shell.execute_reply.started":"2022-08-18T19:33:37.892012Z","shell.execute_reply":"2022-08-18T19:34:01.952995Z"},"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    \n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:34:01.95607Z","iopub.execute_input":"2022-08-18T19:34:01.956774Z","iopub.status.idle":"2022-08-18T19:34:01.964767Z","shell.execute_reply.started":"2022-08-18T19:34:01.956731Z","shell.execute_reply":"2022-08-18T19:34:01.963651Z"},"_kg_hide-input":true,"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\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:34:01.966406Z","iopub.execute_input":"2022-08-18T19:34:01.967137Z","iopub.status.idle":"2022-08-18T19:34:01.97868Z","shell.execute_reply.started":"2022-08-18T19:34:01.967102Z","shell.execute_reply":"2022-08-18T19:34:01.977711Z"},"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":"2022-08-18T19:34:01.981375Z","iopub.execute_input":"2022-08-18T19:34:01.982738Z","iopub.status.idle":"2022-08-18T19:35:02.849263Z","shell.execute_reply.started":"2022-08-18T19:34:01.98271Z","shell.execute_reply":"2022-08-18T19:35:02.848212Z"},"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":"2022-08-18T19:35:18.508897Z","iopub.execute_input":"2022-08-18T19:35:18.509949Z","iopub.status.idle":"2022-08-18T19:35:18.726295Z","shell.execute_reply.started":"2022-08-18T19:35:18.509912Z","shell.execute_reply":"2022-08-18T19:35:18.725358Z"},"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":"2022-08-18T19:37:15.01976Z","iopub.execute_input":"2022-08-18T19:37:15.020741Z","iopub.status.idle":"2022-08-18T19:43:31.673328Z","shell.execute_reply.started":"2022-08-18T19:37:15.020705Z","shell.execute_reply":"2022-08-18T19:43:31.670125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model(test_data.__getitem__(1)[0])","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:43:46.83255Z","iopub.execute_input":"2022-08-18T19:43:46.833193Z","iopub.status.idle":"2022-08-18T19:43:47.753381Z","shell.execute_reply.started":"2022-08-18T19:43:46.833157Z","shell.execute_reply":"2022-08-18T19:43:47.752438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","metadata":{},"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","metadata":{},"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_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_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_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_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_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_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_count":null,"outputs":[]},{"cell_type":"code","source":"data_test = SampleGenerator(test, 4, shuffle = False, is_train = False)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = model.predict(data_test)\n","metadata":{},"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)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = proccess_test(test, pred)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub.to_csv('submission.csv', index=False)\ndf_sub","metadata":{},"execution_count":null,"outputs":[]}]}