{"cells":[{"metadata":{"_uuid":"3adfc30d9dbf7b72cfb046f2345ce1de9b33b910"},"cell_type":"markdown","source":"# Channel normalization\nMany of the kernels here are normalizing their input data dividing by 255 so you end up with values ranging from 0 to 1.\n\nAnother common normalization practice with images is to have a **mean of 0 and a standard deviation of 1 per channel.** This notebook calculates the necessary values for each channel so we can normalize them with:\n\n```channel = (channel - channel_mean) / channel_stdev```\n\nI got slightly better predictions by changin from 0-1 normalization to the latter (from **0.348** to **0.354**) with a *512x512x4 CNN with batch norm* (from scratch). This is not much of an improvement but at least this did not make it worse.\n\nIn conclusion, **if you are using batch normalization in your neural network, the method of input normalization is probably not gonna have a large effect**.\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np \nimport pandas as pd \n\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom PIL import Image\nfrom scipy.misc import imread\n\nimport os\n\n# ignore warnings\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\n\n# Channel visualization\nchannel_names = ['Green','Red','Blue','Yellow']\nchannel_colors = ['mediumseagreen', 'salmon', 'steelblue', 'burlywood']\nchannel_cmaps = ['Greens','Reds','Blues','Oranges']\n\n# Load training labels\ntrain_labels = pd.read_csv(\"../input/train.csv\")\nprint('Number of training images = {0}'.format(train_labels.shape[0]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bb221414a21a1025eb4a7ec7dee516d11ae2874e"},"cell_type":"code","source":"# set the path to our training image folder\ntrain_path = \"../input/train/\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1bb98ce1c875826e526dd1339ea92c51227ae0b9"},"cell_type":"code","source":"# Helper function for loading images\n# Copied from: https://www.kaggle.com/allunia/protein-atlas-exploration-and-baseline\ndef load_image(basepath, image_id):\n    images = np.zeros(shape=(4,512,512))\n    images[0,:,:] = imread(basepath + image_id + \"_green\" + \".png\")\n    images[1,:,:] = imread(basepath + image_id + \"_red\" + \".png\")\n    images[2,:,:] = imread(basepath + image_id + \"_blue\" + \".png\")\n    images[3,:,:] = imread(basepath + image_id + \"_yellow\" + \".png\")\n    return images","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4481e8821a8875a196b42fc4310c58f492e9c813"},"cell_type":"markdown","source":"Test that we can read the images and plot the channels of the first image."},{"metadata":{"trusted":true,"_uuid":"f56d8121ea68be8b5dbcebb01ff52d44958a0d67"},"cell_type":"code","source":"for id in train_labels.Id:\n    images = load_image(train_path, id)\n    fig, ax = plt.subplots(1,4,figsize=(20,5))\n    for n in range(4):\n        ax[n].imshow(images[n], cmap=channel_cmaps[n])\n        ax[n].set_title(channel_names[n])\n    break","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9c28de7b479ddb6496ecd3d21fb73e49a1c167b9"},"cell_type":"markdown","source":"### Functions for calculating mean and standard deviation"},{"metadata":{"trusted":true,"_uuid":"78d97347671e75fa4e905538326518802586b80e"},"cell_type":"code","source":"def mean_from_histogram(arr):\n    hist_sum = 0\n    count = np.sum(arr)\n    for n in range(len(arr)):\n        hist_sum += n * arr[n]\n    return hist_sum / count\n\ndef stdev_from_histogram(arr, mean):\n    count = np.sum(arr)\n    variance = 0\n    for n in range(len(arr)):\n        variance += arr[n] * (n - mean) * (n - mean)\n    return np.sqrt(variance/count)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"63639891f04e33458d0ff2f1cfc3cad0f47ab9c0"},"cell_type":"markdown","source":"### Get histograms from the training images"},{"metadata":{"trusted":true,"_uuid":"74afe16e7a77f4982e42b4ed89520fdc54bc2160"},"cell_type":"code","source":"channel_hist = np.zeros(shape=(4,256))\nchannel_means = np.zeros(shape=(4))\nchannel_stdevs = np.zeros(shape=(4))\n\n# These iterations are divided into two cells because the Jupyter Notebook cell timeout is 20 minutes\n# and going through all images takes about 25 minutes\nfrom tqdm import tqdm\ncounter = 0\nfor id in train_labels.Id:\n    images = load_image(train_path, id)\n    for n in range(4):\n        hist, _ = np.histogram(images[n], 256, density=False)\n        channel_hist[n,:,] = np.sum([hist,channel_hist[n]], axis=0)\n    counter += 1\n    if(counter == 15000):\n        break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"345f2a52e984f836aa3509b57bb0b26f49bf3e53"},"cell_type":"code","source":"for id in train_labels.Id:\n    if(counter > 0):\n        counter -= 1\n        continue\n    images = load_image(train_path, id)\n    for n in range(4):\n        hist, _ = np.histogram(images[n], 256, density=False)\n        channel_hist[n,:,] = np.sum([hist,channel_hist[n]], axis=0)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c99679892a189c70f77f4a480d0ea6cd6ba811a3"},"cell_type":"markdown","source":"## Calculate means and standard deviations for each channel\nPrint results and plot histograms "},{"metadata":{"trusted":true,"_uuid":"4cf6fcc765ff4434f6dc317b13da29b4cb684aa5"},"cell_type":"code","source":"fig, ax = plt.subplots(1,4,figsize=(20,5))\nx = range(256)\nfig.suptitle('Histograms (log)', fontsize=16)\n\n# Calculate means and standard deviations for each channel\nfor n in range(4):\n    ax[n].bar(x, channel_hist[n], color=channel_colors[n], width=1.0)\n    ax[n].set_yscale('log')\n    ax[n].set_title(channel_names[n])\n    channel_means[n] = mean_from_histogram(channel_hist[n])\n    channel_stdevs[n] = stdev_from_histogram(channel_hist[n], channel_means[n])\n    print(channel_names[n] + ': Mean = {0} ,StDev = {1}'.format(channel_means[n], channel_stdevs[n]))\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"96c75e20b7c1fd03059c17d8b23131584ec16cb0"},"cell_type":"markdown","source":"# Channel means and standard deviations\nThese are calculated from the whole training dataset.\n\n| **Channel** | **Mean**| **Standard deviation** |\n| -------------- | :----------: | :---------------------------: |\n| **Green** | `13.528` | `28.700` |\n| **Red** | `20.535` | `38.161` |\n| **Blue** | `14.249` | `40.195` |\n| **Yellow** | `21.106` | `38.172` |\n"},{"metadata":{"_uuid":"a2590bd1f5952c7f2ec4bad363717e2c46255571"},"cell_type":"markdown","source":"## How to normalize when loading images\nHere is an image loading function that returns a normalized 4-channel 512x512 image where each channel is normalized as: ```channel = (channel - channel_mean) / channel_stdev``` "},{"metadata":{"trusted":true,"_uuid":"524d7ff2896de2abe5f2702ee2bf6808852329ca"},"cell_type":"code","source":"import skimage.io\nfrom skimage.transform import resize\ndef load_normalized_image(basepath, image_id):\n        image_green = skimage.io.imread(basepath + image_id + \"_green\" + \".png\")\n        image_red = skimage.io.imread(basepath + image_id + \"_red\" + \".png\")\n        image_blue = skimage.io.imread(basepath + image_id + \"_blue\" + \".png\")\n        image_yellow = skimage.io.imread(basepath + image_id + \"_yellow\" + \".png\")\n\n        # normalize with calculated channel means and standard deviations\n        image = np.stack((\n            (image_red - 20.535) / 38.161,\n            (image_green - 13.528) / 28.700,\n            (image_blue - 14.249) / 40.195, \n            (image_yellow - 21.106) / 38.172), -1)\n        \n        image = resize(image, (512, 512, 4), mode='reflect')\n        return image.astype(np.float)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2682e0981e4a90d03b15491bbee8bfb9f2d0eda1"},"cell_type":"markdown","source":"For comparison. The below function normalizes each channel to 0-1 range with ```channel = channel / 255```"},{"metadata":{"trusted":true,"_uuid":"8b1685f71117d284a8a36c63313cbd5ff97977c5"},"cell_type":"code","source":"def load_normalized_0_1_image(basepath, image_id):\n        image_green = skimage.io.imread(basepath + image_id + \"_green\" + \".png\")\n        image_red = skimage.io.imread(basepath + image_id + \"_red\" + \".png\")\n        image_blue = skimage.io.imread(basepath + image_id + \"_blue\" + \".png\")\n        image_yellow = skimage.io.imread(basepath + image_id + \"_yellow\" + \".png\")\n        \n        image = np.stack((\n            image_red / 255.,\n            image_green / 255.,\n            image_blue / 255., \n            image_yellow / 255.), -1)\n        \n        image = resize(image, (512, 512, 4), mode='reflect')\n        return image.astype(np.float)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5e6ecfa6ca998c5c2b870e94334ad83d0fbe466c"},"cell_type":"markdown","source":"### Value distribution comparison\n- **Without normalization**\n- **Normalized (*(channel - channel_mean) / channel_SD*)**\n- **Normalized (*channel/255*)**\n\nLets look at histograms from batches of 100 first images to see how the pixel values are distributed."},{"metadata":{"trusted":true,"_uuid":"0c2ec715597432cb0eef741dc56cd9c9bff4b4fc"},"cell_type":"code","source":"counter = 0\n# 256 bins\nhist_100 = np.zeros(shape=(256))\nhist_100_reg = np.zeros(shape=(256))\nhist_100_0_1 = np.zeros(shape=(256))\nbin_edges = []\nbin_edges_reg = []\nbin_edges_0_1 = []\nfor id in train_labels.Id:\n    # load normalized (mean SD)\n    image = load_normalized_image(train_path, id)\n    hist, bin_edges = np.histogram(image, 256, (-1,10), density=False)\n    hist_100 = np.sum([hist, hist_100], axis=0)\n    # load normalized to 0-1 range\n    image = load_normalized_0_1_image(train_path, id)\n    hist, bin_edges_0_1 = np.histogram(image, 256, (0,1), density=False)\n    hist_100_0_1 = np.sum([hist, hist_100_0_1], axis=0)\n    # load regular\n    images = load_image(train_path, id)\n    for n in range(4):\n        hist, bin_edges_reg = np.histogram(images[n], 256, density=False)\n        hist_100_reg = np.sum([hist,hist_100_reg], axis=0)\n    \n    counter += 1\n    if(counter == 100):\n        break\n\nfig, ax = plt.subplots(1,3,figsize=(18,5))\nax[0].bar(bin_edges_reg[1:], hist_100_reg, 1, color='steelblue')\n#ax[0].set_yscale('log')\nax[0].set_ylim(0,1000000)\nax[0].set_title('Without normalization')\n\nax[1].bar(bin_edges[1:], hist_100, 0.043, color='mediumaquamarine')\n#ax[1].set_yscale('log')\nax[1].set_ylim(0,1000000)\nax[1].set_title('Normalized ((channel - mean)/SD)')\n\nax[2].bar(bin_edges_0_1[1:], hist_100_0_1, 0.004, color='sandybrown')\n#ax[2].set_yscale('log')\nax[2].set_ylim(0,1000000)\nax[2].set_title('Normalized (channel/255)')\n\nfig.suptitle('Histogram (sum of all channels)', fontsize=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1a88e43fd46d97cd9d0a3f4319acb933f8fe83e6"},"cell_type":"markdown","source":""}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}