{"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":"none","dataSources":[{"sourceId":20561,"databundleVersionId":1187889,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install segmentation_models_pytorch","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport scipy\nimport cv2\nimport os\nimport time\nimport albumentations\nimport numpy                        as np # linear algebra\nimport pandas                       as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot            as plt\nimport segmentation_models_pytorch  as smp\nimport torch.nn.functional          as F\nimport torch.nn                     as nn\nfrom tqdm.notebook      import tqdm\nfrom PIL                import Image\nfrom torchvision        import transforms as T\nfrom torch.utils.data   import Dataset, DataLoader  \n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"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\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\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 5GB 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":"    prefix = '/kaggle/input/covid-segmentation/'\n    \n    images_radiopedia   = np.load(os.path.join(prefix, 'images_radiopedia.npy')).astype(np.float32)\n    masks_radiopedia    = np.load(os.path.join(prefix, 'masks_radiopedia.npy')).astype(np.int8)\n    images_medseg       = np.load(os.path.join(prefix, 'images_medseg.npy')).astype(np.float32)\n    masks_medseg        = np.load(os.path.join(prefix, 'masks_medseg.npy')).astype(np.int8)\n    test_images_medseg  = np.load(os.path.join(prefix, 'test_images_medseg.npy')).astype(np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T17:06:37.56123Z","iopub.status.idle":"2025-09-17T17:06:37.561617Z","shell.execute_reply.started":"2025-09-17T17:06:37.56141Z","shell.execute_reply":"2025-09-17T17:06:37.561425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(images_radiopedia.min(), images_radiopedia.max())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T17:06:37.562916Z","iopub.status.idle":"2025-09-17T17:06:37.563282Z","shell.execute_reply.started":"2025-09-17T17:06:37.563104Z","shell.execute_reply":"2025-09-17T17:06:37.563119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize(image_batch, mask_batch=None, pred_batch=None, num_samples=8, hot_encode=True):\n    num_classes = mask_batch.shape[-1] if mask_batch is not None else 0\n    _, ax       = plt.subplots(num_classes + 1, num_samples, figsize=(num_samples * 2, (num_classes + 1) * 2))\n\n    for i in range(num_samples):\n        ax_image = ax[0, i] if num_classes > 0 else ax[i]\n        if hot_encode: \n            ax_image.imshow(image_batch[i,:,:,0], cmap='Greys')\n        else: \n            ax_image.imshow(image_batch[i,:,:])\n        ax_image.set_xticks([]) \n        ax_image.set_yticks([])\n        \n        if mask_batch is not None:\n            for j in range(num_classes):\n                if pred_batch is None:\n                    mask_to_show = mask_batch[i,:,:,j]\n                else:\n                    mask_to_show = np.zeros(shape=(*mask_batch.shape[1:-1], 3)) \n                    mask_to_show[..., 0] = pred_batch[i,:,:,j] > 0.5\n                    mask_to_show[..., 1] = mask_batch[i,:,:,j]\n                ax[j + 1, i].imshow(mask_to_show, vmin=0, vmax=1)\n                ax[j + 1, i].set_xticks([]) \n                ax[j + 1, i].set_yticks([]) \n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.479632Z","iopub.execute_input":"2025-09-17T16:17:12.479883Z","iopub.status.idle":"2025-09-17T16:17:12.487876Z","shell.execute_reply.started":"2025-09-17T16:17:12.479866Z","shell.execute_reply":"2025-09-17T16:17:12.487102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def onehot_to_mask(mask, palette):\n    \"\"\"\n    Converts a mask (H, W, K) to (H, W, C)\n    \"\"\"\n    x = np.argmax(mask, axis=-1)\n    colour_codes = np.array(palette)\n    x = np.uint8(colour_codes[x.astype(np.uint8)])\n    return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.488688Z","iopub.execute_input":"2025-09-17T16:17:12.488949Z","iopub.status.idle":"2025-09-17T16:17:12.502009Z","shell.execute_reply.started":"2025-09-17T16:17:12.488924Z","shell.execute_reply":"2025-09-17T16:17:12.501424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_images(images_arr, mean_std=None):\n    # Clipping\n    images_arr[images_arr > 500]    = 500\n    images_arr[images_arr < -1500]  = -1500\n    # Robust scaling\n    min_perc, max_perc  = np.percentile(images_arr, 5), np.percentile(images_arr, 95)\n    images_arr_valid    = images_arr[(images_arr > min_perc) & (images_arr < max_perc)]\n    # Standarization\n    mean, std           = (images_arr_valid.mean(), images_arr_valid.std()) if mean_std is None else mean_std\n    images_arr          = (images_arr - mean) / std\n    print(f'mean {mean}, std {std}')\n    return images_arr, (mean, std)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.502762Z","iopub.execute_input":"2025-09-17T16:17:12.503299Z","iopub.status.idle":"2025-09-17T16:17:12.513157Z","shell.execute_reply.started":"2025-09-17T16:17:12.503274Z","shell.execute_reply":"2025-09-17T16:17:12.512461Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_hists(images1, images2=None):\n    plt.hist(images1.ravel(), bins=100, density=True, color='b', alpha=1 if images2 is None else 0.5)\n    if images2 is not None:\n        plt.hist(images2.ravel(), bins=100, density=True, alpha=0.5, color='orange')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.51401Z","iopub.execute_input":"2025-09-17T16:17:12.514258Z","iopub.status.idle":"2025-09-17T16:17:12.523661Z","shell.execute_reply.started":"2025-09-17T16:17:12.514235Z","shell.execute_reply":"2025-09-17T16:17:12.522939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndef pixel_accuracy(output, mask):\n    with torch.no_grad():\n        output = torch.argmax(F.softmax(output, dim=1), dim=1)\n        correct = torch.eq(output, mask).int()\n        accuracy = float(correct.sum()) / float(correct.numel())\n    return accuracy\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.524431Z","iopub.execute_input":"2025-09-17T16:17:12.524626Z","iopub.status.idle":"2025-09-17T16:17:12.535732Z","shell.execute_reply.started":"2025-09-17T16:17:12.524611Z","shell.execute_reply":"2025-09-17T16:17:12.535092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def mIoU(pred_mask, mask, smooth=1e-10, n_classes=4):\n    with torch.no_grad():\n        pred_mask = F.softmax(pred_mask, dim=1)\n        pred_mask = torch.argmax(pred_mask, dim=1)\n        pred_mask = pred_mask.contiguous().view(-1)\n        mask = mask.contiguous().view(-1)\n\n        iou_per_class = []\n        for clas in range(0, n_classes): #loop per pixel class\n            true_class = pred_mask == clas\n            true_label = mask == clas\n\n            if true_label.long().sum().item() == 0: #no exist label in this loop\n                iou_per_class.append(np.nan)\n            else:\n                intersect = torch.logical_and(true_class, true_label).sum().float().item()\n                union = torch.logical_or(true_class, true_label).sum().float().item()\n\n                iou = (intersect + smooth) / (union +smooth)\n                iou_per_class.append(iou)\n        return np.nanmean(iou_per_class)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.536537Z","iopub.execute_input":"2025-09-17T16:17:12.53672Z","iopub.status.idle":"2025-09-17T16:17:12.549611Z","shell.execute_reply.started":"2025-09-17T16:17:12.536705Z","shell.execute_reply":"2025-09-17T16:17:12.548696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_lr(optimizer):\n    for param_group in optimizer.param_groups:\n        return param_group['lr']\n\ndef fit(epochs, model, train_loader, val_loader, criterion, optimizer, scheduler, patch=False):\n    train_losses,val_iou,val_acc,lrs = [],[],[],[]\n    test_losses,train_iou,train_acc  = [],[],[]\n    min_loss    = np.inf\n    decrease    = 1\n    not_improve = 0\n\n    model.to(device)\n    fit_time = time.time()\n    for e in range(epochs):\n        since        = time.time()\n        running_loss = 0\n        iou_score    = 0\n        accuracy     = 0\n        #training loop\n        model.train()\n        for _, data in enumerate(tqdm(train_loader)):\n            #training phase\n            image_tiles, mask_tiles = data\n            image = image_tiles.to(device)\n            mask  = mask_tiles.to(device)\n            #forward\n            output = model(image)\n            \n            loss = criterion(output, mask)\n            #evaluation metrics\n            iou_score += mIoU(output, mask)\n            accuracy  += pixel_accuracy(output, mask)\n            #backward\n            loss.backward()\n            optimizer.step() #update weight          \n            optimizer.zero_grad() #reset gradient\n            \n            #step the learning rate\n            lrs.append(get_lr(optimizer))\n            scheduler.step()\n            \n            running_loss += loss.item()\n            \n\n        else:\n            model.eval()\n            test_loss     = 0\n            test_accuracy = 0\n            val_iou_score = 0\n            #validation loop\n            with torch.no_grad():\n                for _, data in enumerate(tqdm(val_loader)):\n                    image_tiles, mask_tiles = data\n\n                    image = image_tiles.to(device); mask =mask_tiles.to(device);\n                    output = model(image)\n                    #evaluation metrics\n                    val_iou_score +=  mIoU(output, mask)\n                    test_accuracy += pixel_accuracy(output, mask)\n                    #loss\n                    loss = criterion(output, mask)                                  \n                    test_loss += loss.item()\n            \n            #calculatio mean for each batch\n            train_losses.append(running_loss/len(train_loader))\n            test_losses.append(test_loss/len(val_loader))\n\n\n            if min_loss > (test_loss/len(val_loader)):\n                print('Loss Decreasing.. {:.3f} >> {:.3f} '.format(min_loss, (test_loss/len(val_loader))))\n                min_loss = (test_loss/len(val_loader))\n                decrease += 1\n                if decrease % 5 == 0:\n                    print('saving model...')\n                    torch.save(model, 'Unet_efficientnet_b2_mIoU-{:.3f}.pt'.format(val_iou_score/len(val_loader)))\n                    \n\n            if (test_loss/len(val_loader)) > min_loss:\n                not_improve += 1\n                min_loss = (test_loss/len(val_loader))\n                print(f'Loss Not Decrease for {not_improve} time')\n                if not_improve == 7:\n                    print('Loss not decrease for 7 times, Stop Training')\n                    break\n            \n            #iou\n            val_iou.append(val_iou_score/len(val_loader))\n            train_iou.append(iou_score/len(train_loader))\n            train_acc.append(accuracy/len(train_loader))\n            val_acc.append(test_accuracy/ len(val_loader))\n            print(\"Epoch:{}/{}..\".format(e+1, epochs),\n                  \"Train Loss: {:.3f}..\".format(running_loss/len(train_loader)),\n                  \"Val Loss: {:.3f}..\".format(test_loss/len(val_loader)),\n                  \"Train mIoU:{:.3f}..\".format(iou_score/len(train_loader)),\n                  \"Val mIoU: {:.3f}..\".format(val_iou_score/len(val_loader)),\n                  \"Train Acc:{:.3f}..\".format(accuracy/len(train_loader)),\n                  \"Val Acc:{:.3f}..\".format(test_accuracy/len(val_loader)),\n                  \"Time: {:.2f}m\".format((time.time()-since)/60))\n        \n    history = {'train_loss' : train_losses, 'val_loss': test_losses,\n               'train_miou' :train_iou, 'val_miou':val_iou,\n               'train_acc' :train_acc, 'val_acc':val_acc,\n               'lrs': lrs}\n    print('Total time: {:.2f} m' .format((time.time()- fit_time)/60))\n    return history","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.55041Z","iopub.execute_input":"2025-09-17T16:17:12.550629Z","iopub.status.idle":"2025-09-17T16:17:12.565367Z","shell.execute_reply.started":"2025-09-17T16:17:12.550613Z","shell.execute_reply":"2025-09-17T16:17:12.564576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_loss(history):\n    plt.plot(history['val_loss'], label='val', marker='o')\n    plt.plot( history['train_loss'], label='train', marker='o')\n    plt.title('Loss per epoch'); plt.ylabel('loss');\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()\n    \ndef plot_score(history):\n    plt.plot(history['train_miou'], label='train_mIoU', marker='*')\n    plt.plot(history['val_miou'], label='val_mIoU',  marker='*')\n    plt.title('Score per epoch'); plt.ylabel('mean IoU')\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()\n    \ndef plot_acc(history):\n    plt.plot(history['train_acc'], label='train_accuracy', marker='*')\n    plt.plot(history['val_acc'], label='val_accuracy',  marker='*')\n    plt.title('Accuracy per epoch'); plt.ylabel('Accuracy')\n    plt.xlabel('epoch')\n    plt.legend(), plt.grid()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.566074Z","iopub.execute_input":"2025-09-17T16:17:12.566265Z","iopub.status.idle":"2025-09-17T16:17:12.575719Z","shell.execute_reply.started":"2025-09-17T16:17:12.566251Z","shell.execute_reply":"2025-09-17T16:17:12.575077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_image_mask_miou(model, image, mask, mean=[0.485], std=[0.229]):\n    model.eval()\n    model.to(device)\n    image = image.to(device)\n    mask  = mask.to(device)\n\n    with torch.no_grad():\n        output = model(image)\n        score  = mIoU(output, mask)\n        masked = torch.argmax(output, dim=1)\n        masked = masked.cpu().squeeze(0)\n    return masked, score, output.permute(0, 2, 3,1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.576491Z","iopub.execute_input":"2025-09-17T16:17:12.576676Z","iopub.status.idle":"2025-09-17T16:17:12.590532Z","shell.execute_reply.started":"2025-09-17T16:17:12.576662Z","shell.execute_reply":"2025-09-17T16:17:12.589842Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def test_predict(model, image, mean=[0.485], std=[0.229]):\n    t     = T.Compose([T.ToTensor(), T.Normalize(mean, std)])\n    image = t(image)\n    model.eval()\n    \n    model.to(device)\n    image = image.to(device)\n    \n    with torch.no_grad():\n        output = model(torch.unsqueeze(image,1))\n        output = nn.Softmax(dim=1)(output)\n    return output.permute(0, 2, 3,1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.591283Z","iopub.execute_input":"2025-09-17T16:17:12.591558Z","iopub.status.idle":"2025-09-17T16:17:12.602288Z","shell.execute_reply.started":"2025-09-17T16:17:12.59154Z","shell.execute_reply":"2025-09-17T16:17:12.601569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_image_mask_pixel(model, image, mask, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]):\n    model.eval()\n    model.to(device); image=image.to(device)\n    mask = mask.to(device)\n    with torch.no_grad():\n        output = model(image)\n        acc    = pixel_accuracy(output, mask)\n        masked = torch.argmax(output, dim=1)\n        masked = masked.cpu().squeeze(0)\n        \n    return masked, acc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.602972Z","iopub.execute_input":"2025-09-17T16:17:12.603174Z","iopub.status.idle":"2025-09-17T16:17:12.60906Z","shell.execute_reply.started":"2025-09-17T16:17:12.60316Z","shell.execute_reply":"2025-09-17T16:17:12.608532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def mask_to_onehot(mask, palette):\n    \"\"\"\n    Converts a segmentation mask (H, W, C) to (H, W, K) where the last dim is a one\n    hot encoding vector, C is usually 1 or 3, and K is the number of class.\n    \"\"\"\n    semantic_map = []\n    for colour in palette:\n        equality  = np.equal(mask, colour)\n        class_map = np.all(equality, axis=-1)\n        semantic_map.append(class_map)\n    semantic_map = np.stack(semantic_map, axis=-1).astype(np.float32)\n    return torch.from_numpy(semantic_map)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.613043Z","iopub.execute_input":"2025-09-17T16:17:12.613233Z","iopub.status.idle":"2025-09-17T16:17:12.626125Z","shell.execute_reply.started":"2025-09-17T16:17:12.613219Z","shell.execute_reply":"2025-09-17T16:17:12.625498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def miou_score(model, test_set):\n    score_iou = []\n    for _, data in enumerate(tqdm(test_set)):\n        img, mask = data\n        _, score,_ = predict_image_mask_miou(model, img, mask)\n        score_iou.append(score)\n    return score_iou","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.626834Z","iopub.execute_input":"2025-09-17T16:17:12.627583Z","iopub.status.idle":"2025-09-17T16:17:12.637387Z","shell.execute_reply.started":"2025-09-17T16:17:12.627559Z","shell.execute_reply":"2025-09-17T16:17:12.636684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Dataset:   \n    def __init__(self, images, masks,augmentations=None):\n        self.images = images\n        self.masks = masks\n        self.augmentations = augmentations\n        self.mean = [0.485]\n        self.std = [0.229]\n    \n    def __getitem__(self, i):\n        image = self.images[i]\n        mask = self.masks[i]\n        \n        if self.augmentations is not None:\n            sample = self.augmentations(image=image, mask=mask)\n            \n            image, mask = Image.fromarray(np.squeeze(sample['image'], axis=2)), sample['mask']\n        \n        if self.augmentations is None:\n            image = Image.fromarray(image)\n        \n        t = T.Compose([T.ToTensor(), T.Normalize(self.mean, self.std)])\n        image = t(image)\n        mask = torch.from_numpy(mask).long()\n    \n        return image, mask\n    \n    def __len__(self):\n        return len(self.images)\n    \n    def tiles(self, image, mask):\n        img_patches = image.unfold(1, 512, 512).unfold(2, 768, 768) \n        img_patches  = img_patches.contiguous().view(3,-1, 512, 768) \n        img_patches = img_patches.permute(1,0,2,3)\n        \n        mask_patches = mask.unfold(0, 512, 512).unfold(1, 768, 768)\n        mask_patches = mask_patches.contiguous().view(-1, 512, 768)\n        \n        return img_patches, mask_patches","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.638123Z","iopub.execute_input":"2025-09-17T16:17:12.638348Z","iopub.status.idle":"2025-09-17T16:17:12.648538Z","shell.execute_reply.started":"2025-09-17T16:17:12.638326Z","shell.execute_reply":"2025-09-17T16:17:12.64792Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Images from radiopedia are full CT volumes:\nClass 0 is \"ground glass\"<br>\nClass 1 is \"consolidations\"<br>\nClass 2 is \"lungs other\" – it doesn't mean that it is healthy lungs (you don't need to predict this class)<br>\nClass 3 is \"background\" – not lungs (you don't need to predict this class)<br>","metadata":{}},{"cell_type":"code","source":"visualize(images_radiopedia[30:], masks_radiopedia[30:])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:12.649275Z","iopub.execute_input":"2025-09-17T16:17:12.649498Z","iopub.status.idle":"2025-09-17T16:17:14.829706Z","shell.execute_reply.started":"2025-09-17T16:17:12.649479Z","shell.execute_reply":"2025-09-17T16:17:14.829023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"palette = [[0], [1], [2],[3]]\nmasks_radiopedia_recover = onehot_to_mask(masks_radiopedia, palette).squeeze()  # shape = (H, W)\n\nmasks_medseg_recover = onehot_to_mask(masks_medseg, palette).squeeze()  # shape = (H, W)\n\nprint('Hot encoded mask size: ',masks_radiopedia.shape)\nprint('Paletted mask size:',masks_medseg_recover.shape)\n\nvisualize(masks_medseg_recover[30:],hot_encode=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:14.830589Z","iopub.execute_input":"2025-09-17T16:17:14.831149Z","iopub.status.idle":"2025-09-17T16:17:20.266905Z","shell.execute_reply.started":"2025-09-17T16:17:14.831115Z","shell.execute_reply":"2025-09-17T16:17:20.266147Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocess images:","metadata":{}},{"cell_type":"code","source":"images_radiopedia, mean_std = preprocess_images(images_radiopedia)\nimages_medseg, _ = preprocess_images(images_medseg, mean_std)\ntest_images_medseg, _ = preprocess_images(test_images_medseg, mean_std)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:20.267626Z","iopub.execute_input":"2025-09-17T16:17:20.26783Z","iopub.status.idle":"2025-09-17T16:17:27.865181Z","shell.execute_reply.started":"2025-09-17T16:17:20.267796Z","shell.execute_reply":"2025-09-17T16:17:27.864249Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_hists(test_images_medseg, images_radiopedia)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:27.866027Z","iopub.execute_input":"2025-09-17T16:17:27.866289Z","iopub.status.idle":"2025-09-17T16:17:31.347871Z","shell.execute_reply.started":"2025-09-17T16:17:27.866263Z","shell.execute_reply":"2025-09-17T16:17:31.34711Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Split/Train","metadata":{}},{"cell_type":"code","source":"masks_radiopedia_recover = onehot_to_mask(masks_radiopedia, palette).squeeze()  # shape = (H, W)\nmasks_medseg_recover     = onehot_to_mask(masks_medseg, palette).squeeze()  # shape = (H, W)\n\nval_indexes, train_indexes = list(range(24)), list(range(24, 100))\n\ntrain_images = np.concatenate((images_medseg[train_indexes], images_radiopedia))\ntrain_masks  = np.concatenate((masks_medseg_recover[train_indexes], masks_radiopedia_recover))\nval_images   = images_medseg[val_indexes]\nval_masks    = masks_medseg_recover[val_indexes]\n\nbatch_size = len(val_masks)\n\ndel masks_medseg_recover\ndel masks_radiopedia_recover\ndel images_radiopedia\ndel masks_radiopedia\ndel images_medseg\ndel masks_medseg","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:31.348686Z","iopub.execute_input":"2025-09-17T16:17:31.34894Z","iopub.status.idle":"2025-09-17T16:17:36.61817Z","shell.execute_reply.started":"2025-09-17T16:17:31.348915Z","shell.execute_reply":"2025-09-17T16:17:36.617369Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data generator and augmentations","metadata":{}},{"cell_type":"code","source":"SOURCE_SIZE = 512\nTARGET_SIZE = 256\n\ntrain_augs = albumentations.Compose([\n    albumentations.Rotate(limit=360, p=0.9, border_mode=cv2.BORDER_REPLICATE),\n    albumentations.RandomSizedCrop((int(SOURCE_SIZE * 0.75), SOURCE_SIZE), \n                                   (TARGET_SIZE, TARGET_SIZE), \n                                   interpolation=cv2.INTER_NEAREST),\n    albumentations.HorizontalFlip(p=0.5),\n\n])\n\nval_augs = albumentations.Compose([\n    albumentations.Resize(TARGET_SIZE, TARGET_SIZE, interpolation=cv2.INTER_NEAREST)\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:36.618997Z","iopub.execute_input":"2025-09-17T16:17:36.619224Z","iopub.status.idle":"2025-09-17T16:17:36.62748Z","shell.execute_reply.started":"2025-09-17T16:17:36.619207Z","shell.execute_reply":"2025-09-17T16:17:36.626962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset    = Dataset(train_images, train_masks, train_augs)\nval_dataset      = Dataset(val_images, val_masks, val_augs)       \ntrain_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_dataloader   = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:36.628208Z","iopub.execute_input":"2025-09-17T16:17:36.62841Z","iopub.status.idle":"2025-09-17T16:17:36.640846Z","shell.execute_reply.started":"2025-09-17T16:17:36.628395Z","shell.execute_reply":"2025-09-17T16:17:36.640285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"i,train_data     = next(enumerate(train_dataloader))\nmask_hot_encoded = mask_to_onehot(torch.unsqueeze(train_data[1],-1).numpy(),palette)\nvisualize(train_data[0].permute(0, 2, 3,1),mask_hot_encoded)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:36.64153Z","iopub.execute_input":"2025-09-17T16:17:36.64177Z","iopub.status.idle":"2025-09-17T16:17:38.156848Z","shell.execute_reply.started":"2025-09-17T16:17:36.641755Z","shell.execute_reply":"2025-09-17T16:17:38.156091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"max_lr       = 1e-2\nepoch        = 10\nweight_decay = 1e-3\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=max_lr, weight_decay=weight_decay)\nsched     = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr, epochs=epoch,\n                                            steps_per_epoch=len(train_dataloader))\n\nhistory = fit(epoch, model, train_dataloader, val_dataloader, criterion, optimizer, sched)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:38.308913Z","iopub.execute_input":"2025-09-17T16:17:38.309111Z","iopub.status.idle":"2025-09-17T16:20:44.09966Z","shell.execute_reply.started":"2025-09-17T16:17:38.309096Z","shell.execute_reply":"2025-09-17T16:20:44.098836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = smp.Unet('efficientnet-b2',in_channels=1, encoder_weights='imagenet',classes=4, activation=None, encoder_depth=5, decoder_channels=[256, 128, 64, 32, 16])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:17:38.157656Z","iopub.execute_input":"2025-09-17T16:17:38.157888Z","iopub.status.idle":"2025-09-17T16:17:38.308093Z","shell.execute_reply.started":"2025-09-17T16:17:38.157871Z","shell.execute_reply":"2025-09-17T16:17:38.307363Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model, 'Unet-efficientnet.pt')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:44.100517Z","iopub.execute_input":"2025-09-17T16:20:44.100723Z","iopub.status.idle":"2025-09-17T16:20:44.262138Z","shell.execute_reply.started":"2025-09-17T16:20:44.100707Z","shell.execute_reply":"2025-09-17T16:20:44.261332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_loss(history)\nplot_score(history)\nplot_acc(history)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:44.263364Z","iopub.execute_input":"2025-09-17T16:20:44.263681Z","iopub.status.idle":"2025-09-17T16:20:44.711413Z","shell.execute_reply.started":"2025-09-17T16:20:44.263653Z","shell.execute_reply":"2025-09-17T16:20:44.710813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation","metadata":{}},{"cell_type":"markdown","source":"### Result","metadata":{}},{"cell_type":"code","source":"image, mask             = next(iter(val_dataloader))\npred_mask, score,output = predict_image_mask_miou(model, image, mask)\nsemantic_map            = mask_to_onehot(torch.unsqueeze(mask,-1).numpy(),palette)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:44.712125Z","iopub.execute_input":"2025-09-17T16:20:44.712329Z","iopub.status.idle":"2025-09-17T16:20:44.876915Z","shell.execute_reply.started":"2025-09-17T16:20:44.712313Z","shell.execute_reply":"2025-09-17T16:20:44.876216Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize(image.cpu(), semantic_map, pred_batch=output.cpu())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:44.877675Z","iopub.execute_input":"2025-09-17T16:20:44.877915Z","iopub.status.idle":"2025-09-17T16:20:46.305659Z","shell.execute_reply.started":"2025-09-17T16:20:44.877897Z","shell.execute_reply":"2025-09-17T16:20:46.304975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mob_miou = miou_score(model, val_dataloader)\nmob_miou","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:46.306334Z","iopub.execute_input":"2025-09-17T16:20:46.306519Z","iopub.status.idle":"2025-09-17T16:20:46.466767Z","shell.execute_reply.started":"2025-09-17T16:20:46.306504Z","shell.execute_reply":"2025-09-17T16:20:46.466037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"del train_images\ndel train_masks","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:46.467577Z","iopub.execute_input":"2025-09-17T16:20:46.467881Z","iopub.status.idle":"2025-09-17T16:20:46.47193Z","shell.execute_reply.started":"2025-09-17T16:20:46.467856Z","shell.execute_reply":"2025-09-17T16:20:46.470994Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Test preds:","metadata":{}},{"cell_type":"code","source":"image_batch = np.stack([val_augs(image=img)['image'] for img in test_images_medseg], axis=0)\nprint(torch.from_numpy(image_batch).shape)\nprint(image_batch[i].shape)\n#output = test_predict(model, torch.from_numpy(image_batch).permute(0, 3, 1,2))\noutput = np.zeros((10,256,256,4))\nfor i in range(10):   \n    output[i] = test_predict(model, image_batch[i]).cpu().numpy()\nprint(output.shape)\ntest_masks_prediction = output > 0.5\nvisualize(image_batch, test_masks_prediction, num_samples=len(test_images_medseg))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:46.472681Z","iopub.execute_input":"2025-09-17T16:20:46.472935Z","iopub.status.idle":"2025-09-17T16:20:48.760389Z","shell.execute_reply.started":"2025-09-17T16:20:46.472912Z","shell.execute_reply":"2025-09-17T16:20:48.759717Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Resize prediction to original size ","metadata":{}},{"cell_type":"code","source":"test_masks_prediction_original_size = scipy.ndimage.zoom(test_masks_prediction[..., :-2], (1, 2, 2, 1), order=0)\ntest_masks_prediction_original_size.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:48.76103Z","iopub.execute_input":"2025-09-17T16:20:48.761215Z","iopub.status.idle":"2025-09-17T16:20:48.842695Z","shell.execute_reply.started":"2025-09-17T16:20:48.7612Z","shell.execute_reply":"2025-09-17T16:20:48.842071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd.DataFrame(\n             data=np.stack((np.arange(len(test_masks_prediction_original_size.ravel())), \n                            test_masks_prediction_original_size.ravel().astype(int)),\n                            axis=-1), \n             columns=['Id', 'Predicted'])\\\n.set_index('Id').to_csv('sub.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T16:20:48.843512Z","iopub.execute_input":"2025-09-17T16:20:48.844039Z","iopub.status.idle":"2025-09-17T16:20:52.136919Z","shell.execute_reply.started":"2025-09-17T16:20:48.844021Z","shell.execute_reply":"2025-09-17T16:20:52.136328Z"}},"outputs":[],"execution_count":null}]}