{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":2010872,"sourceType":"datasetVersion","datasetId":1016583},{"sourceId":3848283,"sourceType":"datasetVersion","datasetId":2289648},{"sourceId":8293067,"sourceType":"datasetVersion","datasetId":4926688}],"dockerImageVersionId":30698,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"!pip install -U segmentation-models","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:04:49.766104Z","iopub.execute_input":"2024-05-09T05:04:49.766492Z","iopub.status.idle":"2024-05-09T05:05:02.079870Z","shell.execute_reply.started":"2024-05-09T05:04:49.766453Z","shell.execute_reply":"2024-05-09T05:05:02.078629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import cv2\nimport pickle\nimport json, math\nimport numpy as np\nimport random, time\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\nimport shutil, sys, os, gc\nfrom datetime import datetime\nimport matplotlib.pyplot as plt\nfrom ipywidgets import interact, IntSlider\n\n\nimport tensorflow as tf\nfrom tensorflow.keras.utils import Sequence\n\n\nos.environ['SM_FRAMEWORK'] = 'tf.keras'\nimport segmentation_models as sm","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:05:02.085411Z","iopub.execute_input":"2024-05-09T05:05:02.085685Z","iopub.status.idle":"2024-05-09T05:05:05.919557Z","shell.execute_reply.started":"2024-05-09T05:05:02.085656Z","shell.execute_reply":"2024-05-09T05:05:05.918519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utility functions","metadata":{}},{"cell_type":"code","source":"# To read, and store paths of images, and their masks a dataframe.\ndef images_and_masks_crawler(directory):\n    data = {'image_path': [], 'mask_path': []}\n    \n    # Navigate through the directory structure\n    for root, dirs, files in os.walk(directory):\n        for file in files:\n            file_path = os.path.join(root, file)\n            if file_path.endswith('.jpg') or file_path.endswith('.png'):\n                if 'mask' in root.lower():\n                    data['mask_path'].append(file_path)\n                else:\n                    data['image_path'].append(file_path)\n    \n    df = pd.DataFrame(data)\n    return df\n\n# To read return images, and their masks.\ndef image_mask_reader(df):\n    # Load image, and mask\n    x = [cv2.imread(row['image_path'])[:,:,::-1] for _, row in df.iterrows()]\n    y = [np.expand_dims(cv2.imread(row['mask_path'], cv2.IMREAD_GRAYSCALE), axis=-1) for _, row in df.iterrows()]\n    \n    # Clear memory\n    gc.collect()\n    \n    return np.array(x), np.array(y)\n\n# to overlay mask onto its image\ndef overlay_mask(image, mask, opacity=0.75):    \n    # Check if the mask is blank (contains only zeros)\n    if np.max(mask) == 0:\n        return image\n    \n    mask_normalized = mask.astype(float) / np.max(mask)\n    alpha = mask_normalized[:, :, 0] * opacity  # Extract the single channel from the mask & Adjust the opacity by multiplying with a factor\n    alpha = alpha[:, :, np.newaxis]   # Add a third dimension to make it compatible with the image\n    result = alpha * mask + (1 - alpha) * image\n    \n    return result.astype(np.uint8)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:03.540790Z","iopub.execute_input":"2024-05-09T05:08:03.541588Z","iopub.status.idle":"2024-05-09T05:08:03.552793Z","shell.execute_reply.started":"2024-05-09T05:08:03.541554Z","shell.execute_reply":"2024-05-09T05:08:03.551552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def augment(X, Y):\n#     # Optional\n#     np.random.seed(seed_value)\n    \n    # 1. random flip--------------\n    # 1.1 horizontal\n    if np.random.uniform() > 0.5:\n        X = np.fliplr(X)\n        Y = np.fliplr(Y)\n        \n    # 1.2 vertical\n    if np.random.uniform() > 0.5:\n        X = np.flipud(X)\n        Y = np.flipud(Y)\n    \n    # 2. rotation------------------\n    # 2.1 set angle \n    angle = np.random.randint(4)\n    X = np.rot90(X, k=angle)\n    Y = np.rot90(Y, k=angle)\n    \n    # 3. Translatiion --------------\n    if np.random.uniform() > 0.5:\n        max_translation=(30, 30)\n        dx = np.random.randint(-max_translation[0], max_translation[0] + 1)\n        dy = np.random.randint(-max_translation[1], max_translation[1] + 1)\n\n        # Translate the image and mask using OpenCV\n        rows, cols = X.shape[:2]\n        M = np.float32([[1, 0, dx], [0, 1, dy]])\n        X = cv2.warpAffine(X, M, (cols, rows))\n        Y = cv2.warpAffine(Y, M, (cols, rows))\n\n    \n    if len(Y.shape) == 2:\n        Y = np.expand_dims(Y, axis=-1)\n    \n    return X, Y\n\ndef augment_batch(X_batch, Y_batch, augment):\n    augmented_X_batch, augmented_Y_batch = [], []\n    for X, Y in zip(X_batch, Y_batch):\n        augmented_X, augmented_Y = augment(X, Y)\n        augmented_X_batch.append(augmented_X)\n        augmented_Y_batch.append(augmented_Y)\n        \n    return np.array(augmented_X_batch), np.array(augmented_Y_batch)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:03.820575Z","iopub.execute_input":"2024-05-09T05:08:03.820945Z","iopub.status.idle":"2024-05-09T05:08:03.832177Z","shell.execute_reply.started":"2024-05-09T05:08:03.820916Z","shell.execute_reply":"2024-05-09T05:08:03.831158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generates data on which model trains on\nclass DataGenerator(Sequence):\n    def __init__(self, df, normalization, transform=None, batch_size=4, reader=image_mask_reader, **kwargs):\n        super().__init__(**kwargs)\n        self.df = df\n        self.reader = reader\n        self.transform = transform\n        self.batch_size = batch_size\n        self.preprocess_feature = normalization\n\n    def __len__(self):\n        return self.df.shape[0] // self.batch_size\n\n    def __getitem__(self, index):\n        # loading batch\n        batch_df = self.df[index * self.batch_size: (index + 1) * self.batch_size]\n        X, y = self.reader(batch_df)\n        \n        # processing batch\n        X = self.preprocess_feature(X)\n        y = y.astype(np.float32)\n        gc.collect()\n        \n        # applying augmentation\n        if self.transform:\n            X, y = self.transform[1](X, y, self.transform[0])\n\n        return X, y","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:04.024735Z","iopub.execute_input":"2024-05-09T05:08:04.025591Z","iopub.status.idle":"2024-05-09T05:08:04.033757Z","shell.execute_reply.started":"2024-05-09T05:08:04.025558Z","shell.execute_reply":"2024-05-09T05:08:04.032802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Reading data","metadata":{}},{"cell_type":"code","source":"# preparing paths within a dataframe\n# kidney_directory = '/kaggle/input/hupmap-dataframes/kidney_slides_df.csv'\nhpa_directory = '/kaggle/input/hupmap-dataframes/hpa_slides_df.csv'\n\n# we commented it out as we have the csv file ready\n# kidney_df = pd.read_csv(kidney_directory)\nhpa_df = pd.read_csv(hpa_directory)\n\n# # Concatinating df\n# df = pd.concat([kidney_df, hpa_df], axis=0)\n# df.reset_index(drop=True, inplace=True)\n\n# Shuffling dataframe.\ndf = hpa_df\ndf = df.sample(frac=1).reset_index(drop=True)\n\n# viewing df\nprint(df.shape)\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:04.816781Z","iopub.execute_input":"2024-05-09T05:08:04.817705Z","iopub.status.idle":"2024-05-09T05:08:04.862587Z","shell.execute_reply.started":"2024-05-09T05:08:04.817668Z","shell.execute_reply":"2024-05-09T05:08:04.861622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Ignoring blank masks (as its considered a noise)\ndf = df[df['is_blank'] == 0]\nprint(df.shape)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:05.009352Z","iopub.execute_input":"2024-05-09T05:08:05.010176Z","iopub.status.idle":"2024-05-09T05:08:05.016642Z","shell.execute_reply.started":"2024-05-09T05:08:05.010143Z","shell.execute_reply":"2024-05-09T05:08:05.015660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sample data\nX, Y = image_mask_reader(df.iloc[:10])","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:05.577887Z","iopub.execute_input":"2024-05-09T05:08:05.578750Z","iopub.status.idle":"2024-05-09T05:08:06.109940Z","shell.execute_reply.started":"2024-05-09T05:08:05.578714Z","shell.execute_reply":"2024-05-09T05:08:06.108902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# displaying sample image\nidx = random.randint(0, len(X) - 1)\nplt.imshow(overlay_mask(X[idx], Y[idx], opacity=0.5))","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:07.303838Z","iopub.execute_input":"2024-05-09T05:08:07.304589Z","iopub.status.idle":"2024-05-09T05:08:07.689488Z","shell.execute_reply.started":"2024-05-09T05:08:07.304556Z","shell.execute_reply":"2024-05-09T05:08:07.688404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model setup","metadata":{}},{"cell_type":"code","source":"# constants\nEPOCHS=20\nBATCH_SIZE=16\nBACKBONE = 'efficientnetb5'\nMODEL_NAME=\"Unet_efficientnetb5\"\nBEST_MODEL_PATH=F\"best_{MODEL_NAME}_weights.keras\"","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:09.626584Z","iopub.execute_input":"2024-05-09T05:08:09.627273Z","iopub.status.idle":"2024-05-09T05:08:09.631961Z","shell.execute_reply.started":"2024-05-09T05:08:09.627238Z","shell.execute_reply":"2024-05-09T05:08:09.630938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Callback function to save best model weights\ncheckpoint = tf.keras.callbacks.ModelCheckpoint(BEST_MODEL_PATH, monitor='loss', verbose=0, save_best_only=True, mode='min')","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:10.935351Z","iopub.execute_input":"2024-05-09T05:08:10.936020Z","iopub.status.idle":"2024-05-09T05:08:10.941970Z","shell.execute_reply.started":"2024-05-09T05:08:10.935984Z","shell.execute_reply":"2024-05-09T05:08:10.940818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# compiling model\nmodel = sm.Unet(BACKBONE, encoder_weights='imagenet', decoder_use_batchnorm=True, classes=1, activation='sigmoid')\nmodel.compile(\n    'Adam',\n    loss=sm.losses.bce_jaccard_loss,\n    metrics=[sm.metrics.iou_score],\n)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:11.161822Z","iopub.execute_input":"2024-05-09T05:08:11.162887Z","iopub.status.idle":"2024-05-09T05:08:15.653210Z","shell.execute_reply.started":"2024-05-09T05:08:11.162845Z","shell.execute_reply":"2024-05-09T05:08:15.652346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Training","metadata":{}},{"cell_type":"code","source":"# input processor\npreprocess_input = sm.get_preprocessing(BACKBONE)\n\n# getting data loader ready to use\n# DataGenerator(df, normalization, transform=None, batch_size=4, reader=image_mask_reader)\ndatat_generator = DataGenerator(df, transform=(augment, augment_batch), normalization=preprocess_input, batch_size=BATCH_SIZE)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:15.655151Z","iopub.execute_input":"2024-05-09T05:08:15.655543Z","iopub.status.idle":"2024-05-09T05:08:15.661763Z","shell.execute_reply.started":"2024-05-09T05:08:15.655508Z","shell.execute_reply":"2024-05-09T05:08:15.660591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# fit model\nmodel.fit(datat_generator, batch_size=BATCH_SIZE, epochs=EPOCHS, callbacks=[checkpoint])","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:08:15.662792Z","iopub.execute_input":"2024-05-09T05:08:15.663038Z","iopub.status.idle":"2024-05-09T05:25:11.017463Z","shell.execute_reply.started":"2024-05-09T05:08:15.663016Z","shell.execute_reply":"2024-05-09T05:25:11.016376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Validation","metadata":{}},{"cell_type":"code","source":"def predict_mask(model, image):\n    # Ensure the input image has the right shape for prediction\n    if len(image.shape) == 3:\n        image = np.expand_dims(image, axis=0)  # Add batch dimension if needed\n\n    # Predict the probabilities for the input image\n    prob = model.predict(image)\n\n    # Return the predicted mask\n    return prob\n\ndef __get_IOU(Y_true, prob, cutoff = 0.8):\n    \n    Y_pred = (prob > cutoff).astype(int)\n    \n    score_list = []\n    for k in range(prob.shape[0]):\n    \n        and_score =  np.sum(Y_pred[k][Y_true[k] == 1])\n        or_score = np.sum(Y_true[k]) + np.sum(Y_pred[k]) - and_score\n        \n        \n        if or_score == 0:\n            score = 1\n        else:\n            score = and_score/or_score\n        score_list.append(score)\n    \n    return np.round(np.mean(np.array(score_list)), 5) \n\ndef __get_Dice(Y_true, prob, cutoff=0.8):\n    Y_pred = (prob > cutoff).astype(int)\n    \n    dice_list = []\n    for k in range(prob.shape[0]):\n        TP = np.sum((Y_true[k] == 1) & (Y_pred[k] == 1))\n        FP = np.sum((Y_true[k] == 0) & (Y_pred[k] == 1))\n        FN = np.sum((Y_true[k] == 1) & (Y_pred[k] == 0))\n        \n        denominator = 2 * TP + FP + FN\n        \n        # Handle the case when the denominator is zero\n        if denominator == 0:\n            dice = 1  # Return 1, indicating perfect agreement\n        else:\n            # Calculate Dice coefficient\n            dice = (2 * TP) / denominator\n        \n        dice_list.append(dice)\n    \n    average_dice = np.mean(dice_list)\n    \n    return np.round(average_dice, 5)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:25:11.020517Z","iopub.execute_input":"2024-05-09T05:25:11.021021Z","iopub.status.idle":"2024-05-09T05:25:11.033604Z","shell.execute_reply.started":"2024-05-09T05:25:11.020981Z","shell.execute_reply":"2024-05-09T05:25:11.032462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load best model weights\nmodel.load_weights(f\"/kaggle/working/{BEST_MODEL_PATH}\")","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:25:11.034869Z","iopub.execute_input":"2024-05-09T05:25:11.035249Z","iopub.status.idle":"2024-05-09T05:25:49.899717Z","shell.execute_reply.started":"2024-05-09T05:25:11.035222Z","shell.execute_reply":"2024-05-09T05:25:49.898592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# sample data\nX, Y = image_mask_reader(df.sample(n=800) )\n        \n# preprocessing\nX = preprocess_input(X)\nY = Y.astype(np.float32)\ngc.collect()\n\n# predicting\ny_hat = predict_mask(model, X)","metadata":{"execution":{"iopub.status.busy":"2024-05-09T05:25:49.901567Z","iopub.execute_input":"2024-05-09T05:25:49.901908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Calculate average validation IoU, and Dice for each image\ndef get_iou_dice(threshold=0.5):\n    n_val = len(X)\n\n    val_IoU = [__get_IOU(Y[i], y_hat[i], cutoff=threshold) for i in range(n_val)]\n    val_Dice = [__get_Dice(Y[i], y_hat[i], cutoff=threshold) for i in range(n_val)]\n\n    print(\"At cutoff of {}%: IoU score {}%, and Dice coefficient {}%.\"\n          .format(threshold*100, round(np.mean(val_IoU), 5)*100, round(np.mean(val_Dice), 5)*100))\n    \nfor i in [50, 80]:\n    get_iou_dice(i/100)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Plotting predictions\n\nBlue indicates true mask areas<br/>\nRed indicates predicted mask areas<br/>","metadata":{}},{"cell_type":"code","source":"# overlays two masks above each other for visual plotting\ndef overlay_masks(y_true, y_hat, threshold=0.5):\n    cutoff_img = (y_hat[:, :, 0] > threshold).astype(int)\n\n    true_mask = np.zeros((512, 512, 3), dtype=np.uint8)\n    true_mask[:, :, 1] = y_true[:, :, 0] * 200\n\n    pred_mask = np.zeros((512, 512, 3), dtype=np.uint8)\n    pred_mask[:, :, 0] = cutoff_img * 230\n\n    overlaid_mask = np.zeros((512, 512, 3), dtype=np.uint8)\n    overlaid_mask[:, :, 1] = y_true[:, :, 0] * 200  # Green for true mask\n    overlaid_mask[:, :, 0] = cutoff_img * 230  # Red for predicted mask\n\n    return true_mask, pred_mask, overlaid_mask\n\ndef plot_masks(image, true_mask, pred_mask, overlaid_mask):\n    # Display the images\n    fig, axs = plt.subplots(1, 4, figsize=(15, 5))\n\n    # Display the image\n    axs[0].imshow(image)\n    axs[0].set_title('Image')\n    axs[0].axis('off')\n    \n    # Display the true mask\n    axs[1].imshow(true_mask)\n    axs[1].set_title('True Mask')\n    axs[1].axis('off')\n\n    # Display the predicted mask\n    axs[2].imshow(pred_mask)\n    axs[2].set_title('Predicted Mask')\n    axs[2].axis('off')\n\n    # Display the overlayed mask\n    axs[3].imshow(overlaid_mask)\n    axs[3].set_title('Overlayed Mask')\n    axs[3].axis('off')\n\n    # Show the plots\n    plt.show()\n    \ndef plot_random_results():\n    # Display the overlayed image using matplotlib\n    idx = random.randint(0, len(X) - 1)\n    true_mask, pred_mask, overlaid_mask = overlay_masks(Y[idx], y_hat[idx])\n    plot_masks(X[idx], true_mask, pred_mask, overlaid_mask)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_results()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_results()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_results()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_results()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_results()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}