{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.11","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"},{"sourceId":12951401,"sourceType":"datasetVersion","datasetId":8196374}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **Libraries**","metadata":{}},{"cell_type":"code","source":"# ========== Standard Libraries ==========\nimport os\nimport time\n\n# ========== Third-party Libraries ==========\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# ========== IPython Utilities ==========\nfrom IPython.display import clear_output\nfrom IPython.core.magic import register_cell_magic\n\n# ========== TensorFlow ==========\nimport tensorflow as tf\nimport tensorflow.image as tfi\n\n# TensorFlow performance setting\nAUTOTUNE = tf.data.AUTOTUNE\n\n# ========== Keras Layers & Models ==========\nfrom tensorflow.keras import layers, Model, Sequential\nfrom tensorflow.keras.layers import (\n    Layer,\n    Conv2D,\n    Conv2DTranspose,\n    ReLU,\n    Activation,\n    Lambda,\n    GroupNormalization,\n    InputSpec,\n)\n\n# ========== IPython Custom Magic ==========\n@register_cell_magic\ndef skip(line, cell):\n    pass  # Skip execution of cell","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:05.519186Z","iopub.execute_input":"2025-12-19T07:44:05.519448Z","iopub.status.idle":"2025-12-19T07:44:19.254865Z","shell.execute_reply.started":"2025-12-19T07:44:05.519428Z","shell.execute_reply":"2025-12-19T07:44:19.254249Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Preprocessing Configurations**","metadata":{}},{"cell_type":"code","source":"photo_path = \"/kaggle/input/gan-getting-started/photo_jpg\"\nmonet_path = \"/kaggle/input/gan-getting-started/monet_jpg\"\ntest_path = \"/kaggle/input/gan-getting-started/photo_jpg\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.255863Z","iopub.execute_input":"2025-12-19T07:44:19.256377Z","iopub.status.idle":"2025-12-19T07:44:19.260995Z","shell.execute_reply.started":"2025-12-19T07:44:19.256357Z","shell.execute_reply":"2025-12-19T07:44:19.259183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BUFFER_SIZE = 1000\nAUG_PER_IMAGE = 4\nBATCH_SIZE = 4\nRANDOM_SEED = 42","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.261734Z","iopub.execute_input":"2025-12-19T07:44:19.261976Z","iopub.status.idle":"2025-12-19T07:44:19.471059Z","shell.execute_reply.started":"2025-12-19T07:44:19.261942Z","shell.execute_reply":"2025-12-19T07:44:19.470282Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Data loading and Preprocessing**","metadata":{}},{"cell_type":"code","source":"def count_images(directory):\n    return sum(1 for file in os.scandir(directory) \n               if file.name.lower().endswith(('.jpg', '.jpeg', '.png')))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.472522Z","iopub.execute_input":"2025-12-19T07:44:19.472819Z","iopub.status.idle":"2025-12-19T07:44:19.486487Z","shell.execute_reply.started":"2025-12-19T07:44:19.472797Z","shell.execute_reply":"2025-12-19T07:44:19.485791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f'Total Photo images: {count_images(photo_path)}')\nprint(f'Total Monet images: {count_images(monet_path)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.487237Z","iopub.execute_input":"2025-12-19T07:44:19.487503Z","iopub.status.idle":"2025-12-19T07:44:19.589757Z","shell.execute_reply.started":"2025-12-19T07:44:19.487480Z","shell.execute_reply":"2025-12-19T07:44:19.588951Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Load and preprocessing function**","metadata":{}},{"cell_type":"code","source":"def load_and_preprocess(file_path, augmentations_per_image=AUG_PER_IMAGE, seed=RANDOM_SEED):\n    image = tf.io.read_file(file_path)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [286, 286])  # Resize before cropping\n\n    def augment_fn(i):\n        aug_seed = tf.stack([seed, i])\n        aug = tf.image.stateless_random_crop(image, [256, 256, 3], seed=aug_seed)\n        aug = tf.image.stateless_random_flip_left_right(aug, seed=aug_seed + 1)\n        aug = tf.cast(aug, tf.float32) / 127.5 - 1.0\n        return aug\n\n    if augmentations_per_image > 0:\n        return tf.data.Dataset.range(augmentations_per_image).map(augment_fn)\n    else:\n        # Just return the resized + normalized image as a dataset\n        image = tf.image.resize(image, [256, 256])\n        image = tf.cast(image, tf.float32) / 127.5 - 1.0\n        return tf.data.Dataset.from_tensors(image)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.590989Z","iopub.execute_input":"2025-12-19T07:44:19.591573Z","iopub.status.idle":"2025-12-19T07:44:19.596933Z","shell.execute_reply.started":"2025-12-19T07:44:19.591549Z","shell.execute_reply":"2025-12-19T07:44:19.596104Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Create datasets of CT and MRI**","metadata":{}},{"cell_type":"code","source":"#tf_monet = tf.data.Dataset.list_files(monet_path + \"/*.jpg\", shuffle=True)\n#tf_photos = tf.data.Dataset.list_files(photos_path + \"/*.jpg\", shuffle=True)\n\ntf_photo = tf.data.Dataset.list_files(photo_path + \"/*.jpg\", shuffle=False)\ntf_monet = tf.data.Dataset.list_files(monet_path + \"/*.jpg\", shuffle=False)\ntf_test = tf.data.Dataset.list_files(test_path + \"/*.jpg\", shuffle=False)\n\n#Clone\ntf_monet = tf_monet.repeat(20)\n\nphoto_dataset = (\n    tf_photo\n    .flat_map(lambda x: load_and_preprocess(x, seed=RANDOM_SEED))\n    .shuffle(BUFFER_SIZE, seed=RANDOM_SEED)\n    .batch(BATCH_SIZE)\n    .prefetch(tf.data.AUTOTUNE)\n)\n\nmonet_dataset = (\n    tf_monet\n    .flat_map(lambda x: load_and_preprocess(x, seed=RANDOM_SEED))\n    .shuffle(BUFFER_SIZE, seed=RANDOM_SEED)\n    .batch(BATCH_SIZE)\n    .prefetch(tf.data.AUTOTUNE)\n)\n\ntest_dataset = (\n    tf_test\n    .flat_map(lambda x: load_and_preprocess(x, seed = RANDOM_SEED,augmentations_per_image = 0))\n    .batch(BATCH_SIZE)\n    .prefetch(tf.data.AUTOTUNE)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:19.597753Z","iopub.execute_input":"2025-12-19T07:44:19.598027Z","iopub.status.idle":"2025-12-19T07:44:27.795923Z","shell.execute_reply.started":"2025-12-19T07:44:19.597995Z","shell.execute_reply":"2025-12-19T07:44:27.795336Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Sinusoidal Time Embedding**","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers\n\n# 1. Time Embedding (Helps model know \"how noisy\" the image is)\ndef sinusoidal_embedding(x, embedding_min_frequency=1.0, embedding_max_frequency=1000.0, embedding_dims=32):\n    frequencies = tf.exp(\n        tf.linspace(\n            tf.math.log(embedding_min_frequency),\n            tf.math.log(embedding_max_frequency),\n            embedding_dims // 2\n        )\n    )\n    angular_speeds = 2.0 * 3.14159 * frequencies\n    embeddings = tf.concat(\n        [tf.sin(angular_speeds * x), tf.cos(angular_speeds * x)], axis=3\n    )\n    return embeddings\n\n# 2. Residual Block for Diffusion\ndef ResidualBlock(width):\n    def apply(x):\n        input_width = x.shape[3]\n        if input_width == width:\n            residual = x\n        else:\n            residual = layers.Conv2D(width, kernel_size=1)(x)\n        x = layers.BatchNormalization(center=False, scale=False)(x)\n        x = layers.Conv2D(width, kernel_size=3, padding=\"same\", activation=\"swish\")(x)\n        x = layers.Conv2D(width, kernel_size=3, padding=\"same\")(x)\n        return layers.Add()([x, residual])\n    return apply\n\n# 3. Downsample and Upsample Blocks\ndef DownBlock(width, block_depth):\n    def apply(x):\n        x, skips = x\n        for _ in range(block_depth):\n            x = ResidualBlock(width)(x)\n            skips.append(x)\n        x = layers.AveragePooling2D(pool_size=2)(x)\n        return x, skips\n    return apply\n\ndef UpBlock(width, block_depth):\n    def apply(x):\n        x, skips = x\n        x = layers.UpSampling2D(size=2, interpolation=\"bilinear\")(x)\n        for _ in range(block_depth):\n            x = layers.Concatenate()([x, skips.pop()])\n            x = ResidualBlock(width)(x)\n        return x, skips\n    return apply\n\n# 4. The Main Diffusion Model\ndef build_diffusion_model(image_size=256, widths=[32, 64, 96, 128], block_depth=2):\n    images = layers.Input(shape=(image_size, image_size, 3))\n    # Time input is just a scalar (e.g., 0.5 for 50% noise)\n    noise_variances = layers.Input(shape=(1, 1, 1))\n\n    # Embed time\n    e = layers.Lambda(sinusoidal_embedding)(noise_variances)\n    e = layers.UpSampling2D(size=image_size, interpolation=\"nearest\")(e)\n\n    # Combine Image + Time\n    x = layers.Concatenate()([images, e])\n    x = layers.Conv2D(widths[0], kernel_size=1)(x)\n\n    skips = []\n    \n    # Downsampling (Encoder)\n    for width in widths[:-1]:\n        x, skips = DownBlock(width, block_depth)([x, skips])\n\n    # Bottleneck\n    for _ in range(block_depth):\n        x = ResidualBlock(widths[-1])(x)\n\n    # Upsampling (Decoder)\n    for width in reversed(widths[:-1]):\n        x, skips = UpBlock(width, block_depth)([x, skips])\n\n    # Output: Predict the NOISE, not the image\n    x = layers.Conv2D(3, kernel_size=1, kernel_initializer=\"zeros\")(x)\n\n    return tf.keras.Model([images, noise_variances], x, name=\"diffusion_model\")\n\n# Create the model\ndiffusion_model = build_diffusion_model()\nprint(\"Diffusion Model Built!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:27.796683Z","iopub.execute_input":"2025-12-19T07:44:27.796875Z","iopub.status.idle":"2025-12-19T07:44:28.906294Z","shell.execute_reply.started":"2025-12-19T07:44:27.796857Z","shell.execute_reply":"2025-12-19T07:44:28.905597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DiffusionTrainer(tf.keras.Model):\n    def __init__(self, network, batch_size=4):\n        super().__init__()\n        self.network = network\n        self.batch_size = batch_size\n        \n        # Hyperparameters for diffusion\n        self.min_signal_rate = 0.02\n        self.max_signal_rate = 0.95\n        \n        # Loss tracker\n        self.loss_tracker = tf.keras.metrics.Mean(name=\"loss\")\n\n    @property\n    def metrics(self):\n        return [self.loss_tracker]\n\n    def train_step(self, images):\n        # 1. Sample random noise\n        noises = tf.random.normal(shape=tf.shape(images), dtype=images.dtype)\n        \n        # 2. Sample random diffusion times (0 to 1)\n        diffusion_times = tf.random.uniform(\n            shape=(self.batch_size, 1, 1, 1), minval=0.0, maxval=1.0\n        )\n        \n        # 3. Calculate signal and noise rates (linear schedule)\n        # signal_rate = sqrt(alpha_bar), noise_rate = sqrt(1 - alpha_bar)\n        signal_rates = self.max_signal_rate - diffusion_times * (self.max_signal_rate - self.min_signal_rate)\n        noise_rates = tf.sqrt(1.0 - signal_rates**2)\n        \n        # 4. Create noisy images: x_t = sqrt(alpha)*x_0 + sqrt(1-alpha)*epsilon\n        noisy_images = signal_rates * images + noise_rates * noises\n        \n        # 5. Train model to predict the NOISE\n        with tf.GradientTape() as tape:\n            pred_noises = self.network([noisy_images, noise_rates**2], training=True)\n            # Simple MSE Loss: Actual Noise vs Predicted Noise\n            loss = tf.reduce_mean(tf.reduce_mean(tf.square(noises - pred_noises), axis=[1, 2, 3]))\n\n        gradients = tape.gradient(loss, self.network.trainable_weights)\n        self.optimizer.apply_gradients(zip(gradients, self.network.trainable_weights))\n\n        self.loss_tracker.update_state(loss)\n        return {\"loss\": self.loss_tracker.result()}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:28.906979Z","iopub.execute_input":"2025-12-19T07:44:28.907183Z","iopub.status.idle":"2025-12-19T07:44:28.914191Z","shell.execute_reply.started":"2025-12-19T07:44:28.907167Z","shell.execute_reply":"2025-12-19T07:44:28.913406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Instantiate\ndiffusion_trainer = DiffusionTrainer(diffusion_model)\ndiffusion_trainer.compile(optimizer=tf.keras.optimizers.Adamax(learning_rate=1e-3))\n\n# Train ONLY on Monet Dataset\n# Note: repeat() is important because Monet dataset is small (300 imgs)\ndiffusion_trainer.fit(\n    monet_dataset,  # Repeat to match Photo dataset size roughly\n    epochs=15,                 # Needs more epochs than GAN\n    batch_size=4\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T07:44:28.916203Z","iopub.execute_input":"2025-12-19T07:44:28.916439Z","iopub.status.idle":"2025-12-19T08:09:26.736866Z","shell.execute_reply.started":"2025-12-19T07:44:28.916423Z","shell.execute_reply":"2025-12-19T08:09:26.734937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def reverse_diffusion(model, initial_image, diffusion_steps=20, start_step=10):\n    \"\"\"\n    SDEdit: Starts from a partially noisy image and denoises it.\n    start_step: How much noise to add? \n                0 = No change (output is original photo)\n                20 = Pure noise (output is random Monet)\n                10 = Balanced (Structure of Photo, Texture of Monet)\n    \"\"\"\n    batch_size = tf.shape(initial_image)[0]\n    step_size = 1.0 / diffusion_steps\n    \n    # 1. Add partial noise to the Photo (The \"Editing\" part)\n    # We start at 'start_step' (e.g., t=0.5) instead of t=1.0\n    initial_diffusion_time = start_step * step_size\n    \n    signal_rate = 0.95 - initial_diffusion_time * (0.95 - 0.02)\n    noise_rate = tf.sqrt(1.0 - signal_rate**2)\n    \n    noise = tf.random.normal(shape=tf.shape(initial_image))\n    current_images = signal_rate * initial_image + noise_rate * noise # Noisy Photo\n    \n    # 2. Denoise loop (The \"Monet\" part)\n    for step in range(start_step, 0, -1):\n        diffusion_time = step * step_size\n        \n        # Calculate rates for current step\n        next_diffusion_time = (step - 1) * step_size\n        current_signal_rate = 0.95 - diffusion_time * (0.95 - 0.02)\n        next_signal_rate = 0.95 - next_diffusion_time * (0.95 - 0.02)\n        \n        current_noise_rate = tf.sqrt(1.0 - current_signal_rate**2)\n        next_noise_rate = tf.sqrt(1.0 - next_signal_rate**2)\n        \n        # Predict noise using the Monet Model\n        pred_noises = model([current_images, tf.fill([batch_size, 1, 1, 1], current_noise_rate**2)], training=False)\n        \n        # Estimate clean image (x0)\n        pred_images = (current_images - current_noise_rate * pred_noises) / current_signal_rate\n        \n        # Move to next step (x_t-1)\n        current_images = next_signal_rate * pred_images + next_noise_rate * pred_noises\n        \n    return pred_images","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T08:10:31.345189Z","iopub.execute_input":"2025-12-19T08:10:31.345960Z","iopub.status.idle":"2025-12-19T08:10:31.353041Z","shell.execute_reply.started":"2025-12-19T08:10:31.345935Z","shell.execute_reply":"2025-12-19T08:10:31.352234Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- VISUALIZE MULTIPLE IMAGES ---\n\ndef visualize_transformations(model, dataset, num_images=5, start_step=10):\n    plt.figure(figsize=(10, num_images * 4))\n    \n    # Take one batch of 'num_images' (or take multiple batches if batch size is small)\n    for i, photo_batch in enumerate(dataset.take(1)):\n        # If your batch size is smaller than num_images, you might need to adjust logic\n        # Assuming batch size >= num_images for simplicity, or we just show what fits\n        \n        # 1. Generate Monet versions\n        monet_batch = reverse_diffusion(model, photo_batch, diffusion_steps=20, start_step=start_step)\n        \n        # 2. Loop through images in the batch\n        for j in range(min(num_images, len(photo_batch))):\n            \n            # Original Photo\n            plt.subplot(num_images, 2, 2*j + 1)\n            plt.title(\"Original Photo\")\n            plt.imshow((photo_batch[j] + 1) / 2) # Deprocess from [-1, 1] to [0, 1]\n            plt.axis(\"off\")\n            \n            # Monet Diffusion Result\n            plt.subplot(num_images, 2, 2*j + 2)\n            plt.title(f\"SDEdit (Step {start_step})\")\n            plt.imshow((monet_batch[j] + 1) / 2)\n            plt.axis(\"off\")\n            \n    plt.tight_layout()\n    plt.show()\n\n# Run the visualization\n# Tip: Try different 'start_step' values to see how much structure is preserved vs. style added\nprint(\"Generating visualizations with start_step=10 (Balanced)...\")\nvisualize_transformations(diffusion_model, photo_dataset, num_images=5, start_step=10)\n\n# Optional: Run again with higher noise to see more abstract/artistic results\nprint(\"Generating visualizations with start_step=5 (Less Abstract)...\")\nvisualize_transformations(diffusion_model, photo_dataset, num_images=5, start_step=5)\n\nprint(\"Generating visualizations with start_step=15 (More Abstract)...\")\nvisualize_transformations(diffusion_model, photo_dataset, num_images=5, start_step=15)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T08:10:35.045145Z","iopub.execute_input":"2025-12-19T08:10:35.045425Z","iopub.status.idle":"2025-12-19T08:10:50.307630Z","shell.execute_reply.started":"2025-12-19T08:10:35.045403Z","shell.execute_reply":"2025-12-19T08:10:50.306247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport shutil\nimport numpy as np\nfrom PIL import Image\n\n# 1. Setup paths\noutput_directory = 'images'\nif os.path.exists(output_directory):\n    shutil.rmtree(output_directory)  # Clean up if exists\nos.makedirs(output_directory)\n\nprint(\"Starting generation...\")\n\n# 2. Iterate through the Photo Dataset\nMAX_IMAGES = 10000  # Safety limit\nimg_count = 1\n\nfor batch_idx, photo_batch in enumerate(photo_dataset):\n    \n    # Check if we have already reached the limit before processing\n    if img_count > MAX_IMAGES:\n        print(f\"Reached maximum limit of {MAX_IMAGES} images. Stopping.\")\n        break\n\n    # A. Run SDEdit (The \"Translation\")\n    # start_step=10 keeps ~50% structure. Adjust if needed.\n    generated_batch = reverse_diffusion(diffusion_model, photo_batch, diffusion_steps=20, start_step=10)\n    \n    # B. Deprocess images\n    # Diffusion output is [-1, 1], convert to [0, 255] uint8\n    generated_batch = (generated_batch * 0.5 + 0.5) * 255\n    generated_batch = generated_batch.numpy().astype(np.uint8)\n    \n    # C. Save each image in the batch\n    for i in range(len(generated_batch)):\n        if img_count > MAX_IMAGES:\n            break\n            \n        img = Image.fromarray(generated_batch[i])\n        img.save(os.path.join(output_directory, f\"{img_count}.jpg\"))\n        img_count += 1\n    \n    # Print progress every 100 batches\n    if batch_idx % 100 == 0:\n        print(f\"Generated {img_count-1} images...\")\n\nprint(f\"Generation complete. Total images: {img_count-1}\")\n\n# 3. Zip the folder for submission\nprint(\"Zipping images...\")\nshutil.make_archive('images', 'zip', 'images')\nprint(\"images.zip created successfully!\")\n\n# 4. Remove the folder to save space (optional, Kaggle sometimes runs out of disk space)\n# shutil.rmtree(output_directory)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T08:12:07.886105Z","iopub.execute_input":"2025-12-19T08:12:07.886453Z","iopub.status.idle":"2025-12-19T08:12:14.654657Z","execution_failed":"2025-12-19T08:29:09.686Z"}},"outputs":[],"execution_count":null}]}