{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## summary\n\n* 2.5d segmentation\n    *  segmentation_models_pytorch \n    *  Unet\n* use only 6 slices\n* slide inference","metadata":{}},{"cell_type":"code","source":"\nfrom sklearn.metrics import roc_auc_score, accuracy_score, f1_score, log_loss\nimport pickle\nfrom torch.utils.data import DataLoader\nfrom torch.cuda.amp import autocast, GradScaler\nimport warnings\nimport sys\nimport pandas as pd\nimport os\nimport gc\nimport sys\nimport math\nimport time\nimport random\nimport shutil\nfrom pathlib import Path\nfrom contextlib import contextmanager\nfrom collections import defaultdict, Counter\nimport cv2\n\nimport scipy as sp\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\nfrom tqdm.auto import tqdm\nfrom functools import partial\n\nimport argparse\nimport importlib\nimport torch\nimport torch.nn as nn\nfrom torch.optim import Adam, SGD, AdamW\n\nimport datetime\nimport wandb","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:15.410840Z","iopub.execute_input":"2023-04-04T04:51:15.411176Z","iopub.status.idle":"2023-04-04T04:51:20.141027Z","shell.execute_reply.started":"2023-04-04T04:51:15.411146Z","shell.execute_reply":"2023-04-04T04:51:20.139560Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sys.path.append('/kaggle/input/pretrainedmodels/pretrainedmodels-0.7.4')\nsys.path.append('/kaggle/input/efficientnet-pytorch/EfficientNet-PyTorch-master')\nsys.path.append('/kaggle/input/timm-pytorch-image-models/pytorch-image-models-master')\nsys.path.append('/kaggle/input/segmentation-models-pytorch/segmentation_models.pytorch-master')\n\nimport segmentation_models_pytorch as smp","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:20.143786Z","iopub.execute_input":"2023-04-04T04:51:20.144176Z","iopub.status.idle":"2023-04-04T04:51:22.838683Z","shell.execute_reply.started":"2023-04-04T04:51:20.144135Z","shell.execute_reply":"2023-04-04T04:51:22.837613Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom torch.utils.data import DataLoader, Dataset\nimport cv2\nimport torch\nimport os\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom albumentations import ImageOnlyTransform","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:22.840191Z","iopub.execute_input":"2023-04-04T04:51:22.840582Z","iopub.status.idle":"2023-04-04T04:51:23.622503Z","shell.execute_reply.started":"2023-04-04T04:51:22.840544Z","shell.execute_reply":"2023-04-04T04:51:23.621482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## config","metadata":{}},{"cell_type":"code","source":"import os\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nclass CFG:\n    # ============== comp exp name =============\n    comp_name = 'vesuvius'\n\n    # comp_dir_path = './'\n    comp_dir_path = '/kaggle/input/'\n    comp_folder_name = 'vesuvius-challenge-ink-detection'\n    # comp_dataset_path = f'{comp_dir_path}datasets/{comp_folder_name}/'\n    comp_dataset_path = f'{comp_dir_path}{comp_folder_name}/'\n    \n    exp_name = 'vesuvius_2d_slide_exp002'\n\n    # ============== pred target =============\n    target_size = 1\n\n    # ============== model cfg =============\n    model_name = 'Unet'\n    # backbone = 'efficientnet-b0'\n    backbone = 'se_resnext50_32x4d'\n\n    in_chans = 6 # 65\n    # ============== training cfg =============\n    size = 224\n    tile_size = 224\n    stride = tile_size // 8\n\n    batch_size = 16 # 32\n    use_amp = True\n\n    scheduler = 'GradualWarmupSchedulerV2'\n    # scheduler = 'CosineAnnealingLR'\n    epochs = 15\n\n    warmup_factor = 10\n    lr = 1e-4 / warmup_factor\n\n    # ============== fold =============\n    valid_id = 2\n\n    objective_cv = 'binary'  # 'binary', 'multiclass', 'regression'\n    metric_direction = 'maximize'  # maximize, 'minimize'\n    # metrics = 'dice_coef'\n\n    # ============== fixed =============\n    pretrained = True\n    inf_weight = 'best'  # 'best'\n\n    min_lr = 1e-6\n    weight_decay = 1e-6\n    max_grad_norm = 1000\n\n    print_freq = 50\n    num_workers = 4\n\n    seed = 42\n\n    # ============== augmentation =============\n    train_aug_list = [\n        # A.RandomResizedCrop(\n        #     size, size, scale=(0.85, 1.0)),\n        A.Resize(size, size),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.RandomBrightnessContrast(p=0.75),\n        A.ShiftScaleRotate(p=0.75),\n        A.OneOf([\n                A.GaussNoise(var_limit=[10, 50]),\n                A.GaussianBlur(),\n                A.MotionBlur(),\n                ], p=0.4),\n        A.GridDistortion(num_steps=5, distort_limit=0.3, p=0.5),\n        A.CoarseDropout(max_holes=1, max_width=int(size * 0.3), max_height=int(size * 0.3), \n                        mask_fill_value=0, p=0.5),\n        # A.Cutout(max_h_size=int(size * 0.6),\n        #          max_w_size=int(size * 0.6), num_holes=1, p=1.0),\n        A.Normalize(\n            mean= [0] * in_chans,\n            std= [1] * in_chans\n        ),\n        ToTensorV2(transpose_mask=True),\n    ]\n\n    valid_aug_list = [\n        A.Resize(size, size),\n        A.Normalize(\n            mean= [0] * in_chans,\n            std= [1] * in_chans\n        ),\n        ToTensorV2(transpose_mask=True),\n    ]\n","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.625250Z","iopub.execute_input":"2023-04-04T04:51:23.625553Z","iopub.status.idle":"2023-04-04T04:51:23.638088Z","shell.execute_reply.started":"2023-04-04T04:51:23.625523Z","shell.execute_reply":"2023-04-04T04:51:23.636525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IS_DEBUG = False\nmode = 'train' if IS_DEBUG else 'test'\nTH = 0.4","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.639547Z","iopub.execute_input":"2023-04-04T04:51:23.640341Z","iopub.status.idle":"2023-04-04T04:51:23.656451Z","shell.execute_reply.started":"2023-04-04T04:51:23.640303Z","shell.execute_reply":"2023-04-04T04:51:23.655252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.658119Z","iopub.execute_input":"2023-04-04T04:51:23.658550Z","iopub.status.idle":"2023-04-04T04:51:23.811369Z","shell.execute_reply.started":"2023-04-04T04:51:23.658495Z","shell.execute_reply":"2023-04-04T04:51:23.810212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## helper","metadata":{}},{"cell_type":"code","source":"# ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = img.flatten()\n    # pixels = (pixels >= thr).astype(int)\n    \n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.813141Z","iopub.execute_input":"2023-04-04T04:51:23.813569Z","iopub.status.idle":"2023-04-04T04:51:23.824293Z","shell.execute_reply.started":"2023-04-04T04:51:23.813471Z","shell.execute_reply":"2023-04-04T04:51:23.823167Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## dataset","metadata":{}},{"cell_type":"code","source":"def read_image(fragment_id):\n    images = []\n\n    # idxs = range(65)\n    mid = 65 // 2\n    start = mid - CFG.in_chans // 2\n    end = mid + CFG.in_chans // 2\n    idxs = range(start, end)\n\n    for i in tqdm(idxs):\n        \n        image = cv2.imread(CFG.comp_dataset_path + f\"{mode}/{fragment_id}/surface_volume/{i:02}.tif\", 0)\n\n        pad0 = (CFG.tile_size - image.shape[0] % CFG.tile_size)\n        pad1 = (CFG.tile_size - image.shape[1] % CFG.tile_size)\n\n        image = np.pad(image, [(0, pad0), (0, pad1)], constant_values=0)\n\n        images.append(image)\n    images = np.stack(images, axis=2)\n    \n    return images","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.826048Z","iopub.execute_input":"2023-04-04T04:51:23.826492Z","iopub.status.idle":"2023-04-04T04:51:23.835717Z","shell.execute_reply.started":"2023-04-04T04:51:23.826453Z","shell.execute_reply":"2023-04-04T04:51:23.834666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_transforms(data, cfg):\n    if data == 'train':\n        aug = A.Compose(cfg.train_aug_list)\n    elif data == 'valid':\n        aug = A.Compose(cfg.valid_aug_list)\n\n    # print(aug)\n    return aug\n\nclass CustomDataset(Dataset):\n    def __init__(self, images, cfg, labels=None, transform=None):\n        self.images = images\n        self.cfg = cfg\n        self.labels = labels\n        self.transform = transform\n\n    def __len__(self):\n        # return len(self.xyxys)\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        # x1, y1, x2, y2 = self.xyxys[idx]\n        image = self.images[idx]\n        data = self.transform(image=image)\n        image = data['image']\n        return image\n","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.838014Z","iopub.execute_input":"2023-04-04T04:51:23.839479Z","iopub.status.idle":"2023-04-04T04:51:23.848571Z","shell.execute_reply.started":"2023-04-04T04:51:23.839450Z","shell.execute_reply":"2023-04-04T04:51:23.847638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_test_dataset(fragment_id):\n    test_images = read_image(fragment_id)\n    \n    x1_list = list(range(0, test_images.shape[1]-CFG.tile_size+1, CFG.stride))\n    y1_list = list(range(0, test_images.shape[0]-CFG.tile_size+1, CFG.stride))\n    \n    test_images_list = []\n    xyxys = []\n    for y1 in y1_list:\n        for x1 in x1_list:\n            y2 = y1 + CFG.tile_size\n            x2 = x1 + CFG.tile_size\n            \n            test_images_list.append(test_images[y1:y2, x1:x2])\n            xyxys.append((x1, y1, x2, y2))\n    xyxys = np.stack(xyxys)\n            \n    test_dataset = CustomDataset(test_images_list, CFG, transform=get_transforms(data='valid', cfg=CFG))\n    \n    test_loader = DataLoader(test_dataset,\n                          batch_size=CFG.batch_size,\n                          shuffle=False,\n                          num_workers=CFG.num_workers, pin_memory=True, drop_last=False)\n    \n    return test_loader, xyxys","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.853420Z","iopub.execute_input":"2023-04-04T04:51:23.854214Z","iopub.status.idle":"2023-04-04T04:51:23.863163Z","shell.execute_reply.started":"2023-04-04T04:51:23.854184Z","shell.execute_reply":"2023-04-04T04:51:23.862015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## model","metadata":{}},{"cell_type":"code","source":"class CustomModel(nn.Module):\n    def __init__(self, cfg, weight=None):\n        super().__init__()\n        self.cfg = cfg\n\n        self.encoder = smp.Unet(\n            encoder_name=cfg.backbone, \n            encoder_weights=weight,\n            in_channels=cfg.in_chans,\n            classes=cfg.target_size,\n            activation=None,\n        )\n\n    def forward(self, image):\n        output = self.encoder(image)\n        output = output.squeeze(-1)\n        return output\n\ndef build_model(cfg, weight=\"imagenet\"):\n    print('model_name', cfg.model_name)\n    print('backbone', cfg.backbone)\n\n    model = CustomModel(cfg, weight)\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.864743Z","iopub.execute_input":"2023-04-04T04:51:23.865123Z","iopub.status.idle":"2023-04-04T04:51:23.878140Z","shell.execute_reply.started":"2023-04-04T04:51:23.865060Z","shell.execute_reply":"2023-04-04T04:51:23.877181Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class EnsembleModel:\n    def __init__(self, use_tta=False):\n        self.models = []\n        self.use_tta = use_tta\n\n    def __call__(self, x):\n        outputs = [torch.sigmoid(model(x)).to('cpu').numpy()\n                   for model in self.models]\n        avg_preds = np.mean(outputs, axis=0)\n        return avg_preds\n\n    def add_model(self, model):\n        self.models.append(model)\n\ndef build_ensemble_model():\n    model = EnsembleModel()\n    for fold in [1, 2, 3]:\n        _model = build_model(CFG, weight=None)\n        _model.to(device)\n\n        model_path = f'/kaggle/input/vesuvius-models-public/{CFG.exp_name}/vesuvius-models/Unet_fold{fold}_best.pth'\n        state = torch.load(model_path)['model']\n        _model.load_state_dict(state)\n        _model.eval()\n        \n        model.add_model(_model)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.881380Z","iopub.execute_input":"2023-04-04T04:51:23.881751Z","iopub.status.idle":"2023-04-04T04:51:23.891133Z","shell.execute_reply.started":"2023-04-04T04:51:23.881723Z","shell.execute_reply":"2023-04-04T04:51:23.890126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if mode == 'test':\n    fragment_ids = sorted(os.listdir(CFG.comp_dataset_path + mode))\nelse:\n    fragment_ids = [3]","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.892497Z","iopub.execute_input":"2023-04-04T04:51:23.892968Z","iopub.status.idle":"2023-04-04T04:51:23.908000Z","shell.execute_reply.started":"2023-04-04T04:51:23.892927Z","shell.execute_reply":"2023-04-04T04:51:23.906772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = build_ensemble_model()","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:51:23.909273Z","iopub.execute_input":"2023-04-04T04:51:23.909714Z","iopub.status.idle":"2023-04-04T04:52:01.416542Z","shell.execute_reply.started":"2023-04-04T04:51:23.909676Z","shell.execute_reply":"2023-04-04T04:52:01.415258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## main","metadata":{}},{"cell_type":"code","source":"results = []\nfor fragment_id in fragment_ids:\n    \n    test_loader, xyxys = make_test_dataset(fragment_id)\n    \n    binary_mask = cv2.imread(CFG.comp_dataset_path + f\"{mode}/{fragment_id}/mask.png\", 0)\n    binary_mask = (binary_mask / 255).astype(int)\n    \n    ori_h = binary_mask.shape[0]\n    ori_w = binary_mask.shape[1]\n    # mask = mask / 255\n\n    pad0 = (CFG.tile_size - binary_mask.shape[0] % CFG.tile_size)\n    pad1 = (CFG.tile_size - binary_mask.shape[1] % CFG.tile_size)\n\n    binary_mask = np.pad(binary_mask, [(0, pad0), (0, pad1)], constant_values=0)\n    \n    mask_pred = np.zeros(binary_mask.shape)\n    mask_count = np.zeros(binary_mask.shape)\n\n    for step, (images) in tqdm(enumerate(test_loader), total=len(test_loader)):\n        images = images.to(device)\n        batch_size = images.size(0)\n\n        with torch.no_grad():\n            y_preds = model(images)\n\n        start_idx = step*CFG.batch_size\n        end_idx = start_idx + batch_size\n        for i, (x1, y1, x2, y2) in enumerate(xyxys[start_idx:end_idx]):\n            mask_pred[y1:y2, x1:x2] += y_preds[i].squeeze(0)\n            mask_count[y1:y2, x1:x2] += np.ones((CFG.tile_size, CFG.tile_size))\n    \n    plt.imshow(mask_count)\n    plt.show()\n    \n    print(f'mask_count_min: {mask_count.min()}')\n    mask_pred /= mask_count\n    \n    mask_pred = mask_pred[:ori_h, :ori_w]\n    binary_mask = binary_mask[:ori_h, :ori_w]\n    \n    mask_pred = (mask_pred >= TH).astype(int)\n    mask_pred *= binary_mask\n    \n    plt.imshow(mask_pred)\n    plt.show()\n    \n    inklabels_rle = rle(mask_pred)\n    \n    results.append((fragment_id, inklabels_rle))\n    \n\n    del mask_pred, mask_count\n    del test_loader\n    \n    gc.collect()\n    torch.cuda.empty_cache()\n","metadata":{"execution":{"iopub.status.busy":"2023-04-04T04:52:01.417925Z","iopub.execute_input":"2023-04-04T04:52:01.418536Z","iopub.status.idle":"2023-04-04T05:04:59.789188Z","shell.execute_reply.started":"2023-04-04T04:52:01.418493Z","shell.execute_reply":"2023-04-04T05:04:59.788009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## submission","metadata":{}},{"cell_type":"code","source":"sub = pd.DataFrame(results, columns=['Id', 'Predicted'])","metadata":{"execution":{"iopub.status.busy":"2023-04-04T05:04:59.791024Z","iopub.execute_input":"2023-04-04T05:04:59.792530Z","iopub.status.idle":"2023-04-04T05:04:59.802190Z","shell.execute_reply.started":"2023-04-04T05:04:59.792484Z","shell.execute_reply":"2023-04-04T05:04:59.801265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub","metadata":{"execution":{"iopub.status.busy":"2023-04-04T05:04:59.803914Z","iopub.execute_input":"2023-04-04T05:04:59.804364Z","iopub.status.idle":"2023-04-04T05:04:59.832688Z","shell.execute_reply.started":"2023-04-04T05:04:59.804326Z","shell.execute_reply":"2023-04-04T05:04:59.831168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub = pd.read_csv(CFG.comp_dataset_path + 'sample_submission.csv')\nsample_sub = pd.merge(sample_sub[['Id']], sub, on='Id', how='left')","metadata":{"execution":{"iopub.status.busy":"2023-04-04T05:04:59.833854Z","iopub.execute_input":"2023-04-04T05:04:59.834119Z","iopub.status.idle":"2023-04-04T05:04:59.862057Z","shell.execute_reply.started":"2023-04-04T05:04:59.834093Z","shell.execute_reply":"2023-04-04T05:04:59.861126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub","metadata":{"execution":{"iopub.status.busy":"2023-04-04T05:04:59.863677Z","iopub.execute_input":"2023-04-04T05:04:59.864045Z","iopub.status.idle":"2023-04-04T05:04:59.874408Z","shell.execute_reply.started":"2023-04-04T05:04:59.864009Z","shell.execute_reply":"2023-04-04T05:04:59.873278Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-04-04T05:04:59.875995Z","iopub.execute_input":"2023-04-04T05:04:59.876637Z","iopub.status.idle":"2023-04-04T05:04:59.890511Z","shell.execute_reply.started":"2023-04-04T05:04:59.876600Z","shell.execute_reply":"2023-04-04T05:04:59.889190Z"},"trusted":true},"execution_count":null,"outputs":[]}]}