{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":76972,"databundleVersionId":8669583,"sourceType":"competition"}],"dockerImageVersionId":31089,"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":"markdown","source":"# Mask2Former model for building instance segmentation","metadata":{}},{"cell_type":"markdown","source":"## Dataset helper codes\n**Only run at first time**","metadata":{}},{"cell_type":"code","source":"# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/refs/heads/main/gallery/transforms/helpers.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/utils.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_utils.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/coco_eval.py\")\n# os.system(\"wget https://raw.githubusercontent.com/pytorch/vision/main/references/detection/transforms.py\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-30T14:47:09.136212Z","iopub.execute_input":"2025-09-30T14:47:09.136572Z","iopub.status.idle":"2025-09-30T14:47:09.374338Z","shell.execute_reply.started":"2025-09-30T14:47:09.136547Z","shell.execute_reply":"2025-09-30T14:47:09.373462Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Install Hugging face transformer lib","metadata":{}},{"cell_type":"code","source":"!pip install -q git+https://github.com/huggingface/transformers.git","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-30T14:40:20.363168Z","iopub.execute_input":"2025-09-30T14:40:20.363442Z","iopub.status.idle":"2025-09-30T14:41:04.867063Z","shell.execute_reply.started":"2025-09-30T14:40:20.363421Z","shell.execute_reply":"2025-09-30T14:41:04.865617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport json\nimport utils\nimport cv2\nimport torchvision\nimport random\n\nimport numpy as np\n\nfrom torchvision import transforms as _transforms\nfrom typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple, Type, Union\nfrom torchvision.io import read_image\nfrom torchvision.transforms.v2 import functional as F, InterpolationMode, Transform, RandomZoomOut\nfrom torchvision.transforms import v2 as T\n\nfrom coco_eval import CocoEvaluator\nfrom coco_utils import get_coco_api_from_dataset\nfrom transformers import MaskFormerImageProcessor, Mask2FormerForUniversalSegmentation","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T10:55:23.044480Z","iopub.execute_input":"2025-12-20T10:55:23.044635Z","iopub.status.idle":"2025-12-20T10:55:48.838441Z","shell.execute_reply.started":"2025-12-20T10:55:23.044618Z","shell.execute_reply":"2025-12-20T10:55:48.837860Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset transformer functions","metadata":{}},{"cell_type":"code","source":"# Rotating images and masks trasform\nclass Rotate(object):\n    def __call__(self, image, target):        \n        rnd = random.randint(0,3)\n        degree = rnd * 90\n        if (degree != 0):\n            image = F.rotate(image, degree)\n            rotated_masks = F.rotate(target, degree)            \n            return image, rotated_masks\n        else:\n            return image, target\n            \nclass Flip(object):\n    def __call__(self, image, target):\n        # flipped_image = none\n        flipped_masks = None\n        rnd = random.random()\n        if(rnd >= 0.5):\n            if (rnd >= 0.75):\n                flipped_image = F.hflip(image)\n                flipped_masks = F.hflip(target)\n            else:\n                flipped_image = F.vflip(image)\n                flipped_masks = F.vflip(target)                \n            return flipped_image, flipped_masks\n        else:\n            return image, target\n        \nclass Zoomout(object):\n    def __call__(self, image, target):\n        zoom_out = RandomZoomOut(side_range=(1.2, 1.4), fill=0.5, p=0.5)\n        out = zoom_out({\"input\": image, \"target\": target[\"masks\"]})                \n        target[\"boxes\"] = tv_tensors.BoundingBoxes(masks_to_boxes(out[\"target\"]), format=\"XYXY\", canvas_size=F.get_size(image))\n        target[\"masks\"] = out[\"target\"]\n        return out[\"input\"], target\n   \n\n# Decreasing image quality transform\nclass DecreaseQuality(object):\n    \n    def __call__(self, image, target):     \n        if(random.random() >= 0.5):\n            width = image.shape[1]\n            height = image.shape[2]\n            decrease_size = T.Resize((int(width/2), int(height/2)))  # Specify desired size\n            increase_size = T.Resize((width, height))\n            resized_image = decrease_size(image)        \n            decreased_quality_img = increase_size(resized_image)\n            return decreased_quality_img, target\n        else:\n            return image, target\n\nclass Resize:    \n    prev_scale = None\n    def __init__(self, size):\n        self.size = size  # e.g. (height, width)\n\n    def __call__(self, img, masks):  \n        if (self.size == None):\n            scales = [512, 650, 800]\n            scale = random.choice(scales)\n            scale = (scale, scale) \n        else:\n            scale = self.size\n        # print(\"Prev Scale:\", Resize.prev_scale)        \n        # Resize the image (bilinear interpolation)        \n        img = F.resize(img, scale, interpolation=F.InterpolationMode.BILINEAR)\n\n        # Resize each mask (nearest-neighbor to avoid smoothing labels)\n        resized_masks = []\n        for m in masks:\n            m = F.resize(m.unsqueeze(0), scale, interpolation=F.InterpolationMode.NEAREST)\n            resized_masks.append(m.squeeze(0))\n\n        # Stack into a single tensor (N, H, W)\n        resized_masks = torch.stack(resized_masks, dim=0)\n\n        return img, resized_masks","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T08:43:43.171656Z","iopub.execute_input":"2025-12-15T08:43:43.172468Z","iopub.status.idle":"2025-12-15T08:43:43.182529Z","shell.execute_reply.started":"2025-12-15T08:43:43.172442Z","shell.execute_reply":"2025-12-15T08:43:43.181798Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Reading files functions","metadata":{}},{"cell_type":"code","source":"import torchvision.transforms.functional as F\n\nIMAGE_SIZE = (512, 512)\n\n# Dataset Helper Functions\ndef read_json_file(file_path):\n    with open(file_path, 'r') as file:\n        data = json.load(file)\n    return data\n    \ndef get_image_name(image_path):\n    splited = image_path.split(\"/\")\n    return \"/\".join(splited[-2:])\n\ndef get_image_annotations(json_data, image_name):\n    image_dic = None\n    annot_list = []\n    for image in json_data['images']:\n        if (image['file_name'] == image_name):\n            image_dic = image\n            break\n    found = False\n    for annot in json_data['annotations']:\n        if (annot['image_id'] == image_dic['id']):\n            annot_list.append(annot)\n            found = True\n        if (found and annot['image_id'] != image_dic['id']):            \n            break\n    \n    # Convert the list of annotations dictionary to a dictionary of annotaions list\n    annot_dic = {key: [d[key][0] if(key == 'segmentation') else d[key] for d in annot_list] for key in annot_list[0].keys()}    \n    annotation = Annotations(annot_dic, image_dic)\n    return annotation\n\ndef create_pairs(input_list):\n  \"\"\"Converts a list to a set of pairs.\n\n  Args:\n    input_list: The input list.\n\n  Returns:\n    A set of pairs.\n  \"\"\"\n  pairs = []\n  for i in range(0, len(input_list) - 1, 2):\n    pairs.append(tuple(input_list[i:i+2]))\n  return pairs\n\ndef create_contour_mask(contour_points, image_shape):\n    \"\"\"\n    Create a binary mask from contour points.\n\n    Args:\n        contour_points: List of tuples [(x,y), (x1,y1), ..., (xn,yn)]\n        image_shape: Tuple of (height, width) for the output mask\n\n    Returns:\n        Binary mask as numpy array where filled contour is 1, rest is 0\n    \"\"\"\n    # Convert list of tuples to numpy array and reshape for cv2    \n    contour = np.array(contour_points).reshape((-1,1,2)).astype(np.int32)\n\n    # Create empty mask\n    mask = np.zeros(image_shape, dtype=np.uint8)\n\n    # Fill the contour\n    cv2.fillPoly(mask, [contour], color=1)\n\n    return torch.tensor(mask)\n\ndef create_masks_from_contours(point_list, image_width, image_height):\n    masks = []\n    image_shape = (image_height, image_width)\n    for seg in point_list:        \n        points_pair = create_pairs(seg)  \n        if (len(points_pair) > 3):\n            masks.append(create_contour_mask(points_pair, image_shape))            \n    if (len(masks) == 0):\n        return None\n    return torch.stack(masks, dim=0)\n\ndef clean_boxes_masks(boxes, masks):        \n    removed_indices = []\n    for i, box in enumerate(boxes):\n        if( box[0] == box[2] or box[1] == box[3]):\n            removed_indices.append(i)\n            \n    if (len(removed_indices) > 0):\n        filtered_numbers = [num for num in range(0, masks.shape[0]) if num not in removed_indices]\n        boxes = boxes[filtered_numbers]\n        masks = masks[filtered_numbers]\n\n    return boxes, masks\n\ndef get_transform(train):\n    transforms = []\n    if train:        \n        # transforms.append(DecreaseQuality())        \n        transforms.append(Rotate())\n        transforms.append(Flip())\n        # transforms.append(Zoomout())    \n        transforms.append(Resize(None))\n    else:\n        transforms.append(Resize(IMAGE_SIZE))\n    transforms.append(T.ToDtype(torch.float, scale=True))\n    transforms.append(T.ToPureTensor())\n    return T.Compose(transforms)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T07:10:17.751502Z","iopub.execute_input":"2025-12-15T07:10:17.752023Z","iopub.status.idle":"2025-12-15T07:10:17.763431Z","shell.execute_reply.started":"2025-12-15T07:10:17.751996Z","shell.execute_reply":"2025-12-15T07:10:17.762820Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Annotations Class\nclass Annotations():\n    def __init__(self, annotation_dic, image_dic):\n        self.image_id = image_dic['id']\n        self.image_height = image_dic['height']\n        self.image_width = image_dic['width']\n        self.bbox_list = annotation_dic['bbox']\n        self.segmentation_list = annotation_dic['segmentation']\n        self.area_list = annotation_dic['area']    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T07:10:22.012738Z","iopub.execute_input":"2025-12-15T07:10:22.013334Z","iopub.status.idle":"2025-12-15T07:10:22.017414Z","shell.execute_reply.started":"2025-12-15T07:10:22.013307Z","shell.execute_reply":"2025-12-15T07:10:22.016800Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"'''\nReplace all 1 values with their corresponding mask object IDs and merge for Mask2Former preprocessing\nArgs:\n    masks (torch.Tensor): A 3D tensor of shape (N, H, W)\n        - N = number of masks (instances or categories).\n        - Each slice masks[i] is a 2D mask of shape (H, W), where:\n            * 0 = background\n            * 1 (or nonzero) = pixels belonging to that instance.\nReturns:\n    merged (torch.Tensor): A 2D tensor of shape (H, W)\n        - Contains integer labels:\n            * 0 = background\n            * 1 = pixels from first mask\n            * 2 = pixels from second mask\n            * ...\n            * N = pixels from N-th mask\n        - If multiple masks overlap, the later mask in the stack\n          (higher index i) overwrites earlier ones.\n'''\ndef mergeMasks(masks):\n    merged = torch.zeros_like(masks[0])\n    for i in range(masks.shape[0]):\n        msk = torch.where(masks[i] == 1, i+1, masks[i])\n        merged = torch.where(masks[i] != 0, msk, merged)\n    return merged","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T07:10:24.406518Z","iopub.execute_input":"2025-12-15T07:10:24.407056Z","iopub.status.idle":"2025-12-15T07:10:24.411354Z","shell.execute_reply.started":"2025-12-15T07:10:24.407033Z","shell.execute_reply":"2025-12-15T07:10:24.410739Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset class","metadata":{}},{"cell_type":"code","source":"# Create Dataset\nclass BuildingDataset(torch.utils.data.Dataset):\n    def __init__(self, root, annotation_file, transforms):\n        self.root = root\n        self.transforms = transforms\n        # load all image files, sorting them to\n        # ensure that they are aligned\n        self.imgs = list(sorted(os.listdir(os.path.join(root, \"image\"))))\n        self.annot_json = read_json_file(os.path.join(root, annotation_file))\n\n    def __getitem__(self, idx):\n        # load images and masks        \n        img_path = os.path.join(self.root, \"image\", self.imgs[idx])        \n        image_name = get_image_name(img_path)        \n        img = read_image(img_path)                \n        \n        # get image annotations\n        annotations = get_image_annotations(self.annot_json, image_name)                     \n        # num_objs = len(annotations.bbox_list)\n\n        # generate masks from contours          \n        H, W = img.shape[1], img.shape[2]\n        masks = create_masks_from_contours(annotations.segmentation_list, W, H)        \n\n        if(masks == None):            \n            return None\n        \n        if self.transforms is not None:            \n            img, masks = self.transforms(img, masks)\n            \n        merged_masks = mergeMasks(masks)\n        # print(\"merged_masks unique:\", torch.unique(merged_masks))\n        inst2class = {i+1: 0 for i in range(masks.shape[0])}        \n        # print(\"inst2class\", inst2class)\n\n        inputs = processor([img], [merged_masks], instance_id_to_semantic_id=inst2class, return_tensors=\"pt\")\n        inputs = {k: v.squeeze() if isinstance(v, torch.Tensor) else v[0] for k,v in inputs.items()}\n        inputs[\"image_id\"] = annotations.image_id\n        inputs[\"height\"] = annotations.image_height\n        inputs[\"width\"] = annotations.image_width\n        return inputs\n\n    def __len__(self):\n        return len(self.imgs)\n\ndef collate_fn(batch):\n    valid_examples = [example for example in batch if example is not None]\n    if (len(valid_examples) > 0):\n        pixel_values = torch.stack([example[\"pixel_values\"] for example in valid_examples])\n        pixel_mask = torch.stack([example[\"pixel_mask\"] for example in valid_examples])\n        class_labels = [example[\"class_labels\"] for example in valid_examples]\n        mask_labels = [example[\"mask_labels\"] for example in valid_examples]    \n        image_ids = [example[\"image_id\"] for example in valid_examples]\n        image_heights = [example[\"height\"] for example in valid_examples]\n        image_widths = [example[\"width\"] for example in valid_examples]\n        return {\"pixel_values\": pixel_values, \"pixel_mask\": pixel_mask, \"class_labels\": class_labels, \"mask_labels\": mask_labels, \"image_ids\": image_ids, \"image_heights\": image_heights, \"image_widths\": image_widths}\n    else:\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T07:10:27.666426Z","iopub.execute_input":"2025-12-15T07:10:27.667208Z","iopub.status.idle":"2025-12-15T07:10:27.676500Z","shell.execute_reply.started":"2025-12-15T07:10:27.667182Z","shell.execute_reply":"2025-12-15T07:10:27.675908Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Create train and validation dataloaders","metadata":{}},{"cell_type":"code","source":"path = '/kaggle/input/building-extraction-generalization-2024'\ntrain_dataset = BuildingDataset(f'{path}/train', 'train.json', get_transform(train=True))\ntrain_dataloader = torch.utils.data.DataLoader(\n    train_dataset,\n    batch_size=1,\n    shuffle=True,\n    collate_fn=collate_fn\n)\n\nval_dataset = BuildingDataset(f'{path}/val', 'val.json', get_transform(train=False))\nval_dataloader = torch.utils.data.DataLoader(\n    val_dataset,\n    batch_size=2,\n    shuffle=False,\n    collate_fn=collate_fn\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T07:10:31.653108Z","iopub.execute_input":"2025-12-15T07:10:31.653386Z","iopub.status.idle":"2025-12-15T07:10:34.131139Z","shell.execute_reply.started":"2025-12-15T07:10:31.653364Z","shell.execute_reply":"2025-12-15T07:10:34.130456Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Show data sample","metadata":{}},{"cell_type":"code","source":"# Show samples from dataset\nimport matplotlib.pyplot as plt\nfrom helpers import plot\nfrom torchvision.tv_tensors import Image\nfrom torchvision.utils import draw_bounding_boxes\n\ndef display_tensor_image(tensor_image, title=None):\n    \"\"\"\n    Display a PyTorch tensor image that can handle both grayscale and RGB formats\n    \n    Args:\n        tensor_image (torch.Tensor): Image tensor with shape:\n            - [H, W] for grayscale images\n            - [3, H, W] for RGB images (channel-first format)\n        title (str, optional): Custom title for the plot\n    \"\"\"\n    # Convert to numpy and move to CPU if needed\n    image_np = tensor_image.detach().cpu().numpy()\n    \n    # Handle different tensor shapes\n    if len(image_np.shape) == 2:  # Grayscale: [H, W]\n        H, W = image_np.shape\n        cmap = 'gray'\n        shape_info = f\"Grayscale [{H}, {W}]\"\n        \n    elif len(image_np.shape) == 3 and image_np.shape[0] == 3:  # RGB: [3, H, W]\n        C, H, W = image_np.shape\n        # Convert from [C, H, W] to [H, W, C] for matplotlib\n        image_np = image_np.transpose(1, 2, 0)\n        cmap = None\n        shape_info = f\"RGB [3, {H}, {W}]\"\n        \n    else:\n        raise ValueError(f\"Unsupported tensor shape: {tensor_image.shape}. \"\n                        f\"Expected [H, W] for grayscale or [3, H, W] for RGB\")\n    \n    # Normalize values if needed\n    if image_np.max() > 1.0:\n        image_np = image_np / 255.0\n    \n    # Clip values to ensure they're in valid range [0, 1]\n    image_np = np.clip(image_np, 0, 1)\n    \n    # Create the plot\n    plt.figure(figsize=(5, 5))\n    plt.imshow(image_np, cmap=cmap)\n    plt.axis('off')\n    \n    # Set title\n    if title:\n        plt.title(f'{title}\\nShape: {shape_info}')\n    else:\n        plt.title(f'PyTorch Tensor Image\\nShape: {shape_info}')\n    \n    plt.tight_layout()\n    plt.show()\n\n\n\n'''inputs = dataset[0]\nfor k,v in inputs.items():\n    if isinstance(v, torch.Tensor):\n        print(k,v.shape)\n    if k == \"pixel_mask\":\n        print(v)\n    if k == \"mask_labels\":\n        print(display_tensor_image(v[1]))\n   '''     \n# batch = next(iter(train_dataloader))\n# for k,v in batch.items():\n#   if isinstance(v, torch.Tensor):\n#     print(k,v.shape)\n#   else:\n#     print(k,len(v)) \n\n# print(batch['mask_labels'][0].shape)\n# display_tensor_image(batch['pixel_values'][0])\n# display_tensor_image(batch['mask_labels'][0][0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-08T12:25:51.867860Z","iopub.execute_input":"2025-11-08T12:25:51.868567Z","iopub.status.idle":"2025-11-08T12:25:51.878961Z","shell.execute_reply.started":"2025-11-08T12:25:51.868542Z","shell.execute_reply":"2025-11-08T12:25:51.878308Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train Mask2Former pretrained model","metadata":{}},{"cell_type":"code","source":"import torch\nfrom tqdm.auto import tqdm\nfrom transformers import MaskFormerForInstanceSegmentation\nfrom pycocotools.coco import COCO\nfrom pycocotools.cocoeval import COCOeval\nfrom pycocotools import mask as mask_utils\nfrom transformers import Mask2FormerImageProcessor\nfrom transformers import get_scheduler\nfrom torch.optim.lr_scheduler import StepLR, CosineAnnealingLR\nimport torch.nn.functional as FN\n\nos.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'\n\nprocessor = Mask2FormerImageProcessor(reduce_labels=False, ignore_index=0, do_resize=False, do_rescale=False, do_normalize=False)\nnum_epochs = 7\n\ndef to_device(batch, device):\n    if isinstance(batch, torch.Tensor):\n        return batch.to(device)\n    elif isinstance(batch, dict):\n        return {k: to_device(v, device) for k, v in batch.items()}\n    elif isinstance(batch, list):\n        return [to_device(v, device) for v in batch]\n    else:\n        return batch\n\ndef setBackboneUnLearnable(model, learnable):\n    for name, param in model.named_parameters():\n        if (name.startswith('model.pixel_level_module.encoder')):\n            param.requires_grad = learnable\n\ndef convert_to_coco_predictions(outputs, image_ids, image_widths, image_heights, processor, threshold=0.5):\n    \"\"\"\n    Convert batched Mask2Former outputs into COCO-style predictions.\n\n    Args:\n        outputs: model output from Mask2Former (batched)\n        image_ids: list of image IDs (length = batch size)\n        processor: (not used here but kept for consistency)\n        threshold: score threshold\n        target_size: (H, W) for resizing masks to original or fixed size\n    \"\"\"\n    pred_logits = outputs.class_queries_logits     # [B, num_queries, num_classes+1]\n    pred_masks = outputs.masks_queries_logits       # [B, num_queries, H, W]\n\n    batch_size = pred_logits.shape[0]\n    coco_results = []\n\n    for b in range(batch_size):\n        image_id = image_ids[b]\n        logits = pred_logits[b]   # [num_queries, num_classes+1]\n        masks = pred_masks[b]     # [num_queries, H, W]\n        image_height = image_heights[b]\n        image_width = image_widths[b]\n        target_size = (image_width, image_height)\n\n        # Compute probabilities and labels (remove \"no object\" class)\n        probs = logits.softmax(-1)[:, :-1]\n        scores, labels = probs.max(-1)\n\n        # Resize and threshold masks\n        masks = masks.unsqueeze(1)  # [num_queries, 1, H, W]\n        masks = FN.interpolate(masks, size=target_size, mode=\"bilinear\", align_corners=False)\n        masks = masks.squeeze(1).sigmoid().cpu().numpy() > 0.5\n\n        for score, label, mask in zip(scores, labels, masks):\n            if score < threshold:\n                continue\n\n            # RLE encoding for COCO\n            rle = mask_utils.encode(np.asfortranarray(mask.astype(np.uint8)))\n            rle[\"counts\"] = rle[\"counts\"].decode(\"utf-8\")\n\n            coco_results.append({\n                \"image_id\": int(image_id),\n                \"category_id\": int(label.item()) if hasattr(label, \"item\") else int(label),\n                \"segmentation\": rle,\n                \"score\": float(score.item())\n            })\n\n    return coco_results\n\ndef evaluate(model, dataloader, device=\"cuda\"):\n    model.eval()\n    coco_gt = COCO(\"/kaggle/input/building-extraction-generalization-2024/val/val.json\")\n    \n    all_coco_results = []    \n    \n    for batch in tqdm(dataloader, desc=\"Evaluating\", leave=False):\n        # pixel_values = batch[\"pixel_values\"][1::].to(device)\n        # pixel_mask = batch[\"pixel_mask\"][1::].to(device)\n        # image_id = batch[\"image_ids\"][1]\n        pixel_values = batch[\"pixel_values\"].to(device)\n        pixel_mask = batch[\"pixel_mask\"].to(device)\n        image_ids = batch[\"image_ids\"]    \n        image_widths = batch[\"image_widths\"]\n        image_heights = batch[\"image_heights\"]\n        \n        # mask_labels = mergeMasks(batch[\"mask_labels\"][1].to(device)).unsqueeze(0)            \n        \n        with torch.no_grad():\n            outputs = model(pixel_values=pixel_values, pixel_mask=pixel_mask)                \n    \n        preds = convert_to_coco_predictions(outputs, image_ids, image_widths, image_heights, processor)        \n        all_coco_results += preds\n    # print(all_coco_results)\n    coco_gt.dataset['info'] = {\"info\":{\"year\": 2025, \"version\": \"1.0\", \"description\": \"building\", \"contributor\": \"empty\", \"url\": \"empty\", \"date_created\": 20250810}}\n    # print(all_coco_results)\n    coco_dt = coco_gt.loadRes(all_coco_results)\n    coco_eval = COCOeval(coco_gt, coco_dt, \"segm\")\n    coco_eval.evaluate()\n    coco_eval.accumulate()\n    coco_eval.summarize()\n\ndef save_last_steps(last_steps):\n    print(\"Last Steps:\", last_steps)\n    with open(\"last_steps.txt\", \"w\") as f:\n        f.write(str(last_steps))\n\ndef load_last_steps():\n    try:\n        with open(\"last_steps.txt\", \"r\") as f:\n            return int(f.read())\n    except FileNotFoundError:\n        return 0\n\ndef create_scheduler(optimizer, train_dataloader):    \n    lr_scheduler = None\n    num_training_steps = num_epochs * len(train_dataloader)\n    last_steps = max([0, load_last_steps() - 6000])\n    STEP_SIZE = 500\n    type = \"PyTorch\"\n    if (type == \"transformer\"):\n        lr_scheduler = get_scheduler(\n        name=\"linear\",         # or \"cosine\", \"polynomial\", \"constant\", etc.\n        optimizer=optimizer,\n        num_warmup_steps=5,  # typical: 500–1000 for transformer fine-tuning\n        num_training_steps=num_training_steps,)\n    elif (type == \"PyTorch\"):        \n        print(\"LR:\", optimizer.param_groups[0][\"lr\"])\n        print(\"last_steps:\", last_steps)\n        lr = optimizer.param_groups[0]['lr']\n        gamma = 0.95        \n        lr = (gamma ** int(last_steps / STEP_SIZE)) * lr        \n        print(\"LR:\", f\"{lr:.7f}\")\n        lr_scheduler = StepLR(optimizer, step_size=STEP_SIZE, gamma=0.95)\n        optimizer.param_groups[0]['lr'] = lr\n        optimizer.lr = lr\n\n    return lr_scheduler\n\ntry:\n    model = Mask2FormerForUniversalSegmentation.from_pretrained(\"mask2former-checkpoint\")\nexcept Exception as e:\n    if(str(e).startswith(\"mask2former-checkpoint is not a local folder\")):\n        model = Mask2FormerForUniversalSegmentation.from_pretrained(\"facebook/mask2former-swin-large-coco-panoptic\")\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\nsetBackboneUnLearnable(model, True)\n\nbackbone_params = [p for n,p in model.named_parameters() if \"encoder\" in n]\nhead_params = [p for n,p in model.named_parameters() if \"encoder\" not in n]\n\noptimizer = torch.optim.Adam([\n    {\"params\": head_params,     \"lr\": 2e-4},\n    {\"params\": backbone_params, \"lr\": 1e-4}    \n], lr=2e-4)\n\nlr_scheduler = create_scheduler(optimizer, train_dataloader)\n\nlast_steps = load_last_steps()\nsteps = 0\nfor epoch in range(num_epochs):\n  running_loss = 0.0\n  num_samples = 0\n  print(\"Epoch:\", epoch)\n  model.train()\n  for idx, batch in enumerate(tqdm(train_dataloader)):\n      # break\n      # Reset the parameter gradients\n      optimizer.zero_grad()\n      if(batch == None):\n          continue\n      # Forward pass\n      outputs = model(\n              pixel_values=batch[\"pixel_values\"].to(device),\n              mask_labels=[labels.to(device) for labels in batch[\"mask_labels\"]],\n              class_labels=[labels.to(device) for labels in batch[\"class_labels\"]],\n      )\n\n      # Backward propagation\n      loss = outputs.loss\n      loss.backward()\n\n      batch_size = batch[\"pixel_values\"].size(0)\n      running_loss += loss.item()\n      num_samples += batch_size\n\n      \n\n      if idx % 100 == 0:\n          print(\"Loss:\", running_loss/num_samples)   \n          \n          lr_sch = lr_scheduler.get_last_lr()[0]    \n          l = optimizer.param_groups[0]['lr']\n          print(\"LR rate:\" ,f\"{l:.7f}\")\n          print(\"LR scheduler:\", f\"{lr_sch:.7f}\")\n          model.save_pretrained(\"mask2former-checkpoint\")\n          steps += 100\n          save_last_steps(steps + last_steps)\n\n      # Optimization\n      optimizer.step()\n      if (last_steps > 0 or epoch > 0):\n          lr_scheduler.step()      \n      optimizer.zero_grad()\n      \n  evaluate(model, val_dataloader, device=device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-05T07:17:26.273089Z","iopub.execute_input":"2025-12-05T07:17:26.273350Z","iopub.status.idle":"2025-12-05T07:27:33.137349Z","shell.execute_reply.started":"2025-12-05T07:17:26.273329Z","shell.execute_reply":"2025-12-05T07:27:33.136494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -r mask2former-checkpoint\n!rm last_steps.txt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-20T19:27:59.392495Z","iopub.execute_input":"2025-11-20T19:27:59.393415Z","iopub.status.idle":"2025-11-20T19:27:59.864292Z","shell.execute_reply.started":"2025-11-20T19:27:59.393380Z","shell.execute_reply":"2025-11-20T19:27:59.863235Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Write output of test data to submission file","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torch.nn.functional as FN\nfrom PIL import Image\nimport pandas as pd\nfrom transformers import Mask2FormerImageProcessor, AutoImageProcessor\n\n# processor = Mask2FormerImageProcessor(reduce_labels=False, ignore_index=0, do_resize=False, do_rescale=False, do_normalize=False)\nimage_processor = AutoImageProcessor.from_pretrained(\"facebook/mask2former-swin-small-coco-instance\")\nmodel = Mask2FormerForUniversalSegmentation.from_pretrained(\"mask2former-checkpoint\")\nmodel.eval()\n\ndef convert_mask_to_polygon(mask):\n    # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)\n    contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)\n    coords = []        \n    for obj in contours:        \n        for point in obj:\n            coords.append((int(point[0][0]),int(point[0][1])))\n    return coords\n\ndef create_polygon_from_contours(mask):    \n    threshold = 0.5\n    mask = mask.squeeze(0)\n    mask[mask >= threshold] = 1\n    mask[mask < threshold] = 0\n    mask = mask.to(dtype=torch.uint8)\n    numpy_mask = mask.detach().cpu().numpy()    \n    return convert_mask_to_polygon(numpy_mask)\n\ndef generate_submission(image_list):\n    df = pd.DataFrame(image_list)\n    df.rename(columns={\"imageId\": \"ImageID\", \"objectList\": \"Coordinates\"}, inplace=True)                                                            \n    df.to_csv(\"submission.csv\", index=False, header=True)\n    \ntest_data_path = '/kaggle/input/building-extraction-generalization-2024/test'\nimage_file_list = list(sorted(os.listdir(os.path.join(test_data_path, \"image\"))))\nimage_polygon_list = []\nscore_threshold = 0.5\n\nfor image_id, image_file in enumerate(image_file_list): \n    print(\"ImageID:\", image_id)\n    pil_image = Image.open(os.path.join(test_data_path, \"image\", image_file))    \n    inputs = image_processor(pil_image, return_tensors=\"pt\")\n    if 'pixel_values' in inputs:\n        inputs['pixel_values'] = inputs['pixel_values'].float()\n    if 'pixel_mask' in inputs:\n        inputs['pixel_mask'] = inputs['pixel_mask'].float()\n\n    outputs = model(**inputs)\n    mask_logits  = outputs[\"masks_queries_logits\"] \n    class_logits = outputs[\"class_queries_logits\"]\n    \n    masks = FN.interpolate(mask_logits, size=pil_image.size, mode=\"bilinear\", align_corners=False)\n    print(masks.shape)\n    probs = class_logits.softmax(-1)[0, :, :-1]  # ignore \"no object\" class\n    scores, labels = probs.max(-1)\n    polygon_list = []\n    for index, mask in enumerate(masks[0]):             \n        mask_probs = torch.sigmoid(mask)\n        if (scores[index] >= score_threshold):\n            polygon = create_polygon_from_contours(mask_probs)\n            if (len(polygon) > 3): \n                polygon_list.append(polygon)\n        # plt.imshow(mask_probs.cpu().detach(), cmap='gray')\n        # plt.title(\"Predicted Mask\")\n        # plt.colorbar()\n        # plt.show()\n        # if(index == 2):\n        #     break\n    image_polygon_list.append({'imageId': image_id, 'objectList': str(polygon_list)})\n    print(\"LEN:\", len(image_polygon_list))    \n    \ngenerate_submission(image_polygon_list)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!echo '{\"username\":\"mojtabajahan\",\"key\":\"f32732ef8da53c045f87de56c8e1d5a5\"}'","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"After 2 epoch:\n\nLoss: 10.542830624580382\n\nValidation Loss: 17.8016\n\n","metadata":{}}]}