{"cells":[{"metadata":{},"cell_type":"markdown","source":"<body style=\"color:white\">\n<h1 align='center'><font size=\"+3\" color=#CA00BA>Introduction</font></h1>\n\n<h3 align='center'><font>In this <font color=#CA00BA>notebook</font> , we are going to <font color=#A504FE>Generate Art</font> using <font color=#CA00BA>GANS</font>. Explaining it in a brief way , it is an <font color=#FE046A>Artist</font> who forges <font color=#04B0FE>Monet's</font> paintings , but using random normal picture that is assigned to him.  \nThe <font color=#04B0FE>Detective</font> catches the Forger who prefers to be called <font color=#FE046A>The Artist</font> every time he makes a painting , until he learns to make the perfect <font color=#04B0FE>Monetesque</font> painting and fools the <font color=#04B0FE>Detective</font>. \n    \nYES you can have your own <font color=#04B0FE>Monetesqu</font> portrait but you have to <font color=#04B0FE>Vote</font>   so that <font color=#FE046A>The Artist</font> can make it for you!!!\n</font></h3>\n   \n</body>"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import tensorflow as tf\nprint(tf.__version__)\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\n\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n    \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<body style=\"color:white\">\n<h1 align='center'><font size=\"+3\" color=#FF2C00>Loading the Data:</font></h1>\n\n<h3 align='center'><font> We are using the <font color=#CA00BA>TensorFlow record files</font> from the provided datasets.\n</font></h3>\n   \n</body>"},{"metadata":{"trusted":true},"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()\nMONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\n\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BUFFER_SIZE = 1000\nBATCH_SIZE = 1\nIMG_WIDTH = 256\nIMG_HEIGHT = 256","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMAGE_SIZE=[IMG_WIDTH,IMG_HEIGHT]\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example):\n    tfrecord_format = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image\n\ndef augment_image(image):\n    flip_image = tf.image.random_flip_left_right(image)\n    cropped_image = tf.image.random_crop(flip_image, size=[IMG_HEIGHT, IMG_WIDTH, 3])\n\n    return cropped_image\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_dataset(filenames, labeled=True, ordered=False,augment=True):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    if augment:\n        dataset = dataset.map(augment_image, num_parallel_calls=AUTOTUNE)\n        dataset = dataset.repeat(count=1)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"monet_ds = load_dataset(MONET_FILENAMES, labeled=True).batch(1)\nphoto_ds = load_dataset(PHOTO_FILENAMES, labeled=True).batch(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"example_monet = next(iter(monet_ds))\nexample_photo = next(iter(photo_ds))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.subplot(121)\nplt.title('Photo')\nplt.imshow(example_photo[0] * 0.5 + 0.5)\n\nplt.subplot(122)\nplt.title('Monet')\nplt.imshow(example_monet[0] * 0.5 + 0.5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<body style=\"color:white\">\n<h1 align='center'><font size=\"+3\" color=#FF2C00>Setting the Models:</font></h1>\n\n<h3 align='center'><font>We are implementing CycleGan , with a modified UNET model as a generator , and a PatchGan block as a Discriminator. The difference between a normal block and a GAN block is that it uses a InstanceNormalization.</font>. \n</font></h3>\n   \n</body>"},{"metadata":{"trusted":true},"cell_type":"code","source":"class InstanceNormalization(tf.keras.layers.Layer):\n  \"\"\"Instance Normalization Layer (https://arxiv.org/abs/1607.08022).\"\"\"\n\n  def __init__(self, epsilon=1e-5):\n    super(InstanceNormalization, self).__init__()\n    self.epsilon = epsilon\n\n  def build(self, input_shape):\n    self.scale = self.add_weight(\n        name='scale',\n        shape=input_shape[-1:],\n        initializer=tf.random_normal_initializer(1., 0.02),\n        trainable=True)\n\n    self.offset = self.add_weight(\n        name='offset',\n        shape=input_shape[-1:],\n        initializer='zeros',\n        trainable=True)\n\n  def call(self, x):\n    mean, variance = tf.nn.moments(x, axes=[1, 2], keepdims=True)\n    inv = tf.math.rsqrt(variance + self.epsilon)\n    normalized = (x - mean) * inv\n    return self.scale * normalized + self.offset","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Instance Normalisation block"},{"metadata":{"trusted":true},"cell_type":"code","source":"OUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, norm_type='instancenorm', apply_norm=True):\n  \"\"\"Downsamples an input.\n  Conv2D => Batchnorm => LeakyRelu\n  Args:\n    filters: number of filters\n    size: filter size\n    norm_type: Normalization type; either 'batchnorm' or 'instancenorm'.\n    apply_norm: If True, adds the batchnorm layer\n  Returns:\n    Downsample Sequential Model\n  \"\"\"\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  result = tf.keras.Sequential()\n  result.add(\n      tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n\n  if apply_norm:\n    if norm_type.lower() == 'batchnorm':\n      result.add(tf.keras.layers.BatchNormalization())\n    elif norm_type.lower() == 'instancenorm':\n      result.add(InstanceNormalization())\n\n  result.add(tf.keras.layers.LeakyReLU())\n\n  return result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"down_model = downsample(3, 4)\ndown_result = down_model(tf.expand_dims(example_monet[0], 0))\nprint (down_result.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Downsample block"},{"metadata":{"trusted":true},"cell_type":"code","source":"def upsample(filters, size, norm_type='instancenorm', apply_dropout=False):\n  \"\"\"Upsamples an input.\n  Conv2DTranspose => Batchnorm => Dropout => Relu\n  Args:\n    filters: number of filters\n    size: filter size\n    norm_type: Normalization type; either 'batchnorm' or 'instancenorm'.\n    apply_dropout: If True, adds the dropout layer\n  Returns:\n    Upsample Sequential Model\n  \"\"\"\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  result = tf.keras.Sequential()\n  result.add(\n      tf.keras.layers.Conv2DTranspose(filters, size, strides=2,\n                                      padding='same',\n                                      kernel_initializer=initializer,\n                                      use_bias=False))\n\n  if norm_type.lower() == 'batchnorm':\n    result.add(tf.keras.layers.BatchNormalization())\n  elif norm_type.lower() == 'instancenorm':\n    result.add(InstanceNormalization())\n\n  if apply_dropout:\n    result.add(tf.keras.layers.Dropout(0.5))\n\n  result.add(tf.keras.layers.ReLU())\n\n  return result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"up_model = upsample(3, 4)\nup_result = up_model(down_result)\nprint (up_result.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Upsample block"},{"metadata":{"trusted":true},"cell_type":"code","source":"def unet_generator(output_channels, norm_type='instancenorm'):\n  \"\"\"Modified u-net generator model (https://arxiv.org/abs/1611.07004).\n  Args:\n    output_channels: Output channels\n    norm_type: Type of normalization. Either 'batchnorm' or 'instancenorm'.\n  Returns:\n    Generator model\n  \"\"\"\n\n  down_stack = [\n      downsample(64, 4, norm_type, apply_norm=False),  # (bs, 128, 128, 64)\n      downsample(128, 4, norm_type),  # (bs, 64, 64, 128)\n      downsample(256, 4, norm_type),  # (bs, 32, 32, 256)\n      downsample(512, 4, norm_type),  # (bs, 16, 16, 512)\n      downsample(512, 4, norm_type),  # (bs, 8, 8, 512)\n      downsample(512, 4, norm_type),  # (bs, 4, 4, 512)\n      downsample(512, 4, norm_type),  # (bs, 2, 2, 512)\n      downsample(512, 4, norm_type),  # (bs, 1, 1, 512)\n  ]\n\n  up_stack = [\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 2, 2, 1024)\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 4, 4, 1024)\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 8, 8, 1024)\n      upsample(512, 4, norm_type),  # (bs, 16, 16, 1024)\n      upsample(256, 4, norm_type),  # (bs, 32, 32, 512)\n      upsample(128, 4, norm_type),  # (bs, 64, 64, 256)\n      upsample(64, 4, norm_type),  # (bs, 128, 128, 128)\n  ]\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n  last = tf.keras.layers.Conv2DTranspose(\n      output_channels, 4, strides=2,\n      padding='same', kernel_initializer=initializer,\n      activation='tanh')  # (bs, 256, 256, 3)\n\n  concat = tf.keras.layers.Concatenate()\n\n  inputs = tf.keras.layers.Input(shape=[None, None, 3])\n  x = inputs\n\n  # Downsampling through the model\n  skips = []\n  for down in down_stack:\n    x = down(x)\n    skips.append(x)\n\n  skips = reversed(skips[:-1])\n\n  # Upsampling and establishing the skip connections\n  for up, skip in zip(up_stack, skips):\n    x = up(x)\n    x = concat([x, skip])\n\n  x = last(x)\n\n  return tf.keras.Model(inputs=inputs, outputs=x)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The Generator "},{"metadata":{"trusted":true},"cell_type":"code","source":"generator = unet_generator(OUTPUT_CHANNELS, norm_type='instancenorm')\ntf.keras.utils.plot_model(generator, show_shapes=True, dpi=64)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Generator architecture"},{"metadata":{"trusted":true},"cell_type":"code","source":"def discriminator(norm_type='instancenorm', target=True):\n  \"\"\"PatchGan discriminator model (https://arxiv.org/abs/1611.07004).\n  Args:\n    norm_type: Type of normalization. Either 'batchnorm' or 'instancenorm'.\n    target: Bool, indicating whether target image is an input or not.\n  Returns:\n    Discriminator model\n  \"\"\"\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  inp = tf.keras.layers.Input(shape=[None, None, 3], name='input_image')\n  x = inp\n\n  if target:\n    tar = tf.keras.layers.Input(shape=[None, None, 3], name='target_image')\n    x = tf.keras.layers.concatenate([inp, tar])  # (bs, 256, 256, channels*2)\n\n  down1 = downsample(64, 4, norm_type, False)(x)  # (bs, 128, 128, 64)\n  down2 = downsample(128, 4, norm_type)(down1)  # (bs, 64, 64, 128)\n  down3 = downsample(256, 4, norm_type)(down2)  # (bs, 32, 32, 256)\n\n  zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3)  # (bs, 34, 34, 256)\n  conv = tf.keras.layers.Conv2D(\n      512, 4, strides=1, kernel_initializer=initializer,\n      use_bias=False)(zero_pad1)  # (bs, 31, 31, 512)\n\n  if norm_type.lower() == 'batchnorm':\n    norm1 = tf.keras.layers.BatchNormalization()(conv)\n  elif norm_type.lower() == 'instancenorm':\n    norm1 = InstanceNormalization()(conv)\n\n  leaky_relu = tf.keras.layers.LeakyReLU()(norm1)\n\n  zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu)  # (bs, 33, 33, 512)\n\n  last = tf.keras.layers.Conv2D(\n      1, 4, strides=1,\n      kernel_initializer=initializer)(zero_pad2)  # (bs, 30, 30, 1)\n\n  if target:\n    return tf.keras.Model(inputs=[inp, tar], outputs=last)\n  else:\n    return tf.keras.Model(inputs=inp, outputs=last)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The Discriminator"},{"metadata":{},"cell_type":"markdown","source":"## Models\n\nThere are 2 generators (A and B) and 2 discriminators (X and Y) being trained here.\n\nGenerator A learns to transform image X to image Y.  (A:X−>Y) \nGenerator B learns to transform image Y to image X.  (B:Y−>X) \nDiscriminator D_X learns to differentiate between image X and generated image X (B(Y)).\nDiscriminator D_Y learns to differentiate between image Y and generated image Y (A(X))."},{"metadata":{"trusted":true},"cell_type":"code","source":"generator_a = unet_generator(OUTPUT_CHANNELS, norm_type='instancenorm')\ngenerator_b = unet_generator(OUTPUT_CHANNELS, norm_type='instancenorm')\n\ndiscriminator_x = discriminator(norm_type='instancenorm', target=False)\ndiscriminator_y = discriminator(norm_type='instancenorm', target=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"to_monet = generator_a(example_photo)\nto_photo = generator_b(example_monet)\nplt.figure(figsize=(8, 8))\ncontrast = 8\n\nimgs = [example_photo, to_monet, example_monet, to_photo]\ntitle = ['Normal', 'To Monet', 'Monetesque', 'To Normal']\n\nfor i in range(len(imgs)):\n  plt.subplot(2, 2, i+1)\n  plt.title(title[i])\n  if i % 2 == 0:\n    plt.imshow(imgs[i][0] * 0.5 + 0.5)\n  else:\n    plt.imshow(imgs[i][0] * 0.5 * contrast + 0.5)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Without any training the images make no sense😅"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(8, 8))\n\nplt.subplot(121)\nplt.title('Is a real Monet Painting?')\nplt.imshow(discriminator_y(example_monet)[0, ..., -1], cmap='RdBu_r')\n\nplt.subplot(122)\nplt.title('Is a real Normal Pic?')\nplt.imshow(discriminator_x(example_photo)[0, ..., -1], cmap='RdBu_r')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"LAMBDA = 10","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Calculating the loss.\n\nIn CycleGAN, there is no paired data to train on, hence there is no guarantee that the input x and the target y pair are meaningful during training. Thus in order to enforce that the network learns the correct mapping, the authors of CycleGAN propose the cycle consistency loss."},{"metadata":{"trusted":true},"cell_type":"code","source":"loss_obj = tf.keras.losses.BinaryCrossentropy(from_logits=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def discriminator_loss(real, generated):\n  real_loss = loss_obj(tf.ones_like(real), real)\n\n  generated_loss = loss_obj(tf.zeros_like(generated), generated)\n\n  total_disc_loss = real_loss + generated_loss\n\n  return total_disc_loss * 0.5","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Discriminator Loss block"},{"metadata":{"trusted":true},"cell_type":"code","source":"def generator_loss(generated):\n  return loss_obj(tf.ones_like(generated), generated)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Generator Loss block"},{"metadata":{"trusted":true},"cell_type":"code","source":"def calc_cycle_loss(real_image, cycled_image):\n  loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n  \n  return LAMBDA * loss1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Cycle Consistency loss block"},{"metadata":{"trusted":true},"cell_type":"code","source":"def identity_loss(real_image, same_image):\n  loss = tf.reduce_mean(tf.abs(real_image - same_image))\n  return LAMBDA * 0.5 * loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Identity loss blcok"},{"metadata":{},"cell_type":"markdown","source":"### Initializing the optimizers:"},{"metadata":{"trusted":true},"cell_type":"code","source":"generator_a_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\ngenerator_b_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\ndiscriminator_x_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\ndiscriminator_y_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<body style=\"color:white\">\n<h1 align='center'><font size=\"+3\" color=#03379A>Training the Models:</font></h1>\n\n<h3 align='center'><font>We are training the model with <font color=#F19701>250 epochs</font>. However the original CycleGan paper was trained with <font color=#F13A01>200 epochs</font>.More epochs are for the results to be much better. </font>\n</font></h3>\n   \n</body>"},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 250","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_images(model, test_input):\n  prediction = model(test_input)\n    \n  plt.figure(figsize=(12, 12))\n\n  display_list = [test_input[0], prediction[0]]\n  title = ['Input Image', 'Predicted Painting']\n\n  for i in range(2):\n    plt.subplot(1, 2, i+1)\n    plt.title(title[i])\n    # getting the pixel values between [0, 1] to plot it.\n    plt.imshow(display_list[i] * 0.5 + 0.5)\n    plt.axis('off')\n  plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Block that shows the progress of the Artist over each epoch"},{"metadata":{},"cell_type":"markdown","source":"### 4 steps that are in the training loop\n\n* Get the predictions.\n* Calculate the loss.\n* Calculate the gradients using backpropagation.\n* Apply the gradients to the optimizer."},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef train_step(real_x, real_y):\n  # persistent is set to True because the tape is used more than\n  # once to calculate the gradients.\n  with tf.GradientTape(persistent=True) as tape:\n    # Generator G translates X -> Y\n    # Generator F translates Y -> X.\n    \n    fake_y = generator_a(real_x, training=True)\n    cycled_x = generator_b(fake_y, training=True)\n\n    fake_x = generator_b(real_y, training=True)\n    cycled_y = generator_a(fake_x, training=True)\n\n    # same_x and same_y are used for identity loss.\n    same_x = generator_b(real_x, training=True)\n    same_y = generator_a(real_y, training=True)\n\n    disc_real_x = discriminator_x(real_x, training=True)\n    disc_real_y = discriminator_y(real_y, training=True)\n\n    disc_fake_x = discriminator_x(fake_x, training=True)\n    disc_fake_y = discriminator_y(fake_y, training=True)\n\n    # calculate the loss\n    gen_a_loss = generator_loss(disc_fake_y)\n    gen_b_loss = generator_loss(disc_fake_x)\n    \n    total_cycle_loss = calc_cycle_loss(real_x, cycled_x) + calc_cycle_loss(real_y, cycled_y)\n    \n    # Total generator loss = adversarial loss + cycle loss\n    total_gen_a_loss = gen_a_loss + total_cycle_loss + identity_loss(real_y, same_y)\n    total_gen_b_loss = gen_b_loss + total_cycle_loss + identity_loss(real_x, same_x)\n\n    disc_x_loss = discriminator_loss(disc_real_x, disc_fake_x)\n    disc_y_loss = discriminator_loss(disc_real_y, disc_fake_y)\n  \n  # Calculate the gradients for generator and discriminator\n  generator_a_gradients = tape.gradient(total_gen_a_loss, \n                                        generator_a.trainable_variables)\n  generator_b_gradients = tape.gradient(total_gen_b_loss, \n                                        generator_b.trainable_variables)\n  \n  discriminator_x_gradients = tape.gradient(disc_x_loss, \n                                            discriminator_x.trainable_variables)\n  discriminator_y_gradients = tape.gradient(disc_y_loss, \n                                            discriminator_y.trainable_variables)\n  \n  # Apply the gradients to the optimizer\n  generator_a_optimizer.apply_gradients(zip(generator_a_gradients, \n                                            generator_a.trainable_variables))\n\n  generator_b_optimizer.apply_gradients(zip(generator_b_gradients, \n                                            generator_b.trainable_variables))\n  \n  discriminator_x_optimizer.apply_gradients(zip(discriminator_x_gradients,\n                                                discriminator_x.trainable_variables))\n  \n  discriminator_y_optimizer.apply_gradients(zip(discriminator_y_gradients,\n                                                discriminator_y.trainable_variables))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* We are generating the same picture so that we can keep a track on the progress , over each epoch."},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport time\nfrom IPython.display import clear_output\n\nfor epoch in range(EPOCHS):\n  start = time.time()\n\n  n = 0\n  for image_x, image_y in tf.data.Dataset.zip((photo_ds, monet_ds)):\n    train_step(image_x, image_y)\n    if n % 10 == 0:\n      print ('.', end='')\n    n+=1\n\n  clear_output(wait=True)\n  # Using a consistent image (sample_horse) so that the progress of the model\n  # is clearly visible.\n  generate_images(generator_a, example_photo)\n\n\n  print ('Time taken for epoch {} is {} sec\\n'.format(epoch + 1,\n                                                      time.time()-start))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<body style=\"color:white\">\n<h1 align='center'><font size=\"+3\" color=#F1016D>Forging the Great Artistic Style:</font><font size=\"+3\" color=#03379A> Generating on test data !!</font></h1>\n\n<h3 align='center'><font>Here we are generating the paintings using the test data , and we can see how the <font color=#CA00BA>GAN</font> generates <font color=#04B0FE>Monetesque</font> paintings from normal random pictures.\n\n<font color=#F10101>Note:</font> The results might have been better if we had used more epochs </font>. \n</font></h3>\n   \n</body>"},{"metadata":{"trusted":true},"cell_type":"code","source":"for inp in photo_ds.take(10):\n  generate_images(generator_a, inp)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import PIL\n! mkdir ../images","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Creating folder to store generated images"},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 1\nfor img in photo_ds:\n    prediction = generator_a(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    im = PIL.Image.fromarray(prediction)\n    im.save(\"../images/\" + str(i) + \".jpg\")\n    i += 1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Creating the sumission zip file"},{"metadata":{"trusted":true},"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 align=\"center\" > THE END</h3> "}],"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}