{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Happywhale dataset: image normalization\n\nPlaying with this dataset I realized that normalizing the images with standard ImageNet mean and std doesn't work well.\nWater, whales, dolphins - they are mostly gray and blue colors.\n\nThis notebook demontrates this fact and calculates pixels mean and std for this dataset.\n\nTLDR: the calculation produced mean=(0.434, 0.487, 0.544) and std=(0.163, 0.166, 0.173). Will try to use it in future experiments.\n\nHope this would be helpful.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import os\nimport random\nfrom multiprocessing import Pool\nimport numpy as np\nimport cv2\nimport albumentations as A\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\nINPUT_PATH = '../input/happy-whale-and-dolphin'","metadata":{"execution":{"iopub.status.busy":"2022-02-17T19:54:25.949527Z","iopub.execute_input":"2022-02-17T19:54:25.949838Z","iopub.status.idle":"2022-02-17T19:54:25.956078Z","shell.execute_reply.started":"2022-02-17T19:54:25.949809Z","shell.execute_reply":"2022-02-17T19:54:25.955134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create lists of train, test and all images\ntrain_files = os.listdir(os.path.join(INPUT_PATH, 'train_images'))\ntest_files = os.listdir(os.path.join(INPUT_PATH, 'test_images'))\nall_files = [os.path.join(INPUT_PATH, 'train_images', f) for f in train_files] + \\\n            [os.path.join(INPUT_PATH, 'test_images', f) for f in test_files]\nprint(f'Train files: {len(train_files)}, test files: {len(test_files)}, all_files: {len(all_files)}')\n\n# now select 5 random images to show\nshow_images = random.sample(all_files, 5)","metadata":{"execution":{"iopub.status.busy":"2022-02-17T19:54:25.960472Z","iopub.execute_input":"2022-02-17T19:54:25.960759Z","iopub.status.idle":"2022-02-17T19:54:27.371975Z","shell.execute_reply.started":"2022-02-17T19:54:25.960727Z","shell.execute_reply":"2022-02-17T19:54:27.371069Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's create a function that displays original and normalized images in 2 columns to compare.\nRun this function for 5 randomly selected images.","metadata":{}},{"cell_type":"code","source":"def show_orig_norm_images(img_files, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)):\n    nrows, ncols = len(img_files), 2\n    fig, ax = plt.subplots(nrows, ncols, figsize=(20,31))\n\n    for i in range(len(img_files)):\n        img = cv2.imread(img_files[i])\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        norm_img = A.Normalize(mean=mean, std=std)(image=img)['image']\n        \n        ax[i, 0].grid(False)\n        ax[i, 0].axis('off')\n        ax[i, 0].title.set_text(f'{os.path.basename(img_files[i])}: original')\n        ax[i, 0].imshow(img)\n    \n        ax[i, 1].grid(False)\n        ax[i, 1].axis('off')\n        ax[i, 1].title.set_text(f'{os.path.basename(img_files[i])}: normalized')\n        ax[i, 1].imshow(norm_img)\n        \n    plt.tight_layout()\n    plt.show()\n    \nshow_orig_norm_images(show_images)","metadata":{"execution":{"iopub.status.busy":"2022-02-17T19:54:27.373866Z","iopub.execute_input":"2022-02-17T19:54:27.374170Z","iopub.status.idle":"2022-02-17T19:54:34.461312Z","shell.execute_reply.started":"2022-02-17T19:54:27.374129Z","shell.execute_reply":"2022-02-17T19:54:34.460046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"OK, now let's go ahead and calculate pixels mean and std for each color channel for each image.\nThen mean and std are averaged across all images in the dataset.\n\nNote: it took a while on Kaggle, so below it's calculated for 1000 first images only.\n\nRemove \"all_files = all_files[:1000]\" line to do the full calculation.","metadata":{}},{"cell_type":"code","source":"np.set_printoptions(precision=3)\n\nall_files = all_files[:1000] # remove this line to do full calculation\n\ndef process_file(fp):\n    img = cv2.imread(fp)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = img / 255\n    return np.mean(img, axis=(0,1)), np.std(img, axis=(0,1))\n\nmean, std = np.zeros(3), np.zeros(3)\nn, done = len(all_files), 0\n    \nwith Pool(os.cpu_count()) as p:\n    pbar = tqdm(p.imap(process_file, all_files), total=n)\n    for m, s in pbar:\n        done += 1\n        mean += m\n        std += s\n        pbar.set_description(f'{mean/done} {std/done}')\n        \nmean, std = mean/n, std/n\n \nprint(f'Calculated mean: {mean}')\nprint(f'Calculated std: {std}')","metadata":{"execution":{"iopub.status.busy":"2022-02-17T19:54:34.462893Z","iopub.execute_input":"2022-02-17T19:54:34.463126Z","iopub.status.idle":"2022-02-17T19:57:43.245350Z","shell.execute_reply.started":"2022-02-17T19:54:34.463098Z","shell.execute_reply":"2022-02-17T19:57:43.244214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"OK, now let's check new normalization settings.","metadata":{}},{"cell_type":"code","source":"show_orig_norm_images(show_images, mean=mean, std=std)","metadata":{"execution":{"iopub.status.busy":"2022-02-17T19:57:43.248145Z","iopub.execute_input":"2022-02-17T19:57:43.248780Z","iopub.status.idle":"2022-02-17T19:57:50.309671Z","shell.execute_reply.started":"2022-02-17T19:57:43.248741Z","shell.execute_reply":"2022-02-17T19:57:50.308350Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}