{"cells":[{"metadata":{},"cell_type":"markdown","source":"![image](https://github.com/Lexie88rus/APTOS2019/raw/master/assets/cover_image.png)"},{"metadata":{},"cell_type":"markdown","source":"# APTOS 2019: Blindness Detection EDA"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom random import randrange\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.\n\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport seaborn as sns","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"! pip install albumentations","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"from albumentations import (\n    HorizontalFlip, IAAPerspective, ShiftScaleRotate, CLAHE, RandomRotate90,\n    Transpose, ShiftScaleRotate, Blur, OpticalDistortion, GridDistortion, HueSaturationValue,\n    IAAAdditiveGaussianNoise, GaussNoise, MotionBlur, MedianBlur, RandomBrightnessContrast, IAAPiecewiseAffine,\n    IAASharpen, IAAEmboss, Flip, OneOf, Compose, PadIfNeeded\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Introduction"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"[Diabetic retinopathy](https://en.wikipedia.org/wiki/Diabetic_retinopathy) is the leading cause of blindness among working aged adults. Millions of people suffer from this decease.\nPeople with diabetes can have an eye disease called diabetic retinopathy. This is when high blood sugar levels cause damage to blood vessels in the retina. These blood vessels can swell and leak. Or they can close, stopping blood from passing through. Sometimes abnormal new blood vessels grow on the retina. All of these changes can lead to blindness.\n\n### Stages of Diabetic Eye Disease\n\n* __NPDR (non-proliferative diabetic retinopathy)__: With NPDR, tiny blood vessels leak, making the [retina](https://en.wikipedia.org/wiki/Retina) swell. When the [macula](https://en.wikipedia.org/wiki/Macula_of_retina) swells, it is called macular edema. This is the most common reason why people with diabetes lose their vision. Also with NPDR, blood vessels in the retina can close off. This is called macular ischemia. When that happens, blood cannot reach the macula. Sometimes tiny particles called [exudates](https://en.wikipedia.org/wiki/Exudate) can form in the retina. These can affect vision too.\n\n* __PDR (proliferative diabetic retinopathy)__: PDR is the more advanced stage of diabetic eye disease. It happens when the retina starts growing new blood vessels. This is called neovascularization. These fragile new vessels often bleed into the [vitreous](https://en.wikipedia.org/wiki/Vitreous_body). If they only bleed a little, you might see a few dark floaters. If they bleed a lot, it might block all vision. These new blood vessels can form [scar tissue](https://en.wikipedia.org/wiki/Scar). Scar tissue can cause problems with the macula or lead to a [detached retina](https://en.wikipedia.org/wiki/Retinal_detachment). PDR is very serious, and can steal both your [central](https://www.medicinenet.com/script/main/art.asp?articlekey=8544) and [peripheral (side) vision](https://www.medicinenet.com/script/main/art.asp?articlekey=10638).\n\n_[Source](https://www.aao.org/eye-health/diseases/what-is-diabetic-retinopathy)_"},{"metadata":{},"cell_type":"markdown","source":"## Load Data"},{"metadata":{},"cell_type":"markdown","source":"Load training and testing csv files containing image filenames and corresponding labels (only for training set):"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"# load csv files with labels as pandas dataframes\ntrain = pd.read_csv('../input/train.csv')\ntest = pd.read_csv('../input/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# find out the number of images in test ans train sets\nprint('Number of images in training set is {}'.format(len(train)))\nprint('Number of images in test set is {}'.format(len(test)))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Plot pie chart\nlabels = 'Train', 'Test'\nsizes = len(train), len(test)\n\nfig1, ax1 = plt.subplots(figsize=(5,5))\nax1.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)\nax1.axis('equal')\n\nplt.title('Train and Test sets')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Both training and testing datasets are not too large.\n\nTraining dataset is about 3 times greater than the testing dataset."},{"metadata":{},"cell_type":"markdown","source":"## Analyze Train Set Labels"},{"metadata":{},"cell_type":"markdown","source":"Plot pie chart with percentage of images of each diabetic retinopathy severity condition:"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Plot pie chart\nlabels = 'No DR', 'Moderate', 'Mild', 'Proliferative DR', 'Severe'\nsizes = train.diagnosis.value_counts()\n\nfig1, ax1 = plt.subplots(figsize=(10,7))\nax1.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)\nax1.axis('equal')\n\nplt.title('Diabetic retinopathy condition labels')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can see that the training dataset is __very imbalanced__. There are ten times more images with no DR than images with the severe DR condition.\n\n__Data augmentation__ is required to perform the classification."},{"metadata":{},"cell_type":"markdown","source":"## Visualize Sample Images"},{"metadata":{},"cell_type":"markdown","source":"Let's plot [fundus photography](https://en.wikipedia.org/wiki/Fundus_photography) images from the training set of different conditions:"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# define paths to train and test images\nTRAIN_IMG_PATH = \"../input/train_images/\"\nTEST_IMG_PATH = \"../input/test_images/\"\n\n# function to plot a grid of images\ndef view_fundus_images(images, title = ''):\n    \"\"\"\n    Function to plot grid with several examples of fundus images.\n    INPUT:\n        train - array with filenames for images and condition labels\n\n    OUTPUT: None\n    \"\"\"\n    width = 5\n    height = 2\n    fig, axs = plt.subplots(height, width, figsize=(15,5))\n    \n    for im in range(0, height * width):\n        # open image\n        image = Image.open(os.path.join(TRAIN_IMG_PATH,images[im] + '.png'))\n        i = im // width\n        j = im % width\n        axs[i,j].imshow(image) #plot the data\n        axs[i,j].axis('off')\n\n    # set suptitle\n    plt.suptitle(title)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"view_fundus_images(train[train['diagnosis'] == 0][:10].id_code.values, title = 'Images without DR')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"view_fundus_images(train[train['diagnosis'] == 1][:10].id_code.values, title = 'Images with Mild condition')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"view_fundus_images(train[train['diagnosis'] == 2][:10].id_code.values, title = 'Images with Moderate condition')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"view_fundus_images(train[train['diagnosis'] == 3][:10].id_code.values, title = 'Images with Severe condition')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"view_fundus_images(train[train['diagnosis'] == 4][:10].id_code.values, title = 'Images with Proliferative DR')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Just glancing through various images we can say:\n* Images are of different sizes. Height and width ratio varies. That is why __image cropping or padding is required__.\n* Pictures are taken with various scales. __Random cropping__ augmentation is required.\n* Lighting and colors vary greately. Augmentations which __adjust brightness and color scales__ are required."},{"metadata":{},"cell_type":"markdown","source":"## Analyze Image Sizes"},{"metadata":{},"cell_type":"markdown","source":"Plot histograms for image sizes (used code from [this kernel](https://www.kaggle.com/chewzy/eda-weird-images-with-new-updates) for the analysis):"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def get_image_sizes(df, train = True):\n    '''\n    Function to get sizes of images from test and train sets.\n    INPUT:\n        df - dataframe containing image filenames\n        train - indicates whether we are getting sizes of images from train or test set\n    '''\n    if train:\n        path = TRAIN_IMG_PATH\n    else:\n        path = TEST_IMG_PATH\n        \n    widths = []\n    heights = []\n    \n    images = df.id_code\n    \n    max_im = Image.open(os.path.join(path, images[0] + '.png'))\n    min_im = Image.open(os.path.join(path, images[0] + '.png'))\n        \n    for im in range(0, len(images)):\n        image = Image.open(os.path.join(path, images[im] + '.png'))\n        width, height = image.size\n        \n        if len(widths) > 0:\n            if width > max(widths):\n                max_im = image\n\n            if width < min(widths):\n                min_im = image\n\n        widths.append(width)\n        heights.append(height)\n        \n    return widths, heights, max_im, min_im","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# get sizes of images from test and train sets\ntrain_widths, train_heights, max_train, min_train = get_image_sizes(train, train = True)\ntest_widths, test_heights, max_test, min_test = get_image_sizes(test, train = False)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"print('Maximum width for training set is {}'.format(max(train_widths)))\nprint('Minimum width for training set is {}'.format(min(train_widths)))\nprint('Maximum height for training set is {}'.format(max(train_heights)))\nprint('Minimum height for training set is {}'.format(min(train_heights)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Maximum width for test set is {}'.format(max(test_widths)))\nprint('Minimum width for test set is {}'.format(min(test_widths)))\nprint('Maximum height for test set is {}'.format(max(test_heights)))\nprint('Minimum height for test set is {}'.format(min(test_heights)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# Plot Histograms and KDE plots for images from the training set\n# Source: https://www.kaggle.com/chewzy/eda-weird-images-with-new-updates\nplt.figure(figsize=(14,6))\nplt.subplot(121)\nsns.distplot(train_widths, kde=False, label='Train Width')\nsns.distplot(train_heights, kde=False, label='Train Height')\nplt.legend()\nplt.title('Training Image Dimension Histogram', fontsize=15)\n\nplt.subplot(122)\nsns.kdeplot(train_widths, label='Train Width')\nsns.kdeplot(train_heights, label='Train Height')\nplt.legend()\nplt.title('Train Image Dimension KDE Plot', fontsize=15)\n\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Plot Histograms and KDE plots for images from the test set\n# Source: https://www.kaggle.com/chewzy/eda-weird-images-with-new-updates\nplt.figure(figsize=(14,6))\nplt.subplot(121)\nsns.distplot(test_widths, kde=False, label='Test Width')\nsns.distplot(test_heights, kde=False, label='Test Height')\nplt.legend()\nplt.title('Test Image Dimension Histogram', fontsize=15)\n\nplt.subplot(122)\nsns.kdeplot(test_widths, label='Test Width')\nsns.kdeplot(test_heights, label='Test Height')\nplt.legend()\nplt.title('Test Image Dimension KDE Plot', fontsize=15)\n\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We see that we have __very different distributions of image sizes__ for train and test datasets."},{"metadata":{},"cell_type":"markdown","source":"### Plot largest and smallest images"},{"metadata":{},"cell_type":"markdown","source":"Let's look and the largest and the smallest images from both sets.\n\nImage with the largest width from training set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.axis('off')\nplt.imshow(max_train) #plot the data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Image with the smallest width from training set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.axis('off')\nplt.imshow(min_train) #plot the data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Image with the largest width from test set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.axis('off')\nplt.imshow(max_test) #plot the data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Image with the smallest width from training set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.axis('off')\nplt.imshow(min_test) #plot the data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Playing with Augmentations"},{"metadata":{},"cell_type":"markdown","source":"Finally, I would like to play with some augmentations from [https://github.com/albu/albumentations](albumentation package).\n\nThis will help to have an impression of augmented dataset."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# define the dictionary for labels\ndiagnosis_dict = {\n    0:'No DR',\n    1:'Mild',\n    2:'Moderate',\n    3: 'Severe',\n    4: 'Proliferative DR'\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# function to plot a grid of images\ndef view_fundus_images_labels(train, rand_indices, aug = None, title = ''):\n    \"\"\"\n    Function to plot grid with several examples of fundus images.\n    INPUT:\n        train - array with filenames for images and condition labels\n        rand_indices - indices of images to plot\n        title - plot title\n\n    OUTPUT: None\n    \"\"\"\n    width = 5\n    height = 2\n    counter = 0\n    fig, axs = plt.subplots(height, width, figsize=(15,5))\n    \n    for im in rand_indices:\n        # open image\n        image = Image.open(os.path.join(TRAIN_IMG_PATH, train.iloc[im].id_code + '.png'))\n        \n        if aug is not None:\n            image = aug(image=np.array(image))['image']\n        \n        i = counter // width\n        j = counter % width\n        axs[i,j].imshow(image) #plot the data\n        axs[i,j].axis('off')\n        \n        diagnosis = train[train['id_code'] == train.iloc[im].id_code].diagnosis.values[0]\n        \n        axs[i,j].set_title(diagnosis_dict[diagnosis])\n        counter += 1\n\n    # set suptitle\n    plt.suptitle(title)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Plot random images from the training set:"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# get some random image indices from the training set\nrand_indices = [randrange(len(train)) for x in range(0,10)]\nrand_indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot original images\nview_fundus_images_labels(train, rand_indices, title = 'Original images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now let's play with some albumentation filters:"},{"metadata":{},"cell_type":"markdown","source":"Augment the images with [CLAHE](https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE):"},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = CLAHE(p=1)\nview_fundus_images_labels(train, rand_indices, aug, title = 'CLAHE')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Try adding some gaussian noise:"},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = GaussNoise(p=1)\nview_fundus_images_labels(train, rand_indices, aug, title = 'GaussNoise')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Playing with brightness and constrast:"},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = RandomBrightnessContrast(brightness_limit=1, contrast_limit=1, p = 1)\nview_fundus_images_labels(train, rand_indices, aug, title = 'RandomBrightnessContrast')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"See how random brightness and contrast affect images. This filter should certainly be used for data augmentation."},{"metadata":{},"cell_type":"markdown","source":"Padding images:"},{"metadata":{"trusted":true},"cell_type":"code","source":"aug = PadIfNeeded(min_height=1024, min_width=1024, p = 1)\nview_fundus_images_labels(train, rand_indices, aug, title = 'Padding Images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Conclusion\nAfter the EDA we can say the following:\n* The dataset __is heavily imbalanced__. __Data augmentation is required__.\n* The __distribution of sizes of images from train and test sets is different__. This will probably have an impact on classification results.\n\nIn this EDA we also explored augmented images to have an impression what augmented dataset will look like."},{"metadata":{},"cell_type":"markdown","source":"## References and Credits\n\n1. [Wikipedia page on diabetic retinopathy](https://en.wikipedia.org/wiki/Diabetic_retinopathy)\n2. [What is diabetic retinopathy?](https://www.aao.org/eye-health/diseases/what-is-diabetic-retinopathy)\n3. [iMet EDA kernel](https://www.kaggle.com/chewzy/eda-weird-images-with-new-updates)"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}