{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":90316,"databundleVersionId":10477888,"sourceType":"competition"}],"dockerImageVersionId":30823,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2 as cv\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\n# Load train.csv\ntrain_df = pd.read_csv('/kaggle/input/ai-dl-multiclass-segmentation/train.csv')\n\n# Function to decode RLE to mask\ndef decode_rle_to_mask(rle, height, width):\n    mask = np.zeros(height * width, dtype=np.uint8)\n    if pd.isna(rle):\n        return mask.reshape((height, width))\n    rle = list(map(int, rle.split()))\n    for i in range(0, len(rle), 2):\n        start, length = rle[i], rle[i + 1]\n        mask[start:start + length] = 1\n    return mask.reshape((height, width))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define the number of classes\nNUM_CLASSES = 4  # 1: Head, 2: Body, 3: Legs, 4: Tail\n\n# Function to create multi-class mask\ndef create_multi_class_mask(df, image_name, height, width):\n    mask = np.zeros((height, width, NUM_CLASSES), dtype=np.uint8)\n    image_df = df[df['ImageName'] == image_name]\n    for _, row in image_df.iterrows():\n        class_num = row['ClassNumber']\n        rle = row['Encoding']\n        single_mask = decode_rle_to_mask(rle, height, width)\n        mask[:, :, class_num - 1] = single_mask  # Classes 1-4 mapped to 0-3\n    return mask\n\n# Example visualization\nsample_image = '2008_006280'\nsample_height = 375\nsample_width = 500\nsample_mask = create_multi_class_mask(train_df, sample_image, sample_height, sample_width)\n\nplt.figure(figsize=(10, 10))\nfor i in range(NUM_CLASSES):\n    plt.subplot(2, 2, i+1)\n    plt.imshow(sample_mask[:, :, i], cmap='gray')\n    plt.title(f'Class {i+1}')\n    plt.axis('off')\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Get unique image names\nimage_names = train_df['ImageName'].unique()\n\n# Split into train and validation\ntrain_images, val_images = train_test_split(image_names, test_size=0.2, random_state=42)\n\n# Verify the split\nprint(f'Training samples: {len(train_images)}')\nprint(f'Validation samples: {len(val_images)}')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# Define augmentation parameters\ndata_gen_args = dict(\n    rotation_range=20,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    shear_range=0.1,\n    zoom_range=0.1,\n    horizontal_flip=True,\n    fill_mode='nearest'\n)\n\n# Create ImageDataGenerator instances\nimage_datagen = ImageDataGenerator(**data_gen_args)\nmask_datagen = ImageDataGenerator(**data_gen_args)\n\n# Seed for reproducibility\nseed = 1\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import ResNet50\n\ndef unet_model(input_size=(500, 500, 3), num_classes=NUM_CLASSES):\n    inputs = layers.Input(input_size)\n    \n    # Pretrained ResNet34 as encoder\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n    \n    # Extract encoder layers\n    layer_names = [\n        'conv1_relu',   # 256x256\n        'conv2_block3_out',  # 128x128\n        'conv3_block4_out',  # 64x64\n        'conv4_block6_out',  # 32x32\n        'conv5_block3_out',  # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n    \n    # Create the encoder\n    encoder = models.Model(inputs, layers_output)\n    \n    # Decoder\n    x = encoder.output[-1]\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2))(x)\n        x = layers.Concatenate()([x, encoder.output[i]])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n    \n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n    \n    model = models.Model(inputs, outputs)\n    return model\n\n# Instantiate the model\nmodel = unet_model()\n\n# Compile the model\nmodel.compile(optimizer='adam',\n              loss='categorical_crossentropy',\n              metrics=['accuracy', tf.keras.metrics.MeanIoU(num_classes=NUM_CLASSES)])\n              \nmodel.summary()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.utils import class_weight\n\n# Calculate class weights\n# Flatten all masks to compute class weights\nall_classes = []\nfor image in train_images:\n    mask = create_multi_class_mask(train_df, image, 375, 500)\n    all_classes.extend(mask.flatten())\n\nclass_weights = class_weight.compute_class_weight(class_weight='balanced',\n                                                  classes=np.unique(all_classes),\n                                                  y=all_classes)\nclass_weights_dict = {i: class_weights[i-1] for i in range(1, NUM_CLASSES+1)}\nprint(class_weights_dict)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import Necessary Libraries\nimport os\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import class_weight\nfrom sklearn.metrics import jaccard_score\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# Suppress TensorFlow warnings for clarity\nimport logging\nlogging.getLogger(\"tensorflow\").setLevel(logging.ERROR)\n\n# Define Constants\nNUM_CLASSES = 4  # 1: Head, 2: Body, 3: Legs, 4: Tail\nIMAGE_SIZE = (512, 512)  # Updated from (500, 500) to (512, 512)\nBATCH_SIZE = 16\nEPOCHS = 50\nTRAIN_IMAGES_DIR = '/kaggle/input/ai-dl-multiclass-segmentation/TrainImages'\nTEST_IMAGES_DIR = '/kaggle/input/ai-dl-multiclass-segmentation/TestImages'\nTRAIN_CSV = '/kaggle/input/ai-dl-multiclass-segmentation/train.csv'\nTEST_CSV = '/kaggle/input/ai-dl-multiclass-segmentation/test_class.csv'\nSUBMISSION_FILE = 'submission.csv'","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. Data Preprocessing Functions\n\ndef decode_rle_to_mask(rle, height, width):\n    \"\"\"\n    Decodes a Run-Length Encoded (RLE) mask into a binary mask.\n    \n    Parameters:\n    - rle (str): RLE string.\n    - height (int): Height of the mask.\n    - width (int): Width of the mask.\n    \n    Returns:\n    - np.array: Decoded binary mask of shape (height, width).\n    \"\"\"\n    mask = np.zeros(height * width, dtype=np.uint8)\n    if pd.isna(rle):\n        return mask.reshape((height, width))\n    rle = list(map(int, rle.split()))\n    for i in range(0, len(rle), 2):\n        start, length = rle[i], rle[i + 1]\n        start -= 1  # Convert to zero-based index\n        mask[start:start + length] = 1\n    return mask.reshape((height, width))\n\ndef create_multi_class_mask(df, image_name, height, width):\n    \"\"\"\n    Creates a multi-channel mask for a given image.\n    \n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_name (str): Name of the image file.\n    - height (int): Original height of the image.\n    - width (int): Original width of the image.\n    \n    Returns:\n    - np.array: Multi-channel mask of shape (height, width, NUM_CLASSES).\n    \"\"\"\n    mask = np.zeros((height, width, NUM_CLASSES), dtype=np.uint8)\n    image_df = df[df['ImageName'] == image_name]\n    for _, row in image_df.iterrows():\n        class_num = row['ClassNumber']\n        rle = row['Encoding']\n        single_mask = decode_rle_to_mask(rle, height, width)\n        mask[:, :, class_num - 1] = single_mask  # Classes 1-4 mapped to 0-3\n    return mask\n\ndef encode_mask_to_rle(mask):\n    \"\"\"\n    Encodes a binary mask into Run-Length Encoding (RLE).\n    \n    Parameters:\n    - mask (np.array): Binary mask of shape (height, width).\n    \n    Returns:\n    - str: RLE string.\n    \"\"\"\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef preprocess_test_image(image_path, target_size=IMAGE_SIZE):\n    \"\"\"\n    Preprocesses a test image for prediction.\n    \n    Parameters:\n    - image_path (str): Path to the test image.\n    - target_size (tuple): Desired image size.\n    \n    Returns:\n    - np.array: Preprocessed image.\n    \"\"\"\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, target_size)\n    img = img / 255.0  # Normalize\n    return img","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 2. Load and Parse Data\n\n# Load train.csv\ntrain_df = pd.read_csv(TRAIN_CSV)\n\n# Get unique image names\nimage_names = train_df['ImageName'].unique()\n\n# Split into training and validation sets\ntrain_images, val_images = train_test_split(image_names, test_size=0.2, random_state=42)\n\nprint(f'Training samples: {len(train_images)}')\nprint(f'Validation samples: {len(val_images)}')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. Data Generators\n\ndef generate_data(df, image_names, batch_size=BATCH_SIZE):\n    \"\"\"\n    Generator that yields batches of images and masks.\n    \n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_names (list): List of image names.\n    - batch_size (int): Number of samples per batch.\n    \n    Yields:\n    - Tuple of (images, masks).\n    \"\"\"\n    while True:\n        for start in range(0, len(image_names), batch_size):\n            end = min(start + batch_size, len(image_names))\n            batch_images = image_names[start:end]\n            images = []\n            masks = []\n            for image in batch_images:\n                img_path = os.path.join(TRAIN_IMAGES_DIR, f'{image}.jpg')\n                img = cv2.imread(img_path)\n                if img is None:\n                    print(f'Warning: Image {img_path} not found.')\n                    continue\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                img = cv2.resize(img, IMAGE_SIZE)\n                img = img / 255.0  # Normalize\n\n                # Get original image dimensions from DataFrame\n                img_df = df[df['ImageName'] == image]\n                # Assuming all entries for an image have the same height and width\n                orig_height = img_df['ImageHeight'].iloc[0]\n                orig_width = img_df['ImageWidth'].iloc[0]\n\n                mask = create_multi_class_mask(df, image, orig_height, orig_width)\n                mask = cv2.resize(mask, IMAGE_SIZE, interpolation=cv2.INTER_NEAREST)\n                masks.append(mask)\n                images.append(img)\n\n            images = np.array(images)\n            masks = np.array(masks)\n            masks = to_categorical(masks, num_classes=NUM_CLASSES)\n            yield images, masks\n\n# Create training and validation generators\ntrain_gen = generate_data(train_df, train_images, batch_size=BATCH_SIZE)\nval_gen = generate_data(train_df, val_images, batch_size=BATCH_SIZE)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 4. Model Definition\n\ndef unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES):\n    \"\"\"\n    Builds a U-Net model with ResNet50 backbone.\n    \n    Parameters:\n    - input_size (tuple): Shape of the input images.\n    - num_classes (int): Number of segmentation classes.\n    \n    Returns:\n    - tf.keras.Model: Compiled U-Net model.\n    \"\"\"\n    inputs = layers.Input(input_size)\n    \n    # Use ResNet50 as the backbone\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n    \n    # Specify the layers for skip connections\n    layer_names = [\n        'conv1_relu',            # 256x256\n        'conv2_block3_out',      # 128x128\n        'conv3_block4_out',      # 64x64\n        'conv4_block6_out',      # 32x32\n        'conv5_block3_out',      # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n    \n    # Create the encoder model\n    encoder = models.Model(inputs, layers_output)\n    \n    # Decoder\n    x = encoder.output[-1]  # Start from the deepest layer\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n        skip_connection = encoder.output[i]\n        x = layers.Concatenate()([x, skip_connection])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n    \n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n    \n    model = models.Model(inputs, outputs)\n    return model\n\n# Instantiate the model\nmodel = unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 5. Compile the Model\n\n# Define Dice Loss\ndef dice_loss(y_true, y_pred, smooth=1):\n    \"\"\"\n    Computes Dice Loss.\n    \n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n    - smooth (float): Smoothing factor to avoid division by zero.\n    \n    Returns:\n    - tf.Tensor: Dice loss.\n    \"\"\"\n    y_true_f = tf.reshape(y_true, [-1, NUM_CLASSES])\n    y_pred_f = tf.reshape(y_pred, [-1, NUM_CLASSES])\n    intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0)\n    return 1 - (2. * intersection + smooth) / (tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) + smooth)\n\n# Define Combined Loss (Categorical Crossentropy + Dice Loss)\ndef combined_loss(y_true, y_pred):\n    \"\"\"\n    Combines Categorical Crossentropy and Dice Loss.\n    \n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n    \n    Returns:\n    - tf.Tensor: Combined loss.\n    \"\"\"\n    return tf.keras.losses.categorical_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n\n# Compile the model with Combined Loss\nmodel.compile(optimizer='adam',\n              loss=combined_loss,\n              metrics=['accuracy', tf.keras.metrics.MeanIoU(num_classes=NUM_CLASSES)])\n\n# Display Model Summary\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. Define Callbacks\n\ncheckpoint = ModelCheckpoint('best_model.keras', \n                             monitor='val_mean_io_u', \n                             mode='max', \n                             verbose=1, \n                             save_best_only=True)\n\nearly_stopping = EarlyStopping(monitor='val_mean_io_u', \n                               mode='max', \n                               patience=10, \n                               verbose=1, \n                               restore_best_weights=True)\n\nreduce_lr = ReduceLROnPlateau(monitor='val_mean_io_u', \n                              mode='max', \n                              factor=0.5, \n                              patience=5, \n                              verbose=1)\n\ncallbacks = [checkpoint, early_stopping, reduce_lr]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. Train the Model\n\n# Calculate steps per epoch\nsteps_per_epoch = len(train_images) // BATCH_SIZE\nvalidation_steps = len(val_images) // BATCH_SIZE\n\n# Fit the model\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=steps_per_epoch,\n    epochs=EPOCHS,\n    validation_data=val_gen,\n    validation_steps=validation_steps,\n    callbacks=callbacks\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# 3. Data Preprocessing Functions\n\ndef decode_rle_to_mask(rle, height, width):\n    \"\"\"\n    Decodes a Run-Length Encoded (RLE) mask into a binary mask.\n\n    Parameters:\n    - rle (str): RLE string.\n    - height (int): Height of the mask.\n    - width (int): Width of the mask.\n\n    Returns:\n    - np.array: Decoded binary mask of shape (height, width).\n    \"\"\"\n    mask = np.zeros(height * width, dtype=np.uint8)\n    if pd.isna(rle):\n        return mask.reshape((height, width))\n    rle = list(map(int, rle.split()))\n    for i in range(0, len(rle), 2):\n        start, length = rle[i], rle[i + 1]\n        start -= 1  # Convert to zero-based index\n        mask[start:start + length] = 1\n    return mask.reshape((height, width))\n\ndef create_multi_class_mask(df, image_name, height, width):\n    \"\"\"\n    Creates a single-channel mask with class IDs for a given image.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_name (str): Name of the image file.\n    - height (int): Original height of the image.\n    - width (int): Original width of the image.\n\n    Returns:\n    - np.array: Single-channel mask of shape (height, width) with class IDs.\n    \"\"\"\n    mask = np.zeros((height, width), dtype=np.uint8)\n    image_df = df[df['ImageName'] == image_name]\n    for _, row in image_df.iterrows():\n        class_num = row['ClassNumber']\n        rle = row['Encoding']\n        single_mask = decode_rle_to_mask(rle, height, width)\n        mask[single_mask == 1] = class_num - 1  # Classes 1-4 mapped to 0-3\n    return mask\n\ndef encode_mask_to_rle(mask):\n    \"\"\"\n    Encodes a binary mask into Run-Length Encoding (RLE).\n\n    Parameters:\n    - mask (np.array): Binary mask of shape (height, width).\n\n    Returns:\n    - str: RLE string.\n    \"\"\"\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef preprocess_test_image(image_path, target_size=IMAGE_SIZE):\n    \"\"\"\n    Preprocesses a test image for prediction.\n\n    Parameters:\n    - image_path (str): Path to the test image.\n    - target_size (tuple): Desired image size.\n\n    Returns:\n    - np.array: Preprocessed image.\n    \"\"\"\n    img = cv2.imread(image_path)\n    if img is None:\n        raise FileNotFoundError(f'Image not found: {image_path}')\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, target_size)\n    img = img / 255.0  # Normalize\n    return img\n\n# 4. Load and Split Data\n\n# Load train.csv\ntrain_df = pd.read_csv(TRAIN_CSV)\n\n# Get unique image names\nimage_names = train_df['ImageName'].unique()\n\n# Split into training and validation sets\ntrain_images, val_images = train_test_split(image_names, test_size=0.2, random_state=42)\n\nprint(f'Training samples: {len(train_images)}')\nprint(f'Validation samples: {len(val_images)}')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 5. Data Generators\n\ndef generate_data(df, image_names, batch_size=BATCH_SIZE):\n    \"\"\"\n    Generator that yields batches of images and masks.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_names (list): List of image names.\n    - batch_size (int): Number of samples per batch.\n\n    Yields:\n    - Tuple of (images, masks).\n    \"\"\"\n    while True:\n        for start in range(0, len(image_names), batch_size):\n            end = min(start + batch_size, len(image_names))\n            batch_images = image_names[start:end]\n            images = []\n            masks = []\n            for image in batch_images:\n                img_path = os.path.join(TRAIN_IMAGES_DIR, f'{image}.jpg')\n                img = cv2.imread(img_path)\n                if img is None:\n                    print(f'Warning: Image {img_path} not found.')\n                    continue\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                img = cv2.resize(img, IMAGE_SIZE)\n                img = img / 255.0  # Normalize\n\n                # Get original image dimensions from DataFrame\n                img_df = df[df['ImageName'] == image]\n                # Assuming all entries for an image have the same height and width\n                orig_height = img_df['ImageHeight'].iloc[0]\n                orig_width = img_df['ImageWidth'].iloc[0]\n\n                mask = create_multi_class_mask(df, image, orig_height, orig_width)\n                mask = cv2.resize(mask, IMAGE_SIZE, interpolation=cv2.INTER_NEAREST)\n                masks.append(mask)\n                images.append(img)\n\n            images = np.array(images)\n            masks = np.array(masks)\n            masks = to_categorical(masks, num_classes=NUM_CLASSES)  # Shape: (batch_size, 512, 512, 4)\n            yield images, masks\n\n# Create training and validation generators\ntrain_gen = generate_data(train_df, train_images, batch_size=BATCH_SIZE)\nval_gen = generate_data(train_df, val_images, batch_size=BATCH_SIZE)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. Model Definition\n\ndef unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES):\n    \"\"\"\n    Builds a U-Net model with ResNet50 backbone.\n\n    Parameters:\n    - input_size (tuple): Shape of the input images.\n    - num_classes (int): Number of segmentation classes.\n\n    Returns:\n    - tf.keras.Model: Compiled U-Net model.\n    \"\"\"\n    inputs = layers.Input(input_size)\n\n    # Use ResNet50 as the backbone\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n\n    # Specify the layers for skip connections\n    layer_names = [\n        'conv1_relu',            # 256x256\n        'conv2_block3_out',      # 128x128\n        'conv3_block4_out',      # 64x64\n        'conv4_block6_out',      # 32x32\n        'conv5_block3_out',      # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n\n    # Create the encoder model\n    encoder = models.Model(inputs, layers_output)\n\n    # Decoder\n    x = encoder.output[-1]  # Start from the deepest layer\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n        skip_connection = encoder.output[i]\n        x = layers.Concatenate()([x, skip_connection])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n\n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n\n    model = models.Model(inputs, outputs)\n    return model\n\n# Instantiate the model\nmodel = unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. Define Loss Functions\n\ndef dice_loss(y_true, y_pred, smooth=1):\n    \"\"\"\n    Computes Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n    - smooth (float): Smoothing factor to avoid division by zero.\n\n    Returns:\n    - tf.Tensor: Dice loss.\n    \"\"\"\n    y_true_f = tf.reshape(y_true, [-1, NUM_CLASSES])\n    y_pred_f = tf.reshape(y_pred, [-1, NUM_CLASSES])\n    intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0)\n    return 1 - (2. * intersection + smooth) / (tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) + smooth)\n\ndef combined_loss(y_true, y_pred):\n    \"\"\"\n    Combines Categorical Crossentropy and Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n\n    Returns:\n    - tf.Tensor: Combined loss.\n    \"\"\"\n    return tf.keras.losses.categorical_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n\n# 8. Compile the Model\n\nmodel.compile(optimizer='adam',\n              loss=combined_loss,\n              metrics=['accuracy', tf.keras.metrics.MeanIoU(num_classes=NUM_CLASSES)])\n\n# Display Model Summary\nmodel.summary()\n\n# 9. Define Callbacks\n\ncheckpoint = ModelCheckpoint('best_model.keras', \n                             monitor='val_mean_io_u', \n                             mode='max', \n                             verbose=1, \n                             save_best_only=True)\n\nearly_stopping = EarlyStopping(monitor='val_mean_io_u', \n                               mode='max', \n                               patience=10, \n                               verbose=1, \n                               restore_best_weights=True)\n\nreduce_lr = ReduceLROnPlateau(monitor='val_mean_io_u', \n                              mode='max', \n                              factor=0.5, \n                              patience=5, \n                              verbose=1)\n\ncallbacks = [checkpoint, early_stopping, reduce_lr]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 10. Train the Model\n\n# Calculate steps per epoch\nsteps_per_epoch = len(train_images) // BATCH_SIZE\nvalidation_steps = len(val_images) // BATCH_SIZE\n\n# Fit the model\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=steps_per_epoch,\n    epochs=EPOCHS,\n    validation_data=val_gen,\n    validation_steps=validation_steps,\n    callbacks=callbacks\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.summary()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES):\n    inputs = layers.Input(input_size)\n\n    # Use ResNet50 as the backbone\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n\n    # Specify the layers for skip connections\n    layer_names = [\n        'conv1_relu',            # 256x256\n        'conv2_block3_out',      # 128x128\n        'conv3_block4_out',      # 64x64\n        'conv4_block6_out',      # 32x32\n        'conv5_block3_out',      # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n\n    # Create the encoder model\n    encoder = models.Model(inputs, layers_output)\n\n    # Decoder\n    x = encoder.output[-1]  # Start from the deepest layer\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n        skip_connection = encoder.output[i]\n        x = layers.Concatenate()([x, skip_connection])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        \n        # Debugging: Print the shape after each upsampling\n        print(f'After upsampling and concatenation, x shape: {x.shape}')\n\n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n\n    model = models.Model(inputs, outputs)\n    return model\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"layers.Conv2D(filters, (3, 3), activation='relu', padding='same')\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = unet_model()\nmodel.summary()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. Data Preprocessing Functions\n\ndef decode_rle_to_mask(rle, height, width):\n    \"\"\"\n    Decodes a Run-Length Encoded (RLE) mask into a binary mask.\n\n    Parameters:\n    - rle (str): RLE string.\n    - height (int): Height of the mask.\n    - width (int): Width of the mask.\n\n    Returns:\n    - np.array: Decoded binary mask of shape (height, width).\n    \"\"\"\n    mask = np.zeros(height * width, dtype=np.uint8)\n    if pd.isna(rle):\n        return mask.reshape((height, width))\n    rle = list(map(int, rle.split()))\n    for i in range(0, len(rle), 2):\n        start, length = rle[i], rle[i + 1]\n        start -= 1  # Convert to zero-based index\n        mask[start:start + length] = 1\n    return mask.reshape((height, width))\n\ndef create_multi_class_mask(df, image_name, height, width):\n    \"\"\"\n    Creates a single-channel mask with class IDs for a given image.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_name (str): Name of the image file.\n    - height (int): Original height of the image.\n    - width (int): Original width of the image.\n\n    Returns:\n    - np.array: Single-channel mask of shape (height, width) with class IDs.\n    \"\"\"\n    mask = np.zeros((height, width), dtype=np.uint8)\n    image_df = df[df['ImageName'] == image_name]\n    for _, row in image_df.iterrows():\n        class_num = row['ClassNumber']\n        rle = row['Encoding']\n        single_mask = decode_rle_to_mask(rle, height, width)\n        mask[single_mask == 1] = class_num - 1  # Classes 1-4 mapped to 0-3\n    return mask\n\ndef encode_mask_to_rle(mask):\n    \"\"\"\n    Encodes a binary mask into Run-Length Encoding (RLE).\n\n    Parameters:\n    - mask (np.array): Binary mask of shape (height, width).\n\n    Returns:\n    - str: RLE string.\n    \"\"\"\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef preprocess_test_image(image_path, target_size=IMAGE_SIZE):\n    \"\"\"\n    Preprocesses a test image for prediction.\n\n    Parameters:\n    - image_path (str): Path to the test image.\n    - target_size (tuple): Desired image size.\n\n    Returns:\n    - np.array: Preprocessed image.\n    \"\"\"\n    img = cv2.imread(image_path)\n    if img is None:\n        raise FileNotFoundError(f'Image not found: {image_path}')\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, target_size)\n    img = img / 255.0  # Normalize\n    return img\n\n# 4. Load and Split Data\n\n# Load train.csv\ntrain_df = pd.read_csv(TRAIN_CSV)\n\n# Get unique image names\nimage_names = train_df['ImageName'].unique()\n\n# Split into training and validation sets\ntrain_images, val_images = train_test_split(image_names, test_size=0.2, random_state=42)\n\nprint(f'Training samples: {len(train_images)}')\nprint(f'Validation samples: {len(val_images)}')\n\n# 5. Data Generators\n\ndef generate_data(df, image_names, batch_size=BATCH_SIZE):\n    \"\"\"\n    Generator that yields batches of images and masks.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_names (list): List of image names.\n    - batch_size (int): Number of samples per batch.\n\n    Yields:\n    - Tuple of (images, masks).\n    \"\"\"\n    while True:\n        for start in range(0, len(image_names), batch_size):\n            end = min(start + batch_size, len(image_names))\n            batch_images = image_names[start:end]\n            images = []\n            masks = []\n            for image in batch_images:\n                img_path = os.path.join(TRAIN_IMAGES_DIR, f'{image}.jpg')\n                img = cv2.imread(img_path)\n                if img is None:\n                    print(f'Warning: Image {img_path} not found.')\n                    continue\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                img = cv2.resize(img, IMAGE_SIZE)\n                img = img / 255.0  # Normalize\n\n                # Get original image dimensions from DataFrame\n                img_df = df[df['ImageName'] == image]\n                # Assuming all entries for an image have the same height and width\n                orig_height = img_df['ImageHeight'].iloc[0]\n                orig_width = img_df['ImageWidth'].iloc[0]\n\n                mask = create_multi_class_mask(df, image, orig_height, orig_width)\n                mask = cv2.resize(mask, IMAGE_SIZE, interpolation=cv2.INTER_NEAREST)\n                masks.append(mask)\n                images.append(img)\n\n            images = np.array(images)\n            masks = np.array(masks)\n            masks = to_categorical(masks, num_classes=NUM_CLASSES)  # Shape: (batch_size, 512, 512, 4)\n            yield images, masks\n\n# Create training and validation generators\ntrain_gen = generate_data(train_df, train_images, batch_size=BATCH_SIZE)\nval_gen = generate_data(train_df, val_images, batch_size=BATCH_SIZE)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. Model Definition\n\ndef unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES):\n    \"\"\"\n    Builds a U-Net model with ResNet50 backbone.\n\n    Parameters:\n    - input_size (tuple): Shape of the input images.\n    - num_classes (int): Number of segmentation classes.\n\n    Returns:\n    - tf.keras.Model: Compiled U-Net model.\n    \"\"\"\n    inputs = layers.Input(input_size)\n\n    # Use ResNet50 as the backbone\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n\n    # Specify the layers for skip connections\n    layer_names = [\n        'conv1_relu',            # 256x256\n        'conv2_block3_out',      # 128x128\n        'conv3_block4_out',      # 64x64\n        'conv4_block6_out',      # 32x32\n        'conv5_block3_out',      # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n\n    # Create the encoder model\n    encoder = models.Model(inputs, layers_output)\n\n    # Decoder\n    x = encoder.output[-1]  # Start from the deepest layer\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n        skip_connection = encoder.output[i]\n        x = layers.Concatenate()([x, skip_connection])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        print(f'After upsampling and concatenation, x shape: {x.shape}')\n\n    # Additional upsampling step to reach 512x512\n    x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n    # No skip connection for this step\n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)\n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)\n    print(f'After final upsampling, x shape: {x.shape}')\n\n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n\n    model = models.Model(inputs, outputs)\n    return model\n\n# Instantiate the model\nmodel = unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. Define Loss Functions\n\ndef dice_loss(y_true, y_pred, smooth=1):\n    \"\"\"\n    Computes Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n    - smooth (float): Smoothing factor to avoid division by zero.\n\n    Returns:\n    - tf.Tensor: Dice loss.\n    \"\"\"\n    y_true_f = tf.reshape(y_true, [-1, NUM_CLASSES])\n    y_pred_f = tf.reshape(y_pred, [-1, NUM_CLASSES])\n    intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0)\n    return 1 - (2. * intersection + smooth) / (tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) + smooth)\n\ndef combined_loss(y_true, y_pred):\n    \"\"\"\n    Combines Categorical Crossentropy and Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n\n    Returns:\n    - tf.Tensor: Combined loss.\n    \"\"\"\n    return tf.keras.losses.categorical_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n\n# 8. Compile the Model\n\nmodel.compile(optimizer='adam',\n              loss=combined_loss,\n              metrics=['accuracy', tf.keras.metrics.MeanIoU(num_classes=NUM_CLASSES)])\n\n# Display Model Summary\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f'y_true shape: {y_true.shape}')\nprint(f'y_pred shape: {y_pred.shape}')\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# 9. Define Callbacks\n\ncheckpoint = ModelCheckpoint('best_model.keras', \n                             monitor='val_mean_io_u', \n                             mode='max', \n                             verbose=1, \n                             save_best_only=True)\n\nearly_stopping = EarlyStopping(monitor='val_mean_io_u', \n                               mode='max', \n                               patience=10, \n                               verbose=1, \n                               restore_best_weights=True)\n\nreduce_lr = ReduceLROnPlateau(monitor='val_mean_io_u', \n                              mode='max', \n                              factor=0.5, \n                              patience=5, \n                              verbose=1)\n\ncallbacks = [checkpoint, early_stopping, reduce_lr]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 10. Train the Model\n\n# Calculate steps per epoch\nsteps_per_epoch = len(train_images) // BATCH_SIZE\nvalidation_steps = len(val_images) // BATCH_SIZE\n\n# Fit the model\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=steps_per_epoch,\n    epochs=EPOCHS,\n    validation_data=val_gen,\n    validation_steps=validation_steps,\n    callbacks=callbacks\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import Necessary Libraries\nimport os\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import class_weight\nfrom sklearn.metrics import jaccard_score\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# Suppress TensorFlow warnings for clarity\nimport logging\nlogging.getLogger(\"tensorflow\").setLevel(logging.ERROR)\n\n# Define Constants\nNUM_CLASSES = 4  # 1: Head, 2: Body, 3: Legs, 4: Tail\nIMAGE_SIZE = (512, 512)  # Updated from (500, 500) to (512, 512)\nBATCH_SIZE = 4\nEPOCHS = 50\nTRAIN_IMAGES_DIR = '/kaggle/input/ai-dl-multiclass-segmentation/TrainImages'\nTEST_IMAGES_DIR = '/kaggle/input/ai-dl-multiclass-segmentation/TestImages'\nTRAIN_CSV = '/kaggle/input/ai-dl-multiclass-segmentation/train.csv'\nTEST_CSV = '/kaggle/input/ai-dl-multiclass-segmentation/test_class.csv'\nSUBMISSION_FILE = 'submission.csv'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-03T17:29:19.094060Z","iopub.execute_input":"2025-01-03T17:29:19.094431Z","iopub.status.idle":"2025-01-03T17:29:19.100921Z","shell.execute_reply.started":"2025-01-03T17:29:19.094407Z","shell.execute_reply":"2025-01-03T17:29:19.099803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. Data Preprocessing Functions\n\ndef decode_rle_to_mask(rle, height, width):\n    \"\"\"\n    Decodes a Run-Length Encoded (RLE) mask into a binary mask.\n\n    Parameters:\n    - rle (str): RLE string.\n    - height (int): Height of the mask.\n    - width (int): Width of the mask.\n\n    Returns:\n    - np.array: Decoded binary mask of shape (height, width).\n    \"\"\"\n    mask = np.zeros(height * width, dtype=np.uint8)\n    if pd.isna(rle):\n        return mask.reshape((height, width))\n    rle = list(map(int, rle.split()))\n    for i in range(0, len(rle), 2):\n        start, length = rle[i], rle[i + 1]\n        start -= 1  # Convert to zero-based index\n        mask[start:start + length] = 1\n    return mask.reshape((height, width))\n\ndef create_multi_class_mask(df, image_name, height, width):\n    \"\"\"\n    Creates a single-channel mask with class IDs for a given image.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_name (str): Name of the image file.\n    - height (int): Original height of the image.\n    - width (int): Original width of the image.\n\n    Returns:\n    - np.array: Single-channel mask of shape (height, width) with class IDs.\n    \"\"\"\n    mask = np.zeros((height, width), dtype=np.uint8)\n    image_df = df[df['ImageName'] == image_name]\n    for _, row in image_df.iterrows():\n        class_num = row['ClassNumber']\n        rle = row['Encoding']\n        single_mask = decode_rle_to_mask(rle, height, width)\n        mask[single_mask == 1] = class_num - 1  # Classes 1-4 mapped to 0-3\n    return mask\n\ndef encode_mask_to_rle(mask):\n    \"\"\"\n    Encodes a binary mask into Run-Length Encoding (RLE).\n\n    Parameters:\n    - mask (np.array): Binary mask of shape (height, width).\n\n    Returns:\n    - str: RLE string.\n    \"\"\"\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef preprocess_test_image(image_path, target_size=IMAGE_SIZE):\n    \"\"\"\n    Preprocesses a test image for prediction.\n\n    Parameters:\n    - image_path (str): Path to the test image.\n    - target_size (tuple): Desired image size.\n\n    Returns:\n    - np.array: Preprocessed image.\n    \"\"\"\n    img = cv2.imread(image_path)\n    if img is None:\n        raise FileNotFoundError(f'Image not found: {image_path}')\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = cv2.resize(img, target_size)\n    img = img / 255.0  # Normalize\n    return img\n\n# 4. Load and Split Data\n\n# Load train.csv\ntrain_df = pd.read_csv(TRAIN_CSV)\n\n# Get unique image names\nimage_names = train_df['ImageName'].unique()\n\n# Split into training and validation sets\ntrain_images, val_images = train_test_split(image_names, test_size=0.2, random_state=42)\n\nprint(f'Training samples: {len(train_images)}')\nprint(f'Validation samples: {len(val_images)}')\n\n# 5. Data Generators\n\ndef generate_data(df, image_names, batch_size=BATCH_SIZE):\n    \"\"\"\n    Generator that yields batches of images and masks.\n\n    Parameters:\n    - df (pd.DataFrame): DataFrame containing mask information.\n    - image_names (list): List of image names.\n    - batch_size (int): Number of samples per batch.\n\n    Yields:\n    - Tuple of (images, masks).\n    \"\"\"\n    while True:\n        for start in range(0, len(image_names), batch_size):\n            end = min(start + batch_size, len(image_names))\n            batch_images = image_names[start:end]\n            images = []\n            masks = []\n            for image in batch_images:\n                img_path = os.path.join(TRAIN_IMAGES_DIR, f'{image}.jpg')\n                img = cv2.imread(img_path)\n                if img is None:\n                    print(f'Warning: Image {img_path} not found.')\n                    continue\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                img = cv2.resize(img, IMAGE_SIZE)\n                img = img / 255.0  # Normalize\n\n                # Get original image dimensions from DataFrame\n                img_df = df[df['ImageName'] == image]\n                # Assuming all entries for an image have the same height and width\n                orig_height = img_df['ImageHeight'].iloc[0]\n                orig_width = img_df['ImageWidth'].iloc[0]\n\n                mask = create_multi_class_mask(df, image, orig_height, orig_width)\n                mask = cv2.resize(mask, IMAGE_SIZE, interpolation=cv2.INTER_NEAREST)\n                masks.append(mask)\n                images.append(img)\n\n            images = np.array(images)\n            masks = np.array(masks)\n            masks = to_categorical(masks, num_classes=NUM_CLASSES)  # Shape: (batch_size, 512, 512, 4)\n            yield images, masks\n\n# Create training and validation generators\ntrain_gen = generate_data(train_df, train_images, batch_size=BATCH_SIZE)\nval_gen = generate_data(train_df, val_images, batch_size=BATCH_SIZE)\n\n# 6. Model Definition\n\ndef unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES):\n    \"\"\"\n    Builds a U-Net model with ResNet50 backbone.\n\n    Parameters:\n    - input_size (tuple): Shape of the input images.\n    - num_classes (int): Number of segmentation classes.\n\n    Returns:\n    - tf.keras.Model: Compiled U-Net model.\n    \"\"\"\n    inputs = layers.Input(input_size)\n\n    # Use ResNet50 as the backbone\n    base_model = tf.keras.applications.ResNet50(include_top=False, weights='imagenet', input_tensor=inputs)\n\n    # Specify the layers for skip connections\n    layer_names = [\n        'conv1_relu',            # 256x256\n        'conv2_block3_out',      # 128x128\n        'conv3_block4_out',      # 64x64\n        'conv4_block6_out',      # 32x32\n        'conv5_block3_out',      # 16x16\n    ]\n    layers_output = [base_model.get_layer(name).output for name in layer_names]\n\n    # Create the encoder model\n    encoder = models.Model(inputs, layers_output)\n\n    # Decoder\n    x = encoder.output[-1]  # Start from the deepest layer\n    for i in reversed(range(len(layers_output) - 1)):\n        x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n        skip_connection = encoder.output[i]\n        x = layers.Concatenate()([x, skip_connection])\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        x = layers.Conv2D(256 // (2**i), (3, 3), activation='relu', padding='same')(x)\n        print(f'After upsampling and concatenation, x shape: {x.shape}')\n\n    # Additional upsampling step to reach 512x512\n    x = layers.UpSampling2D((2, 2), interpolation='bilinear')(x)\n    # No skip connection for this step\n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)\n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)\n    print(f'After final upsampling, x shape: {x.shape}')\n\n    # Output layer\n    outputs = layers.Conv2D(num_classes, (1, 1), activation='softmax')(x)\n\n    model = models.Model(inputs, outputs)\n    return model\n\n# Instantiate the model\nmodel = unet_model(input_size=(512, 512, 3), num_classes=NUM_CLASSES)\n\n# 7. Define Loss Functions\n\ndef dice_loss(y_true, y_pred, smooth=1):\n    \"\"\"\n    Computes Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n    - smooth (float): Smoothing factor to avoid division by zero.\n\n    Returns:\n    - tf.Tensor: Scalar Dice loss.\n    \"\"\"\n    y_true_f = tf.reshape(y_true, [-1, NUM_CLASSES])\n    y_pred_f = tf.reshape(y_pred, [-1, NUM_CLASSES])\n    intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0)\n    dice = (2. * intersection + smooth) / (tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) + smooth)\n    dice = tf.reduce_mean(dice)  # Aggregate over classes\n    return 1 - dice\n\ndef combined_loss(y_true, y_pred):\n    \"\"\"\n    Combines Categorical Crossentropy and Dice Loss.\n\n    Parameters:\n    - y_true (tf.Tensor): Ground truth one-hot encoded masks.\n    - y_pred (tf.Tensor): Predicted masks.\n\n    Returns:\n    - tf.Tensor: Scalar combined loss.\n    \"\"\"\n    ce_loss = tf.keras.losses.categorical_crossentropy(y_true, y_pred)\n    ce_loss = tf.reduce_mean(ce_loss)  # Aggregate over batch and spatial dimensions\n    dl = dice_loss(y_true, y_pred)\n    return ce_loss + dl\n\n# 8. Compile the Model\n\nmodel.compile(optimizer='adam',\n              loss=combined_loss,\n              metrics=['accuracy', tf.keras.metrics.MeanIoU(num_classes=NUM_CLASSES)])\n\n# Display Model Summary\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-03T17:29:20.832804Z","iopub.execute_input":"2025-01-03T17:29:20.833173Z","iopub.status.idle":"2025-01-03T17:29:22.838723Z","shell.execute_reply.started":"2025-01-03T17:29:20.833139Z","shell.execute_reply":"2025-01-03T17:29:22.837690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 9. Define Callbacks\n\ncheckpoint = ModelCheckpoint('best_model.keras', \n                             monitor='val_mean_io_u', \n                             mode='max', \n                             verbose=1, \n                             save_best_only=True)\n\nearly_stopping = EarlyStopping(monitor='val_mean_io_u', \n                               mode='max', \n                               patience=10, \n                               verbose=1, \n                               restore_best_weights=True)\n\nreduce_lr = ReduceLROnPlateau(monitor='val_mean_io_u', \n                              mode='max', \n                              factor=0.5, \n                              patience=5, \n                              verbose=1)\n\ncallbacks = [checkpoint, early_stopping, reduce_lr]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-03T17:29:25.827521Z","iopub.execute_input":"2025-01-03T17:29:25.827898Z","iopub.status.idle":"2025-01-03T17:29:25.832987Z","shell.execute_reply.started":"2025-01-03T17:29:25.827866Z","shell.execute_reply":"2025-01-03T17:29:25.831793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 10. Train the Model\n\n# Calculate steps per epoch\nsteps_per_epoch = len(train_images) // BATCH_SIZE\nvalidation_steps = len(val_images) // BATCH_SIZE\n\n# Perform a forward pass to verify shapes\nimages, masks = next(train_gen)\npreds = model.predict(images)\nprint(f'Images shape: {images.shape}')  # Expected: (batch_size, 512, 512, 3)\nprint(f'Masks shape: {masks.shape}')    # Expected: (batch_size, 512, 512, 4)\nprint(f'Predictions shape: {preds.shape}')  # Expected: (batch_size, 512, 512, 4)\n\n# Fit the model\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=steps_per_epoch,\n    epochs=EPOCHS,\n    validation_data=val_gen,\n    validation_steps=validation_steps,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-03T17:29:27.988688Z","iopub.execute_input":"2025-01-03T17:29:27.988996Z","iopub.status.idle":"2025-01-04T02:11:32.403738Z","shell.execute_reply.started":"2025-01-03T17:29:27.988973Z","shell.execute_reply":"2025-01-04T02:11:32.402637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 11. Evaluate the Model\n\n# Plot Training History\nplt.figure(figsize=(12, 5))\n\n# Plot Loss\nplt.subplot(1, 2, 1)\nplt.plot(history.history['loss'], label='Train Loss', color='blue')\nplt.plot(history.history['val_loss'], label='Validation Loss', color='orange')\nplt.title('Loss Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\n\n# Plot IoU\nplt.subplot(1, 2, 2)\nplt.plot(history.history['mean_io_u'], label='Train IoU', color='blue')\nplt.plot(history.history['val_mean_io_u'], label='Validation IoU', color='orange')\nplt.title('IoU Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('IoU')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T02:11:32.405272Z","iopub.execute_input":"2025-01-04T02:11:32.405670Z","iopub.status.idle":"2025-01-04T02:11:32.956210Z","shell.execute_reply.started":"2025-01-04T02:11:32.405640Z","shell.execute_reply":"2025-01-04T02:11:32.954382Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Initialize list to store IoUs\nval_ious = []\n\n# Reset the validation generator\nval_gen = generate_data(train_df, val_images, batch_size=BATCH_SIZE)\n\n# Iterate over the validation set\nfor _ in tqdm(range(validation_steps), desc='Calculating IoU on Validation Set'):\n    X_val, y_val = next(val_gen)\n    y_pred = model.predict(X_val)\n    y_pred = np.argmax(y_pred, axis=-1)\n    y_true = np.argmax(y_val, axis=-1)\n    \n    # Calculate IoU for each class\n    for class_num in range(NUM_CLASSES):\n        true_mask = (y_true == class_num).astype(np.uint8)\n        pred_mask = (y_pred == class_num).astype(np.uint8)\n        if np.sum(true_mask) == 0 and np.sum(pred_mask) == 0:\n            iou = 1.0  # Perfect match (both empty)\n        elif np.sum(true_mask) == 0 or np.sum(pred_mask) == 0:\n            iou = 0.0  # One is empty, the other is not\n        else:\n            iou = jaccard_score(true_mask.flatten(), pred_mask.flatten(), average='binary')\n        val_ious.append(iou)\n\n# Convert list to numpy array\nval_ious = np.array(val_ious)\n\n# Calculate mean IoU for each class\nmean_ious = []\nfor i in range(NUM_CLASSES):\n    class_ious = val_ious[i::NUM_CLASSES]\n    mean_iou = np.mean(class_ious)\n    mean_ious.append(mean_iou)\n    print(f'IoU for Class {i+1}: {mean_iou:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T02:12:08.104832Z","iopub.execute_input":"2025-01-04T02:12:08.105215Z","iopub.status.idle":"2025-01-04T02:16:16.291272Z","shell.execute_reply.started":"2025-01-04T02:12:08.105184Z","shell.execute_reply":"2025-01-04T02:16:16.290112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 12. Inference and Submission\n\n# Load Test Image Names\ntest_image_files = [f for f in os.listdir(TEST_IMAGES_DIR) if f.endswith('.jpg')]\ntest_image_names = [os.path.splitext(f)[0] for f in test_image_files]\n\n# Initialize Submission Dictionary\nsubmission_dict = {}\n\n# Iterate over Test Images\nfor image in tqdm(test_image_names, desc='Processing Test Images'):\n    img_path = os.path.join(TEST_IMAGES_DIR, f'{image}.jpg')\n    img = preprocess_test_image(img_path, target_size=IMAGE_SIZE)\n    img = np.expand_dims(img, axis=0)  # Add batch dimension\n    \n    # Predict\n    preds = model.predict(img)\n    preds = np.argmax(preds, axis=-1)[0]  # Shape: (512, 512)\n    \n    # Encode masks for each class\n    rles = []\n    for class_num in range(NUM_CLASSES):\n        class_mask = (preds == class_num).astype(np.uint8)\n        if class_mask.sum() > 0:\n            rle = encode_mask_to_rle(class_mask)\n            rles.append(rle)\n    \n    # Combine RLEs\n    submission_dict[image] = ' '.join(rles) if rles else ''\n\n# Prepare Submission DataFrame\nsubmission = pd.DataFrame({\n    'ImageName': [f'{img}.jpg' for img in test_image_names],\n    'Encoding': [submission_dict[img] for img in test_image_names]\n})\n\n# Save to CSV\nsubmission.to_csv(SUBMISSION_FILE, index=False)\nprint(f'Submission file saved to {SUBMISSION_FILE}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T02:16:16.292972Z","iopub.execute_input":"2025-01-04T02:16:16.293458Z","iopub.status.idle":"2025-01-04T02:19:37.584669Z","shell.execute_reply.started":"2025-01-04T02:16:16.293423Z","shell.execute_reply":"2025-01-04T02:19:37.583483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\ndef decode_rle(rle_string, height, width):\n    \"\"\"\n    Decodes a Run-Length Encoded (RLE) string into a binary mask.\n    \n    Parameters:\n        rle_string (str): The RLE string (e.g., \"3 5 10 2 ...\").\n        height (int): Height of the mask.\n        width (int): Width of the mask.\n    \n    Returns:\n        np.ndarray: Decoded binary mask of shape (height, width).\n    \"\"\"\n    if pd.isna(rle_string) or rle_string.strip() == '':\n        return np.zeros((height, width), dtype=np.uint8)\n    \n    s = rle_string.strip().split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0::2], s[1::2])]\n    starts -= 1  # Convert to zero-based indexing\n    ends = starts + lengths\n    img = np.zeros(height * width, dtype=np.uint8)\n    for start, end in zip(starts, ends):\n        img[start:end] = 1\n    return img.reshape((height, width))\n\ndef overlay_mask(image, mask, color, alpha=0.4):\n    \"\"\"\n    Overlays a single mask on the image with the specified color and transparency.\n    \n    Parameters:\n        image (np.ndarray): The original image in RGB format.\n        mask (np.ndarray): Binary mask to overlay.\n        color (tuple): RGB color tuple (e.g., (255, 0, 0) for red).\n        alpha (float): Transparency factor.\n    \n    Returns:\n        np.ndarray: Image with the mask overlay.\n    \"\"\"\n    for c in range(3):\n        image[:, :, c] = np.where(\n            mask == 1,\n            (1 - alpha) * image[:, :, c] + alpha * color[c],\n            image[:, :, c]\n        )\n    return image\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T02:23:13.761134Z","iopub.execute_input":"2025-01-04T02:23:13.761676Z","iopub.status.idle":"2025-01-04T02:23:13.770104Z","shell.execute_reply.started":"2025-01-04T02:23:13.761523Z","shell.execute_reply":"2025-01-04T02:23:13.768800Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# Define paths\nSUBMISSION_CSV = \"/kaggle/working/submission.csv\"  # Path to your submission CSV\nTEST_IMAGES_DIR = \"/kaggle/input/ai-dl-multiclass-segmentation/TestImages\"      # Directory containing test images\nVISUALIZATION_DIR = \"Visualiz/kaggle/input/ai-dl-multiclass-segmentation/TestImagesations\"  # Directory to save visualization images\n\n# Create visualization directory if it doesn't exist\nos.makedirs(VISUALIZATION_DIR, exist_ok=True)\n\n# Define class names and their corresponding colors (RGB)\ncustom_class_names = [\"head\", \"body\", \"legs\", \"tail\"]\ncustom_colors = {\n    \"head\": (255, 0, 0),    # Red\n    \"body\": (0, 255, 0),    # Green\n    \"legs\": (0, 0, 255),    # Blue\n    \"tail\": (255, 255, 0)   # Yellow\n}\n\n# Read the submission CSV\nsubmission_df = pd.read_csv(SUBMISSION_CSV)\n\ndef visualize_masks(image_path, rle_encodings, image_name, save_path=None):\n    \"\"\"\n    Visualizes masks on the image based on RLE encodings.\n    \n    Parameters:\n        image_path (str): Path to the image file.\n        rle_encodings (str): Space-separated RLE strings.\n        image_name (str): Name of the image (for titles).\n        save_path (str, optional): Path to save the visualization image.\n    \"\"\"\n    # Load image\n    image_bgr = cv2.imread(image_path)\n    if image_bgr is None:\n        print(f\"Warning: Unable to read image {image_path}. Skipping.\")\n        return\n    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)\n    \n    height, width = image_rgb.shape[:2]\n    \n    # Split RLE encodings\n    rle_list = rle_encodings.strip().split('  ')  # Assuming double space separates RLEs\n    if len(rle_list) == 1:\n        rle_list = rle_encodings.strip().split(' ')\n        # Reconstruct RLEs assuming each mask has even number of numbers\n        if len(rle_list) % 2 != 0:\n            print(f\"Warning: Odd number of RLE elements in image {image_name}.\")\n            return\n        rle_list = [' '.join(rle_list[i:i+2]) for i in range(0, len(rle_list), 2)]\n    \n    # Initialize overlay image\n    overlay = image_rgb.copy()\n    \n    # Assign classes and colors\n    for i, rle in enumerate(rle_list[:4]):  # Ensure max 4 masks\n        class_name = custom_class_names[i] if i < len(custom_class_names) else f\"class_{i+1}\"\n        color = custom_colors.get(class_name, (255, 255, 255))  # Default to white if not found\n        \n        # Decode RLE\n        mask = decode_rle(rle, height, width)\n        \n        # Overlay mask\n        overlay = overlay_mask(overlay, mask, color, alpha=0.4)\n    \n    # Display the image\n    plt.figure(figsize=(8, 8))\n    plt.imshow(overlay)\n    plt.title(f\"Image: {image_name}\")\n    plt.axis('off')\n    plt.show()\n    \n    # Save the visualization if a path is provided\n    if save_path:\n        overlay_bgr = cv2.cvtColor(overlay, cv2.COLOR_RGB2BGR)\n        cv2.imwrite(save_path, overlay_bgr)\n        print(f\"Saved visualization to {save_path}\")\n\n# Iterate over each row in the submission CSV\nfor idx, row in submission_df.iterrows():\n    image_name = row['ImageName']\n    encoding = row['Encoding']\n    image_path = os.path.join(TEST_IMAGES_DIR, image_name)\n    save_image_path = os.path.join(VISUALIZATION_DIR, f\"{os.path.splitext(image_name)[0]}_overlay.jpg\")\n    \n    visualize_masks(image_path, encoding, image_name, save_path=save_image_path)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T02:24:13.328738Z","iopub.execute_input":"2025-01-04T02:24:13.329144Z","iopub.status.idle":"2025-01-04T02:24:57.723075Z","shell.execute_reply.started":"2025-01-04T02:24:13.329109Z","shell.execute_reply":"2025-01-04T02:24:57.721265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}