{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "This is a companion notebook for the book [Deep Learning with Python, Second Edition](https://www.manning.com/books/deep-learning-with-python-second-edition?a_aid=keras&a_bid=76564dff). For readability, it only contains runnable code blocks and section titles, and omits everything else in the book: text paragraphs, figures, and pseudocode.\n\n**If you want to be able to follow what's going on, I recommend reading the notebook side by side with your copy of the book.**\n\nThis notebook was generated for TensorFlow 2.6."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "## DeepDream"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "### Implementing DeepDream in Keras"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**Fetching the test image**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "from tensorflow import keras\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "base_image_path = keras.utils.get_file(\n",
    "    \"coast.jpg\", origin=\"https://img-datasets.s3.amazonaws.com/coast.jpg\")\n",
    "\n",
    "plt.axis(\"off\")\n",
    "plt.imshow(keras.utils.load_img(base_image_path))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**Instantiating a pretrained `InceptionV3` model**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "from tensorflow.keras.applications import inception_v3\n",
    "model = inception_v3.InceptionV3(weights=\"imagenet\", include_top=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**Configuring the contribution of each layer to the DeepDream loss**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "layer_settings = {\n",
    "    \"mixed4\": 1.0,\n",
    "    \"mixed5\": 1.5,\n",
    "    \"mixed6\": 2.0,\n",
    "    \"mixed7\": 2.5,\n",
    "}\n",
    "outputs_dict = dict(\n",
    "    [\n",
    "        (layer.name, layer.output)\n",
    "        for layer in [model.get_layer(name) for name in layer_settings.keys()]\n",
    "    ]\n",
    ")\n",
    "feature_extractor = keras.Model(inputs=model.inputs, outputs=outputs_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**The DeepDream loss**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "def compute_loss(input_image):\n",
    "    features = feature_extractor(input_image)\n",
    "    loss = tf.zeros(shape=())\n",
    "    for name in features.keys():\n",
    "        coeff = layer_settings[name]\n",
    "        activation = features[name]\n",
    "        loss += coeff * tf.reduce_mean(tf.square(activation[:, 2:-2, 2:-2, :]))\n",
    "    return loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**The DeepDream gradient ascent process**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "@tf.function\n",
    "def gradient_ascent_step(image, learning_rate):\n",
    "    with tf.GradientTape() as tape:\n",
    "        tape.watch(image)\n",
    "        loss = compute_loss(image)\n",
    "    grads = tape.gradient(loss, image)\n",
    "    grads = tf.math.l2_normalize(grads)\n",
    "    image += learning_rate * grads\n",
    "    return loss, image\n",
    "\n",
    "\n",
    "def gradient_ascent_loop(image, iterations, learning_rate, max_loss=None):\n",
    "    for i in range(iterations):\n",
    "        loss, image = gradient_ascent_step(image, learning_rate)\n",
    "        if max_loss is not None and loss > max_loss:\n",
    "            break\n",
    "        print(f\"... Loss value at step {i}: {loss:.2f}\")\n",
    "    return image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "step = 20.\n",
    "num_octave = 3\n",
    "octave_scale = 1.4\n",
    "iterations = 30\n",
    "max_loss = 15."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**Image processing utilities**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def preprocess_image(image_path):\n",
    "    img = keras.utils.load_img(image_path)\n",
    "    img = keras.utils.img_to_array(img)\n",
    "    img = np.expand_dims(img, axis=0)\n",
    "    img = keras.applications.inception_v3.preprocess_input(img)\n",
    "    return img\n",
    "\n",
    "def deprocess_image(img):\n",
    "    img = img.reshape((img.shape[1], img.shape[2], 3))\n",
    "    img /= 2.0\n",
    "    img += 0.5\n",
    "    img *= 255.\n",
    "    img = np.clip(img, 0, 255).astype(\"uint8\")\n",
    "    return img"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "**Running gradient ascent over multiple successive \"octaves\"**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab_type": "code"
   },
   "outputs": [],
   "source": [
    "original_img = preprocess_image(base_image_path)\n",
    "original_shape = original_img.shape[1:3]\n",
    "\n",
    "successive_shapes = [original_shape]\n",
    "for i in range(1, num_octave):\n",
    "    shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape])\n",
    "    successive_shapes.append(shape)\n",
    "successive_shapes = successive_shapes[::-1]\n",
    "\n",
    "shrunk_original_img = tf.image.resize(original_img, successive_shapes[0])\n",
    "\n",
    "img = tf.identity(original_img)\n",
    "for i, shape in enumerate(successive_shapes):\n",
    "    print(f\"Processing octave {i} with shape {shape}\")\n",
    "    img = tf.image.resize(img, shape)\n",
    "    img = gradient_ascent_loop(\n",
    "        img, iterations=iterations, learning_rate=step, max_loss=max_loss\n",
    "    )\n",
    "    upscaled_shrunk_original_img = tf.image.resize(shrunk_original_img, shape)\n",
    "    same_size_original = tf.image.resize(original_img, shape)\n",
    "    lost_detail = same_size_original - upscaled_shrunk_original_img\n",
    "    img += lost_detail\n",
    "    shrunk_original_img = tf.image.resize(original_img, shape)\n",
    "\n",
    "keras.utils.save_img(\"dream.png\", deprocess_image(img.numpy()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text"
   },
   "source": [
    "### Wrapping up"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "chapter12_part02_deep-dream.i",
   "private_outputs": false,
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}