{"cells":[{"metadata":{},"cell_type":"markdown","source":"# HuBMAP Kaggle Competition - Exploratory Data Analysis - Exploratory Data Analysis"},{"metadata":{},"cell_type":"markdown","source":"## Introduction\n\nThe term **tissue** is used to describe a group of cells found together in the body. The cells within a tissue share a common embryonic origin. Microscopic observation reveals that the cells in a tissue share morphological features and are arranged in an orderly pattern that achieves the tissue’s functions. From the evolutionary perspective, tissues appear in more complex organisms. For example, multicellular protists, ancient eukaryotes, do not have cells organized into tissues.\n\nThis is an exploratory analysis with the goal of becoming more familiar with the dataset and identifying some possible hurdles that could have a negative effect on model performance."},{"metadata":{},"cell_type":"markdown","source":"<a id='section0'></a>\n## Contents:\n\n- [Importing and processing image data](#section1)\n- [Plotting a samples of training images](#section2)\n- [Image tiling](#section3)\n- [Mask-area per image distribution and some statistics](#section4)\n- [Plotting images with the smallest and the largest mask areas](#section5)\n- [Conclusion](#section6)"},{"metadata":{},"cell_type":"markdown","source":"The list of required python libraries for this notebook:"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport tifffile\nimport matplotlib.pyplot as plt\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id='section1'></a>\n    \n## Importing and processing image data\n### The Dataset\nThe dataset is comprised of very large (>500MB - 5GB) TIFF files. \n- The training set has 8 huge \".tiff\" files\n- The public test set has anothe large 5 \".tiff\" files. \n\nThe training set includes annotations in both RLE-encoded and unencoded (JSON) forms. The annotations denote segmentations of glomeruli. \n\nFile **train.csv** contains the unique IDs for each image, as well as an RLE-encoded representation of the mask for the objects in the image. \n\nRLE or Run Length Encoding converts a matrix into a vector and returns the position/starting point of the first pixel from where we observe an object (identified by a 1) and gives us a count of how many pixels from that pixel we see the series of 1s. For example coded Mask will look like [1 1 1 0 0 1 1], running RLE would give us 1 3 6 2, which means 3 pixels from the zeroth pixel (inclusive) and 2 pixels from the 5th pixel we see a series of 1s\n\nFor the begining, let's open end review **train.csv**, it contains all RLE-masks related to each images_IDs"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/hubmap-kidney-segmentation/train.csv')\nprint(df)\ndf.info()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Another quick look at the HuBMAP-20-dataset_information.csv file:"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_info = pd.read_csv('../input/hubmap-kidney-segmentation/HuBMAP-20-dataset_information.csv')\ndf_info\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Most terrifying thing here is the size of the train images:"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(df_info[['image_file', 'width_pixels', 'height_pixels']])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In this notebook I’m going to check 3 images with the smallest size, but there is no problem to do the same with another 5.\n\nInput/output folders and the list of images:"},{"metadata":{"trusted":true},"cell_type":"code","source":"os.makedirs('../output')\ninput_dir = '../input/hubmap-kidney-segmentation/train'\noutput_dir = '../output'\nimage_list = ['0486052bb', '2f6ecfcdf', 'aaa6a05cc']\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"To make it possible to open, plot and review the images it’s better to resize it. Here is the function, which opens images from the folder /input, resize it with some scale (in percent) and store it in the folder /output"},{"metadata":{"trusted":true},"cell_type":"code","source":"def resize_im(im_name, scale_percent):\n    image_path = os.path.join(input_dir, im_name+'.tiff')\n    im_read = tifffile.imread(image_path)\n    width = int(im_read.shape[1] * scale_percent / 100)\n    height = int(im_read.shape[0] * scale_percent / 100)\n    dim = (width, height)\n    print('File name: {}, original size: {}, resized to: {}'.format(im_name, \n                                                                    (im_read.shape[0], im_read.shape[1]), \n                                                                    (width, height)))\n    resized = cv2.resize(im_read, dim, interpolation=cv2.INTER_AREA)\n    image_path = os.path.join(output_dir, ('r_' + im_name+'.tiff'))\n    tifffile.imwrite(image_path, resized)\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Resizing results:"},{"metadata":{"trusted":true},"cell_type":"code","source":"for im in image_list:\n    resize_im(im, 5)\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's do the same with masks. I will decode relevant masks from the train.csv file and then resize and save it in separate file.\n\nThe function for RLE encoding:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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    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    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\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here is the function, which read RLE-mask from the DataFrame, resize it with some scale (in percent) and store it in the folder /output"},{"metadata":{"trusted":true},"cell_type":"code","source":"def resize_mask(im_name, scale_percent):\n    im_read = tifffile.imread(os.path.join(input_dir, im_name+'.tiff'))\n    mask_rle = df[df[\"id\"] == im_name][\"encoding\"].values[0]\n    mask = rle2mask(df[df[\"id\"] == im_name][\"encoding\"].values[0], (im_read.shape[1], im_read.shape[0]))*255\n    width = int(im_read.shape[1] * scale_percent / 100)\n    height = int(im_read.shape[0] * scale_percent / 100)\n    dim = (width, height)\n    print('File name: {}, original size: {}, resized to: {}'.format(im_name, \n                                                                (im_read.shape[0], im_read.shape[1]), \n                                                                (width, height)))\n    resized = cv2.resize(mask, dim, interpolation=cv2.INTER_AREA)\n    image_path = os.path.join(output_dir, ('r_' + im_name+'_m.tiff'))\n    tifffile.imwrite(image_path, resized)\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Resizing results:"},{"metadata":{"trusted":true},"cell_type":"code","source":"for im in image_list:\n    resize_mask(im, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"All resized files:"},{"metadata":{"trusted":true},"cell_type":"code","source":"os.listdir(output_dir)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id='section2'></a>\n## Plotting samples of training images\n\nAnother step is plotting these files with relevant masks. Now, when images are resized it’s not a problem.\nThe function for plotting:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_image(image_id):\n    fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(16, 32))\n    image_path = os.path.join(output_dir, 'r_{}.tiff'.format(image_id))\n    mask_path = os.path.join(output_dir, 'r_{}_m.tiff'.format(image_id))\n    \n    image = tifffile.imread(image_path)\n    mask = tifffile.imread(mask_path)\n    if len(mask.shape)==2:    \n        hybr = image[:, :, 0] + mask[:, :]/2\n    else:\n        hybr = image[:, :, 0] + mask[:,: , 0]/2\n    ax[0].imshow(image)\n    ax[0].axis('off')\n    ax[0].set_title('Real Image')\n    ax[1].imshow(hybr)\n    ax[1].axis('off')\n    ax[1].set_title('Masks')\n    plt.show()\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nshow_image(image_list[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nshow_image(image_list[1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nshow_image(image_list[2])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Looking at those pictures I see no other options to analyze it and even train the model besides to tile (split) the images. The size is too big for model training, and there is no way to analyze the images with such resolution. Let’s tile it…, at least the smallest and will see what we have."},{"metadata":{},"cell_type":"markdown","source":"<a id='section3'></a>\n## Image tiling\n\nFor the beginning I will split **'aaa6a05cc.tiff'** with original size for tiles of size 1024x1024 and store all files into the folder **split**:\n-\tImages will be stored in the folder **split/images/**\n-\tMask-files will be stored in the folder **split/masks/**\n\nAlso I’m going to implement filtering. Images with 0-mask and located in the firs/last 2 rows/columns are totally useless for a further model training. Even in this case I will still have some 0-mask images, it also will be useful for the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nos.makedirs('../working/split/images')\nos.makedirs('../working/split/masks')\nim_name = 'aaa6a05cc.tiff'\nimage_path = os.path.join(input_dir, im_name)\ndf = pd.read_csv('../input/hubmap-kidney-segmentation/train.csv')\nsplit_size = 1024\nim = tifffile.imread(os.path.join(input_dir, im_name))\nmask_rle = df[df[\"id\"] == im_name[:-5]][\"encoding\"].values[0]\nmask = rle2mask(df[df[\"id\"] == im_name[:-5]][\"encoding\"].values[0], (im.shape[1], im.shape[0]))*255\nfor r in range(0, im.shape[0], split_size):\n    for c in range(0, im.shape[1], 1024):\n        im_tile = im[r: r + split_size, c: c + split_size, :]\n        mask_tile = mask[r: r + split_size, c: c + split_size]\n        # here I filter images with 0-mask and white borders around.\n        if (np.sum(mask_tile)==0):\n            if ((2 * split_size <= r <= (im.shape[0] - 2 * split_size)) and \\\n                (2 * split_size <= c <= (im.shape[1] - 2 * split_size))):\n                tifffile.imwrite(f\"split/images/img{r}_{c}.png\", im_tile)\n                tifffile.imwrite(f\"split/masks/img{r}_{c}.png\", mask_tile)\n        else:\n            tifffile.imwrite(f\"split/images/img{r}_{c}.png\", im_tile)\n            tifffile.imwrite(f\"split/masks/img{r}_{c}.png\", mask_tile)\n            ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As a result I received a set of images and masks (label) for a model training. Let’s count just for information: "},{"metadata":{"trusted":true},"cell_type":"code","source":"len(os.listdir('split/images'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And some statistics. Let's calculate the areas of the masks at each images, but for the beginning files with 0-mask. For comfortable calculations I will use Pandas DataFrame where index is file name:"},{"metadata":{"trusted":true},"cell_type":"code","source":"mask_list = os.listdir('split/masks')\ndf=pd.DataFrame(index=mask_list)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Area calculation function:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def area_calc(image_id):\n    mask_path = os.path.join(mask_dir, '{}'.format(image_id))\n    mask = cv2.imread(mask_path)\n    return int(np.count_nonzero(mask) / 3)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Calculate and write mask areas (sum) per image to the DataFrame:"},{"metadata":{"trusted":true},"cell_type":"code","source":"mask_dir = 'split/masks'\nmask_areas=[]\nfor msk in mask_list:\n    mask_areas.append(area_calc(msk))\ndf['area'] = mask_areas\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Total images:', len(df))\nprint('Non-zero images:', len(df[df['area']!=0]))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id='section4'></a>\n## Mask-area per image distribution and some statistics\n\nNon-zero Image distribution: "},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfig, ax = plt.subplots(1,1,figsize=(18,8))\nax.hist(df[df['area']!=0].values, bins=50, color='deeppink', edgecolor='black')  # `density=False` would make counts\nax.set_title('Non-zero Image destribution. Image File: {}     Total images: {}'.format(im_name, \n                                                                                       str(len(df[df['area']!=0]))), \n             fontsize=20)\nax.set_ylabel('Quantity', fontsize=16)\nax.set_xlabel('Area(pixels)', fontsize=16);\nax.grid()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_sorted = df[df['area']!=0].sort_values(by=['area'])\nsmallest_list = df_sorted.head(5)['area'].index\nlargest_list = df_sorted.tail(5)['area'].index\nzero_list = df[df['area']==0].head(5)['area'].index\nprint('Smallest:', list(smallest_list))\nprint('Largest:', list(largest_list))\nprint('Zero_list:', list(zero_list))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's take a look at the pictures with the largest and the smallest areas, bur first of all I will sort non-zero mask areas:"},{"metadata":{},"cell_type":"markdown","source":"<a id='section5'></a>\n## Plotting images with the smallest and the largest mask ares\n\nA bit modified function for small image plotting:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_image(image_name):\n    fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(32, 16))\n    image_path = os.path.join('../working/split/images', image_name)\n    mask_path = os.path.join('../working/split/masks', image_name)\n    image = tifffile.imread(image_path)\n    mask = tifffile.imread(mask_path)\n    if len(mask.shape)==2:    \n        hybr = image[:, :, 0] + mask[:, :]/2\n    else:\n        hybr = image[:, :, 0] + mask[:,: , 0]/2\n    ax[0].imshow(image)\n    ax[0].axis('off')\n    ax[0].set_title('Real Image')\n    ax[1].imshow(hybr)\n    ax[1].axis('off')\n    ax[1].set_title('Masks')\n    plt.show()\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Plot 5 images with the smallest mask areas"},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfor file in smallest_list:\n    show_image(file)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Plot 5 images with the largest mask areas"},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfor file in largest_list:\n    show_image(file)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And some Zero-mask images:"},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfor file in zero_list:\n    show_image(file)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id='section6'></a>\n## Conclusion\n\nSo, that’s it. Here is brief overview of the images provided by **kaggle** for further semantic segmentation and probably it’s going to be a UNet Neural Network model. The biggest challenge here is the size of the train/test images, but if we use tiling it’s not going to be a big problem. This is a basic algorithm only for a couple images, but I’m sure you can use it for other ones.\n\nThank you for your patience and don’t forget up vote this notebook if you find it useful. Have fun and good luck ))!!!\n\n[Jump on top](#section0)"},{"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}