{"cells":[{"metadata":{},"cell_type":"markdown","source":"## DICE COEFFICIENT\n\n* This competition is evaluated on the mean Dice coefficient. \n* The Dice coefficient can be used to compare the pixel-wise agreement between a predicted segmentation and its corresponding ground truth. \n* Dice coefficient is 2 times The area of Overlap divided by the total number of pixels in both the images\n\n![Dice coeff image](https://miro.medium.com/max/429/1*yUd5ckecHjWZf6hGrdlwzA.png)\n\nThe formula is given by:\n\n<center> $ \\huge \\frac{2*|X∩Y|}{|X|+|Y|}$ </center>\n\n<br>\n\nwhere X is the predicted set of pixels and Y is the ground truth.\n* The Dice coefficient is defined to be 1 when both X and Y are empty. The leaderboard score is the mean of the Dice coefficients for each image in the test set.\n\n* [Here's](https://towardsdatascience.com/metrics-to-evaluate-your-semantic-segmentation-model-6bcb99639aa2) an interesting article for further read!"},{"metadata":{},"cell_type":"markdown","source":"### Table of Contents\n\n1. [Import libraries](#libimport)\n2. [Loading Dataset](#loaddatset)\n3. [Writing a Simple DICE Implementation](#simpledice)\n4. [Image and the Mask](#imagemask)\n5. [How Big are the Glomeruli Masks?](#glomersize)\n6. [Dice Coefficient Between same masks](#dicesame)\n7. [Dice Coefficient Between shifted masks](#diceshift)\n8. [Plot Between Dice Coefficient and Shift in masks](#diceplot)"},{"metadata":{},"cell_type":"markdown","source":"<div id=\"libimport\"> </div>\n\n### Import the libraries\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import collections\nimport json\nimport os\nimport uuid\nimport gc\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image, ImageDraw\nimport tifffile as tiff \nimport seaborn as sns\nimport tensorflow as tf\nfrom tqdm.notebook import tqdm\n\nfrom skimage.measure import label, regionprops\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"loaddataset\"> </div>\n\n### Loading Dataset"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"TRAIN_PATH = \"../input/hubmap-kidney-segmentation/train/\"\n\n## Training dataset information\ntrain_df = pd.read_csv(\"../input/hubmap-kidney-segmentation/train.csv\")\nprint(f\"Shape of the Train data - {train_df.shape}\")\n\nprint(f\"First ID - {train_df.iloc[0, 0]}\")\nprint(f\"First Encoding string beginning- {train_df.iloc[0, 1][:100]}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"simpledice\"> </div>\n\n### Writing a Simple DICE Coefficient Implementation"},{"metadata":{},"cell_type":"markdown","source":"* As seen above, we need to calculate the intersection pixels in the masks. \n* Since our masks consist of just 0s and 1s, simple multiplication will leave us with intersection pixels"},{"metadata":{"trusted":true},"cell_type":"code","source":"def DICE_COE(mask1, mask2):\n    intersect = np.sum(mask1*mask2)\n    fsum = np.sum(mask1)\n    ssum = np.sum(mask2)\n    dice = (2 * intersect ) / (fsum + ssum)\n    dice = np.mean(dice)\n    dice = round(dice, 3) # for easy reading\n    return dice    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"imagemask\"> </div>\n\n### Image and The Mask"},{"metadata":{"trusted":true},"cell_type":"code","source":"## Looking into a single training image\nimage1 = tiff.imread(TRAIN_PATH + train_df.iloc[4, 0] + \".tiff\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print(\"Image ID --> \", train_df.iloc[1, 0], \"\\tTraining image shape -->\", image1.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## The kidney tissue image\nimage1 = image1[0][0].transpose(1, 2, 0)\nplt.figure(figsize=(10, 10))\nplt.imshow(image1)\nplt.title(\"Random Image of Kidney Tissue\", size=15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"## We need to decode the mask from encoding column of train.csv\n## https://www.kaggle.com/paulorzp/rle-functions-run-lenght-encode-decode\ndef mask2rle(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels= img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n \ndef rle2mask(mask_rle, shape):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (width,height) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    #print(starts, ends)\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## Plot all the Glomeruli in this particular kidney\nmask = rle2mask(train_df.iloc[4, 1], (image1.shape[1], image1.shape[0])) # Call the RLE2Mask function","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## The same kidney image with all the masks\nplt.figure(figsize=(10, 10))\nplt.imshow(image1)\nplt.imshow(mask, alpha=0.5, cmap='plasma')\nplt.title(\"Image with Masks on Glomeruli\", size=15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# SAVING RAM\ndel train_df\na = gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"glomersize\"> </div>\n\n## How Big are the Glomeruli Masks?"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print(f\"Shape of the Full Glomeruli Mask - {mask.shape}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"####  SKIMAGE HANDY FUNCTIONS\n\n* LABEL [(skimage.measure.label)](https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label)\nThis finds the connected regions from the image array.\n\n* REGION PROPS [(skimage.measure.region_props)](https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.regionprops)\nThis Finds the properties of the labeled array, here we are concerned about the bounding box of the mask."},{"metadata":{"trusted":true},"cell_type":"code","source":"## Identify all the coordinates of the glomeruli in this image\nlabelled = label(mask) \nprops = regionprops(labelled)\n\nprint(f\"Number of Glomeruli identified - {len(props)}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Converting the Properties to Bounding boxes"},{"metadata":{"trusted":true},"cell_type":"code","source":"bboxes = [] \nfor prop in props:\n    bboxes.append([prop.bbox[0] - 20, prop.bbox[1] - 20, \n                   prop.bbox[2] + 20, prop.bbox[3] + 20]) ## Adding a little bit of extra image run","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Visualizing one of the Bounding box"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nplt.imshow(image1[bboxes[0][0]:bboxes[0][2], bboxes[0][1]:bboxes[0][3], :])\nplt.imshow(mask[bboxes[0][0]:bboxes[0][2], bboxes[0][1]:bboxes[0][3]], alpha=0.5, cmap='viridis')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can notice a few things - \n* The Mask is not perfectly positioned on the Glomeruli\n* The Mask is Approximately 400 pixels wide, we can understand the scale of the size of the other masks"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# SAVING RAM\ndel image1, props, labelled, bboxes\na = gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"dicesame\"> </div>\n\n### DICE COEFFICIENT BETWEEN SAME IMAGES\n\nWhat do you think will be the output in this case?"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print(f\"Dice Coefficient of two same masks are {DICE_COE(mask, mask)}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Yes! You can obtain the answer by easy calculation\n\nLet the number of masked pixels be x . Then the intersection between the two images will also have x pixels.\n\n$\\huge \\frac{2\\cdot x }{x + x}  = 1.0$"},{"metadata":{},"cell_type":"markdown","source":"<div id=\"diceshift\"> </div>\n\n### DICE COEFFICIENT OF SHIFTED IMAGES"},{"metadata":{},"cell_type":"markdown","source":"Lets shift the mask a little, say 5 pixels and see the effect on the dice score"},{"metadata":{"trusted":true},"cell_type":"code","source":"# We are just shifting the images towards the bottom to keep it simple\ndef return_shifted(mask, shift=5):\n    nmask = np.zeros((mask.shape[0]+shift, mask.shape[1]))\n    nmask[shift:, :] = mask\n    nmask = nmask[:-shift, :]\n    return nmask\n\n\nsh_mask = return_shifted(mask)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print(f\"The DICE COEFFICIENT of Same Masks shifted by 5 pixels is {DICE_COE(mask, sh_mask)}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can see that the score decreased!\n\n\nNow lets shift it more by 10 20 30 pixels"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# SAVING RAM\ndel sh_mask\na = gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"for shift in [10,20,30]:\n    print(f\"The DICE COEFFICIENT of Same Masks shifted by {shift} pixels is {DICE_COE(mask, return_shifted(mask, shift=shift))}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now lets try to calculate for big shifts like 400, 500 and 600"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"for shift in [400,500,600]:\n    print(f\"The DICE COEFFICIENT of Same Masks shifted by {shift} pixels is {DICE_COE(mask, return_shifted(mask, shift=shift))}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<div id=\"diceplot\"> </div>\n\nLets take a step further and plot a curve between shift and the Dice coefficient"},{"metadata":{"trusted":true},"cell_type":"code","source":"nums = list(range(1, 601, 15))\ndices  = []\nfor num in tqdm(nums):\n    dices.append(DICE_COE(mask, return_shifted(mask, shift=num)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nplt.plot(nums, dices)\nplt.title('SHIFT VS DICE COEFFICIENT', size=20)\nplt.xlabel('Shift in Pixels', size=15)\nplt.ylabel('Dice coefficient', size=15)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### **We can clearly see how the Dice Coefficient Decreases as the shift increases and the masks are less overlapping with the true masks.**\n\nP.S - The Small increase in the end can be attributed to the fact that some masks might be overlapping with other true masks as the shift increases. "},{"metadata":{},"cell_type":"markdown","source":"### References\n\n1. [HuBMAP - Visualize Mask & BBOX 📈](https://www.kaggle.com/ckanth090/hubmap-visualize-mask-bbox)"},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}