{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Image Transformation (i.e. Image Augmentation)\n",
    "\n",
    "Learn how to apply various image augmentation techniques with TensorFlow. 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, 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 0x7F3DB0D81CD0>"
      ]
     },
     "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": [
    "# Create TensorFlow session.\n",
    "session = tf.Session()"
   ]
  },
  {
   "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 0x7F3DDB560BD0>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display randomly flipped image.\n",
    "PIL.Image.fromarray(random_flip_left_right(img_array).eval(session=session))"
   ]
  },
  {
   "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, 365552 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F3DDB56E990>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different contrast.\n",
    "PIL.Image.fromarray(random_contrast(img_array).eval(session=session))"
   ]
  },
  {
   "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, 343352 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F3DDB560D50>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different brightness.\n",
    "PIL.Image.fromarray(random_brightness(img_array).eval(session=session))"
   ]
  },
  {
   "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, 345024 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F3DDB560990>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different staturation.\n",
    "PIL.Image.fromarray(random_saturation(img_array).eval(session=session))"
   ]
  },
  {
   "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, 344044 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=640x464 at 0x7F3DDB560ED0>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display image with different hue.\n",
    "PIL.Image.fromarray(random_hue(img_array).eval(session=session))"
   ]
  },
  {
   "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, 126136 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=385x291 at 0x7F3DD8D2D990>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display cropped image.\n",
    "PIL.Image.fromarray(distorted_random_crop(img_array).eval(session=session))"
   ]
  },
  {
   "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, 155428 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=334x417 at 0x7F3DDB69FC10>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display fully pre-processed image.\n",
    "transformed_img = transform_image(img_array)\n",
    "PIL.Image.fromarray(transformed_img.eval(session=session))"
   ]
  },
  {
   "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, 82912 characters]\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=256x256 at 0x7F3DD6A93AD0>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Display resized image.\n",
    "PIL.Image.fromarray(resize_image(transformed_img).eval(session=session))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "tf1",
   "language": "python",
   "name": "tf1"
  },
  "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
}
