{"cells":[{"metadata":{},"cell_type":"markdown","source":"# EDA on iWildCam 2018 dataset"},{"metadata":{},"cell_type":"markdown","source":"## Imports"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# Imports\nimport glob\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport cv2\nfrom PIL import Image\nfrom io import BytesIO\n\nfrom zipfile import ZipFile\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Exploration - Training Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"def sample_train_images(n=10, grid_size=(5, 2), figsize=(24, 16)):\n    fig = plt.figure(figsize=figsize)\n    with ZipFile(train_zip) as tz:\n        for i, sample in enumerate(np.random.choice(tz.namelist(), n)):\n            ax = fig.add_subplot(*grid_size,1+i)\n            ax.axis('off')\n            img_file = tz.read(sample)\n            img = Image.open(BytesIO(img_file))\n            ax.imshow(img)\n    return fig","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# The images\ntrain_zip = '../input/iwildcam-2019-fgvc6/train_images.zip'\nwith ZipFile(train_zip) as tz:\n    print('Total training images:', len(tz.namelist()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gt = pd.read_csv('../input/iwildcam-2019-fgvc6/train.csv')\ntrain_gt.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Total categories: ', train_gt.category_id.nunique())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# class labels\nclasses_wild = {0: 'empty', 1: 'deer', 2: 'moose', 3: 'squirrel', 4: 'rodent', 5: 'small_mammal', \\\n                6: 'elk', 7: 'pronghorn_antelope', 8: 'rabbit', 9: 'bighorn_sheep', 10: 'fox', 11: 'coyote', \\\n                12: 'black_bear', 13: 'raccoon', 14: 'skunk', 15: 'wolf', 16: 'bobcat', 17: 'cat',\\\n                18: 'dog', 19: 'opossum', 20: 'bison', 21: 'mountain_goat', 22: 'mountain_lion'}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gt = train_gt.assign(category_label=train_gt.category_id.apply(classes_wild.get).values)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"category_samples = list(train_gt.groupby('category_label')['file_name'].apply(pd.Series.sample, n=1).reset_index(inplace=False)[['category_label', 'file_name']].to_records(index=False))\nfig = plt.figure(figsize=(24, 16))\nwith ZipFile(train_zip) as tz:\n    for i, sample in enumerate(category_samples):\n        ax = fig.add_subplot(3,5,1+i)\n        ax.axis('off')\n        ax.set_title(sample[0])\n        img_file = tz.read(sample[1])\n        img = Image.open(BytesIO(img_file))\n        ax.imshow(img)\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"category_counts = train_gt.groupby('category_label')['file_name'].apply(pd.Series.nunique).reset_index(inplace=False)\ncategory_counts.sort_values('file_name', inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pie, ax = plt.subplots(figsize=[10,10])\nplt.pie(x=category_counts.file_name, autopct=\"%.1f%%\", labels=category_counts.category_label, pctdistance=0.75, explode=[0.1]*category_counts.shape[0])\nplt.title(\"Labels distribution\", fontsize=14);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gt.file_name.nunique()/train_gt.shape[0] # Multiple entries per file?","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gt.groupby(by='file_name')['category_id'].agg(['count', lambda x: set(x)]).reset_index(inplace=False).query('count>1')\n# Hmm there are files with multiple labels. Let's look at some","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"multi_animal_imgs = train_gt.groupby(by='file_name')['category_id'].agg(['count', lambda x: set(x)]).reset_index(inplace=False).query('count>1').file_name.sample(12)\nfig = plt.figure(figsize=(24, 16))\nwith ZipFile(train_zip) as tz:\n    for i, fname in enumerate(multi_animal_imgs):\n        ax = fig.add_subplot(4, 3,1+i)\n        ax.axis('off')\n        ax.set_title(fname)\n        img_file = tz.read(fname)\n        img = Image.open(BytesIO(img_file))\n        ax.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's look at the test data too\ntest_gt = pd.read_csv('../input/iwildcam-2019-fgvc6/test.csv')\ntest_gt.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# The images\ntest_zip = '../input/iwildcam-2019-fgvc6/test_images.zip'\nfig = plt.figure(figsize=(24, 12))\nwith ZipFile(test_zip) as tz:\n    for i, sample in enumerate(tz.namelist()[:15]):\n        ax = fig.add_subplot(3,5,1+i)\n        ax.axis('off')\n        img_file = tz.read(sample)\n        img = Image.open(BytesIO(img_file))\n        ax.imshow(img)\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Preprocessing\n### Vignette removal"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's see how bad it is\nfig = plt.figure(figsize=(24, 16))\nwith ZipFile(train_zip) as tz:\n    for i, sample in enumerate(np.random.choice(tz.namelist(), 30)):\n        ax = fig.add_subplot(5,6,1+i)\n        ax.axis('off')\n        img_file = tz.read(sample)\n        img = Image.open(BytesIO(img_file))\n        ax.imshow(img)\n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Isn't so bad for day time images. Night vision needs work though. \n\n**Future work**: Detect grayscale images and de-vignette?"},{"metadata":{},"cell_type":"markdown","source":"### Parking for now"},{"metadata":{},"cell_type":"markdown","source":"### CLAHE"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Extract a few random images to pwd\nwith ZipFile(train_zip) as tz:\n    sample = np.random.choice(tz.namelist(), 20)\n    for i, s in enumerate(sample):\n        i+=1\n        tz.extract(s)\n        print('[', *['.']*i, *[' ']*(20-i), ']', sep='', end='\\r')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"clahe = cv2.createCLAHE(clipLimit=1.0, tileGridSize=(8, 8))\nfig = plt.figure(figsize=(20, 36))\nfor i, f in enumerate(glob.glob('*.jpg')):\n    # read each file, apply CLAHE and show\n    ax = fig.add_subplot(10, 2, i+1)\n    ax.axis('off')\n    img = cv2.cvtColor(cv2.imread(f), cv2.COLOR_BGR2LAB)\n    img[:, :, 0] = clahe.apply(img[:, :, 0])\n    \n    img_raw = cv2.cvtColor(cv2.imread(f), cv2.COLOR_BGR2RGB)\n    stacked_img = np.hstack([img_raw, cv2.cvtColor(img, cv2.COLOR_LAB2RGB)])\n    plt.imshow(stacked_img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Night images improved. Day-time images get high-contrast (expected). \n\n**Future work:** Detect day/night and apply CLAHE?"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"### Denoising"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's look at a few images and see how bad it is\n_ = sample_train_images(n=6, grid_size=(3, 2), figsize=(24, 24))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Not too bad IMO"},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"fig = plt.figure(figsize=(24, 48))\nfor i, f in enumerate(glob.glob('*.jpg')[:6]):\n    # read each file, apply CLAHE and show\n    ax = fig.add_subplot(6, 1, i+1)\n    ax.axis('off')\n    img = cv2.cvtColor(cv2.imread(f), cv2.COLOR_BGR2RGB)\n    # Try with a small h. h~7 blurs it too much and we lose sharpness.\n    img = cv2.fastNlMeansDenoisingColored(img, None, 3, 6, 7, 21)\n    \n    img_raw = cv2.cvtColor(cv2.imread(f), cv2.COLOR_BGR2RGB)\n    stacked_img = np.hstack([img_raw, img])\n    plt.imshow(stacked_img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The images look mostly clean. The main issues seem to be poor illumination in night images. May work without denoising. "},{"metadata":{},"cell_type":"markdown","source":"### White balance"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's run a quick test to see if the grey world assumption is fair to make\n\nfig = plt.figure(figsize=(24, 8))\nwith ZipFile(train_zip) as tz:\n    for i, sample in enumerate(np.random.choice(tz.namelist(), 30)):\n        ax = fig.add_subplot(3,10,1+i)\n        ax.axis('off')\n        img_file = tz.read(sample)\n        img = Image.open(BytesIO(img_file))\n        # img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)\n        means = np.mean(np.mean(img, axis=0), axis=0).astype(np.uint8)\n        #print(means)\n        color_patch = np.ones((32, 32, 3), dtype=np.uint8)*means\n        ax.imshow(color_patch)\n        \n# Yeah not really. It's greens or greys.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Come to think of it, white balancing is essentially a linear transform on the pixel values based on some statistic of the whole image, kind of like regularization at an image level. \nCan try on/off and see if it helps, starting with simpler WB techniques and moving on more complex ideas:\n- [Improving CNN-Based Texture Classification byColor Balancing - Bianco et al.](https://www.researchgate.net/publication/318740203_Improving_CNN-Based_Texture_Classification_by_Color_Balancing)"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Future work:** Can experiment with Learning based WB techniques"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Cleanup pwd\nfor f in os.listdir('.'):\n    os.remove(f)","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}