{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# Introduction\n\nIn this competition, you are required to recognize landmark present in test images. The landmark recognition challenge contains more than 81K classes, available across train folders with their labels provided in a csv file. Each image has a unique image id.\n\nThis notebook contains EDA to explore the folder structure, build visuals of the image distributions and understand the classes and labels.","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Import libraries \nimport pandas as pd\nfrom IPython.display import HTML\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport numpy as np\nimport os\nimport PIL\nimport plotly.graph_objects as go\nfrom plotly.offline import init_notebook_mode, iplot\nimport seaborn as sns\nimport tensorflow as tf\nfrom tqdm import tqdm_notebook as tqdm\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\n\ninit_notebook_mode()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Table of contents\n\n1. [Exploring the folder structures](#folder)\n2. [Image data consolidation](#labelsconsolidate)\n3. [Looking at the images](#look)\n4. [Plots of label frequencies](#frequencies)\n5. [Plots of label distribution](#distributions)\n6. [Image dimensions](#dimensions)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Folder structure <a class=\"anchor\" id=\"folders\"> </a>\n\nAs a first step to understanding the data structures, folders and filepaths for this competition, lets look at the folder structure for the train and test data images.We'll build directories for each relevant folder path.\n\nThe [train] folder containing the images is stored across 16 folders, with names 0-9, a-f.\nEach of these folders is further a series of nested folders, as below: \n<code>\n\n|---0\n    |---0\n        |---0\n            |---0000059611c7d079.jpg\n            |---00...............jpg\n        |---1\n    |---1\n    |---2\n|---1\n</code>\n\n\nLet's assign the folder paths to variables.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f\"{'--'*20} \\n LIST OF FOLDERS: \\n {os.listdir(os.path.join('../'))} \\n {'--'*20} \\n\")\nprint(f\"LIST OF FOLDERS INSIDE [input] FOLDER: \\n {os.listdir(os.path.join('../input'))} \\n {'--'*20} \\n\")\nprint(f\"LIST OF FOLDERS INSIDE [input/landmark-recognition-2020] FOLDER: \\n {os.listdir(os.path.join('../input/landmark-recognition-2020'))} \\n {'--'*20} \\n\")\n\n# Assigning paths to variables\nINPUT_PATH = os.path.join('..', 'input')\nDATASET_PATH = os.path.join(INPUT_PATH, 'landmark-recognition-2020')\nTRAIN_IMAGE_PATH = os.path.join(DATASET_PATH, 'train')\nTEST_IMAGE_PATH = os.path.join(DATASET_PATH, 'test')\nTRAIN_CSV_PATH = os.path.join(DATASET_PATH, 'train.csv')\nSUBMISSION_CSV_PATH = os.path.join(DATASET_PATH, 'sample_submission.csv')\n\ntrain_df = pd.read_csv(TRAIN_CSV_PATH)\nprint(f\"{'--'*20} \\n SNIPPET OF TRAINING DATA: \\n {train_df.head()} \\n {'--'*20} \\n Number of rows in train data: {train_df.shape[0]} \\n {'--'*20}\")\n\nsubmission_df = pd.read_csv(SUBMISSION_CSV_PATH)\nprint(f\"{'--'*20} \\n SNIPPET OF TEST DATA: \\n {submission_df.head()} \\n {'--'*20} \\n Number of rows in test data: {submission_df.shape[0]} \\n {'--'*20}\")\n\nprint(f\"EXAMPLE FOR LANDMARK-LABEL MAPPING FOR [17660ef415d37059.jpg] \\n FOLDER STRUCTURE: \\n |---1 \\n \\t |---7 \\n \\t \\t |---6 \\n \\t \\t \\t|---<17660ef415d37059.jpg>\")\ni=0\nprint(f\"Image name: {train_df['id'].iloc[i]}\")\nprint(f\"First folder to look inside: {train_df['id'][i][0]}\")\nprint(f\"Second folder to look inside: {train_df['id'][i][1]}\")\nprint(f\"Second folder to look inside: {train_df['id'][i][2]}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Consolidate label names <a class=\"anchor\" id=\"labelsconsolidate\"> </a>\n\nLets build a dataframe containing all filenames with their corresponding labels","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Build dictionary to store image paths & labels\nprint(f\"{'---'*20} \\n Creating training data mapping \\n {'---'*20}\")\ndata_label_dict = {'image': [], 'target': []}\nfor i in tqdm(range(train_df.shape[0])):\n    data_label_dict['image'].append(\n        TRAIN_IMAGE_PATH + '/' +\n        train_df['id'][i][0] + '/' + \n        train_df['id'][i][1]+ '/' +\n        train_df['id'][i][2]+ '/' +\n        train_df['id'][i] + \".jpg\")\n    data_label_dict['target'].append(\n        train_df['landmark_id'][i])\n#Convert to dataframe\ntrain_pathlabel_df = pd.DataFrame(data_label_dict)\nprint(train_pathlabel_df.head())\n    \nprint(f\"{'---'*20} \\n Creating test data mapping \\n {'---'*20}\")\ndata_label_dict = {'image': []}\nfor i in tqdm(range(submission_df.shape[0])):\n    data_label_dict['image'].append(\n        TEST_IMAGE_PATH + '/' +\n        submission_df['id'][i][0] + '/' + \n        submission_df['id'][i][1]+ '/' +\n        submission_df['id'][i][2]+ '/' +\n        submission_df['id'][i] + \".jpg\")\n\ntest_pathlabel_df = pd.DataFrame(data_label_dict)\nprint(test_pathlabel_df.head())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Looking at the images <a class=\"anchor\" id='look'> </a>\n\nThe training data has 81,313 unique classes of landmarks.\n\nLets take a look at the first 4 landmarks to understand how the images look, and get an idea of how many instances we have per image.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f\"The data has {train_pathlabel_df['target'].nunique()} unique classes\")\n\nfor tar in train_pathlabel_df['target'].unique()[:4]: \n    #Subset to just that target \n    label_df = train_pathlabel_df[train_pathlabel_df['target']==tar].reset_index()\n    cols = 2\n    rows = 2\n    fig = plt.figure(figsize = (4*cols - 1, 4.5*rows - 1))\n    for c in range(cols):\n        for r in range(rows):\n            ax = fig.add_subplot(rows, cols, c*rows + r + 1)\n            img = mpimg.imread(label_df['image'][c+r])\n            ax.imshow(img)#label_df[][c+r])\n    fig.suptitle(f\"Images corresponding to label [{tar}] with a total of {label_df.shape[0]} images available\")\n    plt.show()\n    plt.close()\n    \n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Plots of label frequencies <a class=\"anchor\" id='plots'> </a> \n\nLets look at how the count of images per landmark is distributed. First we'll build a dataframe of image counts per landmark, and then look at the frequency for the top 20 landmarks.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"counts_df = pd.DataFrame(train_df[['landmark_id']].value_counts().reset_index())\ncounts_df.columns = ['Landmark', 'Count']\ncounts_df.sort_values('Count', ascending=False, inplace=True)\nprint(counts_df.head())\n\nfig = go.Figure(data = [go.Bar(x = counts_df[:20].index,\n                              y=counts_df[:20]['Count'],\n                              text=counts_df[:20]['Count'],\n                              textposition = 'outside')])\nfig.update_layout(title=\"Image counts across top 20 landmarks\",\n                 xaxis_title = \"Landmark id\",\n                 yaxis_title = \"Count of images\")\nfig.update_xaxes(ticktext= counts_df[:20]['Landmark'],\n                tickvals = counts_df[:20].index)\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It looks like the image classes are unbalanced, with 6272 images of `138982` category, followed by 2231 images of `126637` category. The smallest classes like `202176` and`183721` have as few as 2 images each.\n\nLets build some bins of the image counts to look at the number of images across different bins.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"counts_df['Bin'] = np.where((counts_df['Count']<=10), \"(0, 10]\", \"Rest\")\ncounts_df['Bin'] = np.where((counts_df['Count']>10) & (counts_df['Count']<=20), \"(10, 20]\", counts_df['Bin'])\ncounts_df['Bin'] = np.where((counts_df['Count']>20) & (counts_df['Count']<=30), \"(20, 30]\", counts_df['Bin'])\ncounts_df['Bin'] = np.where((counts_df['Count']>30) & (counts_df['Count']<=50), \"(30, 50]\", counts_df['Bin'])\ncounts_df['Bin'] = np.where((counts_df['Count']>50) & (counts_df['Count']<=70), \"(50, 70]\", counts_df['Bin'])\ncounts_df['Bin'] = np.where((counts_df['Count']>70) & (counts_df['Count']<=100), \"(70, 100]\", counts_df['Bin'])\ncounts_df['Bin'] = np.where((counts_df['Count']>100) & (counts_df['Count']<=150), \"(100, 150]\", counts_df['Bin'])\n# counts_df['Bin'] = np.where((counts_df['Count']>=20) & (counts_df['Count']<30), \"Bin 3: 20-30\", counts_df['Bin'])\nbin_df = counts_df.groupby('Bin')['Count'].count().reset_index()\nbin_df['Bin'] = bin_df['Bin'].astype('str')\nprint(bin_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nfig = go.Figure(data = [go.Bar(x=bin_df['Bin'],\n                               y=bin_df['Count'],\n                               text = bin_df['Count'],\n                              textposition = 'outside')])\nfig.update_layout(title='Ímage counts across bins',\n                  xaxis_title = \"Bin/interval of image counts per landmark\",\n                 yaxis_title = \"Count of images in bin\")\n# fig.update_xaxes('Bins/intervals of image counts')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We observe that:\n* 44,646 landmarks have at least 10 images.\n* Only 879 landmarks have more than 100 images","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Distribution of image labels <a class='anchor' id='distributions'> </a>\n\nLet's look at how the target labels are distributed","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (15,10))\nsns.distplot(train_pathlabel_df['target'], hist=True, bins=100)\nplt.title('Distribution of image labels')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Image dimensions <a class='anchor' id='dimensions'> </a>\n\nFor the first 50,000 images, lets look at a histogram of the heights & weights.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"dims_dict = {'image': [], 'width': [], 'height': [], 'channels': []}\nfor i in tqdm(range(50000)):#train_pathlabel_df['image'].unique())):\n    dims = mpimg.imread(train_pathlabel_df['image'][i]).shape\n    dims_dict['image'].append(train_pathlabel_df['image'][i])\n    dims_dict['height'].append(dims[0])\n    dims_dict['width'].append(dims[1])\n    dims_dict['channels'].append(dims[2])\n\ndims_df = pd.DataFrame(dims_dict).head()\n\nsns.distplot(dims_df['height'])\nplt.title('Distribution of image heights for first 50,000 images');\n\nsns.distplot(dims_df['width'])\nplt.title('Distribution of image widths for first 50,000 images');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"That's all folks! Thanks for reading this far, this is my first Kaggle competition. In my next notebook I'll try some simple classifiers on the data.\n\nNote: I am seeing my notebook displayed with some strange margins around markdown cells, the rendering doesn't look too pretty. I can't figure out why this is happening, would appreciate any advice if I'm making a noob mistake.","execution_count":null},{"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}