{"cells":[{"cell_type":"markdown","metadata":{"_cell_guid":"383463d7-a2a3-279b-b684-42f2c6dde093"},"source":"# Preprocess Images\nNormalize the luminance values and resize the images to a standard shape. This is done because the training and test images come in a variety of shapes, sizes, and lighting."},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"4592095c-7d3b-babe-346a-e4a5bf078344"},"outputs":[],"source":"%matplotlib inline\n\nimport glob\nimport os\nimport re\n\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport PIL\nfrom PIL import Image"},{"cell_type":"markdown","metadata":{"_cell_guid":"02f26153-daf3-e7b6-5246-9aa20e6e5a39"},"source":"## Sort numerically\nThis is not really necessary but when you're exploring it's nice to have the images sorted numerically so that `cat.100.jpg` comes before `cat.1000.jpg`, etc."},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"cd3bfe01-1916-212c-3a75-ad3000182eea"},"outputs":[],"source":"def natural_key(string_):\n    \"\"\"\n    Define sort key that is integer-aware\n    \"\"\"\n    return [int(s) if s.isdigit() else s for s in re.split(r'(\\d+)', string_)]"},{"cell_type":"markdown","metadata":{"_cell_guid":"864efd39-ae2e-e0c5-fa4c-e9bb370a9a4f"},"source":"## Normalize the image luminance\nit is common in image analysis to normalize the luminance (brightness) values to have mean 0 and standard deviation 1. We do that here and apply a slight contrast stretch, which also ensures the brightness values stay within the bounds of the image encoding.\n\nThe normalization is applied to the luminance, not to the RGB channels individually. We first convert to `YCbCr` space, operate on the `Y` channel, and then convert back to `RGB`."},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"e19df3a3-fa7c-af03-ff41-bfc05373a400"},"outputs":[],"source":"def norm_image(img):\n    \"\"\"\n    Normalize PIL image\n    \n    Normalizes luminance to (mean,std)=(0,1), and applies a [1%, 99%] contrast stretch\n    \"\"\"\n    img_y, img_b, img_r = img.convert('YCbCr').split()\n    \n    img_y_np = np.asarray(img_y).astype(float)\n\n    img_y_np /= 255\n    img_y_np -= img_y_np.mean()\n    img_y_np /= img_y_np.std()\n    scale = np.max([np.abs(np.percentile(img_y_np, 1.0)),\n                    np.abs(np.percentile(img_y_np, 99.0))])\n    img_y_np = img_y_np / scale\n    img_y_np = np.clip(img_y_np, -1.0, 1.0)\n    img_y_np = (img_y_np + 1.0) / 2.0\n    \n    img_y_np = (img_y_np * 255 + 0.5).astype(np.uint8)\n\n    img_y = Image.fromarray(img_y_np)\n\n    img_ybr = Image.merge('YCbCr', (img_y, img_b, img_r))\n    \n    img_nrm = img_ybr.convert('RGB')\n    \n    return img_nrm"},{"cell_type":"markdown","metadata":{"_cell_guid":"fea6ac25-99d3-0494-0e05-ff4686ad44ba"},"source":"## Resize the image\nWe resize the images to be square with a default side length of 224 to be compatible with models trained on ImageNet. The aspect ratio is preserved and gray bars are added as necessary to make the image square."},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"43e2795d-7474-975c-5770-c5a7e9896742"},"outputs":[],"source":"def resize_image(img, size):\n    \"\"\"\n    Resize PIL image\n    \n    Resizes image to be square with sidelength size. Pads with black if needed.\n    \"\"\"\n    # Resize\n    n_x, n_y = img.size\n    if n_y > n_x:\n        n_y_new = size\n        n_x_new = int(size * n_x / n_y + 0.5)\n    else:\n        n_x_new = size\n        n_y_new = int(size * n_y / n_x + 0.5)\n\n    img_res = img.resize((n_x_new, n_y_new), resample=PIL.Image.BICUBIC)\n\n    # Pad the borders to create a square image\n    img_pad = Image.new('RGB', (size, size), (128, 128, 128))\n    ulc = ((size - n_x_new) // 2, (size - n_y_new) // 2)\n    img_pad.paste(img_res, ulc)\n\n    return img_pad"},{"cell_type":"markdown","metadata":{"_cell_guid":"3e0687d2-172a-9724-fca2-35c4ffd85072"},"source":"## Accumulate the image names"},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"2d8ba464-d1c7-b100-b5df-e7a28cbf55de"},"outputs":[],"source":"TRAIN_DIR = '../input/train/'\nTEST_DIR = '../input/test/'\n\ntrain_cats = sorted(glob.glob(os.path.join(TRAIN_DIR, 'cat*.jpg')), key=natural_key)\ntrain_dogs = sorted(glob.glob(os.path.join(TRAIN_DIR, 'dog*.jpg')), key=natural_key)\ntrain_all = train_cats + train_dogs\n\ntest_all = sorted(glob.glob(os.path.join(TEST_DIR, '*.jpg')), key=natural_key)"},{"cell_type":"markdown","metadata":{"_cell_guid":"ee0a2406-7277-4480-451e-2796a32d7b95"},"source":"## Preprocess a single image\nThis cell will preprocess a specified or random image and display the before and after versions."},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"55ebe7f7-cc5c-754b-85f3-91c710974082"},"outputs":[],"source":"SIZE = 224  # for ImageNet models compatibility\n\n# Read the image\nchoose_random_image = True\nif choose_random_image:\n    idx = np.random.randint(0, len(train_all))\nelse:\n    idx = 24486\nprint(idx)\npath = train_all[idx]\nimg = Image.open(path)\n\n# Normalize it\nimg_nrm = norm_image(img)\n\n\n# Resize it\nimg_res = resize_image(img_nrm, SIZE)\n\n# Show it\nplt.figure(figsize=(8,4))\nplt.subplot(131)\nplt.title('Original')\nplt.imshow(img)\n\nplt.subplot(132)\nplt.title('Normalized')\nplt.imshow(img_nrm)\n\nplt.subplot(133)\nplt.title('Resized')\nplt.imshow(img_res)\n\nplt.tight_layout()\nplt.show()"},{"cell_type":"code","execution_count":null,"metadata":{"_cell_guid":"1ae21816-3017-fa7f-58b4-8b4e3f0e5ee4","collapsed":true},"outputs":[],"source":""}],"metadata":{"_change_revision":0,"_is_fork":false,"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.5.2"}},"nbformat":4,"nbformat_minor":0}