{"cells":[{"metadata":{},"cell_type":"markdown","source":"<center><img src='https://raw.githubusercontent.com/dimitreOliveira/MachineLearning/master/Kaggle/SIIM-ISIC%20Melanoma%20Classification/banner.png' height=\"350\"></center>\n<p>\n<h1><center> SIIM-ISIC Melanoma Classification </center></h1>\n<h2><center> Melanoma Classification - Augmentations EDA </center></h2>\n\n#### The idea of this notebook is to provide a simple and modular way to analyze and explore different kinds of augmentations and its parameters.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Dependencies","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import math\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport tensorflow.keras.backend as K\nfrom kaggle_datasets import KaggleDatasets","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model parameters","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"config = {\n  \"HEIGHT\": 256,\n  \"WIDTH\": 256,\n  \"CHANNELS\": 3,\n  \"AUGMENTED_SAMPLES\": 5,\n  \"DATASET_PATH\": 'melanoma-256x256'\n}\n\nconfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Load data","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"database_base_path = '/kaggle/input/siim-isic-melanoma-classification/'\ntrain = pd.read_csv(database_base_path + 'train.csv')\n\nprint('Train samples: %d' % len(train))\ndisplay(train.head())\n\nGCS_PATH = KaggleDatasets().get_gcs_path(config['DATASET_PATH'])\nTRAINING_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/train*.tfrec')\nTRAINING_FILENAMES = TRAINING_FILENAMES[1] # sample","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Augmentations","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def data_augment_spatial(image, label):\n    p_spatial = tf.random.uniform([1], minval=0, maxval=1, dtype='float32')\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    \n    if p_spatial > .75:\n        image = tf.image.transpose(image)\n\n    return image, label\n\ndef data_augment_rotate(image, label):\n    p_rotate = tf.random.uniform([1], minval=0, maxval=1, dtype='float32')\n    \n    if p_rotate > .66:\n        image = tf.image.rot90(image, k=3) # rotate 270º\n    elif p_rotate > .33:\n        image = tf.image.rot90(image, k=2) # rotate 180º\n    else:\n        image = tf.image.rot90(image, k=1) # rotate 90º\n\n    return image, label\n\ndef data_augment_crop(image, label):\n    p_crop = tf.random.uniform([1], minval=0, maxval=1, dtype='float32')\n    \n    if p_crop > .8:\n        image = tf.image.random_crop(image, size=[int(config['HEIGHT']*.7), int(config['WIDTH']*.7), config['CHANNELS']])\n    elif p_crop > .6:\n        image = tf.image.random_crop(image, size=[int(config['HEIGHT']*.8), int(config['WIDTH']*.8), config['CHANNELS']])\n    elif p_crop > .4:\n        image = tf.image.random_crop(image, size=[int(config['HEIGHT']*.9), int(config['WIDTH']*.9), config['CHANNELS']])\n    elif p_crop > .2:\n        image = tf.image.central_crop(image, central_fraction=.8)\n    else:\n        image = tf.image.central_crop(image, central_fraction=.7)\n    \n    image = tf.image.resize(image, size=[config['HEIGHT'], config['WIDTH']])\n\n    return image, label\n\ndef data_augment_rotation(image, label, max_angle=45.):\n    image = transform_rotation(image, config['HEIGHT'], max_angle)\n        \n    return image, label\n\ndef data_augment_shift(image, label):\n    image = transform_shift(image, config['HEIGHT'], 50., 50.)\n    return image, label\n\ndef data_augment_shear(image, label):\n    image = transform_shear(image, config['HEIGHT'], 25.)\n    return image, label\n\ndef data_augment_hue(image, label):\n    image = tf.image.random_hue(image, 0.02)\n    return image, label\n\ndef data_augment_saturation(image, label):\n    image = tf.image.random_saturation(image, 0.8, 1.2)\n    return image, label\n\ndef data_augment_contrast(image, label):\n    image = tf.image.random_contrast(image, 0.8, 1.2)\n    return image, label\n\ndef data_augment_brightness(image, label):\n    image = tf.image.random_brightness(image, 0.1)\n    return image, label\n\ndef data_augment_cutout(image, label):\n    p_cutout = tf.random.uniform([1], minval=0, maxval=1, dtype='float32')\n    \n    if p_cutout > .9: # 3 cut outs\n        image = random_cutout(image, config['HEIGHT'], config['WIDTH'], min_mask_size=(10, 10), max_mask_size=(80, 80), k=3)\n    elif p_cutout > .75: # 2 cut outs\n        image = random_cutout(image, config['HEIGHT'], config['WIDTH'], min_mask_size=(10, 10), max_mask_size=(80, 80), k=2)\n    else: # 1 cut out\n        image = random_cutout(image, config['HEIGHT'], config['WIDTH'], min_mask_size=(10, 10), max_mask_size=(80, 80), k=1)\n        \n    return image, label\n\ndef data_augment(image, label):\n    image, label = data_augment_spatial(image, label)\n    image, label = data_augment_rotate(image, label)\n    image, label = data_augment_crop(image, label)\n    image, label = data_augment_rotation(image, label)\n    image, label = data_augment_shift(image, label)\n    image, label = data_augment_shear(image, label)\n    image, label = data_augment_hue(image, label)\n    image, label = data_augment_saturation(image, label)\n    image, label = data_augment_contrast(image, label)\n    image, label = data_augment_brightness(image, label)\n    \n    return image, label","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Auxiliary functions","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# Datasets utility functions\nLABELED_TFREC_FORMAT = {\n    \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n    \"target\": tf.io.FixedLenFeature([], tf.int64), # shape [] means single element\n}\n\ndef decode_image(image_data, height, width, channels):\n    image = tf.image.decode_jpeg(image_data, channels=channels)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.reshape(image, [height, width, channels])\n    return image\n\ndef read_labeled_tfrecord_eval(example, height=config['HEIGHT'], width=config['WIDTH'], channels=config['CHANNELS']):\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(example['image'], height, width, channels)\n    label = tf.cast(example['target'], tf.float32)\n    \n    return image, label # returns a dataset of (image, data, label)\n\ndef load_dataset(filenames, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False # disable order, increase speed\n\n    dataset = tf.data.TFRecordDataset(filenames) # automatically interleaves reads from multiple files\n    dataset = dataset.with_options(ignore_order) # uses data as soon as it streams in, rather than in its original order\n    dataset = dataset.map(read_labeled_tfrecord)\n    \n    return dataset # returns a dataset of (image, data, label)\n\ndef load_dataset_display(filenames):\n    dataset = tf.data.TFRecordDataset(filenames) # automatically interleaves reads from multiple files\n    dataset = dataset.map(read_labeled_tfrecord_eval,)\n    \n    return dataset # returns a dataset of (image, data, label, image_name)\n\ndef get_display_dataset(filenames, batch_size=32):\n    dataset = load_dataset_display(filenames)\n    dataset = dataset.batch(batch_size, drop_remainder=False)\n    return dataset\n\ndef display_augmentation(dataset, n_samples, augmentation, rows=1, cols=7):\n    dataset_elements = iter(dataset)\n    for sample in range(n_samples):\n        element = tf.data.Dataset.from_tensors(next(dataset_elements))\n        element_augmented = element.repeat().map(augmentation).batch(rows*cols)\n        for (img, label) in element_augmented:\n            plt.figure(figsize=(15, int(15*rows/cols)))\n            for j in range(rows*cols):\n                plt.subplot(rows, cols, j+1)\n                plt.axis('off')\n                plt.imshow(img[j,])\n            plt.show()\n            break","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Advanced augmentations\ndef transform_rotation(image, height, rotation):\n    # input image - is one image of size [dim,dim,3] not a batch of [b,dim,dim,3]\n    # output - image randomly rotated\n    DIM = height\n    XDIM = DIM%2 #fix for size 331\n    \n    rotation = rotation * tf.random.uniform([1],dtype='float32')\n    # CONVERT DEGREES TO RADIANS\n    rotation = math.pi * rotation / 180.\n    \n    # ROTATION MATRIX\n    c1 = tf.math.cos(rotation)\n    s1 = tf.math.sin(rotation)\n    one = tf.constant([1],dtype='float32')\n    zero = tf.constant([0],dtype='float32')\n    rotation_matrix = tf.reshape(tf.concat([c1,s1,zero, -s1,c1,zero, zero,zero,one],axis=0),[3,3])\n\n    # LIST DESTINATION PIXEL INDICES\n    x = tf.repeat( tf.range(DIM//2,-DIM//2,-1), DIM )\n    y = tf.tile( tf.range(-DIM//2,DIM//2),[DIM] )\n    z = tf.ones([DIM*DIM],dtype='int32')\n    idx = tf.stack( [x,y,z] )\n    \n    # ROTATE DESTINATION PIXELS ONTO ORIGIN PIXELS\n    idx2 = K.dot(rotation_matrix,tf.cast(idx,dtype='float32'))\n    idx2 = K.cast(idx2,dtype='int32')\n    idx2 = K.clip(idx2,-DIM//2+XDIM+1,DIM//2)\n    \n    # FIND ORIGIN PIXEL VALUES \n    idx3 = tf.stack( [DIM//2-idx2[0,], DIM//2-1+idx2[1,]] )\n    d = tf.gather_nd(image, tf.transpose(idx3))\n        \n    return tf.reshape(d,[DIM,DIM,3])\n\ndef transform_shear(image, height, shear):\n    # input image - is one image of size [dim,dim,3] not a batch of [b,dim,dim,3]\n    # output - image randomly sheared\n    DIM = height\n    XDIM = DIM%2 #fix for size 331\n    \n    shear = shear * tf.random.uniform([1],dtype='float32')\n    shear = math.pi * shear / 180.\n        \n    # SHEAR MATRIX\n    one = tf.constant([1],dtype='float32')\n    zero = tf.constant([0],dtype='float32')\n    c2 = tf.math.cos(shear)\n    s2 = tf.math.sin(shear)\n    shear_matrix = tf.reshape(tf.concat([one,s2,zero, zero,c2,zero, zero,zero,one],axis=0),[3,3])    \n\n    # LIST DESTINATION PIXEL INDICES\n    x = tf.repeat( tf.range(DIM//2,-DIM//2,-1), DIM )\n    y = tf.tile( tf.range(-DIM//2,DIM//2),[DIM] )\n    z = tf.ones([DIM*DIM],dtype='int32')\n    idx = tf.stack( [x,y,z] )\n    \n    # ROTATE DESTINATION PIXELS ONTO ORIGIN PIXELS\n    idx2 = K.dot(shear_matrix,tf.cast(idx,dtype='float32'))\n    idx2 = K.cast(idx2,dtype='int32')\n    idx2 = K.clip(idx2,-DIM//2+XDIM+1,DIM//2)\n    \n    # FIND ORIGIN PIXEL VALUES \n    idx3 = tf.stack( [DIM//2-idx2[0,], DIM//2-1+idx2[1,]] )\n    d = tf.gather_nd(image, tf.transpose(idx3))\n        \n    return tf.reshape(d,[DIM,DIM,3])\n\ndef transform_shift(image, height, h_shift, w_shift):\n    # input image - is one image of size [dim,dim,3] not a batch of [b,dim,dim,3]\n    # output - image randomly shifted\n    DIM = height\n    XDIM = DIM%2 #fix for size 331\n    \n    height_shift = h_shift * tf.random.uniform([1],dtype='float32') \n    width_shift = w_shift * tf.random.uniform([1],dtype='float32') \n    one = tf.constant([1],dtype='float32')\n    zero = tf.constant([0],dtype='float32')\n        \n    # SHIFT MATRIX\n    shift_matrix = tf.reshape(tf.concat([one,zero,height_shift, zero,one,width_shift, zero,zero,one],axis=0),[3,3])\n\n    # LIST DESTINATION PIXEL INDICES\n    x = tf.repeat( tf.range(DIM//2,-DIM//2,-1), DIM )\n    y = tf.tile( tf.range(-DIM//2,DIM//2),[DIM] )\n    z = tf.ones([DIM*DIM],dtype='int32')\n    idx = tf.stack( [x,y,z] )\n    \n    # ROTATE DESTINATION PIXELS ONTO ORIGIN PIXELS\n    idx2 = K.dot(shift_matrix,tf.cast(idx,dtype='float32'))\n    idx2 = K.cast(idx2,dtype='int32')\n    idx2 = K.clip(idx2,-DIM//2+XDIM+1,DIM//2)\n    \n    # FIND ORIGIN PIXEL VALUES \n    idx3 = tf.stack( [DIM//2-idx2[0,], DIM//2-1+idx2[1,]] )\n    d = tf.gather_nd(image, tf.transpose(idx3))\n        \n    return tf.reshape(d,[DIM,DIM,3])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Display images","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"row = 4; col = 6;\nall_elements = get_display_dataset(TRAINING_FILENAMES).unbatch()\nbatch_element = all_elements.repeat().batch(row*col)\n\nfor (img, label) in batch_element:\n    plt.figure(figsize=(15, int(15*row/col)))\n    for j in range(row*col):\n        plt.subplot(row,col,j+1)\n        plt.axis('off')\n        plt.imshow(img[j,])\n    plt.show()\n    break","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Spatial augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())            \ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_spatial)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Rotates augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_rotate)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Crop augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_crop)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Rotation augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_rotation)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Shift augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_shift)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Shear augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_shear)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# HUE augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_hue)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Saturation augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_saturation)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Contrast augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_contrast)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Brightness augmentation","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, config['AUGMENTED_SAMPLES'], data_augment_brightness)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# All augmentations","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"all_elements = iter(get_display_dataset(TRAINING_FILENAMES).unbatch())\ndisplay_augmentation(all_elements, 6, data_augment)","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}