{"cells":[{"metadata":{"_uuid":"aa8401d73c7a19e1a43fdd6a992ea9dcb60039a2"},"cell_type":"markdown","source":"##  U-net based with Boundary loss for COVID detection\n* This kernel use Attention structure\n\n* Boundary loss reference by this [GITHUB]( https://github.com/LIVIAETS/boundary-loss)\n\n# * Let's see how it performs!!"},{"metadata":{},"cell_type":"markdown","source":"# Read dicom files"},{"metadata":{"trusted":true},"cell_type":"code","source":"import glob\nimport pandas  as pd\nimport numpy   as np\nimport nibabel as nib\nimport matplotlib.pyplot as plt\nimport pickle\nimport cv2\nimport tensorflow as tf","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Read and examine metadata\ndata = pd.read_csv('../input/covid19-ct-scans/metadata.csv')\ndata.sample(5)\ndata.head(3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_nii(filepath):\n    '''\n    Reads .nii file and returns pixel array\n    '''\n    ct_scan = nib.load(filepath)\n    #n1_header = ct_scan.dataobj.slope\n    array   = ct_scan.get_fdata()\n    array   = np.rot90(np.array(array)) #this data needs to rotate 90 degrees \n    return(array)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Read sample\nsample_ct = read_nii(data.loc[2,'ct_scan'])\nsample_mask= read_nii(data.loc[2, 'infection_mask'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print (sample_ct.shape, np.unique(sample_mask))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Check HU transform is done or not"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nimgs_to_process = sample_ct[...,1]\n\nplt.hist(imgs_to_process.flatten(), bins=50, color='c')\nplt.xlabel(\"Hounsfield Units (HU)\")\nplt.ylabel(\"Frequency\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# No need to transform"},{"metadata":{},"cell_type":"markdown","source":"![Hu scale](https://www.researchgate.net/profile/M_Kholief/publication/306033192/figure/fig2/AS:613926819610632@1523382968429/The-Hounsfield-scale-of-CT-numbers.png)\n\n# Since Lungs HU is in interval[-400, 600], we could discard unnecessary pixels"},{"metadata":{},"cell_type":"markdown","source":"Check if virus is filtered or not"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_ct_windowed = np.copy(sample_ct)\nsample_ct_windowed[sample_ct_windowed <= -600] = -600\nsample_ct_windowed[sample_ct_windowed >= 400] = 400\n\nfig = plt.figure(figsize = (18,15))\n\nplt.subplot(1,6,1)\nplt.imshow(sample_ct[...,100], cmap = 'bone')\nplt.title('original CT')\n\nplt.subplot(1,6,2)\nplt.imshow(sample_mask[...,100], cmap = 'nipy_spectral')\n#plt.imshow(sample_ct_windowed[...,20],alpha = 0.5, cmap = \"bone\")\nplt.title('original infection mask')\n\n\n\nplt.subplot(1,6,3)\nplt.imshow(np.rot90(sample_ct[:, 100, :],1), cmap = 'bone')\nplt.title('original CT')\n\nplt.subplot(1,6,4)\nplt.imshow(np.rot90(sample_mask[:, 100, :],1), cmap = 'nipy_spectral')\nplt.imshow(np.rot90(sample_ct_windowed[:, 100, :],1), alpha = 0.5, cmap = \"bone\")\nplt.title('original infection mask')\n\n\n\nplt.subplot(1,6,5)\nplt.imshow(np.rot90(sample_ct[100],1), cmap = 'bone')\nplt.title('original CT')\n\nplt.subplot(1,6,6)\nplt.imshow(np.rot90(sample_mask[100],1), cmap = 'nipy_spectral')\nplt.imshow(np.rot90(sample_ct_windowed[100],1),alpha = 0.5, cmap = \"bone\")\nplt.title('original infection mask')\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Not filtered, good!"},{"metadata":{},"cell_type":"markdown","source":"# Now we have two choices, we could take lungs mask from CT as input or just use CT images"},{"metadata":{},"cell_type":"markdown","source":"# This time I choose using CT directly "},{"metadata":{"trusted":true},"cell_type":"code","source":"CT = []\nMask = []\nimg_size = 128\nmax = 0\n\n\nfor case in range(len(data)): #Concat all cases to list\n    ct = read_nii(data['ct_scan'][case])\n    mask = read_nii(data['infection_mask'][case])\n    if (max < np.max(ct)):\n        max = np.max(ct)\n    \n    \n    for imgsize in range(ct.shape[2]): #Convert pixals to 1-d array\n        \n        ct_img = cv2.resize(ct[..., imgsize], dsize = (img_size, img_size),interpolation = cv2.INTER_AREA).astype('float64')\n        \n        mask_img = cv2.resize(mask[..., imgsize],dsize=(img_size, img_size),interpolation = cv2.INTER_AREA).astype('uint8')\n        CT.append(ct_img[..., np.newaxis])\n        Mask.append(mask_img[..., np.newaxis])\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"CT = np.array(CT)\n\nMask = np.array(Mask)\nprint (np.unique(Mask))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print (CT.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Show data image"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize = (18,15))\n\nplt.subplot(1,2,1)\nplt.imshow(CT[100][...,0], cmap = 'bone')\nplt.title('original CT')\n\nplt.subplot(1,2,2)\nplt.imshow(CT[100][...,0], cmap = 'bone')\nplt.imshow(Mask[100][...,0],alpha = 0.5, cmap = \"nipy_spectral\")\nplt.title('original infection mask')\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# --------------------------------------------------"},{"metadata":{},"cell_type":"markdown","source":"# Normalize pixel values in range [0,1] is a good idea before training"},{"metadata":{"trusted":true},"cell_type":"code","source":"mins = 0.5*max\nmaxs = 99.5*max\nnorm_data = (CT-mins)/(maxs-mins)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (9,9))\n\nplt.imshow(norm_data[100][...,0], cmap = 'bone')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print (np.unique(norm_data), np.unique(CT))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Split into training and validation groups"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nCT_train, CT_test, Mask_train, Mask_test = train_test_split(norm_data, Mask, test_size = 0.1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ba08494eb9736ec3556b7c879143cdcdea89febf"},"cell_type":"markdown","source":"# Build Attention Unet\nHere we use a slight deviation on the U-Net standard"},{"metadata":{"trusted":true,"_uuid":"e22ae327bed4fbb6fcf604b8755f61f2efbf066e"},"cell_type":"code","source":"class attention_unet():\n  def __init__(self,img_rows=128,img_cols=128):\n    self.img_rows=img_rows\n    self.img_cols=img_cols\n    self.img_shape=(self.img_rows,self.img_cols,1)\n    self.df=64\n    self.uf=64\n    \n  def build_unet(self):\n    def conv2d(layer_input,filters,dropout_rate=0,bn=False):\n      d=layers.Conv2D(filters,kernel_size=(3,3),strides=(1,1),padding='same')(layer_input)\n      if bn:\n        d=layers.BatchNormalization()(d)\n      d=layers.Activation('relu')(d)\n      \n      d=layers.Conv2D(filters,kernel_size=(3,3),strides=(1,1),padding='same')(d)\n      if bn:\n        d=layers.BatchNormalization()(d)\n      d=layers.Activation('relu')(d)\n      \n      if dropout_rate:\n        d=layers.Dropout(dropout_rate)(d)\n      \n      return d\n    \n    def deconv2d(layer_input,filters,bn=False):\n      u=layers.UpSampling2D((2,2))(layer_input)\n      u=layers.Conv2D(filters,kernel_size=(3,3),strides=(1,1),padding='same')(u)\n      if bn:\n        u=layers.BatchNormalization()(u)\n      u=layers.Activation('relu')(u)\n      \n      return u\n    \n    def attention_block(F_g,F_l,F_int,bn=False):\n      g=layers.Conv2D(F_int,kernel_size=(1,1),strides=(1,1),padding='valid')(F_g)\n      if bn:\n        g=layers.BatchNormalization()(g)\n      x=layers.Conv2D(F_int,kernel_size=(1,1),strides=(1,1),padding='valid')(F_l)\n      if bn:\n        x=layers.BatchNormalization()(x)\n#       print(g.shape)\n#       print(x.shape)\n      psi=layers.Add()([g,x])\n      psi=layers.Activation('relu')(psi)\n      \n      psi=layers.Conv2D(1,kernel_size=(1,1),strides=(1,1),padding='valid')(psi)\n      \n      if bn:\n        psi=layers.BatchNormalization()(psi)\n      psi=layers.Activation('sigmoid')(psi)\n      \n      return layers.Multiply()([F_l,psi])\n\n\n    #def con_bt(inputs):\n        \n     #   conv1=conv2d(inputs,self.df)\n\n        \n      #  return conv1\n    \n    \n    inputs=layers.Input(shape=self.img_shape)\n    \n    \n    #concat1 = layers.Concatenate()([a,s,c])\n    \n    pool1=layers.MaxPooling2D((2,2))(inputs)\n    \n    \n    conv1=conv2d(pool1,self.df)\n    pool1=layers.MaxPooling2D((2,2))(conv1)\n    \n    conv2=conv2d(pool1,self.df*2,bn=True)\n    pool2=layers.MaxPooling2D((2,2))(conv2)\n    \n    conv3=conv2d(pool2,self.df*4,bn=True)\n    pool3=layers.MaxPooling2D((2,2))(conv3)\n    \n    conv4=conv2d(pool3,self.df*8,dropout_rate=0.5,bn=True)\n    pool4=layers.MaxPooling2D((2,2))(conv4)\n    \n    conv5=conv2d(pool4,self.df*16,dropout_rate=0.5,bn=True)\n    \n    up6=deconv2d(conv5,self.uf*8,bn=True)\n    conv6=attention_block(up6,conv4,self.uf*8,bn=True)\n    up6=layers.Concatenate()([up6,conv6])\n    conv6=conv2d(up6,self.uf*8)\n    \n    up7=deconv2d(conv6,self.uf*4,bn=True)\n    conv7=attention_block(up7,conv3,self.uf*4,bn=True)\n    up7=layers.Concatenate()([up7,conv7])\n    conv7=conv2d(up7,self.uf*4)\n    \n    up8=deconv2d(conv7,self.uf*2,bn=True)\n    conv8=attention_block(up8,conv2,self.uf*2,bn=True)\n    up8=layers.Concatenate()([up8,conv8])\n    conv8=conv2d(up8,self.uf*2)\n    \n    up9=deconv2d(conv8,self.uf,bn=True)\n    conv9=attention_block(up9,conv1,self.uf,bn=True)\n    up9=layers.Concatenate()([up9,conv9])\n    conv9=conv2d(up9,self.uf)\n    \n    outputs=layers.Conv2D(1,kernel_size=(1,1),strides=(1,1),activation='sigmoid')(conv9)\n    \n    new_up= deconv2d(outputs,self.uf,bn=True)\n        \n    \n    new_conv1=conv2d(new_up,self.df)\n    new_pool1=layers.MaxPooling2D((2,2))(new_conv1)\n    \n    new_conv2=conv2d(new_pool1,self.df*2,bn=True)\n    new_pool2=layers.MaxPooling2D((2,2))(new_conv2)\n    \n    new_conv3=conv2d(new_pool2,self.df*4,bn=True)\n    new_pool3=layers.MaxPooling2D((2,2))(new_conv3)\n    \n    new_conv4=conv2d(new_pool3,self.df*8,dropout_rate=0.5,bn=True)\n    new_pool4=layers.MaxPooling2D((2,2))(new_conv4)\n    \n    new_conv5=conv2d(new_pool4,self.df*16,dropout_rate=0.5,bn=True)\n    \n    new_up6=deconv2d(new_conv5,self.uf*8,bn=True)\n    new_conv6=attention_block(new_up6,new_conv4,self.uf*8,bn=True)\n    new_up6=layers.Concatenate()([new_up6,new_conv6])\n    new_conv6=conv2d(new_up6,self.uf*8)\n    \n    new_up7=deconv2d(new_conv6,self.uf*4,bn=True)\n    new_conv7=attention_block(new_up7,new_conv3,self.uf*4,bn=True)\n    new_up7=layers.Concatenate()([new_up7,new_conv7])\n    new_conv7=conv2d(new_up7,self.uf*4)\n    \n    new_up8=deconv2d(new_conv7,self.uf*2,bn=True)\n    new_conv8=attention_block(new_up8,new_conv2,self.uf*2,bn=True)\n    new_up8=layers.Concatenate()([new_up8,new_conv8])\n    new_conv8=conv2d(new_up8,self.uf*2)\n    \n    new_up9=deconv2d(new_conv8,self.uf,bn=True)\n    new_conv9=attention_block(new_up9,new_conv1,self.uf,bn=True)\n    new_up9=layers.Concatenate()([new_up9,new_conv9])\n    new_conv9=conv2d(new_up9,self.uf)\n    \n    \n    outputs2=layers.Conv2D(1,kernel_size=(1,1),strides=(1,1),activation='sigmoid')(new_conv9)\n\n    \n    \n    \n    model=Model(inputs= inputs, outputs=outputs2)\n    \n    return model\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Define BatchNormalization"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.models import *\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.optimizers import *\nimport keras.layers as layers\nfrom keras.models import Model\n\n\n\n# batchnormalization\ndef BatchActivate(x):\n    x = BatchNormalization()(x)\n    x = Activation('relu')(x)\n    return x\n# block\ndef convolution_block(x, filters, size, strides=(1,1), padding='same', activation=True):\n    x = Conv2D(filters, size, strides=strides, padding=padding)(x)\n    if activation == True:\n        x = BatchActivate(x)\n    return x\n# residual_block\ndef residual_block(blockInput, num_filters=16, batch_activate = False):\n    x = BatchActivate(blockInput)\n    x = convolution_block(x, num_filters, (3,3) )\n    x = convolution_block(x, num_filters, (3,3), activation=False)\n    x = Add()([x, blockInput])\n    if batch_activate:\n        x = BatchActivate(x)\n    return x\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loss functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.losses import binary_crossentropy\nfrom keras import backend as K\n\ndef dice_loss(y_true, y_pred):\n    smooth = 1.\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = y_true_f * y_pred_f\n    score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n    return 1. - score\n\ndef bce_dice_loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n\ndef bce_logdice_loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) - K.log(1. - dice_loss(y_true, y_pred))\n\ndef weighted_bce_loss(y_true, y_pred, weight):\n    epsilon = 1e-7\n    y_pred = K.clip(y_pred, epsilon, 1. - epsilon)\n    logit_y_pred = K.log(y_pred / (1. - y_pred))\n    loss = weight * (logit_y_pred * (1. - y_true) + \n                     K.log(1. + K.exp(-K.abs(logit_y_pred))) + K.maximum(-logit_y_pred, 0.))\n    return K.sum(loss) / K.sum(weight)\n\ndef weighted_dice_loss(y_true, y_pred, weight):\n    smooth = 1.\n    w, m1, m2 = weight, y_true, y_pred\n    intersection = (m1 * m2)\n    score = (2. * K.sum(w * intersection) + smooth) / (K.sum(w * m1) + K.sum(w * m2) + smooth)\n    loss = 1. - K.sum(score)\n    return loss\n\ndef weighted_bce_dice_loss(y_true, y_pred):\n    y_true = K.cast(y_true, 'float32')\n    y_pred = K.cast(y_pred, 'float32')\n    # if we want to get same size of output, kernel size must be odd\n    averaged_mask = K.pool2d(\n            y_true, pool_size=(50, 50), strides=(1, 1), padding='same', pool_mode='avg')\n    weight = K.ones_like(averaged_mask)\n    w0 = K.sum(weight)\n    weight = 5. * K.exp(-5. * K.abs(averaged_mask - 0.5))\n    w1 = K.sum(weight)\n    weight *= (w0 / w1)\n    loss = weighted_bce_loss(y_true, y_pred, weight) + dice_loss(y_true, y_pred)\n    return loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.optimizers import Adam\n\ndef dice_coef(y_true, y_pred, smooth=1):\n    intersection = K.sum(y_true * y_pred, axis=[1,2,3])\n    union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3])\n    return K.mean( (2. * intersection + smooth) / (union + smooth), axis=0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Implement of \"Boundary loss for highly unbalanced segmentation\""},{"metadata":{"trusted":true},"cell_type":"code","source":"from scipy.ndimage import distance_transform_edt as distance\n\n\ndef calc_dist_map(seg):\n    res = np.zeros_like(seg)\n    posmask = seg.astype(np.bool)\n\n    if posmask.any():\n        negmask = ~posmask\n        res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask\n\n    return res\n\n\ndef calc_dist_map_batch(y_true):\n    y_true_numpy = y_true\n    return np.array([calc_dist_map(y)\n                     for y in y_true_numpy]).astype(np.float32)\n\n\ndef surface_loss_keras(y_true, y_pred):\n    y_true_dist_map = tf.py_func(func=calc_dist_map_batch,\n                                     inp=[y_true],\n                                     Tout=tf.float32)\n    multipled = y_pred * y_true_dist_map\n    return K.mean(multipled)\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, Callback\n\n\nclass AlphaScheduler(Callback):\n  def init(self, alpha, update_fn):\n    self.alpha = alpha\n    self.update_fn = update_fn\n  def on_epoch_end(self, epoch, logs=None):\n    updated_alpha = self.update_fn(K.get_value(self.alpha))\n\nalpha = K.variable(1, dtype='float32')\n\ndef update_alpha(value):\n  return np.clip(value - 0.01, 0.01, 1)\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4710a5f4ebbb37d979acb0e9ddb18a0284d8e1e7"},"cell_type":"markdown","source":"## Define Loss function\n \n# We should considering both boundary loss and weighted binary cross entropy dice loss"},{"metadata":{"trusted":true},"cell_type":"code","source":"def gl_sl_wrapper(alpha):\n    def gl_sl(y_true, y_pred):\n        return alpha* weighted_bce_dice_loss(y_true, y_pred) +  (1-alpha)* surface_loss_keras(y_true, y_pred)\n    return gl_sl","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9a9f308bebfc7a8f067ca61bde886d33bd0e4649"},"cell_type":"markdown","source":"## Set Training Check Point"},{"metadata":{"trusted":true,"_uuid":"3128f14c1f1b86e8b383caea926b5f16d4f5094b"},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nweight_path=\"{}_weights.best.hdf5\".format('model')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_dice_coef', verbose=1, \n                             save_best_only=True, mode='max', save_weights_only = True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_dice_coef', factor=0.5, \n                                   patience=3, \n                                   verbose=1, mode='max', epsilon=0.0001, cooldown=2, min_lr=1e-6)\nearly = EarlyStopping(monitor=\"val_dice_coef\", \n                      mode=\"max\", \n                      patience=15) # probably needs to be more patient, but kaggle time is limited\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"50df57cf454854540c1594f0ed7118d3d0427470"},"cell_type":"markdown","source":"## Comiple model"},{"metadata":{"trusted":true},"cell_type":"code","source":"Net=attention_unet()\nunet=Net.build_unet()\n\nunet.compile(loss=gl_sl_wrapper(alpha),\n             optimizer=Adam(1e-4),\n             metrics=[dice_coef, 'binary_accuracy'])\n\nunet.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d09b8deb89b59f2dfb10e664c34dd9514ec0a06d"},"cell_type":"markdown","source":"## Start Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 100\nBS = 16\n\nfrom keras.preprocessing.image import ImageDataGenerator\n\n# construct the training image generator for data augmentation\naug = ImageDataGenerator(rotation_range=20, zoom_range=0.15,\n    width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15,\n    horizontal_flip=True, fill_mode=\"nearest\")\n\n# train the network\nH = unet.fit_generator(aug.flow(CT_train, Mask_train, batch_size=BS),\n    validation_data=(CT_test, Mask_test), steps_per_epoch=len(CT_train) // BS,\n    epochs=EPOCHS, verbose=1,shuffle=True, callbacks=[checkpoint])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ce1167e9f09200f537e61f93f486168a13be1711"},"cell_type":"code","source":"\nunet.load_weights(weight_path)\nunet.save('model.h5')\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0688eacb61c50be9b73f854592dd4066e66300a8"},"cell_type":"markdown","source":"## Plot loss history"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Loss vs Epochs')\nplt.xlabel('Epochs')\nplt.ylabel('Dice loss')\nplt.legend(['Train', 'Val'], loc = 'upper left')\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"17edb177402ae51651692511827a7e9d60646533"},"cell_type":"markdown","source":"# Run the test data"},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted = unet.predict(CT_test)\nfig = plt.figure(figsize = (18,15))\n\nplt.subplot(1,3,1)\nplt.imshow(CT_test[180][...,0], cmap = 'bone')\nplt.title('original CT image')\n\nplt.subplot(1,3,2)\nplt.imshow(CT_test[180][...,0], cmap = 'bone')\nplt.imshow(Mask_test[180][...,0],alpha = 0.5, cmap = \"nipy_spectral\")\nplt.title('original infection mask')\n\nplt.subplot(1,3,3)\nplt.imshow(CT_test[180][...,0], cmap = 'bone')\nplt.imshow(predicted[180][...,0],alpha = 0.5,cmap = \"nipy_spectral\")\nplt.title('predicted infection mask')","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}