{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install /kaggle/input/testtimeaug/ttach-0.0.2-py3-none-any.whl","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\n\nimport math\nimport openslide\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport albumentations\nfrom tqdm import tqdm\nfrom joblib import Parallel, delayed\nfrom matplotlib import pyplot as plt\nfrom PIL import Image, ImageChops\n\nimport cv2\n\nimport torch\nimport torch.utils.data as data_utils\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom torch.optim import lr_scheduler\nfrom torch import nn\n\nimport ttach as tta\nfrom torchvision import transforms,models\nimport torch.nn.functional as F\nfrom tqdm.auto import tqdm\nfrom torch import Tensor\n\nfrom collections import OrderedDict","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls /kaggle/input/prostate-cancer-grade-assessment","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"BASE_DIR = '/kaggle/input/prostate-cancer-grade-assessment'\n# DATA_DIR = os.path.join(BASE_DIR, 'train_images')\nDATA_DIR = os.path.join(BASE_DIR, 'test_images')\nDEVICE = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test_df = pd.read_csv(os.path.join(BASE_DIR, 'train.csv'))[['image_id', 'data_provider']].loc[:10]\n# test_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df = pd.read_csv(os.path.join(BASE_DIR, 'test.csv'))\nsample_sub_df = pd.read_csv(os.path.join(BASE_DIR, 'sample_submission.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"org_test_df = test_df.copy()\norg_test_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"crop_size = 256  # Size of resultant images\ncrop_level = 2  # The level of slide used to get the images (you can use 0 to get very high resolution images)\ndown_samples = [1, 4, 16]  # List of down samples available in any tiff image file","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def split_image(openslide_image):\n    \"\"\"\n    Splits the given image into multiple images if 256x256\n    \"\"\"\n    \n    # Get the size of the given image\n    width, height = openslide_image.level_dimensions[crop_level]\n\n    # Get the dimensions of level 0 resolution, as it's required in \"read_region()\" function\n    base_height = down_samples[crop_level] * height  # height of level 0\n    base_width = down_samples[crop_level] * width  # width of level 0\n\n    # Get the number of smaller images \n    h_crops = math.ceil(width / crop_size)\n    v_crops = math.ceil(height / crop_size)\n\n    splits = []\n    for v in range(v_crops):\n        for h in range(h_crops): \n            x_location = h*crop_size*down_samples[crop_level]\n            y_location = v*crop_size*down_samples[crop_level]\n\n            patch = openslide_image.read_region((x_location, y_location), crop_level, (crop_size, crop_size))\n\n            splits.append(patch)\n    return splits, h_crops, v_crops","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_emptiness(arr):\n    total_ele = arr.size\n    white_ele = np.count_nonzero(arr == 255) + np.count_nonzero(arr == 0)\n    return white_ele / total_ele","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ignore_threshold = 0.95  # If the image is more than 95% empty, consider it as white and ignore","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def filter_white_images(images):\n    non_empty_crops = []\n    for image in images:\n        image_arr = np.array(image)[...,:3]  # Discard the alpha channel\n        emptiness = get_emptiness(image_arr)\n        if emptiness < ignore_threshold:\n            non_empty_crops.append(image)\n    return non_empty_crops","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = []\ndef create_dataset(count):\n    img = os.path.join(DATA_DIR, f'{test_df[\"image_id\"].iloc[count]}.tiff')\n    img = openslide.OpenSlide(img)\n    crops, _, _ = split_image(img)\n    img.close()\n\n    non_empty_crops = filter_white_images(crops)\n    image_id = test_df['image_id'].iloc[count]\n\n    for index, img in enumerate(non_empty_crops):\n        img_metadata = {}\n        img = img.convert('RGB')\n\n        img_metadata['image_id'] = f'{image_id}_{index}'\n        img_metadata['data_provider'] = test_df['data_provider'].iloc[count]\n        img_metadata['group'] = count\n\n        img.save(f'{image_id}_{index}.jpg', 'JPEG', quality=100, optimize=True, progressive=True)\n        dataset.append(img_metadata)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if os.path.exists(DATA_DIR):\n    dataset = Parallel(n_jobs=8)(delayed(create_dataset)(count) for count in tqdm(range(len(test_df))))\n    dataset = [item for sublist in dataset for item in sublist]\n\n    dataset = pd.DataFrame(dataset)\n    dataset.to_csv('new_test.csv', index=False)\n    test_df = pd.read_csv('new_test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class _Transition(nn.Sequential):\n    def __init__(self, num_input_features, num_output_features):\n        super(_Transition, self).__init__()\n        self.add_module('norm', nn.BatchNorm2d(num_input_features))\n        self.add_module('relu', nn.ReLU(inplace=True))\n        self.add_module('conv', nn.Conv2d(num_input_features, num_output_features,\n                                          kernel_size=1, stride=1, bias=False))\n        self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class _DenseLayer(nn.Module):\n    def __init__(self, num_input_features, growth_rate, bn_size, drop_rate, memory_efficient=False):\n        super(_DenseLayer, self).__init__()\n        self.add_module('norm1', nn.BatchNorm2d(num_input_features)),\n        self.add_module('relu1', nn.ReLU(inplace=True)),\n        self.add_module('conv1', nn.Conv2d(num_input_features, bn_size *\n                                           growth_rate, kernel_size=1, stride=1,\n                                           bias=False)),\n        self.add_module('norm2', nn.BatchNorm2d(bn_size * growth_rate)),\n        self.add_module('relu2', nn.ReLU(inplace=True)),\n        self.add_module('conv2', nn.Conv2d(bn_size * growth_rate, growth_rate,\n                                           kernel_size=3, stride=1, padding=1,\n                                           bias=False)),\n        self.drop_rate = float(drop_rate)\n        self.memory_efficient = memory_efficient\n\n    def bn_function(self, inputs):\n        # type: (List[Tensor]) -> Tensor\n        concated_features = torch.cat(inputs, 1)\n        bottleneck_output = self.conv1(self.relu1(self.norm1(concated_features)))  # noqa: T484\n        return bottleneck_output\n\n    # todo: rewrite when torchscript supports any\n    def any_requires_grad(self, input):\n        # type: (List[Tensor]) -> bool\n        for tensor in input:\n            if tensor.requires_grad:\n                return True\n        return False\n\n    @torch.jit.unused  # noqa: T484\n    def call_checkpoint_bottleneck(self, input):\n        # type: (List[Tensor]) -> Tensor\n        def closure(*inputs):\n            return self.bn_function(*inputs)\n\n        return cp.checkpoint(closure, input)\n\n    @torch.jit._overload_method  # noqa: F811\n    def forward(self, input):\n        # type: (List[Tensor]) -> (Tensor)\n        pass\n\n    @torch.jit._overload_method  # noqa: F811\n    def forward(self, input):\n        # type: (Tensor) -> (Tensor)\n        pass\n\n    # torchscript does not yet support *args, so we overload method\n    # allowing it to take either a List[Tensor] or single Tensor\n    def forward(self, input):  # noqa: F811\n        if isinstance(input, Tensor):\n            prev_features = [input]\n        else:\n            prev_features = input\n\n        if self.memory_efficient and self.any_requires_grad(prev_features):\n            if torch.jit.is_scripting():\n                raise Exception(\"Memory Efficient not supported in JIT\")\n\n            bottleneck_output = self.call_checkpoint_bottleneck(prev_features)\n        else:\n            bottleneck_output = self.bn_function(prev_features)\n\n        new_features = self.conv2(self.relu2(self.norm2(bottleneck_output)))\n        if self.drop_rate > 0:\n            new_features = F.dropout(new_features, p=self.drop_rate,\n                                     training=self.training)\n        return new_features","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class _DenseBlock(nn.ModuleDict):\n    _version = 2\n\n    def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate, memory_efficient=False):\n        super(_DenseBlock, self).__init__()\n        for i in range(num_layers):\n            layer = _DenseLayer(\n                num_input_features + i * growth_rate,\n                growth_rate=growth_rate,\n                bn_size=bn_size,\n                drop_rate=drop_rate,\n                memory_efficient=memory_efficient,\n            )\n            self.add_module('denselayer%d' % (i + 1), layer)\n\n    def forward(self, init_features):\n        features = [init_features]\n        for name, layer in self.items():\n            new_features = layer(features)\n            features.append(new_features)\n        return torch.cat(features, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class DenseNet(nn.Module):\n    r\"\"\"Densenet-BC model class, based on\n    `\"Densely Connected Convolutional Networks\" <https://arxiv.org/pdf/1608.06993.pdf>`_\n    Args:\n        growth_rate (int) - how many filters to add each layer (`k` in paper)\n        block_config (list of 4 ints) - how many layers in each pooling block\n        num_init_features (int) - the number of filters to learn in the first convolution layer\n        bn_size (int) - multiplicative factor for number of bottle neck layers\n          (i.e. bn_size * k features in the bottleneck layer)\n        drop_rate (float) - dropout rate after each dense layer\n        num_classes (int) - number of classification classes\n        memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient,\n          but slower. Default: *False*. See `\"paper\" <https://arxiv.org/pdf/1707.06990.pdf>`_\n    \"\"\"\n\n    __constants__ = ['features']\n\n    def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16),\n                 num_init_features=64, bn_size=4, drop_rate=0, num_classes=1000, memory_efficient=False):\n\n        super(DenseNet, self).__init__()\n\n        # First convolution\n        self.features = nn.Sequential(OrderedDict([\n            ('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)),  # 3 is number of channels in input image\n            ('norm0', nn.BatchNorm2d(num_init_features)),\n            ('relu0', nn.ReLU(inplace=True)),\n            ('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1)),\n        ]))\n\n        # Each denseblock\n        num_features = num_init_features\n        for i, num_layers in enumerate(block_config):\n            block = _DenseBlock(\n                num_layers=num_layers,\n                num_input_features=num_features,\n                bn_size=bn_size,\n                growth_rate=growth_rate,\n                drop_rate=drop_rate,\n                memory_efficient=memory_efficient\n            )\n            self.features.add_module('denseblock%d' % (i + 1), block)\n            num_features = num_features + num_layers * growth_rate\n            if i != len(block_config) - 1:\n                trans = _Transition(num_input_features=num_features,\n                                    num_output_features=num_features // 2)\n                self.features.add_module('transition%d' % (i + 1), trans)\n                num_features = num_features // 2\n\n        # Final batch norm\n        self.features.add_module('norm5', nn.BatchNorm2d(num_features))\n\n        # Linear layer\n        self.classifier = nn.Linear(num_features, num_classes)\n\n        # Official init from torch repo.\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight)\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.constant_(m.weight, 1)\n                nn.init.constant_(m.bias, 0)\n            elif isinstance(m, nn.Linear):\n                nn.init.constant_(m.bias, 0)\n\n    def forward(self, x):\n        features = self.features(x)\n        out = F.relu(features, inplace=True)\n        out = F.adaptive_avg_pool2d(out, (1, 1))\n        out = torch.flatten(out, 1)\n        out = self.classifier(out)\n        return out","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def _densenet(growth_rate, block_config, num_init_features, **kwargs):\n    return DenseNet(growth_rate, block_config, num_init_features, **kwargs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def densenet121(**kwargs):\n    r\"\"\"Densenet-121 model from\n    `\"Densely Connected Convolutional Networks\" <https://arxiv.org/pdf/1608.06993.pdf>`_\n    \"\"\"\n    return _densenet(32, (6, 12, 24, 16), 64, **kwargs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class DenseNet121Wrapper(nn.Module):\n    def __init__(self):\n        super(DenseNet121Wrapper, self).__init__()\n        \n        # Load imagenet pre-trained model \n        self.dense_net = densenet121()\n        \n        # Appdend output layers based on our date\n        self.out = nn.Linear(in_features=1000, out_features=6)\n        \n    def forward(self, X):\n        output = self.dense_net(X)\n        output = self.out(output)\n        \n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"paths = ['panda-densenet121-training-images-fold0',\n        'panda-densenet121-training-images-fold1',\n        'panda-densenet121-training-fold2',\n        'panda-densenet121-training-fold3',\n        'panda-densenet121-training-fold4']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"FOLDS = 5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Define TTA parameters\ntransforms = tta.Compose(\n    [\n        tta.HorizontalFlip(),\n        tta.VerticalFlip(),\n        tta.Rotate90([0, 90]),\n        tta.Scale(scales=[1, 2]),\n        tta.Multiply(factors=[0.9, 1, 1.1]),\n    ]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"models = []\nfor fold in range(FOLDS):\n    model = DenseNet121Wrapper()\n    model = nn.DataParallel(model)\n    model.load_state_dict(torch.load(f'/kaggle/input/{paths[fold]}/densenet121_256_fold{fold}.pth', map_location=DEVICE))\n    model = tta.ClassificationTTAWrapper(model, transforms)\n    model.eval()\n    models.append(model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"WORKING_DIR = os.path.join('/', 'kaggle', 'working')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class PandaDataset(Dataset):\n    \"\"\"Custom dataset for PANDA Tests\"\"\"\n    \n    def __init__(self, df, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)):\n        self.df = df\n        self.aug = albumentations.Compose([\n            albumentations.Normalize(mean, std, always_apply=True)\n        ])\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        image_id = self.df.loc[index]['image_id']\n        image = cv2.imread(os.path.join(WORKING_DIR, f'{image_id}.jpg'))\n        image = self.aug(image=image)['image']\n        \n        # Convert from NHWC to NCHW as pytorch expects images in NCHW format\n        image = np.transpose(image, (2, 0, 1))\n        \n        # For now, just return image and ISUP grades\n        return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE=16","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def inference(model, test_loader, device):\n    preds = []\n    for i, images in tqdm(enumerate(test_loader)):\n        images = images.to(device, dtype=torch.float)/255\n            \n        with torch.no_grad():\n            y_preds = model(images)\n\n#         preds.append(y_preds.to('cpu').numpy().argmax(1))\n#         print(preds.append(y_preds.to('cpu').numpy().argmax(1)))\n        preds.append(y_preds.to('cpu').numpy())\n    preds = np.concatenate(preds)\n    return preds","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def submit(sample):\n    global sample_sub_df\n    if os.path.exists(DATA_DIR):\n        test_dataset = PandaDataset(test_df)\n        test_loader = data_utils.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)\n        \n        preds = []\n        for fold in range(FOLDS):\n            preds.append(inference(models[fold], test_loader, DEVICE))\n        preds = np.array(preds)\n        preds = np.argmax(np.mean(preds, axis=0), axis=1)\n        \n        test_df['preds'] = preds\n        sample = sample.drop(['data_provider'], axis=1)\n        sample['isup_grade'] = test_df.groupby('group')['preds'].agg(lambda x:x.value_counts().index[0])\n        return sample\n    return sample_sub_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = submit(org_test_df)\nsubmission['isup_grade'] = submission['isup_grade'].astype(int)\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -rf *","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","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}