{"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":"code","source":"import math\nimport numpy as np\nimport pandas as pd \nimport os\nfrom glob import glob\n%matplotlib inline\nimport matplotlib.pyplot as plt\n\nimport random\nfrom random import sample\nimport sklearn.model_selection as skl\nfrom kaggle_datasets import KaggleDatasets\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom keras_preprocessing.image.dataframe_iterator import DataFrameIterator\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom keras.applications.vgg16 import VGG16\nfrom tensorflow.keras.layers import InputLayer, GlobalAveragePooling2D, BatchNormalization, Dense, Dropout, Flatten, Conv2D, MaxPooling2D \nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.metrics import AUC\nfrom tensorflow.keras.optimizers import RMSprop\nimport tensorflow_hub as tfhub\nfrom tensorflow.keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\n\nimport pydicom\nimport cv2","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:39:58.967650Z","iopub.execute_input":"2021-12-21T07:39:58.967992Z","iopub.status.idle":"2021-12-21T07:40:06.071349Z","shell.execute_reply.started":"2021-12-21T07:39:58.967904Z","shell.execute_reply":"2021-12-21T07:40:06.070468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Replace the root directory BELOW with appropriate directory folder pointing to Brats2021 dataset ->\nhttps://www.kaggle.com/c/rsna-miccai-brain-tumor-radiogenomic-classification","metadata":{}},{"cell_type":"code","source":"#Re\nroot_dir = '../input/rsna-miccai-brain-tumor-radiogenomic-classification/'\ndf = pd.read_csv(root_dir+'train_labels.csv')","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.072678Z","iopub.execute_input":"2021-12-21T07:40:06.073073Z","iopub.status.idle":"2021-12-21T07:40:06.089342Z","shell.execute_reply.started":"2021-12-21T07:40:06.073038Z","shell.execute_reply":"2021-12-21T07:40:06.088401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Add the full paths for each id for different types of sequences to the csv \ndef full_ids(data):\n    zeros = 5 - len(str(data))\n    if zeros > 0:\n        prefix = ''.join(['0' for i in range(zeros)])\n    \n    return prefix+str(data)\n        \n\ndf['BraTS21ID_full'] = df['BraTS21ID'].apply(full_ids)\n\n# Add all the paths to the df for easy access\ndf['flair'] = df['BraTS21ID_full'].apply(lambda file_id : root_dir+'train/'+file_id+'/FLAIR/')\ndf['t1w'] = df['BraTS21ID_full'].apply(lambda file_id : root_dir+'train/'+file_id+'/T1w/')\ndf['t1wce'] = df['BraTS21ID_full'].apply(lambda file_id : root_dir+'train/'+file_id+'/T1wCE/')\ndf['t2w'] = df['BraTS21ID_full'].apply(lambda file_id : root_dir+'train/'+file_id+'/T2w/')","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.093127Z","iopub.execute_input":"2021-12-21T07:40:06.093943Z","iopub.status.idle":"2021-12-21T07:40:06.115068Z","shell.execute_reply.started":"2021-12-21T07:40:06.093899Z","shell.execute_reply":"2021-12-21T07:40:06.114255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_test = pd.read_csv(root_dir+'sample_submission.csv')\n\ndf_test['BraTS21ID_full'] = df_test['BraTS21ID'].apply(full_ids)\n\n# Add all the paths to the df for easy access\ndf_test['flair'] = df_test['BraTS21ID_full'].apply(lambda file_id : root_dir+'test/'+file_id+'/FLAIR/')\ndf_test['t1w'] = df_test['BraTS21ID_full'].apply(lambda file_id : root_dir+'test/'+file_id+'/T1w/')\ndf_test['t1wce'] = df_test['BraTS21ID_full'].apply(lambda file_id : root_dir+'test/'+file_id+'/T1wCE/')\ndf_test['t2w'] = df_test['BraTS21ID_full'].apply(lambda file_id : root_dir+'test/'+file_id+'/T2w/')","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.117179Z","iopub.execute_input":"2021-12-21T07:40:06.117432Z","iopub.status.idle":"2021-12-21T07:40:06.134389Z","shell.execute_reply.started":"2021-12-21T07:40:06.117402Z","shell.execute_reply":"2021-12-21T07:40:06.133429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load the dataset and split into training and testing dataframes","metadata":{}},{"cell_type":"code","source":"def get_train_val_dataframe(mri_type):\n    \n    all_img_files = []\n    all_img_labels = []\n    all_img_patient_ids = []\n    for row in df.iterrows():\n        if row[1]['BraTS21ID_full'] == '00109' and mri_type == 'flair':\n            continue\n        if row[1]['BraTS21ID_full'] == '00123' and mri_type == 't1w':\n            continue\n        if row[1]['BraTS21ID_full'] == '00709' and mri_type == 'flair':\n            continue\n        img_dir = row[1][mri_type]\n        img_files = os.listdir(img_dir)\n        img_nums = sorted([int(ele.replace('Image-', '').replace('.dcm', '')) for ele in img_files])\n        mid_point = int(len(img_nums)/2)\n        start_point = mid_point - max(int(mid_point*0.1), 1)\n        end_point = mid_point + max(int(mid_point*0.1), 1)\n        img_names = [f'Image-{img_nums[i]}.dcm' for i in range(start_point, end_point+1)]\n        img_paths = [img_dir+ele for ele in img_names]\n        img_labels = [row[1]['MGMT_value']]*len(img_paths)\n        img_patient_ids = [row[1]['BraTS21ID']]*len(img_paths)\n        all_img_files.extend(img_paths)\n        all_img_labels.extend(img_labels)\n        all_img_patient_ids.extend(img_patient_ids)\n\n    train_val_df = pd.DataFrame({'patient_ids': all_img_patient_ids,\n                  'labels': all_img_labels,\n                  'file_paths': all_img_files})\n\n    train_val_df['labels'] = train_val_df['labels'].map({1: '1', 0: '0'})\n     \n    class_prop= 0.90\n    \n    classes_splits  = {}\n    for i in range(2):\n        train_val_label_class = train_val_df[train_val_df['labels']==f'{i}']\n        train_val_list_ids =  list(train_val_label_class['patient_ids'].unique())\n        train_threshold = math.ceil(class_prop*len(train_val_list_ids))\n        train_ids = train_val_list_ids[:train_threshold]\n        val_ids = train_val_list_ids[train_threshold:]\n        classes_splits[f'train_{i}'] = train_val_label_class[train_val_label_class['patient_ids'].isin(train_ids)]\n        classes_splits[f'val_{i}'] = val_df = train_val_label_class[train_val_label_class['patient_ids'].isin(val_ids)]\n        \n    train_df = pd.concat([classes_splits['train_0'], classes_splits['train_1']], axis=0)\n    val_df = pd.concat([classes_splits['val_0'], classes_splits['val_1']], axis=0)\n  \n    return train_df, val_df\n    \ndef get_test_dataframe(mri_type):\n    \n    all_test_img_files = []\n    all_test_img_labels = []\n    all_test_img_patient_ids = []\n    for row in df_test.iterrows():\n        img_dir = row[1][mri_type]\n        img_files = os.listdir(img_dir)\n        img_nums = sorted([int(ele.replace('Image-', '').replace('.dcm', '')) for ele in img_files])\n        mid_point = int(len(img_nums)/2)\n        start_point = mid_point - max(int(mid_point*0.1), 1)\n        end_point = mid_point + max(int(mid_point*0.1), 1)\n        img_names = [f'Image-{img_nums[i]}.dcm' for i in range(start_point, end_point+1)]\n        img_paths = [img_dir+ele for ele in img_names]\n        img_labels = [row[1]['MGMT_value']]*len(img_paths)\n        img_patient_ids = [row[1]['BraTS21ID']]*len(img_paths)\n        all_test_img_files.extend(img_paths)\n        all_test_img_labels.extend(img_labels)\n        all_test_img_patient_ids.extend(img_patient_ids)\n\n    test_df = pd.DataFrame({'patient_ids': all_test_img_patient_ids,\n                  'labels': all_test_img_labels,\n                  'file_paths': all_test_img_files})\n    \n    test_df['labels'] = ['1']*(len(test_df)-1) + ['0'] \n    \n    return test_df","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.135607Z","iopub.execute_input":"2021-12-21T07:40:06.135976Z","iopub.status.idle":"2021-12-21T07:40:06.154091Z","shell.execute_reply.started":"2021-12-21T07:40:06.135935Z","shell.execute_reply":"2021-12-21T07:40:06.153502Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"##Create a DCM DataFrame Iterator","metadata":{}},{"cell_type":"code","source":"class DCMDataFrameIterator(DataFrameIterator):\n    def __init__(self, *arg, **kwargs):\n        self.white_list_formats = ('dcm')\n        super(DCMDataFrameIterator, self).__init__(*arg, **kwargs)\n        self.dataframe = kwargs['dataframe']\n        self.x = self.dataframe[kwargs['x_col']]\n        self.y = self.dataframe[kwargs['y_col']]\n        self.color_mode = kwargs['color_mode']\n        self.target_size = kwargs['target_size']\n\n    def _get_batches_of_transformed_samples(self, indices_array):\n        # get batch of images\n        batch_x = np.array([self.read_dcm_as_array(dcm_path, self.target_size, color_mode=self.color_mode)\n                            for dcm_path in self.x.iloc[indices_array]])\n\n        batch_y = np.array(self.y.iloc[indices_array].astype(np.uint8))  # astype because y was passed as str\n        \n        # transform images\n        if self.image_data_generator is not None:\n            for i, (x, y) in enumerate(zip(batch_x, batch_y)):\n                transform_params = self.image_data_generator.get_random_transform(x.shape)\n                batch_x[i] = self.image_data_generator.apply_transform(x, transform_params)\n\n        return batch_x, batch_y\n\n    @staticmethod\n    def read_dcm_as_array(dcm_path, target_size=(300, 300), color_mode='rgb'):\n        image_array = pydicom.dcmread(dcm_path).pixel_array\n        pixels = image_array - np.min(image_array)\n        pixels = pixels / np.max(pixels)\n        image_manual_norm = (pixels * 255).astype(np.uint8)\n        image_array = cv2.resize(image_manual_norm, target_size, interpolation=cv2.INTER_NEAREST)  #this returns a 2d array\n        if color_mode == 'rgb':\n            image_array = np.dstack((image_array, np.zeros_like(image_array), np.zeros_like(image_array)))\n        return image_array","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.155218Z","iopub.execute_input":"2021-12-21T07:40:06.155603Z","iopub.status.idle":"2021-12-21T07:40:06.168773Z","shell.execute_reply.started":"2021-12-21T07:40:06.155561Z","shell.execute_reply":"2021-12-21T07:40:06.168037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 369\nBATCH_SIZE = 128\nCLASS_MODE = 'binary'\nCOLOR_MODE = 'rgb'\nTARGET_SIZE = (300, 300)","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.169689Z","iopub.execute_input":"2021-12-21T07:40:06.170592Z","iopub.status.idle":"2021-12-21T07:40:06.183122Z","shell.execute_reply.started":"2021-12-21T07:40:06.170558Z","shell.execute_reply":"2021-12-21T07:40:06.182538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Generate data by  using appropriate data augmentation properties","metadata":{}},{"cell_type":"code","source":"def get_data_generators(train_df,val_df, test_df):\n    train_augmentation_parameters = dict(\n        rescale=1.0/255,\n        zoom_range=0.2,\n        rotation_range=0.2,\n        fill_mode='nearest',\n        height_shift_range= 0.1,\n        width_shift_range=0.1,\n        horizontal_flip=True,\n        brightness_range = [0.8, 1.2]\n    )\n    \n    val_augmentation_parameters = dict(\n        rescale=1.0/255.0\n    )\n\n    test_augmentation_parameters = dict(\n        rescale=1.0/255.0\n    )\n\n    train_consts = {\n        'seed': SEED,\n        'batch_size': BATCH_SIZE,\n        'class_mode': CLASS_MODE,\n        'color_mode': COLOR_MODE,\n        'target_size': TARGET_SIZE,  \n    }\n    \n    val_consts = {\n    'batch_size': BATCH_SIZE,\n    'class_mode': CLASS_MODE,\n    'color_mode': COLOR_MODE,\n    'target_size': TARGET_SIZE,\n    'shuffle': False\n    }\n\n    test_consts = {\n        'batch_size': BATCH_SIZE,\n        'class_mode': CLASS_MODE,\n        'color_mode': COLOR_MODE,\n        'target_size': TARGET_SIZE,\n        'shuffle': False\n    }\n\n    train_augmenter = ImageDataGenerator(**train_augmentation_parameters)\n    val_augmenter = ImageDataGenerator(**val_augmentation_parameters)\n    test_augmenter = ImageDataGenerator(**test_augmentation_parameters)\n\n    train_generator = DCMDataFrameIterator(dataframe=train_df,\n                                 x_col='file_paths',\n                                 y_col='labels',\n                                 image_data_generator=train_augmenter,\n                                 **train_consts)\n    \n    val_generator = DCMDataFrameIterator(dataframe=val_df,\n                                 x_col='file_paths',\n                                 y_col='labels',\n                                 image_data_generator=None,\n                                 **val_consts)\n    \n    test_generator = DCMDataFrameIterator(dataframe=test_df,\n                                 x_col='file_paths',\n                                 y_col='labels',\n                                 image_data_generator=None,\n                                 **test_consts)\n    return train_generator, val_generator, test_generator","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.184386Z","iopub.execute_input":"2021-12-21T07:40:06.184905Z","iopub.status.idle":"2021-12-21T07:40:06.194197Z","shell.execute_reply.started":"2021-12-21T07:40:06.184874Z","shell.execute_reply":"2021-12-21T07:40:06.193416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build Model\nReplace the following lines in the code below:\n1. model = VGG16(include_top=False,weights=weights_path)\n2. model = Model(model.inputs, outputs, name=\"VGG16\")\nBy default this is set to VGG16 but you can replace this with any model that you would like to run","metadata":{}},{"cell_type":"code","source":"def build_model(weights_path):\n    \n    #Replace this with the model you would like to run\n    #model = VGG16(include_top=False,weights=weights_path)\n    #model = tf.keras.applications.ResNet50(include_top=False,weights=weights_path)\n    model = EfficientNetB3(include_top=False, weights=weights_path)\n    \n    model.trainable = False\n    \n    x = GlobalAveragePooling2D(name=\"avg_pool\")(model.output)\n    x = BatchNormalization()(x)\n\n    top_dropout_rate = 0.4\n    x = Dropout(top_dropout_rate)(x)\n    x = Dense(32, activation=\"relu\")(x)\n    x = BatchNormalization()(x)\n    x = Dropout(top_dropout_rate)(x)\n    outputs = Dense(1, activation=\"sigmoid\", name=\"pred\")(x)\n    \n\n     #Replace this with the model you would like to run\n    #model = Model(model.inputs, outputs, name=\"VGG16\")\n    #model = Model(model.inputs, outputs, name=\"ResNet50\")\n    model = Model(model.inputs, outputs, name=\"EfficientNet\")\n    \n\n    optimizer =  tf.keras.optimizers.Adam(learning_rate=1e-3)\n    \n    model.compile(optimizer=optimizer, loss=\"binary_crossentropy\", metrics=[\"binary_accuracy\",AUC()])\n    #print(model.summary())\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.195352Z","iopub.execute_input":"2021-12-21T07:40:06.196141Z","iopub.status.idle":"2021-12-21T07:40:06.206863Z","shell.execute_reply.started":"2021-12-21T07:40:06.196099Z","shell.execute_reply":"2021-12-21T07:40:06.206285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train Model\nReplace the following lines in the code below:\n#Replace this with appropriate path to the weights of the model you would like to run\n1. model = build_model(\"../input/vgg16-weights/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\")","metadata":{}},{"cell_type":"code","source":"checkpoint_filepath = 'best_model.h5'\n\ndef train_model(model_name, train_generator, val_generator, epochs):\n    \n    print('training', model_name)\n    \n    #Replace this with appropriate path to the weights of the model you would like to run\n    #model = build_model(\"../input/vgg16-weights/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\")\n    model = build_model(\"../input/efficentnet-b0b5-tensorflow-24-notop/efficientnet-b3_tf24_imagenet_1000_notop.h5\")\n    #model = build_model(\"../input/resnet/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\")\n\n    #callbacks\n    \n    checkpoint_cb=ModelCheckpoint(\n        filepath=checkpoint_filepath,\n        save_weights_only=False,\n        monitor='val_loss',\n        mode='min',\n        save_best_only=True,\n        save_freq='epoch',\n        verbose=1)\n    \n    early_stopping_cb = tf.keras.callbacks.EarlyStopping(monitor='val_loss',\n                                                  patience=5,\n                                                  mode='min',\n                                                  verbose=1,\n                                                  restore_best_weights=True)\n\n    reduce_lr_cb=ReduceLROnPlateau(monitor='val_loss', factor=0.5,\n                                   patience=2, min_lr=0.00001,\n                                  verbose=1)\n\n    history = model.fit(\n                        train_generator,\n                        steps_per_epoch=len(train_generator),\n                        validation_data=val_generator,\n                        validation_steps=len(val_generator),\n                        epochs=epochs,\n                        workers=2,\n                        callbacks=[checkpoint_cb, reduce_lr_cb, early_stopping_cb]\n                        )\n    print(history.history.keys())\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.208051Z","iopub.execute_input":"2021-12-21T07:40:06.208511Z","iopub.status.idle":"2021-12-21T07:40:06.218036Z","shell.execute_reply.started":"2021-12-21T07:40:06.208472Z","shell.execute_reply":"2021-12-21T07:40:06.217393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## The code below computes accuracy at MRI Level. \ntest loss, test acc, test AUC are the test results for a particular scan type","metadata":{}},{"cell_type":"code","source":"%%time\nall_test_preds = []\nall_test_preds_with_ids = []\n\nfor mt in ['flair', 't1w', 't1wce', 't2w']:\n    train_df, val_df = get_train_val_dataframe(mt)\n    test_df = get_test_dataframe(mt)\n    train_g, val_g, test_g = get_data_generators(train_df, val_df, test_df)\n    best_model =  train_model(mt, train_g, val_g, epochs=20)\n    results = best_model.evaluate(test_g, steps=len(test_g))\n    #test results for a scan types\n    print(f\"test loss, test acc, test AUC: {results}\")\n    test_pred = best_model.predict(test_g, steps=len(test_g))\n    test_df['pred_y'] = test_pred\n    # aggregate the predictions on all image for each person -- needed to calculate accuracy at patient level\n    mean_pred = test_pred.mean()\n    test_pred_agg = test_df.groupby('patient_ids').apply(\n        lambda x: x['pred_y'].max()\n        if (x['pred_y'].max() - mean_pred) > (mean_pred - x['pred_y'].min()) \n        else x['pred_y'].min())\n    all_test_preds.append(test_pred_agg.values)\n    all_test_preds_with_ids.append(test_pred_agg)","metadata":{"execution":{"iopub.status.busy":"2021-12-21T07:40:06.219250Z","iopub.execute_input":"2021-12-21T07:40:06.219700Z","iopub.status.idle":"2021-12-21T14:08:47.851410Z","shell.execute_reply.started":"2021-12-21T07:40:06.219662Z","shell.execute_reply":"2021-12-21T14:08:47.847941Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Get Accuracy at Patient Level","metadata":{}},{"cell_type":"code","source":"\nall_test_preds = np.array(all_test_preds)\n\ngrouped_df = test_df.groupby('patient_ids')\ngrouped_df = test_df.groupby('patient_ids').apply(lambda x:  pd.to_numeric(x['labels'],errors='coerce').mean())\na = np.array(grouped_df)\nresults = all_test_preds.mean(0)\n\n#labels = pd.to_numeric(test_df['labels'],errors='coerce')\n\ncount = 0\nfor idx in range(len(a)):\n    val = results[idx]\n    if (val >= 0.5 and a[idx] >= 0.5):\n        count = count + 1\n    if (val <= 0.5 and a[idx] < 0.5):\n        count = count + 1\naccuracy = count/len(results)\nprint('Accuracy at Patient Level:')\nprint(accuracy)","metadata":{"execution":{"iopub.status.busy":"2021-12-21T14:08:47.857942Z","iopub.execute_input":"2021-12-21T14:08:47.858345Z","iopub.status.idle":"2021-12-21T14:08:47.906835Z","shell.execute_reply.started":"2021-12-21T14:08:47.858271Z","shell.execute_reply":"2021-12-21T14:08:47.905761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.hist(all_test_preds.mean(0))","metadata":{"execution":{"iopub.status.busy":"2021-12-21T14:08:47.913238Z","iopub.execute_input":"2021-12-21T14:08:47.914154Z","iopub.status.idle":"2021-12-21T14:08:48.309960Z","shell.execute_reply.started":"2021-12-21T14:08:47.914106Z","shell.execute_reply":"2021-12-21T14:08:48.308980Z"},"trusted":true},"execution_count":null,"outputs":[]}]}