{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"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":"nvidiaTeslaT4","dataSources":[{"sourceId":9825091,"sourceType":"datasetVersion","datasetId":6025001}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# TensorFlow and Keras Imports\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, models, metrics\nfrom tensorflow.keras.utils import Sequence\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom tensorflow.keras import optimizers\n\n# Image Processing Libraries\nimport cv2\nfrom cv2 import imread,resize\nfrom scipy.ndimage import label, find_objects\n\n# Data Handling Libraries\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\n\n# Visualization Libraries\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\n\n# File and Operating System Libraries\nimport os\n\n# Warnings Management\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# GPU Configuration\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'\nprint(tf.config.list_physical_devices('GPU'))\n\n# PATHS\nIMG_PATH = '/kaggle/input/chest-x-ray-lungs-segmentation/Chest-X-Ray/Chest-X-Ray/image/'\nMSK_PATH = '/kaggle/input/chest-x-ray-lungs-segmentation/Chest-X-Ray/Chest-X-Ray/mask/'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:16.207332Z","iopub.execute_input":"2025-11-29T07:26:16.207654Z","iopub.status.idle":"2025-11-29T07:26:34.097147Z","shell.execute_reply.started":"2025-11-29T07:26:16.207627Z","shell.execute_reply":"2025-11-29T07:26:34.096310Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    # Use MirroredStrategy to utilize both T4 GPUs\n    strategy = tf.distribute.MirroredStrategy()\n    print('Number of devices: {}'.format(strategy.num_replicas_in_sync))\nexcept Exception as e:\n    print(e)\n    # Fallback to default strategy if something goes wrong\n    strategy = tf.distribute.get_strategy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:34.097958Z","iopub.execute_input":"2025-11-29T07:26:34.098577Z","iopub.status.idle":"2025-11-29T07:26:34.403746Z","shell.execute_reply.started":"2025-11-29T07:26:34.098552Z","shell.execute_reply":"2025-11-29T07:26:34.402802Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metadata = pd.read_csv('/kaggle/input/chest-x-ray-lungs-segmentation/MetaData.csv')\nmetadata.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:34.405714Z","iopub.execute_input":"2025-11-29T07:26:34.405969Z","iopub.status.idle":"2025-11-29T07:26:34.465202Z","shell.execute_reply.started":"2025-11-29T07:26:34.405949Z","shell.execute_reply":"2025-11-29T07:26:34.464362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metadata.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:34.465998Z","iopub.execute_input":"2025-11-29T07:26:34.466212Z","iopub.status.idle":"2025-11-29T07:26:34.487389Z","shell.execute_reply.started":"2025-11-29T07:26:34.466195Z","shell.execute_reply":"2025-11-29T07:26:34.486749Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_colored_mask(image, mask_image,color = [255,20,255]):\n    mask_image_gray = cv2.cvtColor(mask_image, cv2.COLOR_BGR2GRAY)\n    mask = cv2.bitwise_and(mask_image, mask_image, mask=mask_image_gray)\n    mask_coord = np.where(mask!=[0,0,0])\n    mask[mask_coord[0],mask_coord[1],:]= color\n    ret = cv2.addWeighted(image, 0.6, mask, 0.4, 0)\n    return ret\n    \nfilenames = next(os.walk(IMG_PATH))[2][:3]\nfor file in filenames:\n    img = imread(IMG_PATH+file)\n    msk = imread(MSK_PATH+file)\n\n    plt.figure(figsize=(15,5))\n\n    plt.subplot(131)\n    plt.imshow(img)\n    plt.yticks([])\n    plt.xticks([])\n    plt.box(False)\n    \n    plt.subplot(132)\n    plt.imshow(msk,cmap='binary_r')\n    plt.yticks([])\n    plt.xticks([])\n    plt.box(False)\n\n    plt.subplot(133)\n    plt.imshow(get_colored_mask(img,msk))\n    plt.yticks([])\n    plt.xticks([])\n    plt.box(False)\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:34.488305Z","iopub.execute_input":"2025-11-29T07:26:34.488658Z","iopub.status.idle":"2025-11-29T07:26:47.565703Z","shell.execute_reply.started":"2025-11-29T07:26:34.488629Z","shell.execute_reply":"2025-11-29T07:26:47.564947Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"GLOBAL_BATCH_SIZE = 4 * strategy.num_replicas_in_sync\nIMAGE_SIZE = 512\n\n# 1. Define the augmentation layers\n# Geometric augmentations are grouped to be applied to image and mask together\ndata_augmentation_geometric = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.1),\n    layers.RandomZoom(height_factor=0.1, width_factor=0.1),\n], name=\"geometric_augmentation\")\n\n# Color/contrast augmentations are applied only to the image\ndata_augmentation_color = tf.keras.Sequential([\n    layers.RandomContrast(0.1),\n], name=\"color_augmentation\")\n\n\n# 2. Create a function to load and parse the image and mask files\ndef parse_image_mask(img_path, msk_path):\n    # Read and decode the image\n    img = tf.io.read_file(IMG_PATH + img_path)\n    img = tf.image.decode_png(img, channels=1)\n    img = tf.image.resize(img, [IMAGE_SIZE, IMAGE_SIZE])\n    img = tf.cast(img, tf.float32) / 255.0\n\n    # Read and decode the mask\n    msk = tf.io.read_file(MSK_PATH + msk_path)\n    msk = tf.image.decode_png(msk, channels=1)\n    msk = tf.image.resize(msk, [IMAGE_SIZE, IMAGE_SIZE])\n    msk = tf.cast(msk, tf.float32) / 255.0\n    msk = tf.where(msk > 0.5, 1.0, 0.0)  # Binarize the mask\n\n    return img, msk\n\n# 3. Create a function that applies the augmentations\ndef augment_data(image, mask):\n    # Stack the image and mask to apply geometric transformations simultaneously\n    stacked_data = tf.concat([image, mask], axis=-1)\n    \n    # Apply geometric augmentations (flip, rotation, zoom)\n    augmented_stacked = data_augmentation_geometric(stacked_data)\n    \n    # Unstack the image and mask\n    augmented_image = augmented_stacked[..., :1]\n    augmented_mask = augmented_stacked[..., 1:]\n    \n    # Apply color-based augmentations (contrast) ONLY to the image\n    augmented_image = data_augmentation_color(augmented_image)\n    \n    return augmented_image, augmented_mask\n\n# 4. Build the complete tf.data pipeline\ndef create_dataset(img_files, msk_files, augment=False):\n    dataset = tf.data.Dataset.from_tensor_slices((img_files, msk_files))\n    dataset = dataset.shuffle(len(img_files))\n    dataset = dataset.map(parse_image_mask, num_parallel_calls=tf.data.AUTOTUNE)\n    \n    # Apply augmentation only to the training dataset\n    if augment:\n        dataset = dataset.map(augment_data, num_parallel_calls=tf.data.AUTOTUNE)\n        \n    dataset = dataset.batch(GLOBAL_BATCH_SIZE)\n    dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE) # Optimizes performance\n    return dataset\n\n# Get your file lists (you already have this code)\nimg_files, msk_files = sorted(os.listdir(IMG_PATH)), sorted(os.listdir(MSK_PATH))\ntrain_img_files, rest_img_files, train_msk_files, rest_msk_files = train_test_split(\n    img_files, msk_files, test_size=0.30, random_state=1)\nval_img_files, test_img_files, val_msk_files, test_msk_files = train_test_split(\n    rest_img_files, rest_msk_files, test_size=0.5, random_state=1)\n\n# Create the training and validation datasets\n# This replaces your DataGenerator instantiation\ntrain_data = create_dataset(train_img_files, train_msk_files, augment=True)\nval_data = create_dataset(val_img_files, val_msk_files, augment=False)\ntest_data = create_dataset(test_img_files, test_msk_files, augment=False)\n\ndef map_for_deep_supervision(img, msk):\n    \"\"\"\n    Duplicates the mask to match the 4 outputs of the UNet++ model.\n    Keras needs a dictionary that matches the output layer names.\n    \"\"\"\n    outputs = {\n        'output_1': msk, \n        'output_2': msk, \n        'output_3': msk, \n        'output_4': msk\n    }\n    return img, outputs\n\n# Create new datasets for the UNet++ model (model1)\ntrain_data_plus = train_data.map(map_for_deep_supervision, num_parallel_calls=tf.data.AUTOTUNE)\nval_data_plus = val_data.map(map_for_deep_supervision, num_parallel_calls=tf.data.AUTOTUNE)\n\n# Prefetch for performance (already done in create_dataset, but good practice to keep)\ntrain_data_plus = train_data_plus.prefetch(buffer_size=tf.data.AUTOTUNE)\nval_data_plus = val_data_plus.prefetch(buffer_size=tf.data.AUTOTUNE)\n\ntest_data_plus = test_data.map(map_for_deep_supervision, num_parallel_calls=tf.data.AUTOTUNE)\ntest_data_plus = test_data_plus.prefetch(buffer_size=tf.data.AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:47.566603Z","iopub.execute_input":"2025-11-29T07:26:47.566842Z","iopub.status.idle":"2025-11-29T07:26:48.190122Z","shell.execute_reply.started":"2025-11-29T07:26:47.566823Z","shell.execute_reply":"2025-11-29T07:26:48.189456Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def conv_block(inputs, num_filters):\n    \"\"\"Convolutional Block\"\"\"\n    x = layers.Conv2D(num_filters, 3, padding=\"same\")(inputs)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n\n    x = layers.Conv2D(num_filters, 3, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    return x\n\ndef unet_plus_plus(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 1)):\n    \"\"\"\n    Builds the U-Net++ model.\n    The architecture consists of a nested and dense skip-pathway structure.\n    -- NOTE: Number of filters has been halved to reduce memory usage. --\n    \"\"\"\n    inputs = layers.Input(input_shape)\n    \n    # Define filter numbers\n    f = [32, 64, 128, 256, 512]\n\n    # --- Encoder ---\n    # Level 0\n    x0_0 = conv_block(inputs, f[0])\n    p0 = layers.MaxPooling2D((2, 2))(x0_0)\n\n    # Level 1\n    x1_0 = conv_block(p0, f[1])\n    p1 = layers.MaxPooling2D((2, 2))(x1_0)\n\n    # Level 2\n    x2_0 = conv_block(p1, f[2])\n    p2 = layers.MaxPooling2D((2, 2))(x2_0)\n\n    # Level 3\n    x3_0 = conv_block(p2, f[3])\n    p3 = layers.MaxPooling2D((2, 2))(x3_0)\n\n    # Bottleneck (Level 4)\n    x4_0 = conv_block(p3, f[4])\n\n    # --- Decoder with Nested Skip Pathways ---\n    # Level 0, 1\n    u1_0 = layers.UpSampling2D((2, 2))(x1_0)\n    x0_1 = layers.concatenate([u1_0, x0_0])\n    x0_1 = conv_block(x0_1, f[0])\n\n    # Level 1, 1\n    u2_0 = layers.UpSampling2D((2, 2))(x2_0)\n    x1_1 = layers.concatenate([u2_0, x1_0])\n    x1_1 = conv_block(x1_1, f[1])\n\n    # Level 0, 2\n    u1_1 = layers.UpSampling2D((2, 2))(x1_1)\n    x0_2 = layers.concatenate([u1_1, x0_0, x0_1])\n    x0_2 = conv_block(x0_2, f[0])\n\n    # Level 2, 1\n    u3_0 = layers.UpSampling2D((2, 2))(x3_0)\n    x2_1 = layers.concatenate([u3_0, x2_0])\n    x2_1 = conv_block(x2_1, f[2])\n\n    # Level 1, 2\n    u2_1 = layers.UpSampling2D((2, 2))(x2_1)\n    x1_2 = layers.concatenate([u2_1, x1_0, x1_1])\n    x1_2 = conv_block(x1_2, f[1])\n\n    # Level 0, 3\n    u1_2 = layers.UpSampling2D((2, 2))(x1_2)\n    x0_3 = layers.concatenate([u1_2, x0_0, x0_1, x0_2])\n    x0_3 = conv_block(x0_3, f[0])\n\n    # Level 3, 1\n    u4_0 = layers.UpSampling2D((2, 2))(x4_0)\n    x3_1 = layers.concatenate([u4_0, x3_0])\n    x3_1 = conv_block(x3_1, f[3])\n\n    # Level 2, 2\n    u3_1 = layers.UpSampling2D((2, 2))(x3_1)\n    x2_2 = layers.concatenate([u3_1, x2_0, x2_1])\n    x2_2 = conv_block(x2_2, f[2])\n\n    # Level 1, 3\n    u2_2 = layers.UpSampling2D((2, 2))(x2_2)\n    x1_3 = layers.concatenate([u2_2, x1_0, x1_1, x1_2])\n    x1_3 = conv_block(x1_3, f[1])\n\n    # Level 0, 4\n    u1_3 = layers.UpSampling2D((2, 2))(x1_3)\n    x0_4 = layers.concatenate([u1_3, x0_0, x0_1, x0_2, x0_3])\n    x0_4 = conv_block(x0_4, f[0])\n\n    # --- Output Layer ---\n    # We use a 1x1 convolution with a sigmoid activation for binary segmentation.\n    output1 = layers.Conv2D(1, 1, activation=\"sigmoid\", name=\"output_1\")(x0_1)\n    output2 = layers.Conv2D(1, 1, activation=\"sigmoid\", name=\"output_2\")(x0_2)\n    output3 = layers.Conv2D(1, 1, activation=\"sigmoid\", name=\"output_3\")(x0_3)\n    output4 = layers.Conv2D(1, 1, activation=\"sigmoid\", name=\"output_4\")(x0_4)\n\n    # Create the model with four outputs\n    model = models.Model(inputs=inputs, outputs=[output1, output2, output3, output4], name=\"UNet_Plus_Plus\")\n    return model\n\nmodel = unet_plus_plus()\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:48.191040Z","iopub.execute_input":"2025-11-29T07:26:48.191275Z","iopub.status.idle":"2025-11-29T07:26:50.051119Z","shell.execute_reply.started":"2025-11-29T07:26:48.191256Z","shell.execute_reply":"2025-11-29T07:26:50.050180Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def unet_model(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 1)):\n    \"\"\"\n    Builds the standard U-Net model.\n    \"\"\"\n    inputs = layers.Input(input_shape)\n    \n    # Define filter numbers (same as U-Net++)\n    f = [32, 64, 128, 256, 512]\n\n    # --- Encoder ---\n    # Level 0\n    c1 = conv_block(inputs, f[0])\n    p1 = layers.MaxPooling2D((2, 2))(c1)\n\n    # Level 1\n    c2 = conv_block(p1, f[1])\n    p2 = layers.MaxPooling2D((2, 2))(c2)\n\n    # Level 2\n    c3 = conv_block(p2, f[2])\n    p3 = layers.MaxPooling2D((2, 2))(c3)\n\n    # Level 3\n    c4 = conv_block(p3, f[3])\n    p4 = layers.MaxPooling2D((2, 2))(c4)\n\n    # Bottleneck (Level 4)\n    b = conv_block(p4, f[4])\n\n    # --- Decoder ---\n    # Level 3\n    u6 = layers.UpSampling2D((2, 2))(b)\n    s6 = layers.concatenate([u6, c4]) # Skip connection\n    c6 = conv_block(s6, f[3])\n\n    # Level 2\n    u7 = layers.UpSampling2D((2, 2))(c6)\n    s7 = layers.concatenate([u7, c3]) # Skip connection\n    c7 = conv_block(s7, f[2])\n\n    # Level 1\n    u8 = layers.UpSampling2D((2, 2))(c7)\n    s8 = layers.concatenate([u8, c2]) # Skip connection\n    c8 = conv_block(s8, f[1])\n\n    # Level 0\n    u9 = layers.UpSampling2D((2, 2))(c8)\n    s9 = layers.concatenate([u9, c1]) # Skip connection\n    c9 = conv_block(s9, f[0])\n\n    # --- Output Layer ---\n    outputs = layers.Conv2D(1, 1, activation=\"sigmoid\")(c9)\n\n    model = models.Model(inputs, outputs, name=\"Standard_UNet\")\n    return model\n\n# Instantiate the model and print its summary\nmodel_unet = unet_model()\nmodel_unet.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:50.051978Z","iopub.execute_input":"2025-11-29T07:26:50.052204Z","iopub.status.idle":"2025-11-29T07:26:50.398702Z","shell.execute_reply.started":"2025-11-29T07:26:50.052185Z","shell.execute_reply":"2025-11-29T07:26:50.398064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\ndef jaccard_index(y_true, y_pred, smooth=100):\n    \"\"\"Calculates the Jaccard index (IoU), useful for evaluating the model's performance.\"\"\"\n    y_true_f = tf.reshape(tf.cast(y_true, tf.float32), [-1])  # Flatten and cast ground truth\n    y_pred_f = tf.reshape(tf.cast(y_pred, tf.float32), [-1])  # Flatten and cast predictions\n    intersection = tf.reduce_sum(y_true_f * y_pred_f)  # Compute intersection\n    total = tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) - intersection  # Total pixels\n    return (intersection + smooth) / (total + smooth)\n    \n# Define a loss function and a metric\ndef dice_coef(y_true, y_pred, smooth=1e-6):\n    # Flatten and cast true and predicted masks to float32\n    y_true_f = tf.reshape(tf.cast(y_true, tf.float32), [-1])  # Flatten and cast y_true to float32\n    y_pred_f = tf.reshape(tf.cast(y_pred, tf.float32), [-1])  # Flatten and cast y_pred to float32\n    \n    # Calculate the intersection between the true and predicted masks\n    intersection = tf.reduce_sum(y_true_f * y_pred_f)\n    \n    # Calculate the Dice coefficient using the formula\n    return (2. * intersection + smooth) / (tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) + smooth)\n\ndef dice_loss(y_true, y_pred):\n    return 1 - dice_coef(y_true, y_pred)\n\nstrategy = tf.distribute.MirroredStrategy()\n\n# Everything that creates variables must be under the strategy's scope\nwith strategy.scope():\n    # Re-build your model here\n    model1 = unet_model()\n    model2 = unet_model()# Your model-building function\n\n    # Compile the model with its optimizer, loss, and metrics\n    model1.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=5e-4),\n                  loss=dice_loss, # Your custom loss function\n                  metrics=[dice_coef, 'binary_accuracy', jaccard_index])\n    model2.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),\n                  loss=dice_loss, # Your custom loss function\n                  metrics=[dice_coef, 'binary_accuracy', jaccard_index])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:50.400633Z","iopub.execute_input":"2025-11-29T07:26:50.401142Z","iopub.status.idle":"2025-11-29T07:26:51.813193Z","shell.execute_reply.started":"2025-11-29T07:26:50.401121Z","shell.execute_reply":"2025-11-29T07:26:51.812455Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ReduceLROnPlateau\ncallbacks = [\n    ModelCheckpoint(\"unet_1e4.keras\", save_best_only=True, monitor='val_dice_coef', mode='max')\n]\n\n# Train the model\nprint(\"\\nStarting model training...\")\nhistory1 = model1.fit(\n    train_data,    # Use the new generator\n    epochs=100, \n    validation_data=val_data, # Use the new generator\n    callbacks=callbacks\n)\nprint(\"Model training finished.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:26:51.814039Z","iopub.execute_input":"2025-11-29T07:26:51.814247Z","iopub.status.idle":"2025-11-29T07:27:21.435275Z","shell.execute_reply.started":"2025-11-29T07:26:51.814232Z","shell.execute_reply":"2025-11-29T07:27:21.433859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks = [\n    ModelCheckpoint(\"unet_5e3.keras\", save_best_only=True, monitor='val_dice_coef', mode='max')\n]\nprint(\"\\nStarting model training...\")\nhistory2 = model2.fit(\n    train_data,\n    epochs=100, # Increase epochs for better results, e.g., 100\n    validation_data=val_data,\n    callbacks=callbacks\n)\nprint(\"Model training finished.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.436018Z","iopub.status.idle":"2025-11-29T07:27:21.436383Z","shell.execute_reply.started":"2025-11-29T07:27:21.436261Z","shell.execute_reply":"2025-11-29T07:27:21.436275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model1.load_weights(\"unet_1e4.keras\")\nmodel2.load_weights(\"unet_5e3.keras\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss, dice, accuracy, jaccard = model1.evaluate(test_data)\nprint(f\"\\nValidation Dice Coefficient: {dice:.4f}\")\nprint(f\"Jaccard Index (IoU): {jaccard:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.437526Z","iopub.status.idle":"2025-11-29T07:27:21.437814Z","shell.execute_reply.started":"2025-11-29T07:27:21.437663Z","shell.execute_reply":"2025-11-29T07:27:21.437677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss, dice, accuracy, jaccard = model2.evaluate(test_data)\nprint(f\"\\nValidation Dice Coefficient: {dice:.4f}\")\nprint(f\"Jaccard Index (IoU): {jaccard:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.438561Z","iopub.status.idle":"2025-11-29T07:27:21.438805Z","shell.execute_reply.started":"2025-11-29T07:27:21.438694Z","shell.execute_reply":"2025-11-29T07:27:21.438705Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training history\nplt.figure(figsize=(12, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history1.history['loss'], label='Training Loss')\nplt.plot(history1.history['val_loss'], label='Validation Loss')\nplt.title('Loss')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history1.history['dice_coef'], label='Training Dice Coef')\nplt.plot(history1.history['val_dice_coef'], label='Validation Dice Coef')\nplt.title('Dice Coefficient')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.439415Z","iopub.status.idle":"2025-11-29T07:27:21.439678Z","shell.execute_reply.started":"2025-11-29T07:27:21.439560Z","shell.execute_reply":"2025-11-29T07:27:21.439571Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training history\nplt.figure(figsize=(12, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history2.history['loss'], label='Training Loss')\nplt.plot(history2.history['val_loss'], label='Validation Loss')\nplt.title('Loss')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history2.history['dice_coef'], label='Training Dice Coef')\nplt.plot(history2.history['val_dice_coef'], label='Validation Dice Coef')\nplt.title('Dice Coefficient')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.440225Z","iopub.status.idle":"2025-11-29T07:27:21.440543Z","shell.execute_reply.started":"2025-11-29T07:27:21.440385Z","shell.execute_reply":"2025-11-29T07:27:21.440401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def visualize_from_generator(model_unet_plus, model_unet_std, generator, num_samples=5):\n#     \"\"\"\n#     Visualizes model predictions on a batch of data from a tf.data.Dataset.\n#     Displays: Original, True Mask, Standard UNet, and UNet++\n#     \"\"\"\n#     # 1. Get one batch of data from the generator using .take(1)\n#     for images, true_masks in generator.take(1):\n        \n#         # 2. Make predictions with both models\n        \n#         # UNet++ (model1) returns a list of 4 predictions\n#         pred_mask_list_plus = model_unet_plus.predict(images)\n#         pred_masks_plus = pred_mask_list_plus[3] # Get the final (output_4) prediction\n        \n#         # Standard UNet (model2) returns a single prediction\n#         pred_masks_std = model_unet_std.predict(images)\n\n#         # Ensure we don't try to plot more samples than are in the batch\n#         num_samples = min(num_samples, len(images))\n\n#         # Change figsize and subplot grid to (num_samples, 4)\n#         plt.figure(figsize=(20, 5 * num_samples)) \n#         for i in range(num_samples):\n#             # Get the i-th image, true mask, and predicted masks from the batch\n#             image = images[i]\n#             true_mask = true_masks[i]\n            \n#             # Binarize the predictions\n#             pred_mask_plus = (pred_masks_plus[i] > 0.5).astype(np.float32) \n#             pred_mask_std = (pred_masks_std[i] > 0.5).astype(np.float32)\n\n#             # Plot Original Image\n#             plt.subplot(num_samples, 4, i * 4 + 1) # (rows, cols, index)\n#             plt.title(\"Original Image\")\n#             plt.imshow(image, cmap='gray')\n#             plt.axis('off')\n\n#             # Plot True Mask\n#             plt.subplot(num_samples, 4, i * 4 + 2)\n#             plt.title(\"True Mask\")\n#             plt.imshow(true_mask[:, :, 0], cmap='gray')\n#             plt.axis('off')\n\n#             # Plot Standard UNet (model2) Prediction\n#             plt.subplot(num_samples, 4, i * 4 + 3)\n#             plt.title(\"Standard UNet (model2)\")\n#             plt.imshow(pred_mask_std[:, :, 0], cmap='gray')\n#             plt.axis('off')\n\n#             # Plot UNet++ (model1) Prediction\n#             plt.subplot(num_samples, 4, i * 4 + 4)\n#             plt.title(\"UNet++ (model1)\")\n#             plt.imshow(pred_mask_plus[:, :, 0], cmap='gray')\n#             plt.axis('off')\n\n#         plt.tight_layout()\n#         plt.show()\n#         break # We only need one batch\n\n# # --- Update the function call ---\n# # Pass both models to the new function\n# visualize_from_generator(model1, model2, test_data, num_samples=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T07:27:21.441151Z","iopub.status.idle":"2025-11-29T07:27:21.441371Z","shell.execute_reply.started":"2025-11-29T07:27:21.441267Z","shell.execute_reply":"2025-11-29T07:27:21.441277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}