{"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":"code","source":"# Basic Imaports\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\n\n# Extra Basic Imports\nimport gc\nimport glob\nimport math\n\n# Ploting Imports\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport timm\n\n# Import PyTorch\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nfrom torch.utils.tensorboard import SummaryWriter\nimport torch.utils.data as data\n\n# Multiprocessing\nimport multiprocessing\n\n# Algorithm Imports\nfrom sklearn.metrics import accuracy_score, f1_score, mean_squared_error, roc_auc_score\nfrom sklearn.model_selection import KFold, StratifiedGroupKFold, StratifiedKFold\nfrom skimage.transform import resize as resize_ski\nfrom torch.utils.data import DataLoader, Dataset\nfrom tqdm import tqdm\n\n# Warnings\nimport warnings\nwarnings.simplefilter(\"ignore\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-03-21T09:11:56.876510Z","iopub.execute_input":"2023-03-21T09:11:56.876810Z","iopub.status.idle":"2023-03-21T09:12:07.017784Z","shell.execute_reply.started":"2023-03-21T09:11:56.876781Z","shell.execute_reply":"2023-03-21T09:12:07.016587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define UNet Class Definition\n# ================= Unet ========================\n# Ref: https://www.kaggle.com/code/balraj98/unet-for-building-segmentation-pytorch\nclass DoubleConv(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(DoubleConv, self).__init__()\n        self.double_conv = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_channels),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),\n            nn.BatchNorm2d(out_channels),\n            nn.ReLU(inplace=True),\n        )\n\n    def forward(self, x):\n        return self.double_conv(x)\n\nclass DownBlock(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(DownBlock, self).__init__()\n        self.double_conv = DoubleConv(in_channels, out_channels)\n        self.down_sample = nn.MaxPool2d(2)\n\n    def forward(self, x):\n        skip_out = self.double_conv(x)\n        down_out = self.down_sample(skip_out)\n        return (down_out, skip_out)\n\nclass UpBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, up_sample_mode):\n        super(UpBlock, self).__init__()\n        if up_sample_mode == 'conv_transpose':\n            self.up_sample = nn.ConvTranspose2d(in_channels-out_channels, in_channels-out_channels, kernel_size=2, stride=2)\n        elif up_sample_mode == 'bilinear':\n            self.up_sample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        else:\n            raise ValueError(\"Unsupported `up_sample_mode` (can take one of `conv_transpose` or `bilinear`)\")\n        self.double_conv = DoubleConv(in_channels, out_channels)\n\n    def forward(self, down_input, skip_input):\n        x = self.up_sample(down_input)\n        x = torch.cat([x, skip_input], dim=1)\n        return self.double_conv(x)\n\nclass UNet(nn.Module):\n    def __init__(self, in_ch, out_classes=1, up_sample_mode='conv_transpose'):\n        super(UNet, self).__init__()\n        self.up_sample_mode = up_sample_mode\n        # Downsampling Path\n        self.down_conv1 = DownBlock(in_ch, 64)\n        self.down_conv2 = DownBlock(64, 128)\n        self.down_conv3 = DownBlock(128, 256)\n        self.down_conv4 = DownBlock(256, 512)\n\n        # Bottleneck\n        self.double_conv = DoubleConv(512, 1024)\n        # Upsampling Path\n        self.up_conv4 = UpBlock(512 + 1024, 512, self.up_sample_mode)\n        self.up_conv3 = UpBlock(256 + 512, 256, self.up_sample_mode)\n        self.up_conv2 = UpBlock(128 + 256, 128, self.up_sample_mode)\n        self.up_conv1 = UpBlock(128 + 64, 64, self.up_sample_mode)\n\n        # Final Convolution\n        self.conv_last = nn.Conv2d(64, out_classes, kernel_size=1)\n\n    def forward(self, x):\n        x, skip1_out = self.down_conv1(x)\n        x, skip2_out = self.down_conv2(x)\n        x, skip3_out = self.down_conv3(x)\n        x, skip4_out = self.down_conv4(x)\n        x = self.double_conv(x)\n        x = self.up_conv4(x, skip4_out)\n        x = self.up_conv3(x, skip3_out)\n        x = self.up_conv2(x, skip2_out)\n        x = self.up_conv1(x, skip1_out)\n        x = self.conv_last(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:12:07.019839Z","iopub.execute_input":"2023-03-21T09:12:07.020208Z","iopub.status.idle":"2023-03-21T09:12:07.038852Z","shell.execute_reply.started":"2023-03-21T09:12:07.020171Z","shell.execute_reply":"2023-03-21T09:12:07.036957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ================== random patch dataset ==============================\nclass RandomOpt():\n    def __init__(self):\n        self.SHARED_HEIGHT = 4096  # Height to resize all papyrii\n        self.BUFFER = 64  # Half-size of papyrus patches we'll use as model inputs\n        self.Z_DIM = 16  # Number of slices in the z direction. Max value is 64 - Z_START\n        self.Z_START = 25  # Offset of slices in the z direction\n        self.DATA_DIR = \"/kaggle/input/vesuvius-challenge-ink-detection\"\n\ndef resize(img, SHARED_HEIGHT=RandomOpt().SHARED_HEIGHT):\n    current_width, current_height = img.size\n    aspect_ratio = current_width / current_height\n    new_width = int(SHARED_HEIGHT * aspect_ratio)\n    new_size = (new_width, SHARED_HEIGHT)\n    img = img.resize(new_size)\n    return img\n\n\ndef load_mask(split, index, DATA_DIR=RandomOpt().DATA_DIR):\n    img = Image.open(f\"{DATA_DIR}/{split}/{index}/mask.png\").convert('1')\n    img = resize(img)\n    return torch.from_numpy(np.array(img))\n\ndef load_labels(split, index, DATA_DIR=RandomOpt().DATA_DIR):\n    img = Image.open(f\"{DATA_DIR}/{split}/{index}/inklabels.png\")\n    img = resize(img)\n    return torch.from_numpy(np.array(img)).gt(0).float()\n\ndef load_volume(split, index, DATA_DIR=RandomOpt().DATA_DIR, Z_START=RandomOpt().Z_START, Z_DIM=RandomOpt().Z_DIM):\n    # Load the 3d x-ray scan, one slice at a time\n    z_slices_fnames = sorted(glob.glob(f\"{DATA_DIR}/{split}/{index}/surface_volume/*.tif\"))[Z_START:Z_START + Z_DIM]\n    z_slices = []\n    for z, filename in  tqdm(enumerate(z_slices_fnames)):\n        img = Image.open(filename)\n        img = resize(img)\n        z_slice = np.array(img, dtype=\"float32\")\n        z_slices.append(torch.from_numpy(z_slice))\n    return torch.stack(z_slices, dim=0)\n\n# Random choice of patches for training\ndef sample_random_location(shape, BUFFER=RandomOpt().BUFFER):\n    a=BUFFER\n    random_train_x = (shape[0] - BUFFER - 1 - a)*torch.rand(1)+a\n    random_train_y = (shape[1] - BUFFER - 1 - a)*torch.rand(1)+a\n    random_train_location = torch.stack([random_train_x, random_train_y])\n    return random_train_location\n\ndef is_in_masked_zone(location, mask):\n    return mask[location[0].long(), location[1].long()]\n\ndef is_in_val_zone(location, val_location, val_zone_size, BUFFER=RandomOpt().BUFFER):\n    x = location[0]\n    y = location[1]\n    x_match = val_location[0] - BUFFER <= x <= val_location[0] + val_zone_size[0] + BUFFER\n    y_match = val_location[1] - BUFFER <= y <= val_location[1] + val_zone_size[1] + BUFFER\n    return x_match and y_match\n\nclass RandomPatchLocDataset(data.Dataset):\n    def __init__(self, mask, val_location, val_zone_size):\n        self.mask = mask\n        self.val_location = val_location\n        self.val_zone_size = val_zone_size\n        self.sample_random_location_train = lambda x: sample_random_location(mask.shape)\n        self.is_in_mask_train = lambda x: is_in_masked_zone(x, mask)\n\n    def is_proper_train_location(self, location):\n        return not is_in_val_zone(location, self.val_location, self.val_zone_size) and self.is_in_mask_train(location)\n\n    def __len__(self):\n        return 1280\n\n    def __getitem__(self, index):\n        # Generate a random patch\n        # Ignore the index\n        loc = self.sample_random_location_train(0)\n        while not self.is_proper_train_location(loc):\n            loc = self.sample_random_location_train(0)\n        return loc.int().squeeze(1)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:12:07.040590Z","iopub.execute_input":"2023-03-21T09:12:07.041182Z","iopub.status.idle":"2023-03-21T09:12:07.062105Z","shell.execute_reply.started":"2023-03-21T09:12:07.041136Z","shell.execute_reply":"2023-03-21T09:12:07.061062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initiate Model\n# ============= Model ==============\nclass ModelOpt:\n    def __init__(self):\n        # self.GPU_ID = '0'  \n        self.Z_DIM = RandomOpt().Z_DIM\n        self.BUFFER = RandomOpt().BUFFER\n        self.SEED = 0\n        self.BATCH_SIZE = 64\n        self.LEARNING_RATE =1e-4\n        self.TRAINING_EPOCH = 20\n        self.LOG_DIR = '/kaggle/working'\n        self.LOAD_VOLUME = [1, 2, 3]\n        # Val\n        self.VAL_LOC = (1300, 1000)\n        self.VAL_SIZE = (300, 7000)\n\nclass RandomPatchModel():\n    def __init__(self, opt = ModelOpt()):\n        self.opt = opt\n        self._setup_all()\n        self.volume_list = [load_volume('train', i) for i in opt.LOAD_VOLUME]\n        # Here volume: [Z_DIM, SHARED_HEIGHT, W_V1 + W_V2 + ...]\n        self.volume = torch.cat(self.volume_list, dim=2)\n        # Same for mask and label\n        self.mask_list = [load_mask('train', i) for i in opt.LOAD_VOLUME]\n        self.labels_list = [load_labels('train', i) for i in opt.LOAD_VOLUME]\n        # [SHARED_HEIGHT, W_V1 + W_V2 + ...]\n        self.labels = torch.cat(self.labels_list, dim=1)\n        self.mask = torch.cat(self.mask_list, dim=1)\n\n        self.net = UNet(in_ch=opt.Z_DIM).to(self.device)\n\n        # Dataset\n        self.loc_datast = RandomPatchLocDataset(self.mask, val_location=opt.VAL_LOC, val_zone_size=opt.VAL_SIZE)\n        self.loc_loader = data.DataLoader(self.loc_datast, batch_size=opt.BATCH_SIZE)\n        # Val\n        self.val_loc = []\n        for x in range(opt.VAL_LOC[0], opt.VAL_LOC[0] + opt.VAL_SIZE[0], opt.BUFFER):\n            for y in range(opt.VAL_LOC[1], opt.VAL_LOC[1] + opt.VAL_SIZE[1], opt.BUFFER):\n                if is_in_masked_zone([torch.tensor(x),torch.tensor(y)], self.mask):\n                    self.val_loc.append([[x, y]])\n        print(f\"======> Num Patches Val: {len(self.val_loc)}\")\n\n\n    def _setup_all(self):\n        # random seed\n        np.random.seed(self.opt.SEED)\n        torch.manual_seed(self.opt.SEED)\n        torch.cuda.manual_seed_all(self.opt.SEED)\n        # torch\n        # os.environ['CUDA_VISIBLE_DEVICES'] = self.opt.GPU_ID\n        torch.backends.cudnn.enabled = True\n        torch.backends.cudnn.benchmark = True\n        self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n        # Log\n        self.log_dir = self.opt.LOG_DIR\n        self.ckpt = os.path.join(self.log_dir)\n\n    def get_subvolume(self, batch_loc, volume, labels):\n        # batch_loc : [batch_size, 2]\n        subvolume = []\n        label = []\n        for l in batch_loc:\n            x = l[0]\n            y = l[1]\n            sv = volume[:, x - self.opt.BUFFER:x + self.opt.BUFFER, y - self.opt.BUFFER:y + self.opt.BUFFER]\n            sv = sv / 65535.\n            subvolume.append(sv)\n            if labels is not None:\n                lb = labels[x - self.opt.BUFFER:x + self.opt.BUFFER, y - self.opt.BUFFER:y + self.opt.BUFFER]\n                lb = lb.unsqueeze(0)\n                label.append(lb)\n        # [batch, Z_DIM, BUFFER, BUFFER]\n        subvolume = torch.stack(subvolume)\n        # [batch, 1, BUFFER, BUFFER]\n        if labels is not None:\n            label = torch.stack(label)\n        return subvolume, label\n\n    def augment_train_data(self, subvolume, label):\n        # Add Data augmentation here\n        return subvolume, label\n\n    def train_loop(self):\n        print(\"=====> Begin training\")\n        self.criterion = torch.nn.BCEWithLogitsLoss(reduction='mean')\n        self.optimizer = optim.Adam(self.net.parameters(), lr=self.opt.LEARNING_RATE)\n        self.net.train()\n\n        best_val_loss = 100\n        best_val_acc = 0\n        meter = AverageMeter()\n        for epoch in range(self.opt.TRAINING_EPOCH):\n            bar = tqdm(enumerate(self.loc_loader), total=len(self.loc_datast) / self.opt.BATCH_SIZE)\n            bar.set_description_str(f\"Epoch: {epoch}\")\n            for i, loc in bar:\n                subvolume, label = self.get_subvolume(loc, self.volume, self.labels)\n                loss = self._train_step(subvolume, label)\n                meter.update(loss)\n                bar.set_postfix_str(f\"Avg loss: {np.round(meter.get_value(),3)}\")\n\n            val_loss, val_acc = self.validataion_loop()\n            print(f\"======> Val Loss:{np.round(val_loss,3)} | Val Acc:{np.round(val_acc,3)} \")\n            if val_loss < best_val_loss and val_acc > best_val_acc:\n                torch.save(self.net.state_dict(), os.path.join(self.ckpt, \"best.pt\"))\n                print(\"======> Save best val model\")\n\n                best_val_loss = val_loss\n                best_val_acc = val_acc\n\n\n\n    def _train_step(self, subvolume, label):\n        self.optimizer.zero_grad()\n        # inputs: subvolume: [batch, Z_DIM, BUFFER, BUFFER]\n        #         label: [batch, 1, BUFFER, BUFFER]\n        outputs = self.net(subvolume.to(self.device))\n        loss = self.criterion(outputs, label.to(self.device))\n        loss.backward()\n        self.optimizer.step()\n        return loss\n\n    def validataion_loop(self):\n        meter_loss = AverageMeter()\n        meter_acc = AverageMeter()\n        self.net.eval()\n        for loc in self.val_loc:\n            subvolume, label = self.get_subvolume(loc, self.volume, self.labels)\n            outputs = self.net(subvolume.to(self.device))\n            loss = self.criterion(outputs, label.to(self.device))\n            meter_loss.update(loss)\n            pred = torch.sigmoid(outputs) > 0.5\n            meter_acc.update(\n                (pred == label.to(self.device)).sum(),\n                int(torch.prod(torch.tensor(label.shape)))\n            )\n        self.net.train()\n        return meter_loss.get_value(), meter_acc.get_value()\n\n    def load_best_ckpt(self):\n        self.net.load_state_dict(torch.load(os.path.join(self.ckpt, \"best.pt\")))\n\n# For the metric\nclass AverageMeter(object):\n    def __init__(self):\n        self.sum = 0\n        self.n = 0\n\n    def update(self, x, n=1):\n        self.sum += float(x)\n        self.n += n\n\n    def reset(self):\n        self.sum = 0\n        self.n = 0\n\n    def get_value(self):\n        if self.n:\n            return self.sum / self.n\n        return 0","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:12:07.065209Z","iopub.execute_input":"2023-03-21T09:12:07.065780Z","iopub.status.idle":"2023-03-21T09:12:07.096602Z","shell.execute_reply.started":"2023-03-21T09:12:07.065744Z","shell.execute_reply":"2023-03-21T09:12:07.095527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define model\nmodel = RandomPatchModel()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:12:07.098286Z","iopub.execute_input":"2023-03-21T09:12:07.098790Z","iopub.status.idle":"2023-03-21T09:17:11.437377Z","shell.execute_reply.started":"2023-03-21T09:12:07.098752Z","shell.execute_reply":"2023-03-21T09:17:11.436242Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Training\nmodel.train_loop()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:17:11.439059Z","iopub.execute_input":"2023-03-21T09:17:11.439415Z","iopub.status.idle":"2023-03-21T09:26:05.385277Z","shell.execute_reply.started":"2023-03-21T09:17:11.439379Z","shell.execute_reply":"2023-03-21T09:26:05.384009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the best model\nmodel.load_best_ckpt()\nmodel.criterion = torch.nn.BCEWithLogitsLoss(reduction='mean')\nloss, acc = model.validataion_loop()\nmodel.net.eval()\nprint(f\"Val loss: {np.round(loss,3)} | Val acc: {np.round(acc, 3)}\")","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:26:05.386844Z","iopub.execute_input":"2023-03-21T09:26:05.387517Z","iopub.status.idle":"2023-03-21T09:26:08.996992Z","shell.execute_reply.started":"2023-03-21T09:26:05.387474Z","shell.execute_reply":"2023-03-21T09:26:08.995896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Compute Predictions\ndef compute_predictions_map(split, index):\n    print(f\"======> Load data for {split}/{index}\")\n    test_volume = load_volume(split=split, index=index)\n    test_mask = load_mask(split=split, index=index)\n    print(f\"======> Volume shape: {test_volume.shape}\")\n    test_locations = []\n    BUFFER = model.opt.BUFFER\n    stride = BUFFER // 2\n\n    for x in range(BUFFER, test_volume.shape[1] - BUFFER, stride):\n        for y in range(BUFFER, test_volume.shape[2] - BUFFER, stride):\n            if is_in_masked_zone([torch.tensor(x),torch.tensor(y)], test_mask):\n                test_locations.append((x, y))\n    print(f\"======> {len(test_locations)} test locations (after filtering by mask)\")\n\n    predictions_map = torch.zeros((1, 1, test_volume.shape[1], test_volume.shape[2]))\n    predictions_map_counts = torch.zeros((1, 1, test_volume.shape[1], test_volume.shape[2]))\n    print(f\"======> Compute predictions\")\n\n    with torch.no_grad():\n        bar = tqdm(test_locations)\n        for loc in bar:\n            subvolume, label = model.get_subvolume([loc], test_volume, None)\n            outputs = model.net(subvolume.to(model.device))\n            pred = torch.sigmoid(outputs)\n            # print(loc, (pred > 0.5).sum())\n            # Here a single location may be with multiple result\n            predictions_map[:, :, loc[0] - BUFFER : loc[0] + BUFFER, loc[1] - BUFFER : loc[1] + BUFFER] += pred.cpu()\n            predictions_map_counts[:, :, loc[0] - BUFFER : loc[0] + BUFFER, loc[1] - BUFFER : loc[1] + BUFFER] += 1\n\n    # print(predictions_map_b[:,:, 2500, 1000])\n    # print(predictions_map_counts[:,:, 2500, 1000])\n    predictions_map /= (predictions_map_counts + 1e-7)\n    return predictions_map","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:26:08.998799Z","iopub.execute_input":"2023-03-21T09:26:08.999217Z","iopub.status.idle":"2023-03-21T09:26:09.011159Z","shell.execute_reply.started":"2023-03-21T09:26:08.999173Z","shell.execute_reply":"2023-03-21T09:26:09.009872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Call Compute Prediction\npredictions_map_a = compute_predictions_map(split=\"test\", index=\"a\")\npredictions_map_b = compute_predictions_map(split=\"test\", index=\"b\")","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:26:09.012745Z","iopub.execute_input":"2023-03-21T09:26:09.013912Z","iopub.status.idle":"2023-03-21T09:32:25.288139Z","shell.execute_reply.started":"2023-03-21T09:26:09.013872Z","shell.execute_reply":"2023-03-21T09:32:25.286944Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Predictiona Map A\nplt.imshow(predictions_map_a.squeeze() > 0.10, cmap='gray')","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:32:25.293257Z","iopub.execute_input":"2023-03-21T09:32:25.293561Z","iopub.status.idle":"2023-03-21T09:32:27.520573Z","shell.execute_reply.started":"2023-03-21T09:32:25.293533Z","shell.execute_reply":"2023-03-21T09:32:27.519531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Prediction Map B\nplt.imshow(predictions_map_b.squeeze() > 0.10, cmap='gray')","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:32:27.521625Z","iopub.execute_input":"2023-03-21T09:32:27.522253Z","iopub.status.idle":"2023-03-21T09:32:29.339225Z","shell.execute_reply.started":"2023-03-21T09:32:27.522214Z","shell.execute_reply":"2023-03-21T09:32:29.338117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Submission\nDATA_DIR = \"/kaggle/input/vesuvius-challenge-ink-detection\"\noriginal_size_a = Image.open(DATA_DIR + \"/test/a/mask.png\").size\noriginal_size_b = Image.open(DATA_DIR + \"/test/b/mask.png\").size\npredictions_map_a = resize_ski(predictions_map_a.squeeze(), original_size_a).squeeze()\npredictions_map_b = resize_ski(predictions_map_b.squeeze(), original_size_b).squeeze()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:32:29.341164Z","iopub.execute_input":"2023-03-21T09:32:29.341542Z","iopub.status.idle":"2023-03-21T09:32:31.295169Z","shell.execute_reply.started":"2023-03-21T09:32:29.341505Z","shell.execute_reply":"2023-03-21T09:32:31.293876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Submission Details \ndef rle(predictions_map, threshold):\n    flat_img = predictions_map.flatten()\n    flat_img = np.where(flat_img > threshold, 1, 0).astype(np.uint8)\n\n    starts = np.array((flat_img[:-1] == 0) & (flat_img[1:] == 1))\n    ends = np.array((flat_img[:-1] == 1) & (flat_img[1:] == 0))\n    starts_ix = np.where(starts)[0] + 2\n    ends_ix = np.where(ends)[0] + 2\n    lengths = ends_ix - starts_ix\n    return \" \".join(map(str, sum(zip(starts_ix, lengths), ())))","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:32:31.296814Z","iopub.execute_input":"2023-03-21T09:32:31.297231Z","iopub.status.idle":"2023-03-21T09:32:31.304721Z","shell.execute_reply.started":"2023-03-21T09:32:31.297183Z","shell.execute_reply":"2023-03-21T09:32:31.303724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"threshold_a = 0.10\nthreshold_b = 0.10\n\nfrom collections import defaultdict\nsubmission = defaultdict(list)\n\nrle_a = rle(predictions_map_a, threshold=threshold_a)\nrle_b = rle(predictions_map_b, threshold=threshold_b)\n#print(\"Id,Predicted\\na,\" + rle_a + \"\\nb,\" + rle_b, file=open('/kaggle/working/submission.csv', 'w'))\n\nsubmission[\"Id\"].append(rle_a)\nsubmission[\"Predicted\"].append(rle_b)\n\npd.DataFrame.from_dict(submission).to_csv(\"/kaggle/working/submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T09:53:31.589753Z","iopub.execute_input":"2023-03-21T09:53:31.590139Z","iopub.status.idle":"2023-03-21T09:58:14.152687Z","shell.execute_reply.started":"2023-03-21T09:53:31.590107Z","shell.execute_reply":"2023-03-21T09:58:14.151516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Delete the model from output directory\nos.remove(\"/kaggle/working/best.pt\")\n\nsubmission_df = pd.read_csv(\"/kaggle/working/submission.csv\")\nsubmission_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T10:07:27.649647Z","iopub.execute_input":"2023-03-21T10:07:27.650043Z","iopub.status.idle":"2023-03-21T10:07:27.735861Z","shell.execute_reply.started":"2023-03-21T10:07:27.650008Z","shell.execute_reply":"2023-03-21T10:07:27.734830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}