{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sn\nimport cv2\nimport tifffile\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Utilities, forked from the big guy**"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def rle2mask(mask_rle, shape):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (width,height) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [\n        np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])\n    ]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0] * shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo : hi] = 1\n    return img.reshape(shape).T\n\n\ndef read_image(image_id, scale=None, verbose=1):\n    image = tifffile.imread(\n        os.path.join(BASE_PATH, f\"train/{image_id}.tiff\")\n    )\n    if len(image.shape) == 5:\n        image = image.squeeze().transpose(1, 2, 0)\n    \n    mask = rle2mask(\n        df_train[df_train[\"id\"] == image_id][\"encoding\"].values[0], \n        (image.shape[1], image.shape[0])\n    )\n    \n    if verbose:\n        print(f\"[{image_id}] Image shape: {image.shape}\")\n        print(f\"[{image_id}] Mask shape: {mask.shape}\")\n    \n    if scale:\n        new_size = (image.shape[1] // scale, image.shape[0] // scale)\n        image = cv2.resize(image, new_size)\n        mask = cv2.resize(mask, new_size)\n        \n        if verbose:\n            print(f\"[{image_id}] Resized Image shape: {image.shape}\")\n            print(f\"[{image_id}] Resized Mask shape: {mask.shape}\")\n        \n    return image, mask\n\n\ndef read_test_image(image_id, scale=None, verbose=1):\n    image = tifffile.imread(\n        os.path.join(BASE_PATH, f\"test/{image_id}.tiff\")\n    )\n    if len(image.shape) == 5:\n        image = image.squeeze().transpose(1, 2, 0)\n    \n    if verbose:\n        print(f\"[{image_id}] Image shape: {image.shape}\")\n    \n    if scale:\n        new_size = (image.shape[1] // scale, image.shape[0] // scale)\n        image = cv2.resize(image, new_size)\n        \n        if verbose:\n            print(f\"[{image_id}] Resized Image shape: {image.shape}\")\n        \n    return image\n\n\ndef plot_image_and_mask(image, mask, image_id):\n    plt.figure(figsize=(16, 10))\n    \n    plt.subplot(1, 3, 1)\n    plt.imshow(image)\n    plt.title(f\"Image {image_id}\", fontsize=18)\n    \n    plt.subplot(1, 3, 2)\n    plt.imshow(image)\n    plt.imshow(mask, cmap=\"hot\", alpha=0.5)\n    plt.title(f\"Image {image_id} + mask\", fontsize=18)    \n    \n    plt.subplot(1, 3, 3)\n    plt.imshow(mask, cmap=\"hot\")\n    plt.title(f\"Mask\", fontsize=18)    \n    \n    plt.show()\n    \n    \ndef plot_grid_image_with_mask(image, mask):\n    plt.figure(figsize=(16, 16))\n    n_cols = 4\n    n_rows = 4\n    col_start = 0\n    col_w = 2500\n    row_start = 0\n    row_w = 2500\n    for i in range(n_cols):\n        for j in range(n_rows):\n            plt.subplot(n_cols, n_rows, n_rows * i + j + 1)\n            sub_image = image[\n                col_start + i * col_w : col_start + (i + 1) * col_w, \n                row_start + j * row_w : row_start + (j + 1) * row_w, \n                :\n            ]\n            sub_mask = mask[\n                col_start + i * col_w : col_start + (i + 1) * col_w, \n                row_start + j * row_w : row_start + (j + 1) * row_w, \n            ]\n            plt.imshow(sub_image)\n            plt.imshow(sub_mask, cmap=\"hot\", alpha=0.5)\n            plt.axis(\"off\")\n    plt.show()\n    \n\ndef plot_slice_image_and_mask(image, mask, start_h, end_h, start_w, end_w):\n    plt.figure(figsize=(16, 5))\n    \n    sub_image = image[start_h:end_h, start_w:end_w, :]\n    sub_mask = mask[start_h:end_h, start_w:end_w]\n    \n    plt.subplot(1, 3, 1)\n    plt.imshow(sub_image)\n    plt.axis(\"off\")\n    \n    plt.subplot(1, 3, 2)\n    plt.imshow(sub_image)\n    plt.imshow(sub_mask, cmap=\"hot\", alpha=0.5)\n    plt.axis(\"off\")\n    \n    plt.subplot(1, 3, 3)\n    plt.imshow(sub_mask, cmap=\"hot\")\n    plt.axis(\"off\")\n    \n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Necessary utilities"},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom os.path import splitext\nfrom os import listdir\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom glob import glob\nimport torch\nfrom torch.utils.data import Dataset\nimport logging\nfrom PIL import Image\n\n\nclass BasicDataset(Dataset):\n    def __init__(self, imgs_dir, masks_dir, scale=1, mask_suffix=''):\n        self.imgs_dir = imgs_dir\n        self.masks_dir = masks_dir\n        self.scale = scale\n        self.mask_suffix = mask_suffix\n        assert 0 < scale <= 1, 'Scale must be between 0 and 1'\n\n        self.ids = [splitext(file)[0] for file in listdir(imgs_dir)\n                    if not file.startswith('.')]\n        logging.info(f'Creating dataset with {len(self.ids)} examples')\n\n    def __len__(self):\n        return len(self.ids)\n\n    @classmethod\n    def preprocess(cls, pil_img, scale):\n        w, h = pil_img.size\n        newW, newH = int(scale * w), int(scale * h)\n        assert newW > 0 and newH > 0, 'Scale is too small'\n        pil_img = pil_img.resize((newW, newH))\n\n        img_nd = np.array(pil_img)\n\n        if len(img_nd.shape) == 2:\n            img_nd = np.expand_dims(img_nd, axis=2)\n\n        # HWC to CHW\n        img_trans = img_nd.transpose((2, 0, 1))\n        if img_trans.max() > 1:\n            img_trans = img_trans / 255\n\n        return img_trans\n\n    def __getitem__(self, i):\n        idx = self.ids[i]\n        mask_file = glob(self.masks_dir + idx + self.mask_suffix + '.*')\n        img_file = glob(self.imgs_dir + idx + '.*')\n\n        assert len(mask_file) == 1, \\\n            f'Either no mask or multiple masks found for the ID {idx}: {mask_file}'\n        assert len(img_file) == 1, \\\n            f'Either no image or multiple images found for the ID {idx}: {img_file}'\n        mask = Image.open(mask_file[0])\n        img = Image.open(img_file[0])\n\n        assert img.size == mask.size, \\\n            f'Image and mask {idx} should be the same size, but are {img.size} and {mask.size}'\n\n        img = self.preprocess(img, self.scale)\n        mask = self.preprocess(mask, self.scale)\n\n        return {\n            'image': torch.from_numpy(img).type(torch.FloatTensor),\n            'mask': torch.from_numpy(mask).type(torch.FloatTensor)\n        }\n\n\nclass CarvanaDataset(BasicDataset):\n    def __init__(self, imgs_dir, masks_dir, scale=1):\n        super().__init__(imgs_dir, masks_dir, scale, mask_suffix='_mask')\n\n\ndef eval_net(net, loader, device):\n    \"\"\"Evaluation without the densecrf with the dice coefficient\"\"\"\n    net.eval()\n    mask_type = torch.float32 if net.n_classes == 1 else torch.long\n    n_val = len(loader)  # the number of batch\n    tot = 0\n\n    with tqdm(total=n_val, desc='Validation round', unit='batch', leave=False) as pbar:\n        for batch in loader:\n            imgs, true_masks = batch['image'], batch['mask']\n            imgs = imgs.to(device=device, dtype=torch.float32)\n            true_masks = true_masks.to(device=device, dtype=mask_type)\n\n            with torch.no_grad():\n                mask_pred = net(imgs)\n\n            if net.n_classes > 1:\n                tot += F.cross_entropy(mask_pred, true_masks).item()\n            else:\n                pred = torch.sigmoid(mask_pred)\n                pred = (pred > 0.5).float()\n                tot += dice_coeff(pred, true_masks).item()\n            pbar.update()\n\n    net.train()\n    return tot / n_val\n\n\nclass UNet(nn.Module):\n    def __init__(self, n_channels, n_classes, bilinear=True):\n        super(UNet, self).__init__()\n        self.n_channels = n_channels\n        self.n_classes = n_classes\n        self.bilinear = bilinear\n\n        self.inc = DoubleConv(n_channels, 64)\n        self.down1 = Down(64, 128)\n        self.down2 = Down(128, 256)\n        self.down3 = Down(256, 512)\n        factor = 2 if bilinear else 1\n        self.down4 = Down(512, 1024 // factor)\n        self.up1 = Up(1024, 512 // factor, bilinear)\n        self.up2 = Up(512, 256 // factor, bilinear)\n        self.up3 = Up(256, 128 // factor, bilinear)\n        self.up4 = Up(128, 64, bilinear)\n        self.outc = OutConv(64, n_classes)\n\n    def forward(self, x):\n        x1 = self.inc(x)\n        x2 = self.down1(x1)\n        x3 = self.down2(x2)\n        x4 = self.down3(x3)\n        x5 = self.down4(x4)\n        x = self.up1(x5, x4)\n        x = self.up2(x, x3)\n        x = self.up3(x, x2)\n        x = self.up4(x, x1)\n        logits = self.outc(x)\n        return logits\nclass DoubleConv(nn.Module):\n    \"\"\"(convolution => [BN] => ReLU) * 2\"\"\"\n\n    def __init__(self, in_channels, out_channels, mid_channels=None):\n        super().__init__()\n        if not mid_channels:\n            mid_channels = out_channels\n        self.double_conv = nn.Sequential(\n            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1),\n            nn.BatchNorm2d(mid_channels),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(mid_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\n\nclass Down(nn.Module):\n    \"\"\"Downscaling with maxpool then double conv\"\"\"\n\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.maxpool_conv = nn.Sequential(\n            nn.MaxPool2d(2),\n            DoubleConv(in_channels, out_channels)\n        )\n\n    def forward(self, x):\n        return self.maxpool_conv(x)\n\n\nclass Up(nn.Module):\n    \"\"\"Upscaling then double conv\"\"\"\n\n    def __init__(self, in_channels, out_channels, bilinear=True):\n        super().__init__()\n\n        # if bilinear, use the normal convolutions to reduce the number of channels\n        if bilinear:\n            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)\n        else:\n            self.up = nn.ConvTranspose2d(in_channels , in_channels // 2, kernel_size=2, stride=2)\n            self.conv = DoubleConv(in_channels, out_channels)\n\n\n    def forward(self, x1, x2):\n        x1 = self.up(x1)\n        # input is CHW\n        diffY = x2.size()[2] - x1.size()[2]\n        diffX = x2.size()[3] - x1.size()[3]\n\n        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,\n                        diffY // 2, diffY - diffY // 2])\n        # if you have padding issues, see\n        # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a\n        # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd\n        x = torch.cat([x2, x1], dim=1)\n        return self.conv(x)\n\n\nclass OutConv(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(OutConv, self).__init__()\n        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)\n\n    def forward(self, x):\n        return self.conv(x)\n    \ndef plot_img_and_mask(img, mask):\n    classes = mask.shape[2] if len(mask.shape) > 2 else 1\n    fig, ax = plt.subplots(1, classes + 1)\n    ax[0].set_title('Input image')\n    ax[0].imshow(img)\n    if classes > 1:\n        for i in range(classes):\n            ax[i+1].set_title(f'Output mask (class {i+1})')\n            ax[i+1].imshow(mask[:, :, i])\n    else:\n        ax[1].set_title(f'Output mask')\n        ax[1].imshow(mask)\n    plt.xticks([]), plt.yticks([])\n    plt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Loading packages"},{"metadata":{"trusted":true},"cell_type":"code","source":"import argparse\nimport logging\nimport os\nimport sys\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nfrom tqdm import tqdm\nfrom torch.utils.tensorboard import SummaryWriter\nfrom torch.utils.data import DataLoader, random_split","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading images"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nBASE_PATH = \"../input/hubmap-kidney-segmentation/\"\nTRAIN_PATH = os.path.join(BASE_PATH, \"train\")\ndf_train = pd.read_csv(\n    os.path.join(BASE_PATH, \"train.csv\")\n)\ndf_sub = pd.read_csv(\n    os.path.join(BASE_PATH, \"sample_submission.csv\"))\ndf_info = pd.read_csv(\n    os.path.join(BASE_PATH, \"HuBMAP-20-dataset_information.csv\")\n)\n\n\n\n\nsmall_ids = [\n    \"0486052bb\", \"095bf7a1f\", \"1e2425f28\", \"2f6ecfcdf\",\n    \"54f2eec69\", \"aaa6a05cc\", \"cb2d976f4\", \"e79de561c\",\n]\nsmall_images = []\nsmall_masks = []\n\nfor small_id in small_ids:\n    tmp_image, tmp_mask = read_image(small_id, scale=20, verbose=0)\n    small_images.append(tmp_image)\n    small_masks.append(tmp_mask)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Show the images with the masks"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(16, 16))\nfor ind, (tmp_id, tmp_image, tmp_mask) in enumerate(zip(small_ids, small_images, small_masks)):\n    plt.subplot(3, 3, ind + 1)\n    plt.imshow(tmp_image)\n    plt.imshow(tmp_mask, cmap=\"hot\", alpha=0.5)\n    plt.axis(\"off\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Training the image sequence"},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_net(net,\n              device,\n              epochs=5,\n              batch_size=1,\n              lr=0.001,\n              val_percent=0.1,\n              save_cp=True,\n              img_scale=0.5):\n    \n    #dataset = BasicDataset(dir_img, dir_mask, img_scale)\n    \n    #Combine img and mask list into a 8*2 numpy array\n    dataset = np.column_stack((small_images,small_masks))\n    \n    n_val = int(len(dataset) * val_percent)\n    n_train = len(dataset) - n_val\n    train, val = random_split(dataset, [n_train, n_val])\n    train_loader = DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=8, pin_memory=True)\n    val_loader = DataLoader(val, batch_size=batch_size, shuffle=False, num_workers=8, pin_memory=True, drop_last=True)\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Start to train"},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = optim.RMSprop(net.parameters(), lr=lr, weight_decay=1e-8, momentum=0.9)\n    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min' if net.n_classes > 1 else 'max', patience=2)\n    if net.n_classes > 1:\n        criterion = nn.CrossEntropyLoss()\n    else:\n        criterion = nn.BCEWithLogitsLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}