{"metadata":{"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":30892,"databundleVersionId":2715462,"sourceType":"competition"}],"dockerImageVersionId":30588,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# ***1. REFERENCES & IMPORT LIBRARY***","metadata":{}},{"cell_type":"code","source":"!pip install torchsummary\n!pip install torchgeometry\n!pip install torchmetrics","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:38:37.261454Z","iopub.execute_input":"2023-11-17T06:38:37.262077Z","iopub.status.idle":"2023-11-17T06:39:14.141638Z","shell.execute_reply.started":"2023-11-17T06:38:37.262048Z","shell.execute_reply":"2023-11-17T06:39:14.140452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchsummary import summary\nfrom torchgeometry.losses import one_hot\nimport os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nimport cv2\nimport time\nimport imageio\nimport matplotlib.pyplot as plt\nimport time\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nfrom torch import Tensor\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision.transforms import Resize, PILToTensor, ToPILImage, Compose, InterpolationMode\nfrom collections import OrderedDict\nimport wandb\nimport torchmetrics","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:14.143721Z","iopub.execute_input":"2023-11-17T06:39:14.144032Z","iopub.status.idle":"2023-11-17T06:39:22.397957Z","shell.execute_reply.started":"2023-11-17T06:39:14.144007Z","shell.execute_reply":"2023-11-17T06:39:22.397148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ***2. DATA VÀ DATALOADER***","metadata":{}},{"cell_type":"markdown","source":"## 2.1 Làm quen với data, thử in train và train_gt dataset","metadata":{}},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nimage_path = \"/kaggle/input/bkai-igh-neopolyp/train/train/0081835cf877e004e8bfb905b78a9139.jpeg\"\nimage = cv2.imread(image_path)\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)    # cv2 đọc BGR\nplt.imshow(image)     # PIL và plt đọc RGB\nprint(\"1 ảnh gốc\")\nprint(image.shape, \"High:\", image.shape[0], \"Width:\", image.shape[1], \"Chanel:\", image.shape[2])\n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:22.398968Z","iopub.execute_input":"2023-11-17T06:39:22.399378Z","iopub.status.idle":"2023-11-17T06:39:23.120776Z","shell.execute_reply.started":"2023-11-17T06:39:22.399354Z","shell.execute_reply":"2023-11-17T06:39:23.11987Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nmask_path = \"/kaggle/input/bkai-igh-neopolyp/train_gt/train_gt/0081835cf877e004e8bfb905b78a9139.jpeg\"\nmask = cv2.imread(mask_path)    # mặt nạ\nmask = cv2.cvtColor(mask, cv2.COLOR_BGR2RGB)\nplt.imshow(mask)     \nprint(\"Ảnh mặt nạ mask\")\nprint(mask.shape, \"High:\", mask.shape[0], \"Width:\", mask.shape[1], \"Chanel:\", mask.shape[2])","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:23.123006Z","iopub.execute_input":"2023-11-17T06:39:23.123313Z","iopub.status.idle":"2023-11-17T06:39:23.654761Z","shell.execute_reply.started":"2023-11-17T06:39:23.123287Z","shell.execute_reply":"2023-11-17T06:39:23.653891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.2 DataLoader: Transform và class Neopolyp_DataClass","metadata":{}},{"cell_type":"markdown","source":"### 2.2.1 Transform:    \n- https://app.roboflow.com/ sinh ảnh hoặc tự code (đã từng). Có hơn 10 loại aug. \n- Lib: Update transform thêm Augmentation: https://albumentations.ai/docs/getting_started/mask_augmentation/\nNhiều hơn: https://albumentations.ai/docs/api_reference/full_reference/","metadata":{}},{"cell_type":"code","source":"import os\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision.transforms import Resize, PILToTensor, ToPILImage, Compose, InterpolationMode\nfrom PIL import Image\nimport torch\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport cv2\n\n# transform cơ bản, lát có transform update augmentation \nheight, width = 800, 1120\nbasic_transform = Compose([Resize((height, width), interpolation=InterpolationMode.BILINEAR),   # height = 800, width = 1120, kích thước ảnh tất cả ảnh phải cùng nhau và cùng là bội của 32\n                     PILToTensor()])   \n# phương pháp nội suy song tuyến (bilinear interpolation), \n# - `InterpolationMode.BILINEAR` `torchvision` PyTorch, dạng tensor PyTorch.\n#- `cv2.INTER_LINEAR`  `cv2` (OpenCV), mảng numpy hoặc raw.\n\n# Update transform thêm Augmentation: https://albumentations.ai/docs/getting_started/mask_augmentation/\n# Nhiều hơn: https://albumentations.ai/docs/api_reference/full_reference/\ntrain_transform = A.Compose(\n    [\n    A.Resize(256, 256, interpolation=cv2.INTER_LINEAR),       # kiểu numpy array, \n    A.VerticalFlip(),\n    A.HorizontalFlip(),  #  mặc định p=0.5\n        \n    A.OneOf([\n    A.Blur(blur_limit=3, p=0.3),   # làm mờ, độ mờ ngẫu nhiên từ 0-3\n    A.GaussianBlur(blur_limit=3, p=0.3),\n    A.MedianBlur(blur_limit=3, p=0.3),\n    A.RandomBrightnessContrast()  # độ tương phản hình ảnh, mặc định p=0.2\n    ], p=0.3),\n        \n    A.Rotate(limit=30, p=0.3),\n        \n    A.RGBShift (r_shift_limit=20, g_shift_limit=20, b_shift_limit=20, always_apply=False, p=0.5), \n    # ngẫu nhiên dịch chuyển kênh màu từ 0-20\n    A.Cutout (num_holes=5, max_h_size=8, max_w_size=8, fill_value=0, always_apply=False),    # mặc định p=0.5\n    # cắt đi 1 khoảng model vẫn học được    \n    A.GaussNoise(p=0.3),\n        \n    A.Normalize (mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), max_pixel_value=255.0, always_apply=False, p=1.0),\n    # Mean and std of ImageNet (tập data rất lớn) là như trên phổ biến cho các model, \n    ToTensorV2(), \n]\n)\n\n# Compose là 1 lần đi qua các ảnh gốc này, mỗi phép biến đổi xuất hiện với xác suất p \n\n# val ko cần thêm nhiễu gì cả, chỉ cần resize và normalize \nval_transform = A.Compose([\n    A.Resize(256, 256, interpolation=cv2.INTER_LINEAR),\n    A.Normalize (mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), max_pixel_value=255.0, always_apply=False, p=1.0),\n    # Mean and std of ImageNet (tập data rất lớn) là như trên phổ biến cho các model, \n    ToTensorV2(),\n])\n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:23.656351Z","iopub.execute_input":"2023-11-17T06:39:23.656763Z","iopub.status.idle":"2023-11-17T06:39:25.226082Z","shell.execute_reply.started":"2023-11-17T06:39:23.656729Z","shell.execute_reply":"2023-11-17T06:39:25.225042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 2.2.2 DataLoader:   \n- https://pytorch.org/docs/stable/data.html\n- Dataset trong Pytorch có Map-style datasets (thường dùng) và Iterable-style datasets\n- DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,\n           batch_sampler=None, num_workers=0, collate_fn=None,\n           pin_memory=False, drop_last=False, timeout=0,\n           worker_init_fn=None, *, prefetch_factor=2,\n           persistent_workers=False)\n           \n    - DataLoader: tự động tạo các batch từ Dataset, cung cấp xáo trộn data và tải song song. ","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nfrom torch.utils.data import Dataset\n\nclass NeolypsDataset(Dataset):\n    \"\"\"\n    Dataset class cho việc huấn luyện và kiểm tra mô hình trên bộ dữ liệu Neolyps.\n    Attributes:\n        img_list (list): Danh sách đường dẫn tới các hình ảnh.\n        mask_list (list): Danh sách đường dẫn tới các mask ứng với hình ảnh.\n        transform (callable, optional): Transform function áp dụng lên ảnh và mask.\n    \"\"\"\n\n    def __init__(self, img_list, mask_list, transform=None):\n        \"\"\"\n        Khởi tạo dataset với danh sách hình ảnh và mask.\n        Parameters:\n            img_list (list): Danh sách đường dẫn hình ảnh.\n            mask_list (list): Danh sách đường dẫn mask.\n            transform (callable, optional): Hàm biến đổi áp dụng lên dữ liệu.\n        \"\"\"\n        self.img_list = img_list\n        self.mask_list = mask_list\n        self.transform = transform\n\n        print(f'Số lượng hình ảnh: {len(self.img_list)}')\n        print(f'Số lượng mask: {len(self.mask_list)}')\n\n    def __len__(self):\n        \"\"\"\n        Trả về số lượng mẫu trong dataset.\n        \"\"\"\n        return len(self.img_list)\n\n    def _read_mask(self, mask_path):\n        \"\"\"\n        Đọc và xử lý mask từ đường dẫn đã cho.\n        Parameters:\n            mask_path (str): Đường dẫn đến mask.\n        Returns:\n            np.array: Mask đã được xử lý.\n        \"\"\"\n        # Đọc và chuyển đổi sang không gian màu HSV\n        mask = cv2.imread(mask_path, cv2.IMREAD_COLOR)\n        mask = cv2.cvtColor(mask, cv2.COLOR_BGR2HSV)\n\n        # Định nghĩa các ngưỡng màu cho red và green\n        # Red: Màu đỏ\n        lower1, upper1 = np.array([0, 100, 20]), np.array([10, 255, 255])\n        lower2, upper2 = np.array([160, 100, 20]), np.array([179, 255, 255])\n        # Green: Màu xanh lá\n        lower_g, upper_g = np.array([35, 25, 25]), np.array([70, 255, 255])\n\n        # Tạo mask cho màu đỏ và xanh lá\n        red_mask = cv2.inRange(mask, lower1, upper1) + cv2.inRange(mask, lower2, upper2)\n        green_mask = cv2.inRange(mask, lower_g, upper_g)\n\n        # Đánh dấu các pixel tương ứng\n        red_mask[red_mask != 0] = 1   # Neopolyp pixel = 1\n        green_mask[green_mask != 0] = 2 # Non-neopolyp pixel = 2, background = 0\n\n        # Kết hợp mask\n        full_mask = cv2.bitwise_or(red_mask, green_mask).astype(np.int8)\n\n        return full_mask\n\n    def __getitem__(self, idx):\n        \"\"\"\n        Lấy một mẫu dữ liệu từ dataset.\n        Parameters:\n            idx (int): Index của mẫu dữ liệu.\n        Returns:\n            tuple: Bao gồm ảnh và mask tương ứng đã được biến đổi.\n        \"\"\"\n        # Đọc hình ảnh và mask\n        img_path, mask_path = self.img_list[idx], self.mask_list[idx]\n        img = cv2.imread(img_path, cv2.IMREAD_COLOR)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        mask = self._read_mask(mask_path)\n\n        # Áp dụng transform nếu có\n        if self.transform:\n            transformed = self.transform(image=img, mask=mask)\n            img, mask = transformed['image'].float(), transformed['mask'].float()\n\n        return img, mask\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:25.227392Z","iopub.execute_input":"2023-11-17T06:39:25.227921Z","iopub.status.idle":"2023-11-17T06:39:25.243859Z","shell.execute_reply.started":"2023-11-17T06:39:25.227891Z","shell.execute_reply":"2023-11-17T06:39:25.242953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load hết địa chỉ vào 1 list\nimg_dir = '/kaggle/input/bkai-igh-neopolyp/train/train'\nmask_dir = '/kaggle/input/bkai-igh-neopolyp/train_gt/train_gt'\nimg_list = [os.path.join(img_dir, x) for x in os.listdir(img_dir)]\nmask_list = [os.path.join(mask_dir, x) for x in os.listdir(mask_dir)]\nprint(len(img_list), len(mask_list))\n# print(img_list)  # load hết địa chỉ vào 1 list\n\n# Tạo Train, Val Dataset với train_transform, val_transform - chưa Dataloader\ntrain_size = int(0.9 * len(img_list))\ntrain_dataset = NeolypsDataset(img_list[:train_size], mask_list[:train_size], transform=train_transform)\nval_dataset = NeolypsDataset(img_list[train_size:], mask_list[train_size:], transform=val_transform)\n\n# get a sample image and mask from the dataset, # plot the image and mask\nimport matplotlib.pyplot as plt\nimg, mask = train_dataset[0]\nprint(img.shape, img.dtype, mask.shape, mask.dtype)\n\nfig, ax = plt.subplots(1, 2, figsize=(10, 5))\nax[0].imshow(img.permute(1, 2, 0))\nax[0].set_title('Image')\nax[1].imshow(mask)\nax[1].set_title('Mask')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:25.245153Z","iopub.execute_input":"2023-11-17T06:39:25.245436Z","iopub.status.idle":"2023-11-17T06:39:26.100362Z","shell.execute_reply.started":"2023-11-17T06:39:25.245412Z","shell.execute_reply":"2023-11-17T06:39:26.09935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tạo Dataloader\nbatch_size = 4  # kích thước mỗi batch trong train-test, \n# số mẫu data được đưa vào model mỗi lần cập nhật trọng số\n# batch_size càng lớn thì càng nhanh nhưng cũng càng tốn bộ nhớ\n\ntrain_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True)\n\nprint(f\"Number of batches in train_dataloader: {len(train_dataloader)}\")\nprint(f\"Number of samples in train_dataloader: {len(train_dataloader.dataset)}\")\nprint(f\"Batch size of train_dataloader: {train_dataloader.batch_size}\")\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:26.101971Z","iopub.execute_input":"2023-11-17T06:39:26.102275Z","iopub.status.idle":"2023-11-17T06:39:26.108327Z","shell.execute_reply.started":"2023-11-17T06:39:26.102247Z","shell.execute_reply":"2023-11-17T06:39:26.107367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Code for UnNormalize - nếu muốn xem ảnh gốc: \nclass UnNormalize(object): \n    def __init__(self, mean, std): \n        self.mean = mean\n        self.std = std\n        \n    def __call__(self, tensor): \n        for t, m, s in zip(tensor, self.mean, self.std): \n            t.mul_(s).add_(m)  # Sử dụng add_ thay vì add để thực hiện phép tính trực tiếp trên tensor\n        return tensor\n\nunorm = UnNormalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:26.109528Z","iopub.execute_input":"2023-11-17T06:39:26.109833Z","iopub.status.idle":"2023-11-17T06:39:26.119888Z","shell.execute_reply.started":"2023-11-17T06:39:26.109785Z","shell.execute_reply":"2023-11-17T06:39:26.118969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ***3. MODEL***\nLib: https://github.com/qubvel/segmentation_models.pytorch#architectures\n`!pip install segmentation-models-pytorch`. Train tiếp model đã được train (thiết lập tham số config)","metadata":{}},{"cell_type":"code","source":"\ninstall segmentation-models-pytorch","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:26.124132Z","iopub.execute_input":"2023-11-17T06:39:26.124405Z","iopub.status.idle":"2023-11-17T06:39:44.527851Z","shell.execute_reply.started":"2023-11-17T06:39:26.124381Z","shell.execute_reply":"2023-11-17T06:39:44.526803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import segmentation_models_pytorch as smp\n\nmodel = smp.UnetPlusPlus(\n    encoder_name=\"resnet101\",       # Chọn ResNet-101 làm backbone\n    encoder_weights=\"imagenet\",    # Sử dụng trọng số được huấn luyện trên ImageNet\n    in_channels=3,                 # Số kênh đầu vào, = 3 cho ảnh RGB\n    classes=3                      # Số lớp đầu ra = 3, đỏ xanh background\n)\n# print(model)\n# import torchsummary\n# torchsummary.summary(model,(3,256,256))\n\n# Kích thước sau Augmentation: \n# img: torch.Size([3, 256, 256]) torch.float32\n# mask: torch.Size([256, 256]) torch.float32","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:44.529333Z","iopub.execute_input":"2023-11-17T06:39:44.529652Z","iopub.status.idle":"2023-11-17T06:39:50.049424Z","shell.execute_reply.started":"2023-11-17T06:39:44.529624Z","shell.execute_reply":"2023-11-17T06:39:50.048591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training\n\nhttps://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html","metadata":{}},{"cell_type":"code","source":"torch.cuda.empty_cache()\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:50.050843Z","iopub.execute_input":"2023-11-17T06:39:50.051492Z","iopub.status.idle":"2023-11-17T06:39:50.056296Z","shell.execute_reply.started":"2023-11-17T06:39:50.051455Z","shell.execute_reply":"2023-11-17T06:39:50.05525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 4   # thiết lập trên dataloader rồi\n\n#thiết lập cpu/gpu luôn\ndevice = torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)  \n\n# Hàm loss\ncriterion = nn.CrossEntropyLoss()   # # Hàm tính loss là Cross Entropy Loss\n\n# Hàm lr\nlr = 1e-4\noptimizer = optim.Adam(model.parameters(), lr=lr)  # Sử dụng optimizer Adam\n\n# Khởi tạo các metrics để đo lường\ndice_fn = torchmetrics.Dice(num_classes=3, average=\"macro\").to(device)\niou_fn = torchmetrics.JaccardIndex(num_classes=3, task=\"multiclass\", average=\"macro\").to(device)\n\n# \nnum_epochs = 25\nbest_val_loss = 9999999\ndisplay_step = 50\n\ndef accuracy_function(preds, labels):\n    # Hàm tính accuracy\n    return torch.sum(preds == labels).float() / labels.numel()\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:50.057372Z","iopub.execute_input":"2023-11-17T06:39:50.057653Z","iopub.status.idle":"2023-11-17T06:39:53.788879Z","shell.execute_reply.started":"2023-11-17T06:39:50.057629Z","shell.execute_reply":"2023-11-17T06:39:53.787856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import wandb\nwandb.login(key = \"c8767797aae76cbcd389ff29929ace1ac3021161\")    # key's DoanNgocCuong\nwandb.init(\n    project = \"PolypSegment\"\n)","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:39:53.790375Z","iopub.execute_input":"2023-11-17T06:39:53.790688Z","iopub.status.idle":"2023-11-17T06:40:27.199908Z","shell.execute_reply.started":"2023-11-17T06:39:53.790663Z","shell.execute_reply":"2023-11-17T06:40:27.199024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm   # not import tqdm\n\n#thiết lập cpu/gpu lại\ndevice = torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)  \n\nnum_epochs = 30  # Số lượng epochs\n\n# Training loop\n# Initialize best validation loss for tracking improvement\nbest_val_loss = float('inf')\n\nfor epoch in range(0, num_epochs):   # nhớ lát epoch+1\n    model.train()  # Chuyển model sang trạng thái training\n    train_loss, train_dice, train_iou, train_accuracy = 0, 0, 0, 0 \n\n    for batch in tqdm(train_dataloader, desc=f\"Epoch {epoch + 1}/{num_epochs}\"):\n        imgs, labels = batch\n        imgs, labels = imgs.to(device), labels.to(device)  # Chuyển dữ liệu sang thiết bị (GPU/CPU)\n        labels = labels.long()  # Chuyển nhãn sang kiểu dữ liệu Long, thích hợp cho nn.CrossEntropyLoss.\n                                \n        optimizer.zero_grad()  # Xóa gradient cũ\n        outputs = model(imgs)  # Forward pass\n        loss = criterion(outputs, labels)  # Tính loss\n        loss.backward()  # Backpropagation\n        optimizer.step()  # Cập nhật trọng số model\n\n        train_loss += loss.item()  # Cộng dồn loss\n\n        # Tính toán các chỉ số khác\n        preds = torch.argmax(outputs, dim=1)    # preds chỉ mục lớp dự đoán, và labels nhãn ground truth\n        train_dice += dice_fn(preds, labels).item()\n        train_iou += iou_fn(preds, labels).item()\n        train_accuracy += accuracy_function(preds, labels).item()\n\n    # Tính trung bình cho mỗi chỉ số loss, dice, iou, acc \n    avg_train_loss = train_loss / len(train_dataloader)\n    avg_train_dice = train_dice / len(train_dataloader)\n    avg_train_iou = train_iou / len(train_dataloader)\n    avg_train_accuracy = train_accuracy / len(train_dataloader)\n\n    # Sau mỗi vòng for (mỗi epoch) Log các chỉ số vào wandb và print ra console\n    wandb.log({\n        \"Train Loss\": avg_train_loss,\n        \"Train Dice\": avg_train_dice,\n        \"Train IoU\": avg_train_iou,\n        \"Train Accuracy\": avg_train_accuracy,\n        \"Epoch\": epoch+1\n    })\n    # Nhớ cộng 1, ko là bug: epoch từ 0, 1, 2...\n    print(f\"Epoch: {epoch+1}, Train Loss: {avg_train_loss}, Train Dice: {avg_train_dice}, \"\n          f\"Train IoU: {avg_train_iou}, Train Accuracy: {avg_train_accuracy}\")\n    \n    # Validation loop sau mỗi epoch train, tt training loop nhưng không cần backward và step\n    model.eval()  # Chuyển model sang trạng thái đánh giá\n    val_loss = 0\n    with torch.no_grad():  # Không tính toán gradient\n        for imgs, labels in val_dataloader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            labels = labels.long()  # Convert labels to long type <-- Add this line\n            outputs = model(imgs)\n            val_loss += criterion(outputs, labels).item()\n\n    # Tính trung bình loss trên tập validation, print ra console và lưu vào wandb\n    avg_val_loss = val_loss / len(val_dataloader)\n    wandb.log({\"Avg Validation Loss\": avg_val_loss, \"Epoch\": epoch+1}) \n    print(\"at Epoch\", epoch+1, \"Avg Validation Loss\", avg_val_loss )\n\n#     if avg_val_loss < best_val_loss:\n#         best_val_loss = avg_val_loss\n#         torch.save(model.state_dict(), 'best_model.pth')  # Lưu model tốt nhất\n#         wandb.log({\"Best Validation Loss\": best_val_loss})\n    \n    # Ko chỉ lưu model tốt nhất, lưu thêm cả optimizer, best_val_loss, epoch để train tiếp backup\n    if avg_val_loss < best_val_loss:\n        best_val_loss = avg_val_loss     # update best model\n        checkpoint = {\n            'model_state_dict': model.state_dict(),\n            'optimizer_state_dict': optimizer.state_dict(),\n            'best_val_loss': best_val_loss,\n            'epoch': epoch+1\n        }\n        torch.save(checkpoint, 'best_model.pth')  # Lưu trạng thái mô hình và thông tin liên quan\n        wandb.log({\"Best Validation Loss\": best_val_loss, \"Epoch\": epoch+1})\n        print(\"Now Best Validation Loss, at epoch\", epoch+1, \" best_val_loss is\", best_val_loss)\n\n\n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T06:40:27.201214Z","iopub.execute_input":"2023-11-17T06:40:27.2015Z","iopub.status.idle":"2023-11-17T07:40:38.037892Z","shell.execute_reply.started":"2023-11-17T06:40:27.201475Z","shell.execute_reply":"2023-11-17T07:40:38.036503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tải lại thông tin model đã train\ncheckpoint = torch.load('/kaggle/working/best_model.pth')\nmodel.load_state_dict(checkpoint['model_state_dict'])\noptimizer.load_state_dict(checkpoint['optimizer_state_dict'])\nbest_val_loss = checkpoint['best_val_loss']\nepoch = checkpoint['epoch']\n\nprint(\"Now, best model at\", \"epoch\", epoch, \" best_val_loss is\", best_val_loss, \"and Optimizer is\", optimizer)\n    ","metadata":{"execution":{"iopub.status.busy":"2023-11-17T07:40:38.03948Z","iopub.execute_input":"2023-11-17T07:40:38.039937Z","iopub.status.idle":"2023-11-17T07:40:38.802883Z","shell.execute_reply.started":"2023-11-17T07:40:38.039896Z","shell.execute_reply":"2023-11-17T07:40:38.801323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Up được: 0.62 score","metadata":{}},{"cell_type":"markdown","source":"## 4.2 Train tiếp model mình vừa train (). \n","metadata":{}},{"cell_type":"code","source":"#thiết lập cpu/gpu lại\ndevice = torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)  \n\n# Tải lại thông tin model tốt nhất đã train (best acc), hoặc thích thì model mới nhất lastest\ncheckpoint = torch.load('best_model.pth')\nmodel.load_state_dict(checkpoint['model_state_dict'])\noptimizer.load_state_dict(checkpoint['optimizer_state_dict'])\nbest_val_loss = checkpoint['best_val_loss']\nepoch = checkpoint['epoch']\n\nnum_epochs=200\n# Tiếp tục quá trình huấn luyện từ epoch tiếp theo, ko cần load best_val_loss, sửa lại vòng for\nfor epoch in range(epoch, num_epochs): # nhớ lát epoch+1\n    model.train()  # Chuyển model sang trạng thái training\n    train_loss, train_dice, train_iou, train_accuracy = 0, 0, 0, 0 \n\n    for batch in tqdm(train_dataloader, desc=f\"Epoch {epoch + 1}/{num_epochs}\"):\n        imgs, labels = batch\n        imgs, labels = imgs.to(device), labels.to(device)  # Chuyển dữ liệu sang thiết bị (GPU/CPU)\n        labels = labels.long()  # Chuyển nhãn sang kiểu dữ liệu Long, thích hợp cho nn.CrossEntropyLoss.\n                                \n        optimizer.zero_grad()  # Xóa gradient cũ\n        outputs = model(imgs)  # Forward pass\n        loss = criterion(outputs, labels)  # Tính loss\n        loss.backward()  # Backpropagation\n        optimizer.step()  # Cập nhật trọng số model\n\n        train_loss += loss.item()  # Cộng dồn loss\n\n        # Tính toán các chỉ số khác\n        preds = torch.argmax(outputs, dim=1)    # preds chỉ mục lớp dự đoán, và labels nhãn ground truth\n        train_dice += dice_fn(preds, labels).item()\n        train_iou += iou_fn(preds, labels).item()\n        train_accuracy += accuracy_function(preds, labels).item()\n\n    # Tính trung bình cho mỗi chỉ số loss, dice, iou, acc \n    avg_train_loss = train_loss / len(train_dataloader)\n    avg_train_dice = train_dice / len(train_dataloader)\n    avg_train_iou = train_iou / len(train_dataloader)\n    avg_train_accuracy = train_accuracy / len(train_dataloader)\n\n    # Sau mỗi vòng for (mỗi epoch) Log các chỉ số vào wandb và print ra console\n    wandb.log({\n        \"Train Loss\": avg_train_loss,\n        \"Train Dice\": avg_train_dice,\n        \"Train IoU\": avg_train_iou,\n        \"Train Accuracy\": avg_train_accuracy,\n        \"Epoch\": epoch+1\n    })\n    # Nhớ cộng 1, ko là bug: epoch từ 0, 1, 2...\n    print(f\"Epoch: {epoch+1}, Train Loss: {avg_train_loss}, Train Dice: {avg_train_dice}, \"\n          f\"Train IoU: {avg_train_iou}, Train Accuracy: {avg_train_accuracy}\")\n    \n    # Validation loop sau mỗi epoch train, tt training loop nhưng không cần backward và step\n    model.eval()  # Chuyển model sang trạng thái đánh giá\n    val_loss = 0\n    with torch.no_grad():  # Không tính toán gradient\n        for imgs, labels in val_dataloader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            labels = labels.long()  # Convert labels to long type <-- Add this line\n            outputs = model(imgs)\n            val_loss += criterion(outputs, labels).item()\n\n    # Tính trung bình loss trên tập validation, print ra console và lưu vào wandb\n    avg_val_loss = val_loss / len(val_dataloader)\n    wandb.log({\"Avg Validation Loss\": avg_val_loss, \"Epoch\": epoch+1}) \n    print(\"at Epoch\", epoch+1, \"Avg Validation Loss\", avg_val_loss )\n    \n    # Thêm 1 lastest_checkpoint để dễ train tiếp hơn\n    lastest_checkpoint = {\n        'model_state_dict': model.state_dict(),\n        'optimizer_state_dict': optimizer.state_dict(),\n        'best_val_loss': best_val_loss,\n        'epoch': epoch+1\n    }\n    torch.save(lastest_checkpoint, 'lastest_model.pth')  # Lưu trạng thái mô hình và thông tin liên quan\n    \n#     if avg_val_loss < best_val_loss:\n#         best_val_loss = avg_val_loss\n#         torch.save(model.state_dict(), 'best_model.pth')  # Lưu model tốt nhất\n#         wandb.log({\"Best Validation Loss\": best_val_loss})\n    \n    # Ko chỉ lưu model tốt nhất, lưu thêm cả optimizer, best_val_loss, epoch để train tiếp backup\n    if avg_val_loss < best_val_loss:\n        best_val_loss = avg_val_loss     # update best model\n        checkpoint = {\n            'model_state_dict': model.state_dict(),\n            'optimizer_state_dict': optimizer.state_dict(),\n            'best_val_loss': best_val_loss,\n            'epoch': epoch+1\n        }\n        torch.save(checkpoint, 'best_model.pth')  # Lưu trạng thái mô hình và thông tin liên quan\n        wandb.log({\"Best Validation Loss\": best_val_loss, \"Epoch\": epoch+1})\n        print(\"Now Best Validation Loss, at epoch\", epoch+1, \" best_val_loss is\", best_val_loss)\n\n    if epoch%10==0:  # Dự đoán Resnet152-100 epoch, resnet34-300 epochs (loss:0.1), Resnet101-200 epochs\n        label = labels[0].cpu().numpy()\n        label = mask_to_rgb(label,color_dict)\n        outputs[0] = outputs[0].softmax(dim=0)\n        output = outputs[0].cpu().numpy()\n        output = np.argmax(output, axis=0)\n        output = mask_to_rgb(output,color_dict)\n        fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n        axs[0].imshow(label)\n        axs[0].set_title('Label')\n        axs[1].imshow(output)\n        axs[1].set_title('Output')\n        plt.show()\n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T09:40:24.160955Z","iopub.execute_input":"2023-11-17T09:40:24.161723Z","iopub.status.idle":"2023-11-17T10:02:09.957013Z","shell.execute_reply.started":"2023-11-17T09:40:24.161687Z","shell.execute_reply":"2023-11-17T10:02:09.954616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1. 30 epochs đầu\n- Epoch 7/30: 100%|██████████| 225/225 [01:52<00:00,  2.00it/s]\nEpoch: 7, Train Loss: 0.06242561957074536, Train Dice: 0.6765225545565288, Train IoU: 0.6220268830988143, Train Accuracy: 0.9822562832302517\nat Epoch 7 Avg Validation Loss 0.059328709244728085\nNow Best Validation Loss, at epoch 7  best_val_loss is 0.059328709244728085\n- Epoch 8/30: 100%|██████████| 225/225 [01:53<00:00,  1.99it/s]\nEpoch: 8, Train Loss: 0.05881343812578254, Train Dice: 0.6899735760688782, Train IoU: 0.6359356983502706, Train Accuracy: 0.982843509250217\nat Epoch 8 Avg Validation Loss 0.05954265657812357\n\n2. 60 epochs đầu\n- Trong 30 epoch đầu thì: best model at epoch 24  best_val_loss is 0.05103267043828964\n    - Epoch: 24, Train Loss: 0.03385688684673773, Train Dice: 0.8075790292686886, Train IoU: 0.7563386370076074, Train Accuracy: 0.989578111436632\n    - at Epoch 24 Avg Validation Loss 0.05103267043828964\n- Phải đến tận epoch 48, mới có best_val_loss tiếp theo: \n    - Epoch: 48, Train Loss: 0.01923132327902648, Train Dice: 0.8683773051367866, Train IoU: 0.8282751733726925, Train Accuracy: 0.9937682766384549\n    - at Epoch 48 Avg Validation Loss 0.047675849981606004\n    - Now Best Validation Loss, at epoch 48  best_val_loss is 0.047675849981606004\n-   Epoch: 53, Train Loss: 0.024629200345112217, Train Dice: 0.8380803844663832, Train IoU: 0.7914824227492014, Train Accuracy: 0.9918895467122396\n    - at Epoch 53 Avg Validation Loss 0.046704819910228255\n    - Now Best Validation Loss, at epoch 53  best_val_loss is 0.046704819910228255","metadata":{}},{"cell_type":"code","source":"#thiết lập cpu/gpu lại\ndevice = torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)  \n\n# Tải lại thông tin model mới nhất đã train (best acc), hoặc thích thì model mới nhất lastest\nlastest_checkpoint = torch.load('lastest_model.pth')\nmodel.load_state_dict(lastest_checkpoint['model_state_dict'])\noptimizer.load_state_dict(lastest_checkpoint['optimizer_state_dict'])\nbest_val_loss = lastest_checkpoint['best_val_loss']\nepoch = lastest_checkpoint['epoch']\n\nnum_epochs=300\n# Tiếp tục quá trình huấn luyện từ epoch tiếp theo, ko cần load best_val_loss, sửa lại vòng for\nfor epoch in range(epoch, num_epochs): # nhớ lát epoch+1\n    model.train()  # Chuyển model sang trạng thái training\n    train_loss, train_dice, train_iou, train_accuracy = 0, 0, 0, 0 \n\n    for batch in tqdm(train_dataloader, desc=f\"Epoch {epoch + 1}/{num_epochs}\"):\n        imgs, labels = batch\n        imgs, labels = imgs.to(device), labels.to(device)  # Chuyển dữ liệu sang thiết bị (GPU/CPU)\n        labels = labels.long()  # Chuyển nhãn sang kiểu dữ liệu Long, thích hợp cho nn.CrossEntropyLoss.\n                                \n        optimizer.zero_grad()  # Xóa gradient cũ\n        outputs = model(imgs)  # Forward pass\n        loss = criterion(outputs, labels)  # Tính loss\n        loss.backward()  # Backpropagation\n        optimizer.step()  # Cập nhật trọng số model\n\n        train_loss += loss.item()  # Cộng dồn loss\n\n        # Tính toán các chỉ số khác\n        preds = torch.argmax(outputs, dim=1)    # preds chỉ mục lớp dự đoán, và labels nhãn ground truth\n        train_dice += dice_fn(preds, labels).item()\n        train_iou += iou_fn(preds, labels).item()\n        train_accuracy += accuracy_function(preds, labels).item()\n\n    # Tính trung bình cho mỗi chỉ số loss, dice, iou, acc \n    avg_train_loss = train_loss / len(train_dataloader)\n    avg_train_dice = train_dice / len(train_dataloader)\n    avg_train_iou = train_iou / len(train_dataloader)\n    avg_train_accuracy = train_accuracy / len(train_dataloader)\n\n    # Sau mỗi vòng for (mỗi epoch) Log các chỉ số vào wandb và print ra console\n    wandb.log({\n        \"Train Loss\": avg_train_loss,\n        \"Train Dice\": avg_train_dice,\n        \"Train IoU\": avg_train_iou,\n        \"Train Accuracy\": avg_train_accuracy,\n        \"Epoch\": epoch+1\n    })\n    # Nhớ cộng 1, ko là bug: epoch từ 0, 1, 2...\n    print(f\"Epoch: {epoch+1}, Train Loss: {avg_train_loss}, Train Dice: {avg_train_dice}, \"\n          f\"Train IoU: {avg_train_iou}, Train Accuracy: {avg_train_accuracy}\")\n    \n    # Validation loop sau mỗi epoch train, tt training loop nhưng không cần backward và step\n    model.eval()  # Chuyển model sang trạng thái đánh giá\n    val_loss = 0\n    with torch.no_grad():  # Không tính toán gradient\n        for imgs, labels in val_dataloader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            labels = labels.long()  # Convert labels to long type <-- Add this line\n            outputs = model(imgs)\n            val_loss += criterion(outputs, labels).item()\n\n    # Tính trung bình loss trên tập validation, print ra console và lưu vào wandb\n    avg_val_loss = val_loss / len(val_dataloader)\n    wandb.log({\"Avg Validation Loss\": avg_val_loss, \"Epoch\": epoch+1}) \n    print(\"at Epoch\", epoch+1, \"Avg Validation Loss\", avg_val_loss )\n    \n    # Thêm 1 lastest_checkpoint để dễ train tiếp hơn\n    lastest_checkpoint = {\n        'model_state_dict': model.state_dict(),\n        'optimizer_state_dict': optimizer.state_dict(),\n        'best_val_loss': best_val_loss,\n        'epoch': epoch+1\n    }\n    torch.save(lastest_checkpoint, 'lastest_model.pth')  # Lưu trạng thái mô hình và thông tin liên quan\n    \n#     if avg_val_loss < best_val_loss:\n#         best_val_loss = avg_val_loss\n#         torch.save(model.state_dict(), 'best_model.pth')  # Lưu model tốt nhất\n#         wandb.log({\"Best Validation Loss\": best_val_loss})\n    \n    # Ko chỉ lưu model tốt nhất, lưu thêm cả optimizer, best_val_loss, epoch để train tiếp backup\n    if avg_val_loss < best_val_loss:\n        best_val_loss = avg_val_loss     # update best model\n        checkpoint = {\n            'model_state_dict': model.state_dict(),\n            'optimizer_state_dict': optimizer.state_dict(),\n            'best_val_loss': best_val_loss,\n            'epoch': epoch+1\n        }\n        torch.save(checkpoint, 'best_model.pth')  # Lưu trạng thái mô hình và thông tin liên quan\n        wandb.log({\"Best Validation Loss\": best_val_loss, \"Epoch\": epoch+1})\n        print(\"Now Best Validation Loss, at epoch\", epoch+1, \" best_val_loss is\", best_val_loss)\n\n    if epoch%10==0:  # Dự đoán Resnet152-100 epoch, resnet34-300 epochs (loss:0.1), Resnet101-200 epochs\n        label = labels[0].cpu().numpy()\n        label = mask_to_rgb(label,color_dict)\n        outputs[0] = outputs[0].softmax(dim=0)\n        output = outputs[0].cpu().numpy()\n        output = np.argmax(output, axis=0)\n        output = mask_to_rgb(output,color_dict)\n        fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n        axs[0].imshow(label)\n        axs[0].set_title('Label')\n        axs[1].imshow(output)\n        axs[1].set_title('Output')\n        plt.show()\n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-17T10:02:33.757639Z","iopub.execute_input":"2023-11-17T10:02:33.758025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tạo thư mục\n!mkdir test_pred\n!mkdir test_overlappred","metadata":{"execution":{"iopub.status.busy":"2023-11-17T07:40:38.804144Z","iopub.execute_input":"2023-11-17T07:40:38.804483Z","iopub.status.idle":"2023-11-17T07:40:40.861805Z","shell.execute_reply.started":"2023-11-17T07:40:38.804458Z","shell.execute_reply":"2023-11-17T07:40:40.860422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"color_dict= {0: (0, 0, 0),\n             1: (255, 0, 0),\n             2: (0, 255, 0)}\ndef mask_to_rgb(mask, color_dict):\n    output = np.zeros((mask.shape[0], mask.shape[1], 3))\n\n    for k in color_dict.keys():\n        output[mask==k] = color_dict[k]\n\n    return np.uint8(output) ","metadata":{"execution":{"iopub.status.busy":"2023-11-17T07:40:40.863661Z","iopub.execute_input":"2023-11-17T07:40:40.86433Z","iopub.status.idle":"2023-11-17T07:40:40.87136Z","shell.execute_reply.started":"2023-11-17T07:40:40.8643Z","shell.execute_reply":"2023-11-17T07:40:40.870291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load model đã được train, set .to(device)\ncheckpoint = torch.load('/kaggle/working/best_model.pth')\nmodel.load_state_dict(checkpoint['model_state_dict'])\ndevice = torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\ntrainsize = 256       # Kích thước ảnh sẽ được cho vô model eval\n# Chuyển model sang chế độ eval\nmodel.eval()\nfor i in os.listdir(\"/kaggle/input/bkai-igh-neopolyp/test/test\"):\n    img_path = os.path.join(\"/kaggle/input/bkai-igh-neopolyp/test/test\", i)\n    ori_img = cv2.imread(img_path)\n    ori_img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2RGB)\n    ori_w = ori_img.shape[0]\n    ori_h = ori_img.shape[1]\n    img = cv2.resize(ori_img, (trainsize, trainsize))\n    transformed = val_transform(image=img)\n    input_img = transformed[\"image\"]\n    input_img = input_img.unsqueeze(0).to(device)\n    with torch.no_grad():\n        output_mask = model.forward(input_img).squeeze(0).cpu().numpy().transpose(1,2,0)\n    mask = cv2.resize(output_mask, (ori_h, ori_w))\n    mask = np.argmax(mask, axis=2)\n    new_rgb_mask = np.zeros((*mask.shape, 3)).astype(np.uint8)\n    mask_rgb = mask_to_rgb(mask, color_dict)\n    mask_rgb_true = cv2.cvtColor(mask_rgb, cv2.COLOR_BGR2RGB)\n    overlap = 0.7*ori_img+0.3*mask_rgb_true\n    overlap = overlap.astype('uint8')\n    overlap = cv2.cvtColor(overlap, cv2.COLOR_RGB2BGR)\n    mask_rgb = cv2.cvtColor(mask_rgb, cv2.COLOR_RGB2BGR)\n    cv2.imwrite(\"test_pred/{}\".format(i), mask_rgb)\n    cv2.imwrite(\"test_overlappred/{}\".format(i), overlap)\n    print(\"processed \", img_path)\n     ","metadata":{"execution":{"iopub.status.busy":"2023-11-17T07:40:40.873Z","iopub.execute_input":"2023-11-17T07:40:40.873649Z","iopub.status.idle":"2023-11-17T07:41:28.029942Z","shell.execute_reply.started":"2023-11-17T07:40:40.873624Z","shell.execute_reply":"2023-11-17T07:41:28.028783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Mask2csv => https://github.com/sangdv/rle_encode/blob/main/mask2csv.py   # Code được cuộc thi cho\n\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport os\n\ndef rle_to_string(runs):\n    return ' '.join(str(x) for x in runs)\n\ndef rle_encode_one_mask(mask):\n    pixels = mask.flatten()\n    pixels[pixels > 225] = 255\n    pixels[pixels <= 225] = 0\n    use_padding = False\n    if pixels[0] or pixels[-1]:\n        use_padding = True\n        pixel_padded = np.zeros([len(pixels) + 2], dtype=pixels.dtype)\n        pixel_padded[1:-1] = pixels\n        pixels = pixel_padded\n    \n    rle = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    if use_padding:\n        rle = rle - 1\n    rle[1::2] = rle[1::2] - rle[:-1:2]\n    return rle_to_string(rle)\n\ndef rle2mask(mask_rle, shape=(3,3)):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (width,height) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T\n\ndef mask2string(dir):\n    ## mask --> string\n    strings = []\n    ids = []\n    ws, hs = [[] for i in range(2)]\n    for image_id in os.listdir(dir):\n        id = image_id.split('.')[0]\n        path = os.path.join(dir, image_id)\n        print(path)\n        img = cv2.imread(path)[:,:,::-1]\n        h, w = img.shape[0], img.shape[1]\n        for channel in range(2):\n            ws.append(w)\n            hs.append(h)\n            ids.append(f'{id}_{channel}')\n            string = rle_encode_one_mask(img[:,:,channel])\n            strings.append(string)\n    r = {\n        'ids': ids,\n        'strings': strings,\n    }\n    return r\n\n\nMASK_DIR_PATH = 'test_pred/' # change this to the path to your output mask folder\ndir = MASK_DIR_PATH\nres = mask2string(dir)\ndf = pd.DataFrame(columns=['Id', 'Expected'])\ndf['Id'] = res['ids']\ndf['Expected'] = res['strings']\n\ndf.to_csv(r'output.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2023-11-17T07:41:28.031682Z","iopub.execute_input":"2023-11-17T07:41:28.032392Z","iopub.status.idle":"2023-11-17T07:41:31.374827Z","shell.execute_reply.started":"2023-11-17T07:41:28.03235Z","shell.execute_reply":"2023-11-17T07:41:31.373766Z"},"trusted":true},"execution_count":null,"outputs":[]}]}