{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Image Transformation (i.e. Image Augmentation)\n",
    "\n",
    "Learn how to apply various image augmentation techniques with TensorFlow 2.0. The transformations are meant to be applied for each image sample when training only, and each transformation will be performed with random parameters.\n",
    "\n",
    "**Transformations:**\n",
    "- Random flip left-right\n",
    "- Random contrast, brightness, saturation and hue\n",
    "- Random distortion and crop\n",
    "\n",
    "For more information about loading data with TF2.0, see: [load_data.ipynb](load_data.ipynb)\n",
    "\n",
    "- Author: Aymeric Damien\n",
    "- Project: https://github.com/aymericdamien/TensorFlow-Examples/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "\n",
    "from IPython.display import Image as IImage, display\n",
    "import numpy as np\n",
    "import PIL\n",
    "from PIL import Image\n",
    "import random\n",
    "import requests\n",
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Download an image.\n",
    "d = requests.get(\"https://www.paristoolkit.com/Images/xeffel_view.jpg.pagespeed.ic.8XcZNqpzSj.jpg\")\n",
    "with open(\"image.jpeg\", \"wb\") as f:\n",
    "    f.write(d.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load image to numpy array.\n",
    "img = PIL.Image.open('image.jpeg')\n",
    "img.load()\n",
    "img_array = np.array(img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 342768 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4F4BE74C90>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image.\n",
    "PIL.Image.fromarray(img_array)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Randomly flip an image.\n",
    "def random_flip_left_right(image):\n",
    "    return tf.image.random_flip_left_right(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 342108 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4FB1339650>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display randomly flipped image.\n",
    "PIL.Image.fromarray(random_flip_left_right(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Randomly change an image contrast.\n",
    "def random_contrast(image, minval=0.6, maxval=1.4):\n",
    "    r = tf.random.uniform([], minval=minval, maxval=maxval)\n",
    "    image = tf.image.adjust_contrast(image, contrast_factor=r)\n",
    "    return tf.cast(image, tf.uint8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 372148 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4F4BE74450>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different contrast.\n",
    "PIL.Image.fromarray(random_contrast(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Randomly change an image brightness\n",
    "def random_brightness(image, minval=0., maxval=.2):\n",
    "    r = tf.random.uniform([], minval=minval, maxval=maxval)\n",
    "    image = tf.image.adjust_brightness(image, delta=r)\n",
    "    return tf.cast(image, tf.uint8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 335180 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4F4BE74050>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different brightness.\n",
    "PIL.Image.fromarray(random_brightness(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Randomly change an image saturation\n",
    "def random_saturation(image, minval=0.4, maxval=2.):\n",
    "    r = tf.random.uniform((), minval=minval, maxval=maxval)\n",
    "    image = tf.image.adjust_saturation(image, saturation_factor=r)\n",
    "    return tf.cast(image, tf.uint8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 345196 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4F4BE74790>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different staturation.\n",
    "PIL.Image.fromarray(random_saturation(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Randomly change an image hue.\n",
    "def random_hue(image, minval=-0.04, maxval=0.08):\n",
    "    r = tf.random.uniform((), minval=minval, maxval=maxval)\n",
    "    image = tf.image.adjust_hue(image, delta=r)\n",
    "    return tf.cast(image, tf.uint8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 343900 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F4F4BE74590>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different hue.\n",
    "PIL.Image.fromarray(random_hue(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Distort an image by cropping it with a different aspect ratio.\n",
    "def distorted_random_crop(image,\n",
    "                min_object_covered=0.1,\n",
    "                aspect_ratio_range=(3./4., 4./3.),\n",
    "                area_range=(0.06, 1.0),\n",
    "                max_attempts=100,\n",
    "                scope=None):\n",
    "\n",
    "    cropbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=tf.float32, shape=[1, 1, 4])\n",
    "    sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box(\n",
    "        tf.shape(image),\n",
    "        bounding_boxes=cropbox,\n",
    "        min_object_covered=min_object_covered,\n",
    "        aspect_ratio_range=aspect_ratio_range,\n",
    "        area_range=area_range,\n",
    "        max_attempts=max_attempts,\n",
    "        use_image_if_no_bounding_boxes=True)\n",
    "    bbox_begin, bbox_size, distort_bbox = sample_distorted_bounding_box\n",
    "\n",
    "    # Crop the image to the specified bounding box.\n",
    "    cropped_image = tf.slice(image, bbox_begin, bbox_size)\n",
    "    return cropped_image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 153056 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=320x404 at 0x7F4F4C75FF50>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display cropped image.\n",
    "PIL.Image.fromarray(distorted_random_crop(img_array).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Apply all transformations to an image.\n",
    "# That is a common image augmentation technique for image datasets, such as ImageNet.\n",
    "def transform_image(image):\n",
    "    image = distorted_random_crop(image)\n",
    "    image = random_flip_left_right(image)\n",
    "    image = random_contrast(image)\n",
    "    image = random_brightness(image)\n",
    "    image = random_hue(image)\n",
    "    image = random_saturation(image)\n",
    "    return image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 153108 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=338x304 at 0x7F4F4831D810>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display fully pre-processed image.\n",
    "transformed_img = transform_image(img_array).numpy()\n",
    "PIL.Image.fromarray(transformed_img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Resize transformed image to a 256x256px square image, ready for training.\n",
    "def resize_image(image):\n",
    "    image = tf.image.resize(image, size=(256, 256), preserve_aspect_ratio=False)\n",
    "    image = tf.cast(image, tf.uint8)\n",
    "    return image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 104964 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=256x256 at 0x7F4F4BEAED90>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display resized image.\n",
    "PIL.Image.fromarray(resize_image(transformed_img).numpy())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
