{"metadata": {"kernelspec": {"language": "python", "name": "python3", "display_name": "Python 3"}, "language_info": {"version": "3.6.1", "nbconvert_exporter": "python", "name": "python", "codemirror_mode": {"version": 3, "name": "ipython"}, "mimetype": "text/x-python", "pygments_lexer": "ipython3", "file_extension": ".py"}}, "cells": [{"metadata": {"_uuid": "6d06a5e5c6d1e4185cc69d85faaa5ed1755cf24a", "_cell_guid": "d06389b0-6f2d-495d-bb58-c4324bf19a2e"}, "outputs": [], "execution_count": null, "source": "## Summary\n\nSome rudimentary colour correction of the training and test images in the Carvana Image Masking Challenge dataset.\n\nMany of the images in the dataset have a noticeable colour cast. Given that the top-most part of each image is featureless and relatively uniform, this can be used to calculate a mean background colour for each image, and then the mean is taken over all images in the dataset. **Spoiler: the answer is RBG = [241.84525443, 240.75213576, 238.65245818].** The mean background can then be used to colour correct any of the image.\n\nThis may be a useful step in normalising the images for the Carvana Image Masking Challenge. As yet I haven't checked what kind of a difference, if any, it makes to my own efforts. I would be interested to know if this improves anyone's results.", "cell_type": "markdown"}, {"metadata": {"trusted": false, "_uuid": "39a2a5b14090c63944c2c896529c376782db5761", "_cell_guid": "88d8ba77-1ed3-4c71-a852-68457faefdc9", "collapsed": true}, "outputs": [], "execution_count": null, "source": "import glob\nimport numpy as np\nfrom scipy import ndimage\nfrom matplotlib import pyplot as plt\n\nPROJECT_PATH = '..'\nINPUT_PATH = PROJECT_PATH + '/input'\nTRAIN_IMAGE_PATH = INPUT_PATH + '/train'\nTEST_IMAGE_PATH = INPUT_PATH + '/test'", "cell_type": "code"}, {"metadata": {"trusted": true, "_uuid": "db50478f1100054ebf9a8465af8ff4c65d921a4e", "_cell_guid": "06b48f37-903e-4b61-88cb-dbc8992c34bf", "collapsed": true}, "outputs": [], "execution_count": null, "source": "# Use the pixel-wise mean of top strip of an image to calculate the background colour.\n# A strip 32 pixels high seems to work well.\n\ndef extract_mean_backgound_colour(image, top_strip_width = 32):\n    top_strip = image[:top_strip_width, : , :]\n    return np.mean(top_strip, axis=(0,1))", "cell_type": "code"}, {"metadata": {"trusted": false, "_uuid": "57ef96ad646f8310d4950f2a7bc48ad47bd068b1", "_cell_guid": "6e7c863c-04c3-4762-96c8-29461c603651", "collapsed": true}, "outputs": [], "execution_count": null, "source": "# Now do this for all images (or a limited number of images by setting max_images to a value\n# less than the total number of images).\n\ndef find_mean_background_colour(max_images = 1000*1000):\n    \n    image_paths = glob.glob(TRAIN_IMAGE_PATH + '/*.jpg') + glob.glob(TEST_IMAGE_PATH + '/*.jpg')\n    image_paths = image_paths[:max_images]\n\n    print('Finding images ...')\n    image_count = 0\n    num_images = len(image_paths)\n    mean_bg_colour = np.asarray([0.0, 0.0, 0.0])\n    print('\\nStarting ...\\n')\n    for image_path in image_paths:\n        image = ndimage.imread(image_path, mode = 'RGB')\n        mean_bg_colour = mean_bg_colour + extract_mean_backgound_colour(image)\n        image_count += 1\n        if image_count % 1000 == 0:\n            print('  .. completed', image_count, 'of', num_images, 'images: mean bg',\n                    mean_bg_colour/image_count, ' ..')\n    mean_bg_colour = mean_bg_colour/image_count\n    print('\\nDone.')\n    print('Mean background colour:', mean_bg_colour)\n    return mean_bg_colour", "cell_type": "code"}, {"metadata": {"trusted": false, "_uuid": "b7563c74cc618231f018d7c0c987895a389e855b", "_cell_guid": "806666e5-32e2-4d50-86fe-a52a30eb9215", "collapsed": true}, "outputs": [], "execution_count": null, "source": "# THIS TAKES A LONG TIME SO WE WON'T DO IT HERE.\n# MEAN_BACKGROUND_COLOUR = find_mean_background_colour()\n\n# Instead we'll use the answer calculated previously:\nMEAN_BACKGROUND_COLOUR = np.asarray((241.84525443, 240.75213576, 238.65245818))", "cell_type": "code"}, {"metadata": {"trusted": false, "_uuid": "a4217407f4d61ddf8ef19ae1817b21f072270222", "_cell_guid": "5aa39bba-80b0-47a6-9d9e-40fdba71f683", "collapsed": true}, "outputs": [], "execution_count": null, "source": "# A numpy implementation of rudimentary colour correction.\n# I actually use a Tensorflow implementation of this in training (appended to this notebook),\n# which doesn't use clipping since there is all sort of normalisation later.\n\ndef colour_correct_image(image):\n    mean_bg_colour = extract_mean_backgound_colour(image)\n    colour_correction_factor = mean_bg_colour/MEAN_BACKGROUND_COLOUR\n    corrected_image = np.round(image/colour_correction_factor)\n    corrected_image = np.clip(corrected_image, 0.0, 255.0)\n    return corrected_image.astype(np.uint8)", "cell_type": "code"}, {"metadata": {"trusted": false, "collapsed": true, "_uuid": "a6e943bb0014ae6a2356b059a692d23b53517f87", "_cell_guid": "4b80562c-b143-46a2-a0de-549262121e0d", "scrolled": false}, "outputs": [], "execution_count": null, "source": "# Somes tests/examples:\n\ndef test_colour_correction():\n    image_file_names = [\n        '0d53224da2b7_05.jpg',\n        '0d3adbbc9a8b_14.jpg',\n        '1a17a1bd648b_15.jpg',\n        '2ea62c1beee7_15.jpg',\n        '11fcda0a9e1c_04.jpg'\n    ]\n    image_paths = [TRAIN_IMAGE_PATH + '/' + file_name for file_name in image_file_names]\n    images = [ndimage.imread(path, mode = 'RGB') for path in image_paths]\n    count = 0\n    for image in images:\n        count += 1; print('\\n--------\\nImage #' + str(count))\n        plt.figure(figsize=(12, 10))\n        plt.subplot(221); plt.title('Original Image'); plt.imshow(image)\n        plt.subplot(223); plt.title('Original RBG'); plt.hist(image.flatten(), bins=100); \n        corrected_image = colour_correct_image(image)\n        plt.subplot(222); plt.title('Corrected Image'); plt.imshow(corrected_image)\n        plt.subplot(224); plt.title('Corrected RBG'); plt.hist(corrected_image.flatten(), bins=100)\n        plt.show()\n        \n\n\ntest_colour_correction()", "cell_type": "code"}, {"metadata": {"trusted": false, "_uuid": "cee984deb17a706dc0e701a6e65e5b700ccd13e0", "_cell_guid": "01a87db7-6140-4f87-af81-ffa748e0d797", "collapsed": true}, "outputs": [], "execution_count": null, "source": "# My Tensorflow implemenation for those interested (NOTE: I did the inverse of the\n# corrected_rgb_image that I used in the numpy version, not that it makes any difference):\n\ndef tf_colour_correction(rgb_image, top_strip_width = 32):\n    global_mean_bg_colour = tf.constant(MEAN_BACKGROUND_COLOUR/255.0, dtype = tf.float32)\n    mean_bg_colour = tf.reduce_mean(rgb_image[:, :top_strip_width, : , :], axis = (1, 2), keep_dims=True)\n    colour_correction_factor = global_mean_bg_colour/mean_bg_colour\n    corrected_rgb_image = colour_correction_factor*rgb_image\n    return corrected_rgb_image", "cell_type": "code"}], "nbformat": 4, "nbformat_minor": 1}