{"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":"import json\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport tifffile as tiff\nfrom glob import glob\nfrom tqdm import tqdm\nfrom pathlib import Path\nfrom collections import defaultdict\nfrom collections import OrderedDict\nfrom sklearn.model_selection import train_test_split\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchinfo import summary\n\n\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-05-26T03:53:46.801221Z","iopub.execute_input":"2023-05-26T03:53:46.801597Z","iopub.status.idle":"2023-05-26T03:53:51.875186Z","shell.execute_reply.started":"2023-05-26T03:53:46.801564Z","shell.execute_reply":"2023-05-26T03:53:51.874246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install pycocotools","metadata":{"execution":{"iopub.status.busy":"2023-05-26T05:52:52.895998Z","iopub.execute_input":"2023-05-26T05:52:52.897323Z","iopub.status.idle":"2023-05-26T05:53:26.978530Z","shell.execute_reply.started":"2023-05-26T05:52:52.897278Z","shell.execute_reply":"2023-05-26T05:53:26.977293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import base64\nimport numpy as np\nfrom pycocotools import _mask as coco_mask\nimport typing as t\nimport zlib\n\n\ndef encode_binary_mask(mask: np.ndarray) -> t.Text:\n  \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n\n  # check input mask --\n  if mask.dtype != np.bool:\n    raise ValueError(\n        \"encode_binary_mask expects a binary mask, received dtype == %s\" %\n        mask.dtype)\n\n  mask = np.squeeze(mask)\n  if len(mask.shape) != 2:\n    raise ValueError(\n        \"encode_binary_mask expects a 2d mask, received shape == %s\" %\n        mask.shape)\n\n  # convert input mask to expected COCO API input --\n  mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1)\n  mask_to_encode = mask_to_encode.astype(np.uint8)\n  mask_to_encode = np.asfortranarray(mask_to_encode)\n\n  # RLE encode mask --\n  encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n\n  # compress and base64 encoding --\n  binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n  base64_str = base64.b64encode(binary_str)\n  return base64_str","metadata":{"execution":{"iopub.status.busy":"2023-05-26T05:53:26.982462Z","iopub.execute_input":"2023-05-26T05:53:26.982777Z","iopub.status.idle":"2023-05-26T05:53:27.000433Z","shell.execute_reply.started":"2023-05-26T05:53:26.982745Z","shell.execute_reply":"2023-05-26T05:53:26.999346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Parameters","metadata":{}},{"cell_type":"markdown","source":"### Directories","metadata":{}},{"cell_type":"code","source":"# File path settings\nBASE_DIR = Path('/kaggle/input/hubmap-hacking-the-human-vasculature')\n\ntest_paths = glob(f'{BASE_DIR}/test/*')\npolygons_path = f'{BASE_DIR}/polygons.jsonl'\n\nmodel_path = '/kaggle/input/unet-brain/unet.pt'","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:51.876928Z","iopub.execute_input":"2023-05-26T03:53:51.877290Z","iopub.status.idle":"2023-05-26T03:53:51.887167Z","shell.execute_reply.started":"2023-05-26T03:53:51.877256Z","shell.execute_reply":"2023-05-26T03:53:51.886104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Hyper parameters","metadata":{}},{"cell_type":"code","source":"# device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu') \n\nBATCH_SIZE = 16\nNUM_WORKERS = 2\nEPOCHS = 10","metadata":{"execution":{"iopub.status.busy":"2023-05-26T04:50:23.410779Z","iopub.execute_input":"2023-05-26T04:50:23.411178Z","iopub.status.idle":"2023-05-26T04:50:23.417701Z","shell.execute_reply.started":"2023-05-26T04:50:23.411147Z","shell.execute_reply":"2023-05-26T04:50:23.416604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data","metadata":{}},{"cell_type":"code","source":"def load_image(path):\n    \"\"\"\n    Load image from .tif file \n    \n    parameters\n    ----------\n    path: str\n        path of laoding target image\n    \n    returns\n    ----------\n    image: numpy.array [height, width, channel]\n        loaded image\n    \"\"\"\n    image = tiff.imread(path)\n    \n    return image\n\ndef create_vessel_annotations():\n    \"\"\"\n    Create annotations set which have blood_vessel label.\n    \n    returns\n    ----------\n    annotations_dict: dict {key=id, value=coordinates}\n        annotations dict with key id and value coordinates of blood_vessel\n    \"\"\"\n    # load polygons data\n    polygons = pd.read_json(polygons_path, orient='records', lines=True)\n    \n    # extract blood_vessel annotation\n    annotations_dict = defaultdict(list)\n    for idx, row in polygons.iterrows():\n        id_ = row['id']\n        annotations = row['annotations']\n        for annotation in annotations:\n            if annotation['type'] == 'blood_vessel':\n                annotations_dict[id_].append(annotation['coordinates'])\n    \n    return annotations_dict\n\n\ndef transform_into_mask(coordinates):\n    \"\"\"\n    transform coordinate data into mask image\n    \n    \n    parameters\n    ----------\n    coordinates: list\n        coordinate of mask\n    \n    returns\n    ----------\n    mask: numpy.array [height, width]\n        image of mask\n    \"\"\"\n    mask = np.zeros(shape=(512, 512))\n    \n    # transform coordinate into mask image\n    for coordinate in coordinates:\n        for axis in coordinate[0]:\n            mask[axis[1], axis[0]] = 1\n    \n    # add channel\n    mask = mask[np.newaxis, :, :]\n    \n    return mask","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:52.041103Z","iopub.execute_input":"2023-05-26T03:53:52.041526Z","iopub.status.idle":"2023-05-26T03:53:52.053134Z","shell.execute_reply.started":"2023-05-26T03:53:52.041497Z","shell.execute_reply":"2023-05-26T03:53:52.051924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Albumentation settings\ndef transform_train():\n    transforms = [\n        ToTensorV2(p=1)\n    ]    \n    return A.Compose(transforms)\n\n\ndef transform_val():\n    transforms = [\n        ToTensorV2(p=1)\n    ]\n    return A.Compose(transforms)","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:52.054939Z","iopub.execute_input":"2023-05-26T03:53:52.055586Z","iopub.status.idle":"2023-05-26T03:53:52.063963Z","shell.execute_reply.started":"2023-05-26T03:53:52.055552Z","shell.execute_reply":"2023-05-26T03:53:52.063174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Datasets","metadata":{}},{"cell_type":"code","source":"# Define Dataset class\nclass Hubmap(Dataset):\n    def __init__(self, paths, annotations_dict=None, transforms=None):\n        self.paths = paths\n        self.annotations_dict = annotations_dict\n        self.length = len(self.paths)\n        self.transforms = transforms\n    \n    def __getitem__(self, idx):\n        path = self.paths[idx]\n        id_ = Path(path).stem\n        \n        # Load band images\n        image = load_image(path)\n        \n        # Normalization\n        image = image / 255\n        \n        if self.annotations_dict:\n            coordinates = self.annotations_dict[id_]\n            mask = transform_into_mask(coordinates)\n            \n            if self.transforms:\n                transformed = self.transforms(image=image, mask=mask)\n            \n            return {'image': transformed['image'], 'mask': transformed['mask']}\n        else:\n            if self.transforms:\n                transformed = self.transforms(image=image)\n            \n            return {'image': transformed['image']}\n    \n    def __len__(self):\n        return self.length","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:52.065550Z","iopub.execute_input":"2023-05-26T03:53:52.066647Z","iopub.status.idle":"2023-05-26T03:53:52.076401Z","shell.execute_reply.started":"2023-05-26T03:53:52.066611Z","shell.execute_reply":"2023-05-26T03:53:52.075312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create vessel annotations dict(key=id, value=coordinate of blood_vessel annotations)\nannotations_dict = create_vessel_annotations()","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:52.077914Z","iopub.execute_input":"2023-05-26T03:53:52.078301Z","iopub.status.idle":"2023-05-26T03:53:56.196172Z","shell.execute_reply.started":"2023-05-26T03:53:52.078265Z","shell.execute_reply":"2023-05-26T03:53:56.195254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create train paths list which have blood_vessel annotaions\ntrain_paths = [f'{BASE_DIR}/train/{id_}.tif' for id_ in annotations_dict.keys()]\n\n# Split indices into train and val\ntrain_indices, val_indices = train_test_split(\n    [i for i in range(len(train_paths))], test_size=0.2, random_state=42\n)\nprint(f'Train size: {len(train_indices)}')\nprint(f'Val size: {len(val_indices)}')","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:56.197546Z","iopub.execute_input":"2023-05-26T03:53:56.198017Z","iopub.status.idle":"2023-05-26T03:53:56.208534Z","shell.execute_reply.started":"2023-05-26T03:53:56.197971Z","shell.execute_reply":"2023-05-26T03:53:56.207238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create dataset\ntrain_dataset = Hubmap(\n    [train_paths[i] for i in train_indices], \n    annotations_dict=annotations_dict, transforms=transform_train()\n)\nval_dataset  = Hubmap(\n    [train_paths[i] for i in val_indices], \n    annotations_dict=annotations_dict, transforms=transform_val()\n)\ntest_dataset = Hubmap(\n    test_paths, transforms=transform_val()\n)\n\n# Create dataloader\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, num_workers=NUM_WORKERS)\ntest_loader = DataLoader(test_dataset, batch_size=1, num_workers=NUM_WORKERS)","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:56.210431Z","iopub.execute_input":"2023-05-26T03:53:56.210935Z","iopub.status.idle":"2023-05-26T03:53:56.219504Z","shell.execute_reply.started":"2023-05-26T03:53:56.210899Z","shell.execute_reply":"2023-05-26T03:53:56.218356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# for data in tqdm(val_loader):\n#     mask = data['mask']\n#     image = data['image']\n    \n#     plt.imshow(mask[0,:,:])\n#     plt.show()\n#     plt.imshow(image[0,:,:,:].permute(1, 2, 0))\n#     break","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:56.223591Z","iopub.execute_input":"2023-05-26T03:53:56.224068Z","iopub.status.idle":"2023-05-26T03:53:56.230646Z","shell.execute_reply.started":"2023-05-26T03:53:56.224034Z","shell.execute_reply":"2023-05-26T03:53:56.229861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"class UNet(nn.Module):\n\n    def __init__(self, in_channels=3, out_channels=1, init_features=32):\n        super(UNet, self).__init__()\n\n        features = init_features\n        self.encoder1 = UNet._block(in_channels, features, name=\"enc1\")\n        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.encoder2 = UNet._block(features, features * 2, name=\"enc2\")\n        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.encoder3 = UNet._block(features * 2, features * 4, name=\"enc3\")\n        self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.encoder4 = UNet._block(features * 4, features * 8, name=\"enc4\")\n        self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)\n\n        self.bottleneck = UNet._block(features * 8, features * 16, name=\"bottleneck\")\n\n        self.upconv4 = nn.ConvTranspose2d(\n            features * 16, features * 8, kernel_size=2, stride=2\n        )\n        self.decoder4 = UNet._block((features * 8) * 2, features * 8, name=\"dec4\")\n        self.upconv3 = nn.ConvTranspose2d(\n            features * 8, features * 4, kernel_size=2, stride=2\n        )\n        self.decoder3 = UNet._block((features * 4) * 2, features * 4, name=\"dec3\")\n        self.upconv2 = nn.ConvTranspose2d(\n            features * 4, features * 2, kernel_size=2, stride=2\n        )\n        self.decoder2 = UNet._block((features * 2) * 2, features * 2, name=\"dec2\")\n        self.upconv1 = nn.ConvTranspose2d(\n            features * 2, features, kernel_size=2, stride=2\n        )\n        self.decoder1 = UNet._block(features * 2, features, name=\"dec1\")\n\n        self.conv = nn.Conv2d(\n            in_channels=features, out_channels=out_channels, kernel_size=1\n        )\n\n    def forward(self, x):\n        enc1 = self.encoder1(x)\n        enc2 = self.encoder2(self.pool1(enc1))\n        enc3 = self.encoder3(self.pool2(enc2))\n        enc4 = self.encoder4(self.pool3(enc3))\n\n        bottleneck = self.bottleneck(self.pool4(enc4))\n\n        dec4 = self.upconv4(bottleneck)\n        dec4 = torch.cat((dec4, enc4), dim=1)\n        dec4 = self.decoder4(dec4)\n        dec3 = self.upconv3(dec4)\n        dec3 = torch.cat((dec3, enc3), dim=1)\n        dec3 = self.decoder3(dec3)\n        dec2 = self.upconv2(dec3)\n        dec2 = torch.cat((dec2, enc2), dim=1)\n        dec2 = self.decoder2(dec2)\n        dec1 = self.upconv1(dec2)\n        dec1 = torch.cat((dec1, enc1), dim=1)\n        dec1 = self.decoder1(dec1)\n        return torch.sigmoid(self.conv(dec1))\n\n    @staticmethod\n    def _block(in_channels, features, name):\n        return nn.Sequential(\n            OrderedDict(\n                [\n                    (\n                        name + \"conv1\",\n                        nn.Conv2d(\n                            in_channels=in_channels,\n                            out_channels=features,\n                            kernel_size=3,\n                            padding=1,\n                            bias=False,\n                        ),\n                    ),\n                    (name + \"norm1\", nn.BatchNorm2d(num_features=features)),\n                    (name + \"relu1\", nn.ReLU(inplace=True)),\n                    (\n                        name + \"conv2\",\n                        nn.Conv2d(\n                            in_channels=features,\n                            out_channels=features,\n                            kernel_size=3,\n                            padding=1,\n                            bias=False,\n                        ),\n                    ),\n                    (name + \"norm2\", nn.BatchNorm2d(num_features=features)),\n                    (name + \"relu2\", nn.ReLU(inplace=True)),\n                ]\n            )\n        )","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:56.232279Z","iopub.execute_input":"2023-05-26T03:53:56.232923Z","iopub.status.idle":"2023-05-26T03:53:56.254527Z","shell.execute_reply.started":"2023-05-26T03:53:56.232892Z","shell.execute_reply":"2023-05-26T03:53:56.253567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = UNet()\nmodel.load_state_dict(torch.load(model_path))\nsummary(model=model, input_size=(16, 3, 512, 512))","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:53:56.256001Z","iopub.execute_input":"2023-05-26T03:53:56.256679Z","iopub.status.idle":"2023-05-26T03:54:04.581631Z","shell.execute_reply.started":"2023-05-26T03:53:56.256648Z","shell.execute_reply":"2023-05-26T03:54:04.580716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"criterion = nn.BCELoss().to(device)\noptimizer = optim.Adam(model.parameters())\n\ntrain_losses = []\nval_losses = []\n\nprint(f'{\"=\" * 10} Start Training {\"=\" * 10}')\n# for epoch in range(EPOCHS):\nfor epoch in range(100):\n    train_loss = 0\n    val_loss = 0\n    \n    print(f'Epoch {epoch + 1}:')\n    # Training\n    for dataset in tqdm(train_loader):\n        image = dataset['image'].to(device).float()\n        mask = dataset['mask'].to(device).float()\n        \n        optimizer.zero_grad()\n        \n        output = model(image)\n        loss = criterion(output, mask)\n        \n        loss.backward()\n        optimizer.step()\n        \n        train_loss += loss.item()\n    train_loss /= len(train_loader)\n    train_losses.append(train_loss)\n    \n    # Validation\n    with torch.no_grad():\n        for dataset in tqdm(val_loader):\n            image = dataset['image'].to(device).float()\n            mask = dataset['mask'].to(device).float()\n\n            output = model(image)\n            loss = criterion(output, mask)\n\n            val_loss += loss.item()\n    val_loss /= len(val_loader)\n    val_losses.append(val_loss)\n    \n    print(f'Train Loss: {train_loss}, Validation Los: {val_loss}')\n    torch.save(model, f'model_weight_{epoch}.pth')\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2023-05-26T03:54:04.583175Z","iopub.execute_input":"2023-05-26T03:54:04.583773Z","iopub.status.idle":"2023-05-26T04:46:25.398275Z","shell.execute_reply.started":"2023-05-26T03:54:04.583736Z","shell.execute_reply":"2023-05-26T04:46:25.396724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot error\nplt.plot(train_losses, label='Train Loss')\nplt.plot(val_losses, label='Val Loss')\nplt.legend()\nplt.xlabel('Epochs')\nplt.ylabel('Error')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-05-26T04:49:21.045207Z","iopub.execute_input":"2023-05-26T04:49:21.045553Z","iopub.status.idle":"2023-05-26T04:49:21.333811Z","shell.execute_reply.started":"2023-05-26T04:49:21.045525Z","shell.execute_reply":"2023-05-26T04:49:21.332849Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for dataset in tqdm(train_loader):\n    image = dataset['image'].to(device).float()\n    mask = dataset['mask'].to(device).float()\n    with torch.no_grad():\n        out = model(image)\n    break\nfig, ax = plt.subplots(1, 3, figsize=(20, 8))\nax0 = ax[0].imshow(image[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nax1 = ax[1].imshow(out[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nax2 = ax[2].imshow(mask[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nfig.colorbar(ax0, ax=ax[0])\nfig.colorbar(ax1, ax=ax[1])\n","metadata":{"execution":{"iopub.status.busy":"2023-05-26T05:09:55.268766Z","iopub.execute_input":"2023-05-26T05:09:55.269143Z","iopub.status.idle":"2023-05-26T05:09:57.923553Z","shell.execute_reply.started":"2023-05-26T05:09:55.269109Z","shell.execute_reply":"2023-05-26T05:09:57.922298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for dataset in tqdm(val_loader):\n    image = dataset['image'].to(device).float()\n    mask = dataset['mask'].to(device).float()\n    with torch.no_grad():\n        out = model(image)\n    break\nfig, ax = plt.subplots(1, 3, figsize=(20, 8))\nax0 = ax[0].imshow(image[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nax1 = ax[1].imshow(out[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nax2 = ax[2].imshow(mask[10,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\nfig.colorbar(ax0, ax=ax[0])\nfig.colorbar(ax1, ax=ax[1])\n","metadata":{"execution":{"iopub.status.busy":"2023-05-26T05:56:03.132275Z","iopub.execute_input":"2023-05-26T05:56:03.132722Z","iopub.status.idle":"2023-05-26T05:56:05.901642Z","shell.execute_reply.started":"2023-05-26T05:56:03.132691Z","shell.execute_reply":"2023-05-26T05:56:05.900490Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for dataset in tqdm(test_loader):\n    image = dataset['image'].to(device).float()\n    with torch.no_grad():\n        out = model(image)\nout = np.where(out.permute(0, 2, 3, 1).to('cpu').numpy() >= 0.1, 1.0, 0.0)\nfig, ax = plt.subplots(1, 2, figsize=(20, 10))\nax0 = ax[0].imshow(image[0,:,:,:].permute(1, 2, 0).to('cpu'), cmap='viridis')\n# ax[1].imshow(np.round(out[0,:,:,:].permute(1, 2, 0).to('cpu')))\nax1 = ax[1].imshow(out[0,:,:,:], cmap='viridis')\nfig.colorbar(ax0, ax=ax[0])\nfig.colorbar(ax1, ax=ax[1])","metadata":{"execution":{"iopub.status.busy":"2023-05-26T05:56:22.056109Z","iopub.execute_input":"2023-05-26T05:56:22.056487Z","iopub.status.idle":"2023-05-26T05:56:23.354415Z","shell.execute_reply.started":"2023-05-26T05:56:22.056454Z","shell.execute_reply":"2023-05-26T05:56:23.352645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}