{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":61446,"databundleVersionId":6962461,"sourceType":"competition"},{"sourceId":7187369,"sourceType":"datasetVersion","datasetId":4087873},{"sourceId":7312958,"sourceType":"datasetVersion","datasetId":4229452},{"sourceId":7317236,"sourceType":"datasetVersion","datasetId":4243245},{"sourceId":7322239,"sourceType":"datasetVersion","datasetId":4249424,"isSourceIdPinned":true},{"sourceId":7340169,"sourceType":"datasetVersion","datasetId":4250312},{"sourceId":7351547,"sourceType":"datasetVersion","datasetId":4265818},{"sourceId":7361057,"sourceType":"datasetVersion","datasetId":4267714},{"sourceId":7500816,"sourceType":"datasetVersion","datasetId":4367835},{"sourceId":7564708,"sourceType":"datasetVersion","datasetId":4382600},{"sourceId":7567499,"sourceType":"datasetVersion","datasetId":4392788},{"sourceId":150248402,"sourceType":"kernelVersion"},{"sourceId":156694315,"sourceType":"kernelVersion"}],"dockerImageVersionId":30636,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# <center style=\"font-family: consolas; font-size: 32px; font-weight: bold;\">🩺 SenNet + HOA - Hacking the Human Vasculature in 3D</center>\n<p><center style=\"color:#949494; font-family: consolas; font-size: 20px;\">Clean Code 📚| Weighted Ensemble </center></p>\n***","metadata":{}},{"cell_type":"markdown","source":"<i>\nThis is a Clean Code inference notebook for Kaggle's SenNet + HOA Competition. <span style='color:#FFCE30'> I have commented most of the parts of this notebook for clarity and self explaination.</span>\n</i>\n\nThis Notebook is based on Misaki's [Notebook](https://www.kaggle.com/code/misakimatsutomo/inference-1024-should-have-a-percentile-of-0-00149) and Ada Luo Dao's [Notebook](https://www.kaggle.com/code/adaluodao/inference-1024-ensemble-512-1024).\n\n\n<b>\nDuring My experiments I found that an ensemble of 512 Image Size and 1024 Image Size Model provides more diverse predictions but Model trained on 1024 sized images has greater influence on the predictions so, I have given more weightage to 1024 image model.\n</b>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family: Cambria; background-color:#e0ffcd; color:#19180F; font-size:19px;  padding:10px; border: 2px solid #19180F; border-radius:10px\"> \n    <h4>If you find my Kaggle notebook helpful, please consider giving it an <span style='color:#005792'><b>upvote!</b></span>👍</h4>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Training Codes of this inference\n\nFirst model: https://www.kaggle.com/nguynvnthtr/the-training-image-is-1024-x-1024\nVersion 1\n\nSecond model: https://www.kaggle.com/code/lispprolog/training-512-kidney-1-and-3/notebook Version 9","metadata":{}},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Import Libraries</div> ","metadata":{}},{"cell_type":"code","source":"# Importing PyTorch library \nimport torch as tc \n# Importing neural network module from PyTorch\nimport torch.nn as nn\n# Importing automatic mixed precision (AMP) for faster training on GPU\nfrom torch.cuda.amp import autocast\n# Importing Dataset and DataLoader classes for handling data in PyTorch\nfrom torch.utils.data import Dataset, DataLoader\n# Importing DataParallel for parallel processing in PyTorch\nfrom torch.nn.parallel import DataParallel\n\n\n# Importing NumPy \nimport numpy as np\n# Importing tqdm for displaying progress bars\nfrom tqdm import tqdm\n\n# Importing OpenCV library\nimport cv2\n# Importing os and sys modules for interacting with the operating system\nimport os\nimport sys\n# Importing the glob module for searching directories with wildcard patterns\nfrom glob import glob\n# Importing matplotlib \nimport matplotlib.pyplot as plt\n# Importing pandas \nimport pandas as pd\n\n# Installing segmentation Models\n!python -m pip install --no-index --find-links=/kaggle/input/pip-download-for-segmentation-models-pytorch segmentation-models-pytorch -q\n# Importing the segmentation_models_pytorch library \nimport segmentation_models_pytorch as smp\n\n# Importing load_dotenv for loading environment variables\nfrom dotenv import load_dotenv","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-01-24T19:26:19.898223Z","iopub.execute_input":"2024-01-24T19:26:19.898736Z","iopub.status.idle":"2024-01-24T19:26:47.35778Z","shell.execute_reply.started":"2024-01-24T19:26:19.898707Z","shell.execute_reply":"2024-01-24T19:26:47.356744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Config</div> ","metadata":{}},{"cell_type":"code","source":"# Defining variables to specify model paths\nmodel_path_i = 0  \nmodel_path_i9 = 1\nmodel_path_i11 = 2\n\n# Configuration class containing various model and training parameters\nclass CFG:\n    # Model configuration\n    model_name = 'Unet'\n    backbone = 'se_resnext50_32x4d'\n    in_chans = 1\n    image_size = 1536\n    input_size = 1536\n    tile_size = image_size\n    stride = tile_size // 3\n    drop_egde_pixel = 0\n    target_size = 1\n    chopping_percentile = 1e-3\n\n    # Fold and validation configuration\n    valid_id = 1\n    batch = 16\n    th_percentile = 0.0014\n    axis_w = [0.328800989, 0.336629584, 0.334569427]\n    axis_second_model = 0\n\n    # Model paths for different folds\n    model_path = [\n#         \"/kaggle/input/2-5d-cutting-model-baseline-training/se_resnext50_32x4d_19_loss0.12_score0.79_val_loss0.25_val_score0.79.pt\",\n#         \"/kaggle/input/training-6-512/se_resnext50_32x4d_19_loss0.09_score0.83_val_loss0.28_val_score0.83.pt\",\n#         \"/kaggle/input/training-6-512/se_resnext50_32x4d_19_loss0.05_score0.90_val_loss0.25_val_score0.86.pt\",\n#         \"/kaggle/input/training-6-512/se_resnext50_32x4d_19_loss0.05_score0.89_val_loss0.24_val_score0.86_midd.pt\",\n#         \"/kaggle/input/training-6-512/se_resnext50_32x4d_24_loss0.05_score0.90_val_loss0.23_val_score0.88_midd.pt\",\n#         \"/kaggle/input/training-6-512/se_resnext50_32x4d_24_loss0.04_score0.91_val_loss0.23_val_score0.88_midd.pt\",  # 25 025 rot 512 center\n#         \"/kaggle/input/blood-vessel-model-1024/se_resnext50_32x4d_24_loss0.10_score0.90_val_loss0.16_val_score0.85_midd_1024.pt\",\n#         \"/kaggle/input/blood-vessel-model-1024/se_resnext50_32x4d_24_loss0.10_score0.90_val_loss0.12_val_score0.88_midd_1024.pt\",  # lr = 8e-5\n#         \"/kaggle/input/blood-vessel-model-1024/se_resnext50_32x4d_24_loss0.91_score0.09_val_loss0.91_val_score0.09_midd_1024.pt\",  # 60e-5 \n        \"/kaggle/input/sn-hoa-8e-5-27-rot0-5/se_resnext50_32x4d_26_loss0.10_score0.90_val_loss0.12_val_score0.88_midd_1024.pt\",  # 8e-5-27-rot0-5\n#         \"/kaggle/input/sn-hoa-8e-5-27-rot0-5/se_resnext50_32x4d_30_loss0.10_score0.90_val_loss0.13_val_score0.88_midd_1024.pt\",\n#         \"/kaggle/input/sennet-kidney-1-and-3/model_real_23.pt\"  # 31 8e 05\n        \"/kaggle/input/sennet-new-ckpts/512_model_after_DataLoader_kidney_3_dense.pth_epoch_24.pt\",  ## 512\n        \"/kaggle/input/sennet-new-ckpts/768_model_after_DataLoader_kidney_3_dense.pth_epoch_24.pt\"  ## 768\n    ]","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.359652Z","iopub.execute_input":"2024-01-24T19:26:47.359986Z","iopub.status.idle":"2024-01-24T19:26:47.368637Z","shell.execute_reply.started":"2024-01-24T19:26:47.359957Z","shell.execute_reply":"2024-01-24T19:26:47.367634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Model </div> ","metadata":{}},{"cell_type":"code","source":"# Custom model class definition\nclass CustomModel(nn.Module):\n    def __init__(self, CFG, weight=None):\n        super().__init__()\n        \n        # Storing configuration parameters within the model\n        self.CFG = CFG\n        \n        # Creating an instance of the Unet model from segmentation_models_pytorch\n        self.model = 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        # Setting batch size from the configuration\n        self.batch = CFG.batch\n\n    def forward_(self, image):\n        # Forward pass through the model\n        output = self.model(image)\n        # Extracting the first channel from the output\n        return output[:, 0]\n\n    def forward(self, x: tc.Tensor):\n        # Converting input tensor to float32\n        x = x.to(tc.float32)\n        \n        # Normalizing the input tensor using a custom function 'norm_with_clip'\n        x = norm_with_clip(x.reshape(-1, *x.shape[2:])).reshape(x.shape)\n        \n        # Interpolating the input tensor if input size is not equal to image size\n        if CFG.input_size != CFG.image_size:\n            x = nn.functional.interpolate(x, size=(CFG.input_size, CFG.input_size), mode='bilinear', align_corners=True)\n        \n        # Performing data augmentation by rotating the input tensor\n        shape = x.shape\n        x = [tc.rot90(x, k=i, dims=(-2, -1)) for i in range(4)]\n        x = tc.cat(x, dim=0)\n        \n        # Using autocast for mixed precision training and no_grad to disable gradient computation\n        with autocast():\n            with tc.no_grad():\n                # Forward pass for each rotated batch\n                x = [self.forward_(x[i * self.batch:(i + 1) * self.batch]) for i in range(x.shape[0] // self.batch + 1)]\n                # Concatenating the results along the batch dimension\n                x = tc.cat(x, dim=0)\n        \n        # Applying sigmoid activation and reshaping the tensor\n        x = x.sigmoid()\n        x = x.reshape(4, shape[0], *shape[2:])\n        \n        # Rotating the tensor back to the original orientation\n        x = [tc.rot90(x[i], k=-i, dims=(-2, -1)) for i in range(4)]\n        # Stacking along a new dimension and taking the mean\n        x = tc.stack(x, dim=0).mean(0)\n        \n        # Interpolating the output tensor if input size is not equal to image size\n        if CFG.input_size != CFG.image_size:\n            x = nn.functional.interpolate(x[None], size=(CFG.image_size, CFG.image_size), mode='bilinear', align_corners=True)[0]\n        \n        return x\n\n# Function to build the model\ndef build_model(weight=None):\n    # Loading environment variables using dotenv\n    load_dotenv()\n\n    # Printing model name and backbone information\n    print('model_name', CFG.model_name)\n    print('backbone', CFG.backbone)\n\n    # Creating an instance of the CustomModel with specified weight\n    model = CustomModel(CFG, weight)\n\n    # Moving the model to the GPU\n    return model.cuda()","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.370402Z","iopub.execute_input":"2024-01-24T19:26:47.371043Z","iopub.status.idle":"2024-01-24T19:26:47.39441Z","shell.execute_reply.started":"2024-01-24T19:26:47.370959Z","shell.execute_reply":"2024-01-24T19:26:47.393339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Image Size </div> ","metadata":{}},{"cell_type":"code","source":"# Function to resize image to 1024x1024 with rotation\ndef to_1024(img, image_size=1024):\n    if image_size > img.shape[1]:\n        # Rotate image 90 degrees\n        img = np.rot90(img)\n        \n        # Calculate padding for top and bottom\n        start1 = (CFG.image_size - img.shape[0]) // 2\n        top = img[0: start1, 0: img.shape[1]]\n        bottom = img[img.shape[0] - start1: img.shape[0], 0: img.shape[1]]\n        \n        # Concatenate top, rotated image, and bottom\n        img_result = np.concatenate((top, img, bottom), axis=0)\n        \n        # Rotate image back to the original orientation\n        img_result = np.rot90(img_result)\n        img_result = np.rot90(img_result)\n        img_result = np.rot90(img_result)\n    else:\n        img_result = img\n    \n    return img_result\n\n# Function to resize image to 1024x1024 without rotation\ndef to_1024_no_rot(img, image_size=1024):\n    if image_size > img.shape[0]:\n        # Calculate padding for top and bottom\n        start1 = (image_size - img.shape[0]) // 2\n        top = img[0: start1, 0: img.shape[1]]\n        bottom = img[img.shape[0] - start1: img.shape[0], 0: img.shape[1]]\n        \n        # Concatenate top, image, and bottom\n        img_result = np.concatenate((top, img, bottom), axis=0)\n    else:\n        img_result = img\n    \n    return img_result\n\n# Function to resize image to 1024x1024 using to_1024 function\ndef to_1024_1024(img, image_size=1024):\n    img_result = to_1024(img, image_size)\n    return img_result\n\n# Function to resize image back to original size\ndef to_original(im_after, img, image_size=1024):\n    top_ = 0\n    left_ = 0\n    \n    # Calculate padding for top\n    if im_after.shape[0] > img.shape[0]:\n        top_ = (image_size - img.shape[0]) // 2\n    \n    # Calculate padding for left\n    if im_after.shape[1] > img.shape[1]:\n        left_ = (image_size - img.shape[1]) // 2\n    \n    # Extract the region of interest from the resized image\n    if (top_ > 0) or (left_ > 0):\n        img_result = im_after[top_: img.shape[0] + top_, left_: img.shape[1] + left_]\n    else:\n        img_result = im_after\n    \n    return img_result","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.397431Z","iopub.execute_input":"2024-01-24T19:26:47.398502Z","iopub.status.idle":"2024-01-24T19:26:47.417151Z","shell.execute_reply.started":"2024-01-24T19:26:47.398467Z","shell.execute_reply":"2024-01-24T19:26:47.415932Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Helper Functions </div> ","metadata":{}},{"cell_type":"code","source":"# Function to encode a binary mask using Run-Length Encoding (RLE)\ndef rle_encode(mask):\n    pixel = mask.flatten()\n    pixel = np.concatenate([[0], pixel, [0]])\n    run = np.where(pixel[1:] != pixel[:-1])[0] + 1\n    run[1::2] -= run[::2]\n    rle = ' '.join(str(r) for r in run)\n    if rle == '':\n        rle = '1 0'\n    return rle\n\n# Function for min-max normalization of a PyTorch tensor\ndef min_max_normalization(x: tc.Tensor) -> tc.Tensor:\n    \"\"\"input.shape=(batch,f1,...)\"\"\"\n    shape = x.shape\n    if x.ndim > 2:\n        x = x.reshape(x.shape[0], -1)\n\n    min_ = x.min(dim=-1, keepdim=True)[0]\n    max_ = x.max(dim=-1, keepdim=True)[0]\n    if min_.mean() == 0 and max_.mean() == 1:\n        return x.reshape(shape)\n\n    x = (x - min_) / (max_ - min_ + 1e-9)\n    return x.reshape(shape)\n\n# Function for normalization with clipping of a PyTorch tensor\ndef norm_with_clip(x: tc.Tensor, smooth=1e-5):\n    dim = list(range(1, x.ndim))\n    mean = x.mean(dim=dim, keepdim=True)\n    std = x.std(dim=dim, keepdim=True)\n    x = (x - mean) / (std + smooth)\n    x[x > 5] = (x[x > 5] - 5) * 1e-3 + 5\n    x[x < -3] = (x[x < -3] + 3) * 1e-3 - 3\n    return x\n\n# Function to add an edge to an image tensor\ndef add_edge(x: tc.Tensor, edge: int):\n    # x=(C,H,W)\n    # output=(C,H+2*edge,W+2*edge)\n    mean_ = int(x.to(tc.float32).mean())\n    x = tc.cat([x, tc.ones([x.shape[0], edge, x.shape[2]], dtype=x.dtype, device=x.device) * mean_], dim=1)\n    x = tc.cat([x, tc.ones([x.shape[0], x.shape[1], edge], dtype=x.dtype, device=x.device) * mean_], dim=2)\n    x = tc.cat([tc.ones([x.shape[0], edge, x.shape[2]], dtype=x.dtype, device=x.device) * mean_, x], dim=1)\n    x = tc.cat([tc.ones([x.shape[0], x.shape[1], edge], dtype=x.dtype, device=x.device) * mean_, x], dim=2)\n    return x\n","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.41894Z","iopub.execute_input":"2024-01-24T19:26:47.419299Z","iopub.status.idle":"2024-01-24T19:26:47.439819Z","shell.execute_reply.started":"2024-01-24T19:26:47.419249Z","shell.execute_reply":"2024-01-24T19:26:47.437054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Data Loader </div> ","metadata":{}},{"cell_type":"code","source":"# Dataset class for loading images\nclass Data_loader(Dataset):\n    def __init__(self, path, s=\"/images/\"):\n        self.paths = glob(path + f\"{s}*.tif\")\n        self.paths.sort()\n        self.bool = s == \"/labels/\"\n\n    def __len__(self):\n        return len(self.paths)\n\n    def __getitem__(self, index):\n        img = cv2.imread(self.paths[index], cv2.IMREAD_GRAYSCALE)\n        img = to_1024_1024(img, image_size=CFG.image_size)\n\n        img = tc.from_numpy(img.copy())\n        if self.bool:\n            img = img.to(tc.bool)\n        else:\n            img = img.to(tc.uint8)\n        return img\n\n# Function to load data using Data_loader and perform normalization\ndef load_data(path, s):\n    data_loader = Data_loader(path, s)\n    data_loader = DataLoader(data_loader, batch_size=16, num_workers=2)\n    data = []\n    for x in tqdm(data_loader):\n        data.append(x)\n    x = tc.cat(data, dim=0)\n    \n    # Chopping values above and below a certain percentile\n    TH = x.reshape(-1).numpy()\n    index = -int(len(TH) * CFG.chopping_percentile)\n    TH: int = np.partition(TH, index)[index]\n    x[x > TH] = int(TH)\n    \n    TH = x.reshape(-1).numpy()\n    index = -int(len(TH) * CFG.chopping_percentile)\n    TH: int = np.partition(TH, -index)[-index]\n    x[x < TH] = int(TH)\n    \n    x=(min_max_normalization(x.to(tc.float16)[None])[0]*255).to(tc.uint8)\n    \n    return x\n\n# Dataset class for pipeline processing\nclass Pipeline_Dataset(Dataset):\n    def __init__(self, x, path):\n        self.img_paths = glob(path + \"/images/*\")\n        self.img_paths.sort()\n        self.in_chan = CFG.in_chans\n        z = tc.zeros(self.in_chan // 2, *x.shape[1:], dtype=x.dtype)\n        self.x = tc.cat((z, x, z), dim=0)\n\n    def __len__(self):\n        return self.x.shape[0] - self.in_chan + 1\n\n    def __getitem__(self, index):\n        x = self.x[index:index + self.in_chan]\n        return x, index\n\n    def get_mark(self, index):\n        id = self.img_paths[index].split(\"/\")[-3:]\n        id.pop(1)\n        id = \"_\".join(id)\n        return id[:-4]\n\n    def get_marks(self):\n        ids = []\n        for index in range(len(self)):\n            ids.append(self.get_mark(index))\n        return ids","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.440792Z","iopub.execute_input":"2024-01-24T19:26:47.441093Z","iopub.status.idle":"2024-01-24T19:26:47.599182Z","shell.execute_reply.started":"2024-01-24T19:26:47.441068Z","shell.execute_reply":"2024-01-24T19:26:47.598123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Buiding Models </div> ","metadata":{}},{"cell_type":"code","source":"# Building and loading the 1024 x 1024 Image Model\nmodel = build_model()  # Creating an instance of the model\nmodel.load_state_dict(tc.load(CFG.model_path[model_path_i], \"cpu\"))  # Loading the pre-trained weights\nmodel.eval()  # Setting the model to evaluation mode\nmodel = DataParallel(model)  # Using DataParallel for parallel processing\n\n# Building and loading the 512 x 512 Image Model\nmodel9 = build_model()  # Creating an instance of the model\nmodel9.load_state_dict(tc.load(CFG.model_path[model_path_i9], \"cpu\"))  # Loading the pre-trained weights\nmodel9.eval()  # Setting the model to evaluation mode\nmodel9 = DataParallel(model9)  # Using DataParallel for parallel processing\n\n# Building and loading the 768 x 768 Image Model\nmodel11 = build_model()  # Creating an instance of the model\nmodel11.load_state_dict(tc.load(CFG.model_path[model_path_i11], \"cpu\"))  # Loading the pre-trained weights\nmodel11.eval()  # Setting the model to evaluation mode\nmodel11 = DataParallel(model11)  # Using DataParallel for parallel processing\n","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:47.600493Z","iopub.execute_input":"2024-01-24T19:26:47.600781Z","iopub.status.idle":"2024-01-24T19:26:49.053775Z","shell.execute_reply.started":"2024-01-24T19:26:47.600757Z","shell.execute_reply":"2024-01-24T19:26:49.052409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Prediction & Weighted Ensemble </div> ","metadata":{}},{"cell_type":"code","source":"# Function to get segmentation outputs\ndef get_output(debug=False):\n    outputs = []\n\n    # Specify paths for testing\n    if debug:\n        paths = [\"/kaggle/input/blood-vessel-segmentation/train/kidney_2\"]\n    else:\n        paths = glob(\"/kaggle/input/blood-vessel-segmentation/test/*\")\n\n    outputs = [[], []]\n\n    # Loop through each path\n    for path in paths:\n        # Load data and initialize labels\n        x = load_data(path, \"/images/\")\n        labels = tc.zeros_like(x, dtype=tc.uint8)\n        mark = Pipeline_Dataset(x, path).get_marks()\n\n        # Loop through each axis\n        for axis in [0, 1, 2]:\n            debug_count = 0\n\n            # Rotate input data based on the current axis\n            if axis == 0:\n                x_ = x\n                labels_ = labels\n            elif axis == 1:\n                x_ = x.permute(1, 2, 0)\n                labels_ = labels.permute(1, 2, 0)\n            elif axis == 2:\n                x_ = x.permute(2, 0, 1)\n                labels_ = labels.permute(2, 0, 1)\n\n            # Skip if the input data is RGB and the axis is not 0\n            if x.shape[0] == 3 and axis != 0:\n                break\n\n            # Create dataset and dataloader for processing\n            dataset = Pipeline_Dataset(x_, path)\n            dataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=1)\n            shape = dataset.x.shape[-2:]\n\n            # Generate indices for processing tiles\n            x1_list = np.arange(0, shape[0] + CFG.tile_size - CFG.tile_size + 1, CFG.stride)\n            y1_list = np.arange(0, shape[1] + CFG.tile_size - CFG.tile_size + 1, CFG.stride)\n\n            # Loop through each image in the dataloader\n            for img, index in tqdm(dataloader):\n                # img=(1,C,H,W)\n                img = img.to(\"cuda:0\")\n                img = add_edge(img[0], CFG.tile_size // 2)[None]\n\n                mask_pred = tc.zeros_like(img[:, 0], dtype=tc.float32, device=img.device)\n                mask_count = tc.zeros_like(img[:, 0], dtype=tc.float32, device=img.device)\n\n                indexs = []\n                chip = []\n                \n                # Loop through each tile\n                for y1 in y1_list:\n                    for x1 in x1_list:\n                        x2 = x1 + CFG.tile_size\n                        y2 = y1 + CFG.tile_size\n                        indexs.append([x1 + CFG.drop_egde_pixel, x2 - CFG.drop_egde_pixel,\n                                       y1 + CFG.drop_egde_pixel, y2 - CFG.drop_egde_pixel])\n                        chip.append(img[..., x1:x2, y1:y2])\n\n                # Get predictions from the model\n                y_preds = model.forward(tc.cat(chip)).to(device=0)\n\n                # Weighted Ensemble\n                if axis == CFG.axis_second_model:\n                    p1 = y_preds\n                    p2 = model9.forward(tc.cat(chip)).to(device=0)\n                    p3 = model11.forward(tc.cat(chip)).to(device=0)\n                    y_preds = (3/((1/p1)+(1/p2)+(1/p3)))\n\n                # Adjust for drop_edge_pixel\n                if CFG.drop_egde_pixel:\n                    y_preds = y_preds[..., CFG.drop_egde_pixel:-CFG.drop_egde_pixel,\n                                        CFG.drop_egde_pixel:-CFG.drop_egde_pixel]\n                \n                # Aggregate predictions over tiles\n                for i, (x1, x2, y1, y2) in enumerate(indexs):\n                    mask_pred[..., x1:x2, y1:y2] += y_preds[i]\n                    mask_count[..., x1:x2, y1:y2] += 1\n\n                mask_pred /= mask_count\n\n                # Recover the region after processing\n                mask_pred = mask_pred[..., CFG.tile_size // 2:-CFG.tile_size // 2, CFG.tile_size // 2:-CFG.tile_size // 2]\n\n                # Update labels with the processed mask\n                labels_[index] += (mask_pred[0] * 255 * CFG.axis_w[axis]).to(tc.uint8).cpu()\n\n                # Display debug images if enabled\n                if debug:\n                    debug_count += 1\n                    plt.subplot(121)\n                    plt.imshow(img[0, CFG.in_chans // 2].cpu().detach().numpy())\n                    plt.subplot(122)\n                    plt.imshow(mask_pred[0].cpu().detach().numpy())\n                    plt.show()\n                    if debug_count > 3:\n                        break\n        \n        # Append the labels and marks to the outputs list\n        outputs[0].append(labels)\n        outputs[1].extend(mark)\n    \n    return outputs\n","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:49.054995Z","iopub.status.idle":"2024-01-24T19:26:49.055453Z","shell.execute_reply.started":"2024-01-24T19:26:49.055224Z","shell.execute_reply":"2024-01-24T19:26:49.055245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# <div style= \"font-family: Cambria; font-weight:bold; letter-spacing: 0px; color:black; font-size:120%; text-align:center;padding:5.0px; background: #FFE2E2; border: 4px solid #a2a8d3\"> Submission </div> ","metadata":{}},{"cell_type":"code","source":"# Check if it's for submission based on the presence of test images\nis_submit = len(glob(\"/kaggle/input/blood-vessel-segmentation/test/kidney_5/images/*.tif\")) != 3\n\n# Get segmentation output and associated ids\noutput, ids = get_output(not is_submit)\n\n# Calculate threshold for binary predictions\nTH = [x.flatten().numpy() for x in output]\nTH = np.concatenate(TH)\nindex = -int(len(TH) * CFG.th_percentile)\nTH: int = np.partition(TH, index)[index]\nprint(TH)\n\n# Read an example image for visualization\nimg = cv2.imread(\"/kaggle/input/blood-vessel-segmentation/test/kidney_5/images/0001.tif\", cv2.IMREAD_GRAYSCALE)\n\n# Initialize a list for submission dataframe\nsubmission_df = []\ndebug_count = 0\n\n# Loop through each prediction and generate RLE encoding\nfor index in range(len(ids)):\n    id = ids[index]\n    i = 0\n\n    # Find the corresponding output based on the index\n    for x in output:\n        if index >= len(x):\n            index -= len(x)\n            i += 1\n        else:\n            break\n\n    # Extract the binary mask based on the threshold\n    mask_pred = (output[i][index] > TH).numpy()\n\n    # Convert the binary mask to the original size\n    mask_pred2 = to_original(mask_pred, img, image_size=1024)\n    mask_pred = mask_pred2.copy()\n\n    # Visualization (if not for submission)\n    if not is_submit:\n        plt.subplot(121)\n        plt.imshow(mask_pred)\n        plt.show()\n        debug_count += 1\n        if debug_count > 6:\n            break\n\n    # Encode the binary mask using Run-Length Encoding\n    rle = rle_encode(mask_pred)\n\n    # Append information to the submission dataframe\n    submission_df.append(\n        pd.DataFrame(data={\n            'id': id,\n            'rle': rle,\n        }, index=[0])\n    )\n\n# Concatenate the submission dataframes and save to a CSV file\nsubmission_df = pd.concat(submission_df)\nsubmission_df.to_csv('submission.csv', index=False)\nsubmission_df.head(6)\n","metadata":{"execution":{"iopub.status.busy":"2024-01-24T19:26:49.057689Z","iopub.status.idle":"2024-01-24T19:26:49.058186Z","shell.execute_reply.started":"2024-01-24T19:26:49.057938Z","shell.execute_reply":"2024-01-24T19:26:49.057961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}