{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30840,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-02-01T05:58:03.907322Z","iopub.execute_input":"2025-02-01T05:58:03.907588Z","iopub.status.idle":"2025-02-01T05:58:04.629535Z","shell.execute_reply.started":"2025-02-01T05:58:03.907539Z","shell.execute_reply":"2025-02-01T05:58:04.628632Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport zipfile\nfrom random import sample\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models, optimizers, losses, Input\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.data import Dataset\nimport numpy as np\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n# Device configuration (automatically uses GPU if available)\ndevice = \"GPU\" if tf.config.list_physical_devices('GPU') else \"CPU\"\nprint(f\"Using {device}\")\n\n# Dataset class to load Monet and photo images\nclass Images:\n    def __init__(self, photo_path, monet_path, transform):\n        self.photo_path = photo_path\n        self.monet_path = monet_path\n        self.transform = transform\n        self.photos = os.listdir(photo_path)\n        self.monets = os.listdir(monet_path)\n\n        self.photos = sample(self.photos, min(50, len(self.photos)))\n        self.monets = sample(self.monets, min(50, len(self.monets)))\n\n        self.l_photo = len(self.photos)\n        self.l_monet = len(self.monets)\n\n    def __len__(self):\n        return max(len(self.photos), len(self.monets))\n\n    def __getitem__(self, idx):\n        # Load and transform images\n        photo = load_img(os.path.join(self.photo_path, self.photos[idx % self.l_photo]))\n        monet = load_img(os.path.join(self.monet_path, self.monets[idx % self.l_monet]))\n\n        photo = self.transform(photo)\n        monet = self.transform(monet)\n\n        return photo, monet\n\n\n# Image transformations\ndef transform(image):\n    image = image.resize((128, 128))  # Resize to 128x128\n    image = img_to_array(image) / 255.0  # Normalize to [0, 1]\n    image = (image - 0.5) / 0.5  # Normalize to [-1, 1]\n    return image\n\n\n# Define dataset and dataloader\nphoto_path = '/kaggle/input/gan-getting-started/photo_jpg/'\nmonet_path = '/kaggle/input/gan-getting-started/monet_jpg/'\ndataset = Images(photo_path, monet_path, transform)\n\n# Create a TensorFlow dataset using from_generator\ndef dataset_generator():\n    for i in range(len(dataset)):\n        yield dataset[i]\n\ndataloader = tf.data.Dataset.from_generator(\n    dataset_generator,\n    output_signature=(\n        tf.TensorSpec(shape=(128, 128, 3), dtype=tf.float32),\n        tf.TensorSpec(shape=(128, 128, 3), dtype=tf.float32)\n    )\n).batch(8).prefetch(tf.data.AUTOTUNE)\n\n# Calculate the length of the dataset\ndataset_length = len(dataset)\n\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\n\ndef unet_generator(input_shape=(128, 128, 3)):\n    # Noise input\n    noise_input = layers.Input(shape=input_shape, name='noise_input')\n    \n    # Conditional input (Monet image)\n    monet_input = layers.Input(shape=input_shape, name='monet_input')\n    \n    # Concatenate noise and conditional input\n    combined_input = layers.Concatenate()([noise_input, monet_input])\n\n    # Encoder (Downsampling)\n    down1 = layers.Conv2D(64, kernel_size=4, strides=2, padding='same')(combined_input)\n    down1 = layers.LeakyReLU(0.2)(down1)\n\n    down2 = layers.Conv2D(128, kernel_size=4, strides=2, padding='same')(down1)\n    down2 = layers.BatchNormalization()(down2)\n    down2 = layers.LeakyReLU(0.2)(down2)\n\n    down3 = layers.Conv2D(256, kernel_size=4, strides=2, padding='same')(down2)\n    down3 = layers.BatchNormalization()(down3)\n    down3 = layers.LeakyReLU(0.2)(down3)\n\n    down4 = layers.Conv2D(512, kernel_size=4, strides=2, padding='same')(down3)\n    down4 = layers.BatchNormalization()(down4)\n    down4 = layers.LeakyReLU(0.2)(down4)\n\n    # Bottleneck\n    bottleneck = layers.Conv2D(512, kernel_size=4, strides=2, padding='same')(down4)\n    bottleneck = layers.BatchNormalization()(bottleneck)\n    bottleneck = layers.LeakyReLU(0.2)(bottleneck)\n\n    # Decoder (Upsampling)\n    up1 = layers.Conv2DTranspose(512, kernel_size=4, strides=2, padding='same')(bottleneck)\n    up1 = layers.BatchNormalization()(up1)\n    up1 = layers.ReLU()(up1)\n    up1 = layers.Concatenate()([up1, down4])  # Skip connection\n\n    up2 = layers.Conv2DTranspose(256, kernel_size=4, strides=2, padding='same')(up1)\n    up2 = layers.BatchNormalization()(up2)\n    up2 = layers.ReLU()(up2)\n    up2 = layers.Concatenate()([up2, down3])  # Skip connection\n\n    up3 = layers.Conv2DTranspose(128, kernel_size=4, strides=2, padding='same')(up2)\n    up3 = layers.BatchNormalization()(up3)\n    up3 = layers.ReLU()(up3)\n    up3 = layers.Concatenate()([up3, down2])  # Skip connection\n\n    up4 = layers.Conv2DTranspose(64, kernel_size=4, strides=2, padding='same')(up3)\n    up4 = layers.BatchNormalization()(up4)\n    up4 = layers.ReLU()(up4)\n    up4 = layers.Concatenate()([up4, down1])  # Skip connection\n\n    # Output layer\n    outputs = layers.Conv2DTranspose(3, kernel_size=4, strides=2, padding='same', activation='tanh')(up4)\n\n    return models.Model([noise_input, monet_input], outputs, name='cU-Net_Generator')\n\n\ndef patchgan_discriminator(input_shape=(128, 128, 3)):\n    # Image input (real or fake)\n    image_input = layers.Input(shape=input_shape, name='image_input')\n    \n    # Conditional input (Monet image)\n    monet_input = layers.Input(shape=input_shape, name='monet_input')\n    \n    # Concatenate image and conditional input\n    combined_input = layers.Concatenate()([image_input, monet_input])\n\n    # Layer 1\n    x = layers.Conv2D(64, kernel_size=4, strides=2, padding='same')(combined_input)\n    x = layers.LeakyReLU(0.2)(x)\n\n    # Layer 2\n    x = layers.Conv2D(128, kernel_size=4, strides=2, padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(0.2)(x)\n\n    # Layer 3\n    x = layers.Conv2D(256, kernel_size=4, strides=2, padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(0.2)(x)\n\n    # Layer 4\n    x = layers.Conv2D(512, kernel_size=4, strides=1, padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(0.2)(x)\n\n    # Output layer\n    outputs = layers.Conv2D(1, kernel_size=4, strides=1, padding='same', activation='sigmoid')(x)\n\n    return models.Model([image_input, monet_input], outputs, name='cPatchGAN_Discriminator')\n\ndef build_gan(generator, discriminator):\n    # Freeze the discriminator during generator training\n    discriminator.trainable = False\n\n    # Inputs\n    noise_input = layers.Input(shape=(128, 128, 3), name='noise_input')\n    monet_input = layers.Input(shape=(128, 128, 3), name='monet_input')\n\n    # Generated image\n    generated_img = generator([noise_input, monet_input])\n\n    # Discriminator output\n    validity = discriminator([generated_img, monet_input])\n\n    # Combined model\n    gan = models.Model([noise_input, monet_input], [validity, generated_img], name='cGAN')\n    return gan\n\n\n# Define the generator and discriminator models\ngenerator = unet_generator()  # Define the generator\ndiscriminator = patchgan_discriminator()  # Define the discriminator\n\n# Build the GAN model\ngan = build_gan(generator, discriminator)\n# Compile models\n# Compile models\ndiscriminator.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0002, beta_1=0.5),\n                      loss='binary_crossentropy')\ngan.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0002, beta_1=0.5),\n            loss=['binary_crossentropy', 'mae'])  # Combined adversarial + L1 loss\n# Training loop\n# Training loop\nepochs = 100\nbatch_size = 32\nfor epoch in range(epochs):\n    for real_photos, real_monets in dataloader:\n        batch_size = real_photos.shape[0]\n\n        # Adversarial ground truths\n        valid = tf.ones((batch_size, 16, 16, 1))  # PatchGAN output shape\n        fake = tf.zeros((batch_size, 16, 16, 1))\n\n        # ---------------------\n        #  Train Discriminator\n        # ---------------------\n        # Generate fake images\n        noise = tf.random.normal((batch_size, 128, 128, 3))  # Random noise as input\n        generated_photos = generator([noise, real_monets], training=False)\n\n        # Train discriminator on real and fake images\n        d_loss_real = discriminator.train_on_batch([real_photos, real_monets], valid)\n        d_loss_fake = discriminator.train_on_batch([generated_photos, real_monets], fake)\n        d_loss = 0.5 * (d_loss_real + d_loss_fake)\n\n        # ---------------------\n        #  Train Generator\n        # ---------------------\n        # Train generator to fool the discriminator\n        g_loss = gan.train_on_batch([noise, real_monets], [valid, real_photos])\n\n    # Print progress\n    print(f\"Epoch [{epoch + 1}/{epochs}] | D loss: {d_loss:.4f} | G loss: {g_loss[0]:.4f}\")\n\n    # Save generated images every 10 epochs\n    if (epoch + 1) % 10 == 0:\n        noise = tf.random.normal((1, 128, 128, 3))\n        monet_sample = real_monets[:1]  # Take one Monet image as conditional input\n        generated_image = generator([noise, monet_sample], training=False)\n        generated_image = (generated_image.numpy() * 0.5 + 0.5)  # Rescale to [0, 1]\n        plt.imshow(generated_image[0])\n        plt.title(f\"Epoch {epoch + 1}\")\n        plt.axis('off')\n        plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-01T06:02:45.105983Z","iopub.execute_input":"2025-02-01T06:02:45.106276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}