{"metadata":{"colab":{"provenance":[],"machine_shape":"hm","gpuType":"A100"},"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"accelerator":"GPU","kaggle":{"accelerator":"none","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"},{"sourceId":7908231,"sourceType":"datasetVersion","datasetId":4645576}],"dockerImageVersionId":30673,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Importing the libraries","metadata":{"id":"I9u9cdZ6A1q0"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install kaggle\nfrom google.colab import files\n\n# Upload the kaggle.json file downloaded from Kaggle\nfiles.upload()\n\n!mkdir ~/.kaggle\n!cp kaggle.json ~/.kaggle/\n!chmod 600 ~/.kaggle/kaggle.json\n!kaggle competitions download -c gan-getting-started\nimport zipfile\nwith zipfile.ZipFile(\"gan-getting-started.zip\", \"r\") as zip_ref:\n    zip_ref.extractall(\"painter_dataset\")","metadata":{"id":"t3AWGNOmB4f2","outputId":"bfd4ec13-7656-4006-b87c-5a8a13fb7b4f"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models , initializers\nfrom tensorflow.keras.initializers import RandomNormal\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom tensorflow.keras.optimizers import legacy\nimport pandas as pd\nimport random\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nimport os\nimport shutil","metadata":{"id":"RvXHPoDwA1q4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# load data","metadata":{"id":"V00kcgnkA1q6"}},{"cell_type":"code","source":"def load_images_from_dir(directory, target_size=(320, 320)):\n    image_list = []\n    for filename in os.listdir(directory):\n        if filename.endswith(\".jpg\") or filename.endswith(\".png\"):\n            image_path = os.path.join(directory, filename)\n            image = cv2.imread(image_path)\n            if image is None:\n                print(f\"Warning: Unable to load image '{filename}'\")\n                continue\n\n            image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n            resized_image = cv2.resize(image_rgb, target_size)\n            image_list.append(resized_image)\n    return image_list\n\n","metadata":{"id":"L6lQghy8A1q7"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"directory_path = \"/content/painter_dataset/monet_jpg/\"\nmonet_image_list = load_images_from_dir(directory_path)\nprint(\"Number of images loaded:\", len(monet_image_list))","metadata":{"id":"kFYG84RhA1q7","outputId":"ea742cbe-e5bb-41fc-93ed-8a4439ff2a12"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"directory_path = \"/content/painter_dataset/photo_jpg/\"\nimage_list = load_images_from_dir(directory_path)\nprint(\"Number of images loaded:\", len(image_list))","metadata":{"id":"yuy8jCdTA1q8","outputId":"eb660021-fcfa-47c4-84ea-b62aa4d61e90"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# EDA images","metadata":{"id":"WJ0r5z6yA1q8"}},{"cell_type":"code","source":"def plot_random_images(image_list):\n    if len(image_list) < 5:\n        print(\"Error: Insufficient number of images.\")\n        return\n    random_indices = random.sample(range(len(image_list)), 5)\n\n    fig, axes = plt.subplots(1, 5, figsize=(15, 5))\n    for i, idx in enumerate(random_indices):\n        axes[i].imshow(image_list[idx])\n        axes[i].axis('off')\n        axes[i].set_title(f\"Image {idx}\")\n    plt.show()\n","metadata":{"id":"HxEsO5LEA1q9"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_images(image_list)","metadata":{"id":"WBnc1qPqA1q9","outputId":"f67d3cf7-7627-4e14-85be-a579df470ffc"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_images(monet_image_list)","metadata":{"id":"QIdPG584A1q-","outputId":"9f97d4f2-62a1-469e-f230-1fecfdb94031"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# cleaning the data","metadata":{"id":"wjdjFH4hA1q-"}},{"cell_type":"code","source":"def has_white_corner(image):\n    # Convert the image to grayscale for corner detection\n    gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n\n    # Define the size of the corner area to check\n    corner_size = 5\n\n    # Extract corner regions of the image\n    corners = [\n        gray_image[:corner_size, :corner_size],                # Top-left corner\n        gray_image[:corner_size, -corner_size:],              # Top-right corner\n        gray_image[-corner_size:, :corner_size],              # Bottom-left corner\n        gray_image[-corner_size:, -corner_size:]              # Bottom-right corner\n    ]\n\n    # Check if all corners are white (brightness value over 220)\n    return all(np.all(corner > 220) for corner in corners)\n\ndef crop_and_resize(image, target_size=(320, 320)):\n    # Get dimensions of the original image\n    height, width = image.shape[:2]\n\n    # Calculate the center point of the image\n    center_x = width // 2\n    center_y = height // 2\n\n    # Define the cropping area centered at the image's center\n    crop_left = max(0, center_x - 215 // 2)\n    crop_top = max(0, center_y - 215 // 2)\n    crop_right = min(width, center_x + 215 // 2)\n    crop_bottom = min(height, center_y + 215 // 2)\n\n    # Crop and resize the image to the target size\n    cropped_image = image[crop_top:crop_bottom, crop_left:crop_right]\n    resized_image = cv2.resize(cropped_image, target_size)\n\n    return resized_image\n\ndef show_images_with_white_corner(images):\n    # Iterate through each image in the list\n    for idx, image in enumerate(images):\n        # Check if the image has white corners\n        if has_white_corner(image):\n            # Crop and resize the image if it has white corners\n            cropped_resized_image = crop_and_resize(image)\n            # Display the original image\n            plt.imshow(image)\n            plt.title(f\"Image {idx}\")\n            plt.axis('off')\n            plt.show()\n\n            # Display the cropped and resized image\n            plt.imshow(cropped_resized_image)\n            plt.title(f\"Cropped & Resized Image {idx}\")\n            plt.axis('off')\n            plt.show()\n\n# Assuming `monet_image_list` is a predefined list of images\nshow_images_with_white_corner(monet_image_list)\n","metadata":{"id":"4AbJCyHTA1q-","outputId":"1fa84c9c-b16b-4083-d816-3b0a7320eedd"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### we notice that there are a few round images with white corners. We can crop and resize these images to remove the white corners.","metadata":{"id":"dkLM-plkA1q_"}},{"cell_type":"code","source":"def change_images_with_white_corner(images):\n    new_images = []\n    for idx, image in enumerate(images):\n        if has_white_corner(image):\n            new_images.append(crop_and_resize(image))\n        else:\n            new_images.append(image)\n\n    return new_images\n\nmonet_image_list = change_images_with_white_corner(monet_image_list)","metadata":{"id":"m9xOXAb3A1q_"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Augmentation of the data","metadata":{"id":"IGMrNvMCA1q_"}},{"cell_type":"code","source":"import numpy as np\nimport cv2\n\ndef split_and_resize_quarters(original_image):\n    \"\"\"\n    Splits an image into quarters and resizes each quarter back to 320x320 pixels.\n\n    Parameters:\n        original_image: A PIL Image object of size 320x320.\n\n    Returns:\n        A tuple containing the original image and a list of the resized quarter images.\n        If the original image is not 320x320, it returns None.\n    \"\"\"\n\n    # Convert PIL Image to NumPy array for processing\n    original_image_array = np.array(original_image)\n\n    # Ensure the image size is exactly 320x320 pixels\n    if original_image_array.shape[:2] != (320, 320):\n        print(\"Error: Image size must be 320x320.\")\n        return None\n\n    # Initialize an empty list to store the quarters of the image\n    quarters = []\n\n    # Loop to divide the image into 4 quarters\n    for i in range(2):  # Rows\n        for j in range(2):  # Columns\n            # Calculate the coordinates for each quarter\n            left = j * 160  # x-coordinate of the left edge\n            upper = i * 160  # y-coordinate of the top edge\n            right = left + 160  # x-coordinate of the right edge\n            lower = upper + 160  # y-coordinate of the bottom edge\n\n            # Extract the quarter using calculated coordinates\n            quarter = original_image_array[upper:lower, left:right, :]\n\n            # Add the extracted quarter to the list\n            quarters.append(quarter)\n\n    # Resize each quarter back to 320x320 pixels\n    resized_quarters = [cv2.resize(quarter, (320, 320)) for quarter in quarters]\n\n    # Return the original image and the list of resized quarter images\n    return original_image, resized_quarters\n","metadata":{"id":"GQoiruomA1q_"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Assuming split_and_resize_quarters function is defined and monet_image_list is a list of images\noriginal_image, quarters = split_and_resize_quarters(monet_image_list[2])\n\n# Check if the quarters list is not empty\nif quarters:\n    # Set up a figure for displaying the original image\n    plt.figure(figsize=(8, 8))\n    plt.imshow(original_image)  # Display the original image\n    plt.axis('off')  # Remove axes for clarity\n    plt.title(\"Original Image\")  # Set the title of the figure\n    plt.show()  # Display the figure\n\n    # Set up a 2x2 subplot structure for displaying the quarter images\n    fig, axes = plt.subplots(2, 2, figsize=(10, 10))\n\n    # Loop through each quarter image for plotting\n    for i, quarter in enumerate(quarters):\n        # Determine the position of the subplot using integer division and modulus\n        row = i // 2  # Row index for the subplot\n        col = i % 2   # Column index for the subplot\n\n        # Display the quarter image in the appropriate subplot\n        axes[row, col].imshow(quarter)\n        axes[row, col].axis('off')  # Remove axes for clarity\n        axes[row, col].set_title(f\"Quarter {i+1}\")  # Set the title for each subplot\n\n    plt.show()  # Display all the subplots\n","metadata":{"id":"U_Mt56rWA1rA","outputId":"44147cd5-068b-428e-dfae-d6efabd318c6"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_images_with_high_color_variance(images, threshold):\n    \"\"\"\n    Filters a list of images, returning those with a color variance higher than a specified threshold.\n\n    Parameters:\n        images (list): A list of image objects, which can be converted into NumPy arrays.\n        threshold (float): The minimum color variance required for an image to be included in the return list.\n\n    Returns:\n        list: A list of images that have a color variance greater than the threshold.\n    \"\"\"\n\n    # Initialize an empty list to store images with high color variance\n    high_color_var_images = []\n\n    # Iterate through each image in the provided list\n    for image in images:\n        # Convert the image to a NumPy array for analysis\n        image_array = np.array(image)\n\n        # Calculate the variance of the colors in the image\n        color_variance = np.var(image_array)\n\n        # If the color variance is greater than the threshold, add the image to the result list\n        if color_variance > threshold:\n            high_color_var_images.append(image)\n\n    # Return the list of images with high color variance\n    return high_color_var_images\n","metadata":{"id":"jpFNufD_A1rA"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"most_colorful_image = get_images_with_high_color_variance(quarters , 2500)\nif most_colorful_image:\n    fig, axes = plt.subplots(2, 2, figsize=(10, 10))\n    for i, quarter in enumerate(most_colorful_image):\n        axes[i//2, i%2].imshow(quarter)\n        axes[i//2, i%2].axis('off')\n    plt.show()","metadata":{"id":"ws-6X6j0A1rA","outputId":"39929ed6-e2a4-4fb7-e5c0-2eb7f4760d7b"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# we notice that we dont have engoh data in order to train the model, so we need to augment the data, here we just take the same iamge and flip it to get more data","metadata":{"id":"JVKlipazA1rA"}},{"cell_type":"code","source":"def mirror_flip(image):\n    flipped_image = cv2.flip(image, 1)\n    return flipped_image\n\n\ndef aug_pipeline(images, threshold):\n    augmented_images = []\n\n    for image in images:\n        _ , quarters = split_and_resize_quarters(image)\n\n        important_quarters = get_images_with_high_color_variance(quarters, threshold)\n\n        mirrored_image = mirror_flip(image)\n\n        augmented_images.append(image)\n        augmented_images.append(mirrored_image)\n        if len(important_quarters) != 0:\n            augmented_images.extend(important_quarters)\n\n    return augmented_images","metadata":{"id":"EEgwNlB-A1rA"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(monet_image_list))\naugmented_images = aug_pipeline(monet_image_list, 2500)\nprint(len(augmented_images))\nplot_random_images(augmented_images)","metadata":{"id":"TucwFpFFA1rB","outputId":"efaca402-4893-435d-9600-aafd4a392618"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# save the data in batch for model","metadata":{"id":"4SD_7zbrA1rB"}},{"cell_type":"code","source":"# for debug\n# augmented_images = augmented_images[:50]\n# image_list = image_list[:100]","metadata":{"id":"sTNDvcPXA1rB"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{"id":"eSRgEvbBA1rB"}},{"cell_type":"code","source":"def decode_image(image):\n    \"\"\"\n    Decodes an image by normalizing its pixel values to the range [-1, 1] and reshaping it.\n\n    This is commonly done as a preprocessing step before feeding the image to a neural network\n    to standardize the range of input values and ensure consistent input dimensions.\n\n    Parameters:\n        image: A tensor representing the image.\n\n    Returns:\n        A tensor of the image with pixel values normalized and reshaped to [320, 320, 3].\n    \"\"\"\n    # Normalize the image pixel values to the range [-1, 1]\n    # This helps in stabilizing the training process in neural networks.\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n\n    # Reshape the image to the expected input size of the neural network, which is 320x320 with 3 color channels.\n    # This ensures the image has a consistent size for processing in neural networks.\n    image = tf.reshape(image, [320, 320, 3])\n\n    return image\n\ndef encode_image(image):\n    \"\"\"\n    Encodes an image by converting its pixel values from the range [-1, 1] back to [0, 255].\n\n    This is typically done after processing an image through a neural network that outputs normalized pixel values,\n    to convert it back to the original pixel value range for displaying or saving as an image file.\n\n    Parameters:\n        image: A tensor representing the image with pixel values in the range [-1, 1].\n\n    Returns:\n        A tensor of the image with pixel values in the range [0, 255] as unsigned 8-bit integers.\n    \"\"\"\n    # Reverse the normalization process to convert the pixel values back to the range [0, 255].\n    # This is necessary for converting the output of neural networks back to a standard image format.\n    image = (image + 1) * 127.5\n\n    # Clip the pixel values to ensure they remain within the valid range [0, 255].\n    # This is important to avoid any values outside the range due to numeric computations.\n    image = tf.clip_by_value(image, 0, 255)\n\n    # Convert the pixel values back to unsigned 8-bit integers, which is a standard format for images.\n    # This is necessary for saving the image in a standard image file format like JPEG or PNG.\n    image = tf.cast(image, tf.uint8)\n\n    return image\n","metadata":{"id":"L27e65K5A1rB"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_list = random.sample(image_list, len(augmented_images))","metadata":{"id":"nUiwLBv-A1rC"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set the batch size for the dataset, used in training the model in batches\nBATCH_SIZE = 10\n\n# Calculate the length of the augmented images and the real image list\nmonet_len, real_len = len(augmented_images), len(image_list)\n\n# Decode the images using the previously defined decode_image function\n# This normalizes the pixel values and reshapes the images\ndecoded_monet_images = [decode_image(image) for image in augmented_images]\ndecoded_real_images = [decode_image(image) for image in image_list]\n\n# Print the lengths of the decoded images lists to verify the decoding process\nprint(len(decoded_monet_images), monet_len)\nprint(len(decoded_real_images), real_len)\n\n# Split the decoded images into training, validation, and test sets\n# Here, 80% of the images are used for training, 10% for validation, and 10% for testing\ntrain_monet = decoded_monet_images[:int(0.8 * monet_len)]\nval_monet = decoded_monet_images[int(0.8 * monet_len):int(0.9 * monet_len)]\ntest_monet = decoded_monet_images[int(0.9 * monet_len):]\n\ntrain_real = decoded_real_images[:int(0.8 * real_len)]\nval_real = decoded_real_images[int(0.8 * real_len):int(0.9 * real_len)]\ntest_real = decoded_real_images[int(0.9 * real_len):]\n\n# Create TensorFlow datasets from the image lists to efficiently manage memory and training speed\ntrain_real = tf.data.Dataset.from_tensor_slices(train_real)\nval_real = tf.data.Dataset.from_tensor_slices(val_real)\ntest_real = tf.data.Dataset.from_tensor_slices(test_real)\n\ntrain_monet = tf.data.Dataset.from_tensor_slices(train_monet)\nval_monet = tf.data.Dataset.from_tensor_slices(val_monet)\ntest_monet = tf.data.Dataset.from_tensor_slices(test_monet)\n\n# Pair the real and Monet datasets for training, validation, and testing\ntrain_dataset = tf.data.Dataset.zip((train_real, train_monet))\nval_dataset = tf.data.Dataset.zip((val_real, val_monet))\ntest_dataset = tf.data.Dataset.zip((test_real, test_monet))\n\n# Shuffle and batch the datasets for training\n# Shuffling helps in reducing variance and making sure that the model remains general and overfits less\ntrain_dataset = train_dataset.shuffle(buffer_size=len(train_real)).batch(BATCH_SIZE)\nval_dataset = val_dataset.batch(BATCH_SIZE)\ntest_dataset = test_dataset.batch(BATCH_SIZE)\n\n# Individual datasets are also shuffled and batched for potential separate use or evaluation\ntrain_monet = train_monet.shuffle(buffer_size=len(train_monet)).batch(BATCH_SIZE)\ntrain_real = train_real.shuffle(buffer_size=len(train_real)).batch(BATCH_SIZE)\nval_monet = val_monet.shuffle(buffer_size=len(val_monet)).batch(BATCH_SIZE)\nval_real = val_real.shuffle(buffer_size=len(val_real)).batch(BATCH_SIZE)\n","metadata":{"id":"JCTLZj_wA1rC","outputId":"81ba2ca0-f76e-4b4d-bbf1-8cebc201062e"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport tensorflow as tf\n\ndef plot_images_from_batch(dataset, title, img_to_plot=10):\n    \"\"\"\n    Plots a specified number of images from both Monet and real image batches in a dataset.\n\n    Parameters:\n        dataset: A tf.data.Dataset object containing batches of Monet and real images.\n        title: A string to set as the title of the plotted figure.\n        img_to_plot: An integer specifying how many images to plot from each batch.\n    \"\"\"\n    # Take one batch from the dataset\n    for real_batch, monet_batch in dataset.take(1):\n        # Initialize the figure with a specified size\n        plt.figure(figsize=(12, 8))\n\n        # Loop through the number of images specified to plot from the batch\n        for i in range(img_to_plot):\n            # Plot Monet images in the top row\n            plt.subplot(2, img_to_plot, i + 1)\n            monet_image = encode_image(monet_batch[i])  # Encode the image back to displayable format\n            plt.imshow(monet_image)  # Display the Monet image\n            plt.title(\"Monet\")  # Set title for the Monet image subplot\n            plt.axis(\"off\")  # Hide axes for cleaner visualization\n\n            # Plot real images in the bottom row\n            plt.subplot(2, img_to_plot, i + 1 + img_to_plot)\n            real_image = encode_image(real_batch[i])  # Encode the image back to displayable format\n            plt.imshow(real_image)  # Display the real image\n            plt.title(\"Real\")  # Set title for the real image subplot\n            plt.axis(\"off\")  # Hide axes for cleaner visualization\n\n        # Set the main title for the figure and display it\n        plt.suptitle(title)\n        plt.show()\n\n# Example usage, plotting 5 images from each of the Monet and real image batches in the training dataset\nplot_images_from_batch(train_dataset, \"Monet and Real Images\", img_to_plot=5)\n","metadata":{"id":"1KXV9MgrA1rC","outputId":"6f4cea8a-d25e-4216-fbff-8e042ceea098"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# create the objects that neccessary for the training","metadata":{"id":"Qh7fyIwBA1rC"}},{"cell_type":"code","source":"def downsample(filters, size, apply_batchnorm=True):\n    \"\"\"\n    Creates a downsampling layer for a convolutional neural network.\n\n    Parameters:\n        filters: Number of filters in the convolutional layer.\n        size: Kernel size for the convolutional layer.\n        apply_batchnorm: Boolean flag to determine whether to apply batch normalization.\n\n    Returns:\n        A TensorFlow Sequential model containing the downsampling layers.\n    \"\"\"\n    # Initialize the weights with a normal distribution for the convolutional layer\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    # Create a sequential model to hold the downsampling layers\n    result = tf.keras.Sequential()\n\n    # Add a convolutional layer with specified filters and kernel size\n    # Stride of 2 for downsampling, 'same' padding to keep dimensions consistent\n    result.add(\n        tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',\n                               kernel_initializer=initializer, use_bias=False))\n\n    # Optionally add batch normalization to improve training stability and performance\n    if apply_batchnorm:\n        result.add(tf.keras.layers.BatchNormalization())\n\n    # Add Leaky ReLU activation to introduce non-linearity and allow gradients to flow even for negative values\n    result.add(tf.keras.layers.LeakyReLU())\n\n    return result\n\n# Getting a sample from the training dataset\nsample, _ = next(iter(train_dataset))\n# Casting the sample to float32, typically needed for model processing in TensorFlow\nsample_float32 = tf.cast(sample, tf.float32)\n\n# Create a downsampling model with 3 filters and kernel size 4\ndown_model = downsample(3, 4)\n# Apply the downsampling model to the sample\ndown_result = down_model(sample_float32)\n\n# Print the shape of the output from the downsampling model to verify the result\nprint(down_result.shape)\n","metadata":{"id":"FsiPz2P4A1rC","outputId":"5ae842ed-8d34-408c-f04b-672238fa96f5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n    \"\"\"\n    Creates an upsampling layer for a convolutional neural network.\n\n    Parameters:\n        filters: Number of filters in the transposed convolutional layer.\n        size: Kernel size for the transposed convolutional layer.\n        apply_dropout: Boolean flag to determine whether to apply dropout.\n\n    Returns:\n        A TensorFlow Sequential model containing the upsampling layers.\n    \"\"\"\n    # Initialize the weights with a normal distribution for the transposed convolutional layer\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    # Create a sequential model to hold the upsampling layers\n    result = tf.keras.Sequential()\n\n    # Add a transposed convolutional layer (also known as deconvolution)\n    # which increases the spatial dimensions of the input\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    # Always add batch normalization to standardize the activations from the previous layer,\n    # which helps to speed up training and reduce the number of training epochs required\n    result.add(tf.keras.layers.BatchNormalization())\n\n    # Optionally add dropout to prevent overfitting by randomly setting a fraction of input units to 0\n    # at each update during training time, which helps to prevent overfitting\n    if apply_dropout:\n        result.add(tf.keras.layers.Dropout(0.5))\n\n    # Add ReLU activation function to introduce non-linearity to the model and allow it to learn more complex patterns\n    result.add(tf.keras.layers.ReLU())\n\n    return result\n\n# Example of using the upsample function to create a model and apply it to an input\nup_model = upsample(3, 4)  # Create an upsampling model with 3 filters and kernel size 4\nup_result = up_model(down_result)  # Apply the upsampling model to the result of a previous downsampling operation\nprint(up_result.shape)  # Print the shape of the output to verify the upsampling effect\n","metadata":{"id":"liN_56HZA1rC","outputId":"d0da6539-12a5-499b-9009-179d940a195c"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define Generator model\ndef Generator():\n# Input layer of the generator with shape 320x320x3\n  inputs = tf.keras.layers.Input(shape=[320, 320, 3])\n\n  # Define the downsampling part of the generator\n  down_stack = [\n    downsample(80, 4, apply_batchnorm=False),  # First layer without batchnorm, reduces size to 160x160\n    downsample(160, 4),  # Reduces size to 80x80\n    downsample(320, 4),  # Reduces size to 80x80\n    downsample(640, 4), # Reduces size to 80x80\n    downsample(640, 4),  # Reduces size to 10x10\n    downsample(640, 4),  # Reduces size to 5x5\n  ]\n\n  up_stack = [\n    upsample(640, 4, apply_dropout=True),  # Extends size to 10x10, uses dropout to prevent overfitting\n    upsample(640, 4 ,apply_dropout=True),  # Extends size to 20x20\n    upsample(320, 4),  # Extends size to 40x40\n    upsample(160, 4),  # Extends size to 80x80\n    upsample(80, 4),   # Extends size to 160x160\n  ]\n\n  # Output layer of the generator\n  initializer = tf.random_normal_initializer(0., 0.02)\n  last = tf.keras.layers.Conv2DTranspose(3, 4,\n                                         strides=2,\n                                         padding='same',\n                                         kernel_initializer=initializer,\n                                         activation='tanh')  # Extends size to 320x320, output 3 channels\n\n\n  # Process the input through the down_stack\n  x = inputs\n  skips = []\n  for down in down_stack:\n    x = down(x)\n    skips.append(x)\n\n   # Prepare skip connections for the upsampling layers\n  skips = reversed(skips[:-1]) # Reverse and exclude the last downsampled output\n   # Process through the up_stack using skip connections\n  for up, skip in zip(up_stack, skips):\n    x = up(x)\n    x = tf.keras.layers.Concatenate()([x, skip])# Concatenate skip connection with the upsampled output\n  # Final layer to generate the output image\n  x = last(x)\n\n  # Create the model\n  return tf.keras.Model(inputs=inputs, outputs=x)\n\ndef test_build_generator():\n\n    # Instantiate the Generator\n    generator = Generator()\n    # Define the input shape and generate a batch of random noise\n    input_shape = (320, 320, 3)\n    batch_size = 4\n    random_input_batch = tf.random.uniform((batch_size,) + input_shape, minval=0, maxval=1)\n\n    # Generate images from the random noise\n    generated_images = generator(random_input_batch)\n    print(generator.summary())\n    # Check the output shape of the generated images\n    output_shape = generated_images.shape\n    expected_output_shape = (batch_size, 320, 320, 3)\n    # Assert the output shape is as expected\n    assert output_shape == expected_output_shape, \"Output shape mismatch\"\n    print(\"Test passed!\")\n\ntest_build_generator()","metadata":{"id":"sJlBMPCSA1rD","outputId":"f1ada669-1597-4e86-f294-4c6afebecb0d"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def Discriminator(input_nc=3, ndf=80, n_layers=3):\n    \"\"\"\n    Defines the Discriminator model for a GAN, which classifies images as real or fake.\n\n    Parameters:\n        input_nc: Number of channels in the input images, typically 3 for RGB images.\n        ndf: Number of filters in the first convolutional layer, controls model capacity.\n        n_layers: Number of layers in the discriminator.\n\n    Returns:\n        A TensorFlow model representing the discriminator.\n    \"\"\"\n    # Input layer of the discriminator with shape 320x320x(input_nc)\n    inputs = tf.keras.layers.Input(shape=(320, 320, input_nc))\n    x = inputs\n\n    # Build the convolutional layers of the discriminator\n    for n in range(n_layers + 1):\n        # Increase the number of filters as we go deeper, capped at 5 times the initial number\n        nf_mult = min(2**n, 5)\n        # Add a convolutional layer with increasing number of filters\n        x = tf.keras.layers.Conv2D(ndf * nf_mult, kernel_size=4, strides=2, padding='same')(x)\n        # Use LeakyReLU to allow small gradients for negative values, improving training stability\n        x = tf.keras.layers.LeakyReLU(0.2)(x)\n        # Apply batch normalization except for the last layer to help stabilize training\n        if n < n_layers:\n            x = tf.keras.layers.BatchNormalization()(x)\n\n    # Output layer: a convolutional layer that produces a single output channel\n    outputs = tf.keras.layers.Conv2D(1, kernel_size=4, strides=1, padding='same')(x)\n\n    # Construct and return the discriminator model\n    return tf.keras.Model(inputs=inputs, outputs=outputs)\n\ndef test_build_discriminator():\n    # Define the input shape for the test\n    input_shape = (320, 320, 3)\n\n    # Create a discriminator model\n    discriminator = Discriminator(3)\n\n    # Define a batch size for testing\n    batch_size = 4\n    # Generate a batch of random input data\n    random_input_batch = tf.random.uniform((batch_size,) + input_shape, minval=0, maxval=1)\n\n    # Get the discriminator output for the random input\n    discriminator_output = discriminator(random_input_batch)\n\n    # Check and print the output shape\n    output_shape = discriminator_output.shape\n    print(output_shape)\n    # Print the discriminator model summary\n    print(discriminator.summary())\n    # Define the expected output shape\n    expected_output_shape = (batch_size, 20, 20, 1)\n\n    # Assert the output shape matches the expected shape\n    assert output_shape == expected_output_shape, \"Output shape mismatch\"\n    print(\"Test passed!\")\n\n# Run the test function to build and validate the discriminator\ntest_build_discriminator()\n","metadata":{"id":"6N5qqk_uA1rD","outputId":"5f28bf86-3acd-47d6-c13d-adecaa328e74"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# step 1","metadata":{"id":"AQe5p-iaA1rE"}},{"cell_type":"code","source":"import tensorflow as tf\nimport pandas as pd\nfrom tqdm import tqdm\n\n# Initialize the optimizer; legacy.Adam() suggests using a version of Adam optimizer with some custom settings or from a previous version\noptimizer = tf.keras.optimizers.legacy.Adam()\n\n# Define the loss function; MeanSquaredError is commonly used for regression tasks and measures the average of the squares of the errors\nloss_function = tf.keras.losses.MeanSquaredError()\n\ndef train_model(model, train_dataset, val_dataset, optimizer, loss_function, num_epochs):\n    \"\"\"\n    Trains a machine learning model using the specified optimizer and loss function.\n\n    Parameters:\n        model: The neural network model to train.\n        train_dataset: The dataset for training the model.\n        val_dataset: The dataset for validating the model performance.\n        optimizer: The optimization algorithm to use for training.\n        loss_function: The loss function to use for training.\n        num_epochs: The number of epochs to train the model.\n\n    Returns:\n        A DataFrame containing the training and validation losses for each epoch.\n    \"\"\"\n    # Compile the model with the given optimizer and loss function\n    model.compile(optimizer=optimizer, loss=loss_function)\n\n    # Lists to store the loss values for each epoch\n    train_losses = []\n    val_losses = []\n\n    # Train the model for a specified number of epochs\n    for epoch in range(num_epochs):\n        train_epoch_loss = 0\n        val_epoch_loss = 0\n\n        # Iterate over each batch in the training dataset\n        for batch in tqdm(train_dataset, desc=f'Epoch {epoch}'):\n            # Record operations for automatic differentiation\n            with tf.GradientTape() as tape:\n                generated_images = model(batch, training=True)\n                loss = loss_function(batch, generated_images)\n            # Compute gradients of the loss with respect to the model parameters\n            gradients = tape.gradient(loss, model.trainable_variables)\n            # Apply gradients to update the model parameters\n            optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n            train_epoch_loss += loss\n\n        # Iterate over each batch in the validation dataset\n        for val_batch in tqdm(val_dataset, desc=f'Epoch {epoch}'):\n            val_generated_images = model(val_batch, training=False)\n            val_loss = loss_function(val_batch, val_generated_images)\n            val_epoch_loss += val_loss\n\n        # Calculate the average loss for the epoch\n        train_epoch_loss /= len(train_dataset)\n        val_epoch_loss /= len(val_dataset)\n\n        # Append the average loss for each epoch to the respective lists\n        train_losses.append(train_epoch_loss.numpy())\n        val_losses.append(val_epoch_loss.numpy())\n\n        # Print the loss values for the epoch\n        print(f'Epoch {epoch + 1}, Training Loss: {train_epoch_loss.numpy()}, Validation Loss: {val_epoch_loss.numpy()}')\n\n    # Create a DataFrame to store and display the loss values for each epoch\n    losses_df = pd.DataFrame({'Epoch': range(1, num_epochs + 1), 'Training Loss': train_losses, 'Validation Loss': val_losses})\n\n    return losses_df\n\n# Set the number of epochs for training\nnum_epochs = 1\n\n# Train the model for the 'Monet' generator\nprint(\"Training monet_gen:\")\nmonet_gen_trained = Generator()  # Instantiate the generator model\nmonet_losses = train_model(monet_gen_trained, train_monet, val_monet, optimizer, loss_function, num_epochs)  # Train the model\n\n# Train the model for the 'Real' generator\nprint(\"Training real_gen:\")\nreal_gen_trained = Generator()  # Instantiate another generator model\nreal_losses = train_model(real_gen_trained, train_real, val_real, optimizer, loss_function, num_epochs)  # Train the model\n","metadata":{"id":"GAKbIf35A1rE"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save the trained weights of the real image generator\n# This allows the model's learned parameters to be reused without retraining from scratch.\n# 'generator_m2r.h5' is the filename, where 'm2r' indicates Monet to Real conversion.\nreal_gen_trained.save_weights(os.path.join(\"pre_train\", f'generator_m2r.h5'))\n\n# Save the trained weights of the Monet-style image generator\n# Similar to above, 'generator_r2m.h5' is the filename, where 'r2m' indicates Real to Monet conversion.\nmonet_gen_trained.save_weights(os.path.join(\"pre_train\", f'generator_r2m.h5'))\n","metadata":{"id":"JWV380iJA1rE"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save the losses DataFrame to a CSV file for both models.\n# This is useful for later analysis of the training process, such as plotting loss curves.\n# 'monet_losses.csv' stores the loss data for the Monet generator.\nmonet_losses.to_csv('pre_train/monet_losses.csv', index=False)\n# 'real_losses.csv' stores the loss data for the real image generator.\nreal_losses.to_csv('pre_train/real_losses.csv', index=False)","metadata":{"id":"drwuQnDDA1rE"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the training and validation loss data for the Monet-style generator from a CSV file.\n# This data is used for analyzing the model's performance over the training period.\nmonet_losses = pd.read_csv('monet_losses.csv')\n\n# Load the training and validation loss data for the real-image generator from a CSV file.\n# Similar to the Monet generator, this data helps in evaluating the training effectiveness.\nreal_losses = pd.read_csv('real_losses.csv')\n\n# Instantiate new generator models.\n# These generators are neural network models that were previously trained to convert images.\nmonet_gen_trained = Generator()  # For generating Monet-styled images\nreal_gen_trained = Generator()   # For generating real-styled images\n\n# Load the saved weights into the instantiated models.\n# This step effectively restores the models to their trained state,\n# allowing them to generate images without needing to be retrained.\n\n# Load weights for the real image generator (Monet to Real conversion).\n# The weights file 'generator_m2r.h5' contains the learned parameters of the model.\n\nreal_gen_trained.load_weights('generator_monet2real_epoch_30.h5')\n\n# Load weights for the Monet-style image generator (Real to Monet conversion).\n# The weights file 'generator_r2m.h5' stores the trained parameters of the model.\nmonet_gen_trained.load_weights('generator_real2monet_epoch_30.h5')\n","metadata":{"id":"Ot3uvcfNA1rE"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Start a new matplotlib figure with specified size\nplt.figure(figsize=(10, 6))\n\n# Plot the training loss per epoch.\n# 'Epoch' column from the 'monet_losses' DataFrame is used as the x-axis,\n# and 'Training Loss' column as the y-axis.\n# This line graph represents how the training loss changes over epochs.\nplt.plot(monet_losses['Epoch'], monet_losses['Training Loss'], label='Training Loss')\n\n# Similarly, plot the validation loss per epoch.\n# 'Validation Loss' column is used for the y-axis.\n# This line graph shows the change in validation loss across epochs.\nplt.plot(monet_losses['Epoch'], monet_losses['Validation Loss'], label='Validation Loss')\n\n# Label the x-axis as 'Epoch' to indicate the training progress.\nplt.xlabel('Epoch')\n# Label the y-axis as 'Loss' to indicate the value being measured.\nplt.ylabel('Loss')\nplt.title('Training and Validation Losses')\nplt.legend()\nplt.grid(True)\n\nplt.show()","metadata":{"id":"OC5sDJibA1rS","outputId":"42637b1e-51ea-456d-de2f-d2c70e898a6f"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Initialize a new plot with a width of 10 inches and a height of 6 inches\nplt.figure(figsize=(10, 6))\n\n# Plot the training loss for each epoch.\n# 'Epoch' column from 'real_losses' DataFrame is the x-axis, showing the training progress over time.\n# 'Training Loss' column is the y-axis, representing the magnitude of loss during training.\nplt.plot(real_losses['Epoch'], real_losses['Training Loss'], label='Training Loss')\n# Plot the validation loss for each epoch alongside the training loss.\n# 'Validation Loss' shows how well the model is performing on unseen data over the epochs.\nplt.plot(real_losses['Epoch'], real_losses['Validation Loss'], label='Validation Loss')\n# Set the label for the x-axis to 'Epoch' to indicate the time dimension of the training process.\nplt.xlabel('Epoch')\n# Set the label for the y-axis to 'Loss' indicating the metric being measured and optimized.\nplt.ylabel('Loss')\n# Add a title to the graph to describe what is being displayed.\nplt.title('Training and Validation Losses')\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"id":"ctNZjhH4A1rT","outputId":"c75260d9-dc87-4bd1-a32b-a7385be33fc7"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def generate_images(generator, img):\n    \"\"\"\n    Generates an image using the trained generator model.\n\n    Parameters:\n        generator: The trained generator model.\n        img: The input image for generating a new image.\n\n    Returns:\n        The generated image from the model.\n    \"\"\"\n    # Add an extra dimension to the image making it suitable for the model (batch size of 1)\n    img = np.expand_dims(img, axis=0)\n\n    # Use the generator model to predict the output image from the input image\n    generated_img = generator.predict(img)[0]  # [0] to get the first item from the batch\n\n    return generated_img\n\n# Iterate through one batch from the test dataset\nfor real_batch, monet_batch in test_dataset.take(1):\n    # Process and display the first real image from the batch\n    real_image = encode_image(real_batch[0])  # Decode image to displayable format\n    plt.imshow(real_image)\n    plt.title(\"Real\")\n    plt.axis(\"off\")  # Hide the axis\n    plt.show(block=False)  # Display the image without blocking execution\n\n    # Generate an image from the real image using the trained real-to-Monet generator\n    real_generate = generate_images(real_gen_trained, real_batch[0])\n    real_generate_img = encode_image(real_generate)  # Decode the generated image for display\n    plt.imshow(real_generate_img)\n    plt.title(\"real_generate\")\n    plt.axis(\"off\")\n    plt.show(block=False)\n\n    print(\"-\" * 60)  # Print a separator line\n\n    # Process and display the first Monet-style image from the batch\n    monet_image = encode_image(monet_batch[0])\n    plt.imshow(monet_image)\n    plt.title(\"Monet\")\n    plt.axis(\"off\")\n    plt.show(block=False)\n\n    # Generate an image from the Monet image using the trained Monet-to-real generator\n    monet_generate = generate_images(monet_gen_trained, monet_batch[0])\n    monet_generate_img = encode_image(monet_generate)\n    plt.imshow(monet_generate_img)\n    plt.title(\"monet_generate\")\n    plt.axis(\"off\")\n    plt.show(block=False)\n","metadata":{"id":"k0f2u2P5A1rT","outputId":"e1a27391-ab02-489a-a093-29d017c7083d"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# step 2\n","metadata":{"id":"SuYUpsRxA1rT"}},{"cell_type":"code","source":"class CycleGan(tf.keras.Model):\n    def __init__(self, generator_real2monet, generator_monet2real, discriminator_real, discriminator_monet, lr=5e-4, beta_1=0.5):\n        # Initialize the parent class\n        super(CycleGan, self).__init__()\n\n        # Assign the provided generators and discriminators to the class instance\n        self.generator_real2monet = generator_real2monet  # Converts real images to Monet style\n        self.generator_monet2real = generator_monet2real  # Converts Monet images to real style\n        self.discriminator_real = discriminator_real      # Distinguishes between real and generated real images\n        self.discriminator_monet = discriminator_monet    # Distinguishes between Monet and generated Monet images\n        self.lr = lr  # Learning rate for the optimizers\n        self.beta_1 = beta_1  # Beta_1 parameter for the Adam optimizer\n\n\n    def compile(self, mac=True):\n        # Compile method customizes the training process\n        # The 'mac' parameter is used to choose between custom or standard Adam optimizer\n        optimizer = tf.keras.optimizers.Adam(learning_rate=self.lr, beta_1=self.beta_1) if not mac else legacy.Adam(learning_rate=self.lr, beta_1=self.beta_1)\n\n        # Compile all generators and discriminators with the same optimizer\n        self.generator_real2monet.compile(optimizer=optimizer)\n        self.generator_monet2real.compile(optimizer=optimizer)\n        self.discriminator_real.compile(optimizer=optimizer)\n        self.discriminator_monet.compile(optimizer=optimizer)\n\n    def adversarial_loss(self, discriminator, generated):\n        # Computes the loss for fake images to fool the discriminator\n        fake_output = discriminator(generated)  # Discriminator's prediction on generated images\n        return tf.reduce_mean(tf.keras.losses.BinaryCrossentropy(from_logits=True)(tf.ones_like(fake_output), fake_output))\n\n\n    def cycle_consistency_loss(self, real_images, reconstructed_images):\n        # Computes the cycle consistency loss to enforce forward and backward consistency\n        return tf.reduce_mean(tf.abs(real_images - reconstructed_images))\n\n    def identity_loss(self, real_image, generated_image):\n        # Computes the identity loss to preserve color and composition between input and generated images\n        return tf.reduce_mean(tf.square(real_image - generated_image))\n\n    def discriminator_loss(self, real_output, fake_output):\n        # Binary Cross Entropy (BCE) loss is used to distinguish between real and fake images.\n        bce_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n\n        # Labels for real images are ones, and for fake images are zeros.\n        real_labels = tf.ones_like(real_output)\n        fake_labels = tf.zeros_like(fake_output)\n\n        # Calculate the loss for the real and fake images separately.\n        real_loss = bce_loss(real_labels, real_output)  # Loss for correctly identifying real images\n        fake_loss = bce_loss(fake_labels, fake_output)  # Loss for correctly identifying fake images\n\n        # Total discriminator loss is the sum of real and fake losses.\n        total_loss = real_loss + fake_loss\n\n        return total_loss\n\n\n    def train_step(self, batch_data):\n        # Unpack the real images and Monet-styled images from the batch data\n        batch_real, batch_monet = batch_data\n\n        # Open a GradientTape to record the operations for automatic differentiation\n        with tf.GradientTape(persistent=True) as tape:\n            # Generate fake Monet images from real images and fake real images from Monet images\n            fake_monet = self.generator_real2monet(batch_real, training=True)\n            fake_real = self.generator_monet2real(batch_monet, training=True)\n\n            # Generate reconstructed real and Monet images for cycle consistency loss\n            reconstr_real = self.generator_monet2real(fake_monet, training=True)\n            reconstr_monet = self.generator_real2monet(fake_real, training=True)\n\n            # Generate images for identity loss calculation\n            same_real = self.generator_monet2real(batch_real, training=True)\n            same_monet = self.generator_real2monet(batch_monet, training=True)\n\n            # Calculate the adversarial loss for both generators\n            adv_loss_R2M = self.adversarial_loss(self.discriminator_monet, fake_monet)\n            adv_loss_M2R = self.adversarial_loss(self.discriminator_real, fake_real)\n\n            # Calculate the cycle consistency loss for both real and Monet cycles\n            cycle_loss = self.cycle_consistency_loss(batch_real, reconstr_real) + \\\n                         self.cycle_consistency_loss(batch_monet, reconstr_monet)\n\n            # Calculate the identity loss to preserve the color and composition of the input images\n            identity_loss_real = self.identity_loss(batch_real, same_real)\n            identity_loss_monet = self.identity_loss(batch_monet, same_monet)\n\n            # Total generator loss includes adversarial, cycle consistency, and identity losses\n            total_gen_R2M_loss = 2 * adv_loss_R2M + cycle_loss + 2 * identity_loss_monet\n            total_gen_M2R_loss = 2 * adv_loss_M2R + cycle_loss + 2 * identity_loss_real\n\n            # Evaluate the real and fake images through the discriminators\n            disc_real_real_output = self.discriminator_real(batch_real, training=True)\n            disc_real_fake_output = self.discriminator_real(fake_real, training=True)\n            disc_monet_real_output = self.discriminator_monet(batch_monet, training=True)\n            disc_monet_fake_output = self.discriminator_monet(fake_monet, training=True)\n\n            # Calculate the discriminator loss for real and Monet discriminators\n            disc_real_loss = self.discriminator_loss(disc_real_real_output, disc_real_fake_output)\n            disc_monet_loss = self.discriminator_loss(disc_monet_real_output, disc_monet_fake_output)\n\n        # Compute gradients for generators and discriminators\n        gen_R2M_gradients = tape.gradient(total_gen_R2M_loss, self.generator_real2monet.trainable_variables)\n        gen_M2R_gradients = tape.gradient(total_gen_M2R_loss, self.generator_monet2real.trainable_variables)\n        disc_real_gradients = tape.gradient(disc_real_loss, self.discriminator_real.trainable_variables)\n        disc_monet_gradients = tape.gradient(disc_monet_loss, self.discriminator_monet.trainable_variables)\n\n        # Apply the gradients to the respective models\n        self.generator_real2monet.optimizer.apply_gradients(zip(gen_R2M_gradients, self.generator_real2monet.trainable_variables))\n        self.generator_monet2real.optimizer.apply_gradients(zip(gen_M2R_gradients, self.generator_monet2real.trainable_variables))\n        self.discriminator_real.optimizer.apply_gradients(zip(disc_real_gradients, self.discriminator_real.trainable_variables))\n        self.discriminator_monet.optimizer.apply_gradients(zip(disc_monet_gradients, self.discriminator_monet.trainable_variables))\n\n        # Return a dictionary of the computed losses\n        return {\n            \"gen_R2M_loss\": total_gen_R2M_loss,\n            \"gen_M2R_loss\": total_gen_M2R_loss,\n            \"disc_real_loss\": disc_real_loss,\n            \"disc_monet_loss\": disc_monet_loss\n        }\n\n    def test_step(self, batch_data):\n        # Unpack real and Monet images from the batch data provided to the method.\n        batch_real, batch_monet = batch_data\n\n        # Generate fake Monet images from real images and vice versa, setting training to False to avoid updating batch statistics.\n        fake_monet = self.generator_real2monet(batch_real, training=False)\n        fake_real = self.generator_monet2real(batch_monet, training=False)\n\n        # Reconstruct the original domain images from the fakes to compute the cycle consistency loss.\n        reconstr_real = self.generator_monet2real(fake_monet, training=False)\n        reconstr_monet = self.generator_real2monet(fake_real, training=False)\n\n        # Generate images from the real and Monet domains to calculate the identity loss.\n        same_real = self.generator_monet2real(batch_real, training=False)\n        same_monet = self.generator_real2monet(batch_monet, training=False)\n\n        # Calculate adversarial losses to evaluate how well the generators are fooling the discriminators.\n        adv_loss_R2M = self.adversarial_loss(self.discriminator_monet, fake_monet)\n        adv_loss_M2R = self.adversarial_loss(self.discriminator_real, fake_real)\n\n        # Compute cycle consistency loss to ensure that the input image can be reconstructed after a round-trip transformation.\n        cycle_loss = self.cycle_consistency_loss(batch_real, reconstr_real) + \\\n                     self.cycle_consistency_loss(batch_monet, reconstr_monet)\n\n        # Compute identity loss to ensure the generator preserves the original image when no translation is needed.\n        identity_loss_real = self.identity_loss(batch_real, same_real)\n        identity_loss_monet = self.identity_loss(batch_monet, same_monet)\n\n        # Calculate the total generator losses, combining the adversarial, cycle, and identity losses.\n        total_gen_R2M_loss = 2*adv_loss_R2M + cycle_loss + 2*identity_loss_monet\n        total_gen_M2R_loss = 2*adv_loss_M2R + cycle_loss + 2*identity_loss_real\n\n        # Evaluate the discriminators' performances on distinguishing real images from fake ones.\n        disc_real_real_output = self.discriminator_real(batch_real, training=False)\n        disc_real_fake_output = self.discriminator_real(fake_real, training=False)\n        disc_monet_real_output = self.discriminator_monet(batch_monet, training=False)\n        disc_monet_fake_output = self.discriminator_monet(fake_monet, training=False)\n\n        # Compute the total discriminator losses for real and Monet images.\n        disc_real_loss = self.discriminator_loss(disc_real_real_output, disc_real_fake_output)\n        disc_monet_loss = self.discriminator_loss(disc_monet_real_output, disc_monet_fake_output)\n\n        # Return a dictionary of the computed losses for monitoring and evaluation.\n        return {\n            \"gen_R2M_loss\": total_gen_R2M_loss,\n            \"gen_M2R_loss\": total_gen_M2R_loss,\n            \"disc_real_loss\": disc_real_loss,\n            \"disc_monet_loss\": disc_monet_loss\n        }","metadata":{"id":"soz_xntLA1rT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# check for GPU","metadata":{"id":"if-OLUJHA1rU"}},{"cell_type":"code","source":"print(\"GPU Available:\", tf.config.list_physical_devices('GPU'))","metadata":{"id":"hjM3_hsvA1rU"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize two instances of the Generator model for real-to-Monet and Monet-to-real conversions.\ngenerator_real2monet, generator_monet2real = Generator(), Generator()\n\n# Uncomment below lines to load pretrained weights if available\n# generator_real2monet.load_weights('pre_train/generator_r2m.h5')\n# generator_monet2real.load_weights('pre_train/generator_m2r.h5')\n\n# Initialize two instances of the Discriminator model for real and Monet images.\ndiscriminator_real, discriminator_monet = Discriminator(), Discriminator()\n\n# Set the learning rate and beta_1 parameter for the Adam optimizer.\nlr = 0.0001\nbeta_1 = 0.5\n\n# Create directories to save the models' weights and results.\nmodels_folder = f\"models/lr_{lr}_beta_{beta_1}\"\nos.makedirs(models_folder, exist_ok=True)  # Create the models directory if it doesn't exist\nos.makedirs(\"results\", exist_ok=True)  # Create the results directory if it doesn't exist\n\n# Initialize the CycleGan model with the previously created generator and discriminator instances.\ncycle_gan_model = CycleGan(generator_real2monet, generator_monet2real, discriminator_real, discriminator_monet, lr=lr, beta_1=beta_1)\n\n# Compile the CycleGAN model to set up the optimizers.\ncycle_gan_model.compile()\n\n# Define the number of epochs to train the model and the interval for evaluation.\nnum_epochs = 1\neval_interval = 2\n\n# Initialize a dictionary to log the training and evaluation metrics.\nlog_data = {\n    'Epoch': [],\n    'Gen R2M Loss Train': [], 'Gen M2R Loss Train': [],\n    'Disc Real Loss Train': [], 'Disc Monet Loss Train': [],\n    'Gen R2M Loss Eval': [], 'Gen M2R Loss Eval': [],\n    'Disc Real Loss Eval': [], 'Disc Monet Loss Eval': []\n}\n\n# Start the training process.\nfor epoch in range(1, num_epochs + 1):\n    # Dictionary to collect losses for each batch in the current epoch.\n    train_losses = {\n        'Gen R2M Loss': [], 'Gen M2R Loss': [],\n        'Disc Real Loss': [], 'Disc Monet Loss': []\n    }\n\n    # Loop through the training dataset in batches.\n    for batch_real, batch_monet in tqdm(train_dataset, desc=f'Epoch {epoch}'):\n        # Perform a single training step and return the losses.\n        train_logs = cycle_gan_model.train_step((batch_real, batch_monet))\n\n        # Aggregate the losses for later calculation of average loss.\n        train_losses['Gen R2M Loss'].append(train_logs['gen_R2M_loss'])\n        train_losses['Gen M2R Loss'].append(train_logs['gen_M2R_loss'])\n        train_losses['Disc Real Loss'].append(train_logs['disc_real_loss'])\n        train_losses['Disc Monet Loss'].append(train_logs['disc_monet_loss'])\n\n    # Calculate the average loss for each metric over all training batches in the current epoch.\n    avg_train_losses = {k + ' Train': np.mean(v) for k, v in train_losses.items()}\n\n    # Save the models' weights after each epoch.\n    cycle_gan_model.generator_real2monet.save_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{epoch}.h5'))\n    cycle_gan_model.generator_monet2real.save_weights(os.path.join(models_folder, f'generator_monet2real_epoch_{epoch}.h5'))\n    cycle_gan_model.discriminator_real.save_weights(os.path.join(models_folder, f'discriminator_real_epoch_{epoch}.h5'))\n    cycle_gan_model.discriminator_monet.save_weights(os.path.join(models_folder, f'discriminator_monet_epoch_{epoch}.h5'))\n\n    # Perform evaluation at specified intervals.\n    if epoch % eval_interval == 0:\n        # Dictionary to collect evaluation metrics.\n        eval_metrics = {\n            'Gen R2M Loss': 0.0, 'Gen M2R Loss': 0.0,\n            'Disc Real Loss': 0.0, 'Disc Monet Loss': 0.0\n        }\n        num_batches = 0\n\n        # Loop through the validation dataset in batches.\n        for batch_real_eval, batch_monet_eval in tqdm(val_dataset, desc=f'Eval at Epoch {epoch}'):\n            # Perform an evaluation step and return the losses.\n            eval_logs = cycle_gan_model.test_step((batch_real_eval, batch_monet_eval))\n\n            # Accumulate the losses for later calculation of average loss.\n            eval_metrics['Gen R2M Loss'] += eval_logs['gen_R2M_loss']\n            eval_metrics['Gen M2R Loss'] += eval_logs['gen_M2R_loss']\n            eval_metrics['Disc Real Loss'] += eval_logs['disc_real_loss']\n            eval_metrics['Disc Monet Loss'] += eval_logs['disc_monet_loss']\n            num_batches += 1\n\n        # Calculate the average loss for each metric over all evaluation batches.\n        avg_eval_metrics = {k + ' Eval': v / num_batches for k, v in eval_metrics.items()}\n\n        # Append the current epoch's average training and evaluation metrics to the log data.\n        log_data['Epoch'].append(epoch)\n        for key, value in {**avg_train_losses, **avg_eval_metrics}.items():\n            log_data[key].append(value)\n\n        print(log_data)  # Optionally print the log data to monitor the training and evaluation progress.\n\n# Convert the collected log data into a pandas DataFrame for easier handling and visualization.\nlog_df = pd.DataFrame(log_data)\n\n# Save the compiled log data to a CSV file for further analysis and review.\nlog_df.to_csv(f'results/log_lr_{lr}_beta_{beta_1}.csv', index=False)","metadata":{"id":"6iutpgLfA1rU"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_losses(df_losses):\n    epochs = df_losses['Epoch']\n    gen_R2M_loss_train = df_losses['Gen R2M Loss Train']\n    gen_M2R_loss_train = df_losses['Gen M2R Loss Train']\n    disc_real_loss_train = df_losses['Disc Real Loss Train']\n    disc_monet_loss_train = df_losses['Disc Monet Loss Train']\n    gen_R2M_loss_eval = df_losses['Gen R2M Loss Eval']\n    gen_M2R_loss_eval = df_losses['Gen M2R Loss Eval']\n    disc_real_loss_eval = df_losses['Disc Real Loss Eval']\n    disc_monet_loss_eval = df_losses['Disc Monet Loss Eval']\n\n    # Plot the data\n    plt.figure(figsize=(12, 8))\n\n    # Plot training loss\n    plt.plot(epochs, gen_R2M_loss_train, label='Gen R2M Loss Train', marker='o')\n    plt.plot(epochs, gen_M2R_loss_train, label='Gen M2R Loss Train', marker='o')\n    plt.plot(epochs, disc_real_loss_train, label='Disc Real Loss Train', marker='o')\n    plt.plot(epochs, disc_monet_loss_train, label='Disc Monet Loss Train', marker='o')\n\n    # Plot evaluation loss\n    plt.plot(epochs, gen_R2M_loss_eval, label='Gen R2M Loss Eval', marker='o')\n    plt.plot(epochs, gen_M2R_loss_eval, label='Gen M2R Loss Eval', marker='o')\n    plt.plot(epochs, disc_real_loss_eval, label='Disc Real Loss Eval', marker='o')\n    plt.plot(epochs, disc_monet_loss_eval, label='Disc Monet Loss Eval', marker='o')\n\n    # Add labels and title\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.title('Loss Over Epochs')\n    plt.legend()\n    plt.grid(True)\n\n    # Show plot\n    plt.show()","metadata":{"id":"4ESDNip4A1rU"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\ndef generate_images(generator, img):\n    # Add a batch dimension to the image, making it suitable for the generator model\n    img = np.expand_dims(img, axis=0)\n\n    # Generate an image using the provided generator model, then remove the batch dimension\n    generated_img = generator.predict(img)[0]\n\n    return generated_img\n\ndef plot_predictions(test_dataset, generator_monet2real, generator_real2monet):\n    # Take one batch from the test dataset\n    for real_batch, monet_batch in test_dataset.take(1):\n        # Decode and display the first real image from the batch\n        real_image = encode_image(real_batch[0])\n        plt.imshow(real_image)\n        plt.title(\"Real\")\n        plt.axis(\"off\")\n        plt.show(block=False)  # Display the image without blocking the execution\n\n        # Generate a Monet-style image from the real image and display it\n        monet_generate = generate_images(generator_real2monet, real_batch[0])\n        monet_generate_img = encode_image(monet_generate)\n        plt.imshow(monet_generate_img)\n        plt.title(\"Monet Generated\")\n        plt.axis(\"off\")\n        plt.show(block=False)\n\n        # Convert the Monet-generated image back to real style and display it\n        real_generate = generate_images(generator_monet2real, monet_generate)\n        real_generate_img = encode_image(real_generate)\n        plt.imshow(real_generate_img)\n        plt.title(\"Real Regenerated\")\n        plt.axis(\"off\")\n        plt.show(block=False)\n\n        # Print a separator line\n        print(\"-\" * 60)\n\n        # Decode and display the first Monet image from the batch\n        monet_image = encode_image(monet_batch[0])\n        plt.imshow(monet_image)\n        plt.title(\"Monet\")\n        plt.axis(\"off\")\n        plt.show(block=False)\n\n        # Generate a real-style image from the Monet image and display it\n        real_generate = generate_images(generator_monet2real, monet_batch[0])\n        real_generate_img = encode_image(real_generate)\n        plt.imshow(real_generate_img)\n        plt.title(\"Real Generated\")\n        plt.axis(\"off\")\n        plt.show(block=False)\n\n        # Convert the real-generated image back to Monet style and display it\n        monet_generate = generate_images(generator_real2monet, real_generate)\n        monet_generate_img = encode_image(monet_generate)\n        plt.imshow(monet_generate_img)\n        plt.title(\"Monet Regenerated\")\n        plt.axis(\"off\")\n        plt.show(block=False)\n","metadata":{"id":"pkXhjhukA1rV"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_losses_identity = pd.read_csv('log_lr_0.0001_beta_0.5_identity.csv')\nprint(df_losses_identity.head())\n\ndf_losses_pretrain_identity = pd.read_csv('log_lr_0.0001_beta_0.5_pretrain_identity.csv')\nprint(df_losses_pretrain_identity.head())\n\ndf_losses_pretrain = pd.read_csv('log_lr_0.0001_beta_0.5_pretrain.csv')\nprint(df_losses_pretrain.head())","metadata":{"id":"HcLfROhnA1rV","outputId":"29d7d32b-a44e-43fd-e3f9-402c2f7ad11a"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# loss plot","metadata":{"id":"_orqkI6vA1rV"}},{"cell_type":"code","source":"plot_losses(df_losses_identity)","metadata":{"id":"nHrp8IHQA1rV","outputId":"319f1270-98ec-4265-cedc-eeb4f8acc4ad"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_losses(df_losses_pretrain_identity)","metadata":{"id":"_E3e9vxPA1rV","outputId":"98473301-cbf4-4039-9f5b-7f277d880780"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_losses(df_losses_pretrain)","metadata":{"id":"ewbaskGHA1rV","outputId":"61cb0fa7-979d-40ea-ad99-6c4195704b21"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the epoch number of the best-performing model to use for loading weights.\nbest_epoch = 30\n\n# Specify the folder where the model weights are saved.\nmodels_folder = \"\"\n\n# Create instances of the Generator model for both real-to-Monet and Monet-to-real image translation.\ngenerator_real2monet, generator_monet2real = Generator(), Generator()\n\n# Load the weights of the best-performing model for the real-to-Monet generator from the specified epoch.\ngenerator_real2monet.load_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{best_epoch}.h5'))\n\n# Similarly, load the weights of the best-performing model for the Monet-to-real generator from the same epoch.\ngenerator_monet2real.load_weights(os.path.join(models_folder, f'generator_monet2real_epoch_{best_epoch}.h5'))\n\n# Print a statement indicating that predictions are being made using the models from the specified batch number.\nprint(\"identity batch 30\")\n\n# Visualize the predictions using the loaded generator models on a set of images from the test dataset.\nplot_predictions(test_dataset, generator_monet2real, generator_real2monet)\n","metadata":{"id":"Yu7uKEY1A1rW","outputId":"66634960-4471-49bd-81a2-9af36f9c5bfc"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the epoch number from which to load the trained model weights. This indicates the chosen \"best\" model.\nbest_epoch = 20\n\n# Specify the directory where the models trained with specific hyperparameters are saved.\nmodels_folder = \"\"\n\n# Initialize two Generator instances: one for converting real images to Monet style, and another for the reverse process.\ngenerator_real2monet, generator_monet2real = Generator(), Generator()\n\n# Load the weights into the real-to-Monet generator model from the specified epoch, indicating this model's parameters produced satisfactory results at this stage of training.\ngenerator_real2monet.load_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{best_epoch}.h5'))\n\n# Similarly, load the weights into the Monet-to-real generator model from the same epoch, ensuring consistency in performance evaluation.\ngenerator_monet2real.load_weights(os.path.join(models_folder, f'generator_monet2real_epoch_{best_epoch}.h5'))\n\n# Output to indicate that the predictions being made use the models from the specified epoch, here associated with a \"pretraining\" phase or strategy.\nprint(\"pretrain batch 20\")\n\n# Call the function to visualize predictions. This function demonstrates the generators' abilities by converting test dataset images between real and Monet styles and displaying these transformations.\nplot_predictions(test_dataset, generator_monet2real, generator_real2monet)\n","metadata":{"id":"3w1bexGWA1rW"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set the epoch number to identify the best model performance for loading the weights.\nbest_epoch = 20\n\n# Specify the directory where the trained models' weights are stored, indicating it's for a pre-trained model with identity loss.\nmodels_folder = \"models/lr_0.0001_beta_0.5_pretrain_identity\"\n\n# Initialize two Generator models, one for converting real images to Monet style and the other for Monet to real.\ngenerator_real2monet, generator_monet2real = Generator(), Generator()\n\n# Load the weights for the real-to-Monet generator from the specified best epoch within the given models folder.\ngenerator_real2monet.load_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{best_epoch}.h5'))\n\n# Load the weights for the Monet-to-real generator from the same epoch, indicating these models have been pre-trained with identity loss consideration.\ngenerator_monet2real.load_weights(os.path.join(models_folder, f'generator_monet2real_epoch_{best_epoch}.h5'))\n\n# Print a statement to signify that predictions are being made using the pre-trained models with identity loss at the 20th batch.\nprint(\"pretrain_identity batch 20\")\n\n# Display the predictions by converting images from the test dataset between real and Monet styles using the loaded generator models.\nplot_predictions(test_dataset, generator_monet2real, generator_real2monet)","metadata":{"id":"Kub_T9mhA1rW"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## evel best model","metadata":{"id":"BPGmxGW6A1rW"}},{"cell_type":"code","source":"# need to change all this","metadata":{"id":"xcb4L8cnA1rX"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_epoch = 30\nmodels_folder = \"\"\ngenerator_real2monet, generator_monet2real = Generator(),Generator()\ngenerator_real2monet.load_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{best_epoch}.h5'))\ngenerator_monet2real.load_weights(os.path.join(models_folder, f'generator_monet2real_epoch_{best_epoch}.h5'))\n","metadata":{"id":"-dJ0qpnsA1rX"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nfrom scipy.spatial.distance import cosine\n\n# Function to calculate FID\ndef calculate_fid(mu_real, sigma_real, mu_gen, sigma_gen):\n    fid = np.sum((mu_real - mu_gen)**2) + np.trace(sigma_real + sigma_gen - 2 * np.sqrt(np.dot(sigma_real, sigma_gen)))\n    return fid\n\n# Function to calculate memorization distance\ndef calculate_memorization_distance(real_samples, generated_samples):\n    distances = []\n    for gen_sample in generated_samples:\n        min_distance = np.inf\n        for real_sample in real_samples:\n            distance = cosine(gen_sample, real_sample)\n            if distance < min_distance:\n                min_distance = distance\n        distances.append(min_distance)\n    return np.mean(distances)","metadata":{"id":"APZyWAwfA1rX"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess_images_for_inception(images):\n    \"\"\"\n    Adjusts image sizes from 320x320 to 299x299 and scales pixel values to [-1, 1].\n\n    Parameters:\n    - images: TensorFlow tensor of images with shape (N, 320, 320, C) and pixel values scaled to [-1, 1].\n\n    Returns:\n    - TensorFlow tensor of processed images with shape (N, 299, 299, C).\n    \"\"\"\n    images_resized = tf.image.resize(images, (299, 299))\n    return images_resized","metadata":{"id":"nRQXrjz5A1rX"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"inception_model = tf.keras.applications.InceptionV3(include_top=False, pooling='avg')","metadata":{"id":"3IQ4BmLmA1rX","outputId":"30b9767d-b681-41a1-9fad-e68535fbb0eb"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Assuming `test_dataset` is already normalized to [-1, 1] and resized to 320x320\nreal_images_features_all = []\nmonet_images_features_all = []\ngenerated_monet_features_all = []\ngenerated_real_features_all = []\n\n# We use train data set since the test data is too small to calc FID\nfor real_batch, monet_batch in train_dataset:\n    # Preprocess images for InceptionV3\n    real_batch_processed = preprocess_images_for_inception(encode_image(real_batch))\n    monet_batch_processed = preprocess_images_for_inception(encode_image(monet_batch))\n\n    # Feature extraction without unnecessary encoding\n    real_images_features = inception_model.predict(real_batch_processed)\n    monet_images_features = inception_model.predict(monet_batch_processed)\n\n    # Assuming generator_real2monet and generator_monet2real output images normalized in [-1, 1]\n    generated_monet = generator_real2monet(real_batch, training=False)\n    generated_real = generator_monet2real(monet_batch, training=False)\n\n    # Preprocess generated images for InceptionV3\n    generated_monet_processed = preprocess_images_for_inception(encode_image(generated_monet))\n    generated_real_processed = preprocess_images_for_inception(encode_image(generated_real))\n\n    # Feature extraction for generated images\n    generated_monet_features = inception_model.predict(generated_monet_processed)\n    generated_real_features = inception_model.predict(generated_real_processed)\n\n    # Accumulate features for the entire dataset\n    real_images_features_all.append(real_images_features)\n    monet_images_features_all.append(monet_images_features)\n    generated_monet_features_all.append(generated_monet_features)\n    generated_real_features_all.append(generated_real_features)\n\n# Concatenate features from all batches\nreal_images_features_all = np.concatenate(real_images_features_all)\nmonet_images_features_all = np.concatenate(monet_images_features_all)\ngenerated_monet_features_all = np.concatenate(generated_monet_features_all)\ngenerated_real_features_all = np.concatenate(generated_real_features_all)","metadata":{"id":"fRgmRB56A1rX","outputId":"26aa24b9-e111-4b64-e5e1-e710ec15a438"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def calculate_fid(mu1, sigma1, mu2, sigma2, eps=1e-6):  # Increased epsilon\n    mu1 = np.atleast_1d(mu1)\n    mu2 = np.atleast_1d(mu2)\n\n    sigma1 = np.atleast_2d(sigma1)\n    sigma2 = np.atleast_2d(sigma2)\n\n    assert mu1.shape == mu2.shape, \"Mean vectors have different lengths\"\n    assert sigma1.shape == sigma2.shape, \"Covariance matrices have different dimensions\"\n\n    diff = mu1 - mu2\n\n    covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)\n    if not np.isfinite(covmean).all():\n        print(\"FID calculation produces singular product; adding epsilon to diagonals\")\n        offset = np.eye(sigma1.shape[0]) * eps\n        covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))\n\n    if np.iscomplexobj(covmean):\n        if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):\n            raise ValueError(f\"Imaginary component found with magnitude: {np.max(np.abs(covmean.imag))}\")\n        covmean = covmean.real\n\n    tr_covmean = np.trace(covmean)\n\n    return (diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean).real\n","metadata":{"id":"P_2PEOblA1rY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom scipy import linalg\n\n# Compute mean and covariance of real features\nmu1_real = np.mean(real_images_features_all, axis=0)\nsigma1_real = np.cov(real_images_features_all, rowvar=False)\n\n# Compute mean and covariance of generated features (Monet)\nmu2_generated_monet = np.mean(generated_monet_features_all, axis=0)\nsigma2_generated_monet = np.cov(generated_monet_features_all, rowvar=False)\n\n# Compute mean and covariance of Monet features\nmu1_monet = np.mean(monet_images_features_all, axis=0)\nsigma1_monet = np.cov(monet_images_features_all, rowvar=False)\n\n# Compute mean and covariance of generated features (Real)\nmu2_generated_real = np.mean(generated_real_features_all, axis=0)\nsigma2_generated_real = np.cov(generated_real_features_all, rowvar=False)\n\n# Calculate FID Scores\nfid_score_real_vs_generated_monet = calculate_fid(mu1_real, sigma1_real, mu2_generated_monet, sigma2_generated_monet)\nfid_score_monet_vs_generated_real = calculate_fid(mu1_monet, sigma1_monet, mu2_generated_real, sigma2_generated_real)\n\nprint(f'FID score (Real vs. Generated Monet): {fid_score_real_vs_generated_monet}')\nprint(f'FID score (Monet vs. Generated Real): {fid_score_monet_vs_generated_real}')","metadata":{"id":"Who5sEk5A1rY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"real_images_features_all = np.concatenate(real_images_features_all)\nmonet_images_features_all = np.concatenate(monet_images_features_all)\ngenerated_monet_features_all = np.concatenate(generated_monet_features_all)\ngenerated_real_features_all = np.concatenate(generated_real_features_all)","metadata":{"id":"eLpTgJuKA1rY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# MiFID calculation\ndef calculate_mifid(fid, generated_features, real_features, epsilon):\n    N = len(generated_features)\n    d = 0\n    for f_gi in generated_features:\n        min_distance = float('inf')\n        for f_rj in real_features:\n            cosine_distance = 1 - np.dot(f_gi, f_rj) / (np.linalg.norm(f_gi) * np.linalg.norm(f_rj))\n            min_distance = min(min_distance, cosine_distance)\n        d += min_distance\n    d /= N\n    if d > epsilon:\n        d = 1.0\n    mifid = fid * (1 / d)\n    return mifid\n","metadata":{"id":"MEb-b09RA1rY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mfid_score_real_vs_generated_monet = calculate_mifid(\n    fid_score_real_vs_generated_monet,\n    generated_monet_features_all,\n    real_images_features_all,\n    epsilon=0.1\n)","metadata":{"id":"AZlQCBl8A1rY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mfid_score_monet_vs_generated_real = calculate_mifid(\n    fid_score_monet_vs_generated_real,\n    generated_real_features_all,\n    monet_images_features_all,\n    epsilon=0.1\n)","metadata":{"id":"9SMGEj1SA1rZ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'FID score (Real vs. Generated Monet): {fid_score_real_vs_generated_monet}')\nprint(f'FID score (Monet vs. Generated Real): {fid_score_monet_vs_generated_real}')\nprint(f'MFID score (Real vs. Generated Monet): {mfid_score_real_vs_generated_monet}')\nprint(f'MFID score (Monet vs. Generated Real): {mfid_score_monet_vs_generated_real}')","metadata":{"id":"AHiQWJs8A1rZ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\nimage_path = 'mfid_eval_scores.png'\n\nimg = mpimg.imread(image_path)\n\n# Plot the image\nplt.imshow(img)\nplt.axis('off')  # Hide the axes\nplt.show()","metadata":{"id":"IPq6MINaA1rZ","outputId":"b7afdd5b-fd83-4bbb-8d13-2c31e3424890"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"ERqplpOCA1rZ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save Results to Kaggle","metadata":{"id":"Ic7fmsbjA1rZ"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\nimage_path = 'mfid_eval_scores.png'\n\nimg = mpimg.imread(image_path)\n\n# Plot the image\nplt.imshow(img)\nplt.axis('off')  # Hide the axes\nplt.show()","metadata":{"id":"9T19_-uMA1rZ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"CgVp7fTwA1ra"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save Results to Kaggle","metadata":{"id":"lQOXtwjwA1ra"}},{"cell_type":"code","source":"!mkdir ../images","metadata":{"id":"WbY7yjv8A1ra"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"directory_path = \"data/photo_jpg/\"\nimage_list = load_images_from_dir(directory_path)\nprint(\"Number of images loaded:\", len(image_list))","metadata":{"id":"ud5ABr0DA1ra"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_epoch = 30\nmodels_folder = \"models/lr_0.0001_beta_0.5_identity\"\ngenerator_real2monet = Generator()\ngenerator_real2monet.load_weights(os.path.join(models_folder, f'generator_real2monet_epoch_{best_epoch}.h5'))\n","metadata":{"id":"NB5bMrnSA1ra"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 1\nfor image in image_list:\n    real_image = decode_image(image)\n\n    monet_generate = generate_images(generator_real2monet , real_image)\n    monet_generate_img = encode_image(monet_generate).numpy()\n    monet_generate_img_fixed_size = cv2.resize(monet_generate_img, (256, 256))\n    # print(monet_generate_img_fixed_size.shape)\n    # plt.imshow(monet_generate_img_fixed_size)\n    # plt.title(\"Real\")\n    # plt.axis(\"off\")\n    # plt.show(block = False)\n\n    cv2.imwrite(f\"../images/{i}.jpg\", monet_generate_img_fixed_size)\n    i += 1\n","metadata":{"id":"v2WBgTAcA1ra"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images_folder_path = '../images'\nzip_filename = 'images.zip'\nshutil.make_archive(zip_filename.split('.')[0], 'zip', images_folder_path)\nprint(f\"Images folder has been successfully zipped to {zip_filename}.\")\n","metadata":{"id":"m2w1Zz3AA1ra"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}