{"cells":[{"metadata":{"id":"_OhaSGwhCFak","colab_type":"code","colab":{},"trusted":true,"collapsed":true},"cell_type":"code","source":"import os\nimport random\nimport csv\nimport pydicom\nimport numpy as np\nfrom skimage.transform import resize","execution_count":null,"outputs":[]},{"metadata":{"id":"oO_I_6mWDGa2","colab_type":"code","colab":{},"trusted":true,"collapsed":true},"cell_type":"code","source":"# empty dictionary\npneumonia_locations = {}\n# load table\nwith open(os.path.join('../input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv'), mode='r') as infile:\n    # open reader\n    reader = csv.reader(infile)\n    # skip header\n    next(reader, None)\n    # loop through rows\n    for rows in reader:\n        # retrieve information\n        filename = rows[0]\n        location = rows[1:5]\n        pneumonia = rows[5]\n        # if row contains pneumonia add label to dictionary\n        # which contains a list of pneumonia locations per filename\n        if pneumonia == '1':\n            # convert string to float to int\n            location = [int(float(i)) for i in location]\n            # save pneumonia location in dictionary\n            if filename in pneumonia_locations:\n                pneumonia_locations[filename].append(location)\n            else:\n                pneumonia_locations[filename] = [location]","execution_count":null,"outputs":[]},{"metadata":{"id":"_WfCrRD5i25V","colab_type":"code","outputId":"e89e7064-2edd-41ac-a362-8a2c15a7b7ed","colab":{"base_uri":"https://localhost:8080/","height":81},"trusted":true},"cell_type":"code","source":"import keras\nimport tensorflow as tf\nfrom keras.models import Model\nfrom keras.layers import Input, BatchNormalization, Activation, Dense, Dropout\nfrom keras.layers.convolutional import Conv2D, Conv2DTranspose\nfrom keras.layers.pooling import MaxPooling2D, GlobalMaxPool2D\nfrom keras.layers.merge import concatenate, add\nfrom keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom keras.optimizers import Adam","execution_count":null,"outputs":[]},{"metadata":{"id":"mOLF-cjYMis7","colab_type":"code","colab":{},"trusted":true,"collapsed":true},"cell_type":"code","source":"class generator_single_channel(keras.utils.Sequence):\n    \n    def __init__(self, folder, filenames, pneumonia_locations=None, batch_size=32,\n                 image_size=256, shuffle=True, predict=False):\n        self.folder = folder\n        self.filenames = filenames\n        self.pneumonia_locations = pneumonia_locations\n        self.batch_size = batch_size\n        self.image_size = image_size\n        self.shuffle = shuffle\n        self.predict = predict\n        self.on_epoch_end()\n        \n    def __load__(self, filename):\n        # load dicom file as numpy array\n        img = pydicom.dcmread(os.path.join(self.folder, filename)).pixel_array\n        # create empty mask\n        msk = np.zeros(img.shape)\n        # get filename without extension\n        filename = filename.split('.')[0]\n        # if image contains pneumonia\n        is_pneumonia = int(0)\n        if filename in self.pneumonia_locations:\n            # loop through pneumonia\n            is_pneumonia = int(1)\n            for location in self.pneumonia_locations[filename]:\n                # add 1's at the location of the pneumonia\n                x, y, w, h = location\n                msk[y:y+h, x:x+w] = 1\n        # resize both image and mask\n        img = resize(img, (self.image_size, self.image_size), mode='reflect')\n        msk = resize(msk, (self.image_size, self.image_size), mode='reflect')\n        # if augment then horizontal flip half the time\n        # if self.augment and random.random() > 0.5:\n        #     img = np.fliplr(img)\n        #     msk = np.fliplr(msk)\n\n        # add trailing channel dimension\n        img = np.expand_dims(img, axis=-1)\n        msk = np.expand_dims(msk, axis=-1)\n        is_pneumonia = np.array(is_pneumonia)\n\n        return img, msk\n    \n    def __loadpredict__(self, filename):\n        # load dicom file as numpy array\n        img = pydicom.dcmread(os.path.join(self.folder, filename)).pixel_array\n        # resize image\n        img = resize(img, (self.image_size, self.image_size), mode='reflect')\n        # add trailing channel dimension\n        img = np.expand_dims(img, axis=-1)\n        \n        return img\n        \n    def __getitem__(self, index):\n        # select batch\n        filenames = self.filenames[index*self.batch_size:(index+1)*self.batch_size]\n        # predict mode: return images and filenames\n        if self.predict:\n            # load files\n            imgs = [self.__loadpredict__(filename) for filename in filenames]\n            # create numpy batch\n            imgs = np.array(imgs)\n            \n            return imgs,filenames\n        # train mode: return images and masks\n        else:\n            # load files\n            items = [self.__load__(filename) for filename in filenames]\n            # unzip images and masks\n            imgs, msks = zip(*items)\n            # create numpy batch\n            imgs = np.array(imgs)\n            msks = np.array(msks)\n\n            return imgs,msks\n        \n    def on_epoch_end(self):\n        if self.shuffle:\n            random.shuffle(self.filenames)\n        \n    def __len__(self):\n        if self.predict:\n            # return everything\n            return int(np.ceil(len(self.filenames) / self.batch_size))\n        else:\n            # return full batches only\n            return int(len(self.filenames) / self.batch_size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"def conv2d_block(input_tensor, n_filters, kernel_size=3, batchnorm=True):\n    # first layer\n    x = Conv2D(filters=n_filters, kernel_size=(kernel_size, kernel_size), kernel_initializer=\"he_normal\",\n               padding=\"same\")(input_tensor)\n    if batchnorm:\n        x = BatchNormalization()(x)\n    x = Activation(\"relu\")(x)\n    # second layer\n    x = Conv2D(filters=n_filters, kernel_size=(kernel_size, kernel_size), kernel_initializer=\"he_normal\",\n               padding=\"same\")(x)\n    if batchnorm:\n        x = BatchNormalization()(x)\n    x = Activation(\"relu\")(x)\n    return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"def get_unet(input_img, n_filters=16, dropout=0.5, batchnorm=True):\n    # contracting path\n    c1 = conv2d_block(input_img, n_filters=n_filters*1, kernel_size=3, batchnorm=batchnorm)\n    p1 = MaxPooling2D((2, 2)) (c1)\n    p1 = Dropout(dropout*0.5)(p1)\n\n    c2 = conv2d_block(p1, n_filters=n_filters*2, kernel_size=3, batchnorm=batchnorm)\n    p2 = MaxPooling2D((2, 2)) (c2)\n    p2 = Dropout(dropout)(p2)\n\n    c3 = conv2d_block(p2, n_filters=n_filters*4, kernel_size=3, batchnorm=batchnorm)\n    p3 = MaxPooling2D((2, 2)) (c3)\n    p3 = Dropout(dropout)(p3)\n\n    c4 = conv2d_block(p3, n_filters=n_filters*8, kernel_size=3, batchnorm=batchnorm)\n    p4 = MaxPooling2D(pool_size=(2, 2)) (c4)\n    p4 = Dropout(dropout)(p4)\n    \n    c5 = conv2d_block(p4, n_filters=n_filters*16, kernel_size=3, batchnorm=batchnorm)\n    \n    # expansive path\n    u6 = Conv2DTranspose(n_filters*8, (3, 3), strides=(2, 2), padding='same') (c5)\n    u6 = concatenate([u6, c4])\n    u6 = Dropout(dropout)(u6)\n    c6 = conv2d_block(u6, n_filters=n_filters*8, kernel_size=3, batchnorm=batchnorm)\n\n    u7 = Conv2DTranspose(n_filters*4, (3, 3), strides=(2, 2), padding='same') (c6)\n    u7 = concatenate([u7, c3])\n    u7 = Dropout(dropout)(u7)\n    c7 = conv2d_block(u7, n_filters=n_filters*4, kernel_size=3, batchnorm=batchnorm)\n\n    u8 = Conv2DTranspose(n_filters*2, (3, 3), strides=(2, 2), padding='same') (c7)\n    u8 = concatenate([u8, c2])\n    u8 = Dropout(dropout)(u8)\n    c8 = conv2d_block(u8, n_filters=n_filters*2, kernel_size=3, batchnorm=batchnorm)\n\n    u9 = Conv2DTranspose(n_filters*1, (3, 3), strides=(2, 2), padding='same') (c8)\n    u9 = concatenate([u9, c1], axis=3)\n    u9 = Dropout(dropout)(u9)\n    c9 = conv2d_block(u9, n_filters=n_filters*1, kernel_size=3, batchnorm=batchnorm)\n    \n    outputs = Conv2D(1, (1, 1), activation='sigmoid') (c9)\n    model = Model(inputs=[input_img], outputs=[outputs])\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"input_img = Input((224, 224, 1), name='img')\nmodel = get_unet(input_img, n_filters=16, dropout=0.05, batchnorm=True)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"nSF8U7dqpv4Z","colab_type":"code","colab":{},"trusted":true,"collapsed":true},"cell_type":"code","source":"def iou_loss(y_true, y_pred):\n    y_true = tf.reshape(y_true, [-1])\n    y_pred = tf.reshape(y_pred, [-1])\n    intersection = tf.reduce_sum(y_true * y_pred)\n    score = (intersection + 1.) / (tf.reduce_sum(y_true) + tf.reduce_sum(y_pred) - intersection + 1.)\n    return 1 - score\n\n# def bce_loss(y_true,y_pred):\n#   return keras.losses.binary_crossentropy(y_true,y_pred)\n\n# mean iou as a metric\ndef mean_iou(y_true, y_pred):\n    y_pred = tf.round(y_pred)\n    intersect = tf.reduce_sum(y_true * y_pred)\n    union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred)\n    smooth = tf.ones(tf.shape(intersect))\n    return tf.reduce_mean((intersect + smooth) / (union - intersect + smooth))","execution_count":null,"outputs":[]},{"metadata":{"id":"bYOiB8EDSaUO","colab_type":"code","outputId":"6ec381f1-a249-4694-9cb1-686af07738e4","colab":{"base_uri":"https://localhost:8080/","height":158},"trusted":true,"collapsed":true},"cell_type":"code","source":"model.compile(optimizer='adam',\n                     loss=iou_loss,\n                     metrics=[mean_iou,'accuracy'])\n\ncheckpoint = keras.callbacks.ModelCheckpoint(\"pnuemonia-detection-unet_{val_loss:.4f}.h5\",monitor='val_loss',\n                             verbose=1, save_best_only=False,save_weights_only=True, mode=\"auto\")\n\nes = keras.callbacks.EarlyStopping(monitor='val_loss', patience=4)","execution_count":null,"outputs":[]},{"metadata":{"id":"Sn9sNJQrCAQG","colab_type":"code","outputId":"c53eae7d-dded-41a5-d698-cfa77f7fe1aa","colab":{"base_uri":"https://localhost:8080/","height":51},"trusted":true},"cell_type":"code","source":"folder = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images'\nfilenames = os.listdir(folder)\nrandom.shuffle(filenames)\n# split into train and validation filenames\nn_valid_samples = 6000\ntrain_filenames = filenames[n_valid_samples:]\nvalid_filenames = filenames[:n_valid_samples]\nprint('n train samples', len(train_filenames))\nprint('n valid samples', len(valid_filenames))\nn_train_samples = len(filenames) - n_valid_samples","execution_count":null,"outputs":[]},{"metadata":{"id":"qY6utdJzmabd","colab_type":"code","colab":{},"trusted":true,"collapsed":true},"cell_type":"code","source":"# create train and validation generators\ntrain_gen_simple = generator_single_channel(folder, train_filenames, pneumonia_locations, batch_size=64, image_size=224, shuffle=True, predict=False)\nvalid_gen_simple = generator_single_channel(folder, valid_filenames, pneumonia_locations, batch_size=64, image_size=224, shuffle=False, predict=False)","execution_count":null,"outputs":[]},{"metadata":{"id":"ONMrkQUfT9P_","colab_type":"code","outputId":"191e78ed-f96d-4174-961c-6cb3fdc56eb4","colab":{"base_uri":"https://localhost:8080/","height":349},"trusted":true,"collapsed":true},"cell_type":"code","source":"history = model.fit_generator(train_gen_simple, validation_data=valid_gen_simple, callbacks=[checkpoint,es], epochs=15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Model Training Metrics"},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"import pandas as pd \nfrom skimage.segmentation import mark_boundaries\nfrom skimage.measure import label, regionprops\nfrom skimage.util.montage import montage2d as montage\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"plt.figure(figsize=(12,4))\nplt.subplot(131)\nplt.plot(history.epoch, history.history[\"loss\"], label=\"Train loss\")\nplt.plot(history.epoch, history.history[\"val_loss\"], label=\"Valid loss\")\nplt.legend()\nplt.subplot(132)\nplt.plot(history.epoch, history.history[\"acc\"], label=\"Train accuracy\")\nplt.plot(history.epoch, history.history[\"val_acc\"], label=\"Valid accuracy\")\nplt.legend()\nplt.subplot(133)\nplt.plot(history.epoch, history.history[\"mean_iou\"], label=\"Train iou\")\nplt.plot(history.epoch, history.history[\"val_mean_iou\"], label=\"Valid iou\")\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"lZmAtJ_6AAIk","colab_type":"text"},"cell_type":"markdown","source":"### Evaluating Classfication Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls ../input/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"model.load_weights('../input/capstone-rsna-pneumonia-detection-using-u-net/pnuemonia-detection-unet_0.6716.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"valid_gen_pred = generator_single_channel(folder, valid_filenames, pneumonia_locations,\n                                            batch_size=64, image_size=224, shuffle=False, predict=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"validation_preds = model.predict_generator(valid_gen_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"validation_preds_flat = np.reshape(validation_preds,newshape=(len(validation_preds),224,224))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(validation_preds_flat[0],cmap='Blues')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cv2.imwrite('abc.png',validation_preds_flat[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"im2 = cv2.cvtColor(cv2.imread('abc.png'),cv2.COLOR_BGR2GRAY)\nret,thresh = cv2.threshold(im2,0.9,1,cv2.THRESH_BINARY)\ncontours = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"from skimage import measure\nfrom skimage.transform import resize","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"folder = '../input/rsna-pneumonia-detection-challenge/stage_2_test_images'\ntest_filenames = os.listdir(folder)\nprint('n test samples:', len(test_filenames))\n\n# create test generator with predict flag set to True\ntest_gen = generator_single_channel(folder, test_filenames, None, batch_size=20, image_size=224, shuffle=False, predict=True)\n\n# create submission dictionary\nsubmission_dict = {}\n# loop through testset\nfor imgs, filenames in test_gen:\n    # predict batch of images\n    preds = model.predict(imgs)\n    # loop through batch\n    for pred, filename in zip(preds, filenames):\n        # resize predicted mask\n        pred = resize(pred, (1024, 1024), mode='reflect')\n        # threshold predicted mask\n        comp = pred[:, :, 0] > 0.5\n        # apply connected components\n        comp = measure.label(comp)\n        # apply bounding boxes\n        predictionString = ''\n        for region in measure.regionprops(comp):\n            # retrieve x, y, height and width\n            y, x, y2, x2 = region.bbox\n            height = y2 - y\n            width = x2 - x\n            # proxy for confidence score\n            conf = np.mean(pred[y:y+height, x:x+width])\n            # add to predictionString\n            predictionString += str(conf) + ' ' + str(x) + ' ' + str(y) + ' ' + str(width) + ' ' + str(height) + ' '\n        # add filename and predictionString to dictionary\n        filename = filename.split('.')[0]\n        submission_dict[filename] = predictionString\n    # stop if we've got them all\n    if len(submission_dict) >= len(test_filenames):\n        break\n\n# save dictionary as csv file\nsub = pd.DataFrame.from_dict(submission_dict,orient='index')\nsub.index.names = ['patientId']\nsub.columns = ['PredictionString']\nsub.to_csv('submission.csv')","execution_count":null,"outputs":[]}],"metadata":{"colab":{"name":"Capstone-ObjectDetection-Model.ipynb","provenance":[],"collapsed_sections":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"accelerator":"TPU"},"nbformat":4,"nbformat_minor":1}