{"cells":[{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"!pip install catalyst\n!pip install pretrainedmodels\n!pip install git+https://github.com/qubvel/segmentation_models.pytorch@b83e00088c0f8af024531206d2ccc2f3c96a8fb4\n!pip install pytorch_toolbelt\n!pip install torchvision==0.4\n!pip install -v --no-cache-dir --global-option=\"--cpp_ext\" --global-option=\"--cuda_ext\" ../input/apex-325f5a0/apex-master/\nfrom apex import amp","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"import os\nimport cv2\nimport collections\nimport time \nimport tqdm\nfrom PIL import Image\nfrom functools import partial\ntrain_on_gpu = True\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n%matplotlib inline\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score\n\nimport torchvision\nimport torchvision.transforms as transforms\nimport torch\nfrom torch.utils.data import TensorDataset, DataLoader,Dataset\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nfrom torch.utils.data.sampler import SubsetRandomSampler\nfrom torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau, CosineAnnealingLR\n\nimport albumentations as albu\nfrom albumentations import pytorch as AT\n\nfrom catalyst.data import Augmentor\nfrom catalyst.dl import utils\nfrom catalyst.data.reader import ImageReader, ScalarReader, ReaderCompose, LambdaReader\nfrom catalyst.dl.runner import SupervisedRunner\nfrom catalyst.contrib.models.segmentation import Unet\nfrom catalyst.dl.callbacks import DiceCallback, EarlyStoppingCallback, InferCallback, CheckpointCallback\n\nimport segmentation_models_pytorch as smp\ndevice=torch.device('cuda')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def get_img(x, folder: str='train_images_525/train_images_525'):\n    \"\"\"\n    Return image based on image name and folder.\n    \"\"\"\n    data_folder = f\"{img_paths}/{folder}\"\n    image_path = os.path.join(data_folder, x)\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    return img\n\n\ndef rle_decode(mask_rle: str = '', shape: tuple = (1400, 2100)):\n    '''\n    Decode rle encoded mask.\n    \n    :param mask_rle: run-length as string formatted (start length)\n    :param shape: (height, width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\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, order='F')\n\n\ndef make_mask(df: pd.DataFrame, image_name: str='img.jpg', shape: tuple = (350, 525)):\n    \"\"\"\n    Create mask based on df, image name and shape.\n    \"\"\"\n    masks = np.zeros((shape[0], shape[1], 4), dtype=np.float32)\n    df = df[df[\"im_id\"] == image_name]\n    for idx, im_name in enumerate(df[\"im_id\"].values):\n        for classidx, classid in enumerate([\"Fish\", \"Flower\", \"Gravel\", \"Sugar\"]):\n            mask = cv2.imread(\"../input/understanding-clouds-resized/train_masks_525/train_masks_525/\" + classid + im_name)\n            if mask is None:\n                continue\n            if mask[:,:,0].shape != (350,525):\n                mask = cv2.resize(mask, (525,350))\n            masks[:, :, classidx] = mask[:,:,0]\n    masks = masks/255\n    return masks\n\n\ndef to_tensor(x, **kwargs):\n    \"\"\"\n    Convert image or mask.\n    \"\"\"\n    return x.transpose(2, 0, 1).astype('float32')\n\n\ndef mask2rle(img):\n    '''\n    Convert mask to rle.\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels= img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n\ndef visualize(image, mask, original_image=None, original_mask=None):\n    \"\"\"\n    Plot image and masks.\n    If two pairs of images and masks are passes, show both.\n    \"\"\"\n    fontsize = 14\n    class_dict = {0: 'Fish', 1: 'Flower', 2: 'Gravel', 3: 'Sugar'}\n    \n    if original_image is None and original_mask is None:\n        f, ax = plt.subplots(1, 5, figsize=(24, 24))\n\n        ax[0].imshow(image)\n        for i in range(4):\n            ax[i + 1].imshow(mask[:, :, i])\n            ax[i + 1].set_title(f'Mask {class_dict[i]}', fontsize=fontsize)\n    else:\n        f, ax = plt.subplots(2, 5, figsize=(24, 12))\n\n        ax[0, 0].imshow(original_image)\n        ax[0, 0].set_title('Original image', fontsize=fontsize)\n                \n        for i in range(4):\n            ax[0, i + 1].imshow(original_mask[:, :, i])\n            ax[0, i + 1].set_title(f'Original mask {class_dict[i]}', fontsize=fontsize)\n        \n        ax[1, 0].imshow(image)\n        ax[1, 0].set_title('Transformed image', fontsize=fontsize)\n        \n        \n        for i in range(4):\n            ax[1, i + 1].imshow(mask[:, :, i])\n            ax[1, i + 1].set_title(f'Transformed mask {class_dict[i]}', fontsize=fontsize)\n            \n            \ndef visualize_with_raw(image, mask, original_image=None, original_mask=None, raw_image=None, raw_mask=None):\n    \"\"\"\n    Plot image and masks.\n    If two pairs of images and masks are passes, show both.\n    \"\"\"\n    fontsize = 14\n    class_dict = {0: 'Fish', 1: 'Flower', 2: 'Gravel', 3: 'Sugar'}\n\n    f, ax = plt.subplots(3, 5, figsize=(24, 12))\n\n    ax[0, 0].imshow(original_image)\n    ax[0, 0].set_title('Original image', fontsize=fontsize)\n\n    for i in range(4):\n        ax[0, i + 1].imshow(original_mask[:, :, i])\n        ax[0, i + 1].set_title(f'Original mask {class_dict[i]}', fontsize=fontsize)\n\n\n    ax[1, 0].imshow(raw_image)\n    ax[1, 0].set_title('Original image', fontsize=fontsize)\n\n    for i in range(4):\n        ax[1, i + 1].imshow(raw_mask[:, :, i])\n        ax[1, i + 1].set_title(f'Raw predicted mask {class_dict[i]}', fontsize=fontsize)\n        \n    ax[2, 0].imshow(image)\n    ax[2, 0].set_title('Transformed image', fontsize=fontsize)\n\n\n    for i in range(4):\n        ax[2, i + 1].imshow(mask[:, :, i])\n        ax[2, i + 1].set_title(f'Predicted mask with processing {class_dict[i]}', fontsize=fontsize)\n            \n            \ndef plot_with_augmentation(image, mask, augment):\n    \"\"\"\n    Wrapper for `visualize` function.\n    \"\"\"\n    augmented = augment(image=image, mask=mask)\n    image_flipped = augmented['image']\n    mask_flipped = augmented['mask']\n    visualize(image_flipped, mask_flipped, original_image=image, original_mask=mask)\n    \n    \nsigmoid = lambda x: 1 / (1 + np.exp(-x))\n\n\ndef post_process(probability, threshold, min_size):\n    \"\"\"\n    Post processing of each predicted mask, components with lesser number of pixels\n    than `min_size` are ignored\n    \"\"\"\n    # don't remember where I saw it\n    mask = cv2.threshold(probability, threshold, 1, cv2.THRESH_BINARY)[1]\n    num_component, component = cv2.connectedComponents(mask.astype(np.uint8))\n    predictions = np.zeros((350, 525), np.float32)\n    num = 0\n    for c in range(1, num_component):\n        p = (component == c)\n        if p.sum() > min_size:\n            predictions[p] = 1\n            num += 1\n    return predictions, num\n\n\ndef get_training_augmentation():\n    train_transform = [\n        albu.Resize(320, 640),\n        albu.HorizontalFlip(p=0.25),\n        albu.VerticalFlip(p=0.25),\n        albu.ShiftScaleRotate(scale_limit=0.5, rotate_limit=0, shift_limit=0.1, p=0.5, border_mode=0)\n#         albu.GridDistortion(p=0.5),\n#         albu.OpticalDistortion(p=0.5, distort_limit=2, shift_limit=0.5),\n\n    ]\n    return albu.Compose(train_transform)\n\n\ndef get_validation_augmentation():\n    \"\"\"Add paddings to make image shape divisible by 32\"\"\"\n    test_transform = [\n        albu.Resize(320, 640)\n    ]\n    return albu.Compose(test_transform)\n\n\ndef get_preprocessing(preprocessing_fn):\n    \"\"\"Construct preprocessing transform\n    \n    Args:\n        preprocessing_fn (callbale): data normalization function \n            (can be specific for each pretrained neural network)\n    Return:\n        transform: albumentations.Compose\n    \n    \"\"\"\n    \n    _transform = [\n        albu.Lambda(image=preprocessing_fn),\n        albu.Lambda(image=to_tensor, mask=to_tensor),\n    ]\n    return albu.Compose(_transform)\n\n\ndef dice(img1, img2):\n    img1 = np.asarray(img1).astype(np.bool)\n    img2 = np.asarray(img2).astype(np.bool)\n\n    intersection = np.logical_and(img1, img2)\n\n    return 2. * intersection.sum() / (img1.sum() + img2.sum())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = '../input/understanding_cloud_organization'\nimg_paths = '../input/understanding-clouds-resized'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(f'{path}/train.csv')\nsub = pd.read_csv(f'{path}/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train['label'] = train['Image_Label'].apply(lambda x: x.split('_')[1])\ntrain['im_id'] = train['Image_Label'].apply(lambda x: x.split('_')[0])\n\n\nsub['label'] = sub['Image_Label'].apply(lambda x: x.split('_')[1])\nsub['im_id'] = sub['Image_Label'].apply(lambda x: x.split('_')[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"id_mask_count = train.loc[train['EncodedPixels'].isnull() == False, 'Image_Label'].apply(lambda x: x.split('_')[0]).value_counts().\\\nreset_index().rename(columns={'index': 'img_id', 'Image_Label': 'count'})\ntrain_ids, valid_ids = train_test_split(id_mask_count['img_id'].values, random_state=42, stratify=id_mask_count['count'], test_size=0.1)\ntest_ids = sub['Image_Label'].apply(lambda x: x.split('_')[0]).drop_duplicates().values","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CloudDataset(Dataset):\n    def __init__(self, df: pd.DataFrame = None, datatype: str = 'train', img_ids: np.array = None,\n                 transforms = albu.Compose([albu.HorizontalFlip(),AT.ToTensor()]),\n                preprocessing=None):\n        self.df = df\n        if datatype != 'test':\n            self.data_folder = f\"{img_paths}/train_images_525/train_images_525\"\n        else:\n            self.data_folder = f\"{img_paths}/test_images_525/test_images_525\"\n        self.img_ids = img_ids\n        self.transforms = transforms\n        self.preprocessing = preprocessing\n\n    def __getitem__(self, idx):\n        image_name = self.img_ids[idx]\n        mask = make_mask(self.df, image_name)\n        image_path = os.path.join(self.data_folder, image_name)\n        img = cv2.imread(image_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        augmented = self.transforms(image=img, mask=mask)\n        img = augmented['image']\n        mask = augmented['mask']\n        if self.preprocessing:\n            preprocessed = self.preprocessing(image=img, mask=mask)\n            img = preprocessed['image']\n            mask = preprocessed['mask']\n        return img, mask\n\n    def __len__(self):\n        return len(self.img_ids)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ENCODER = 'resnet18'\nENCODER_WEIGHTS = 'imagenet'\n\nACTIVATION = None\nmodel = smp.Linknet(\n    encoder_name=ENCODER, \n    encoder_weights=ENCODER_WEIGHTS, \n    classes=4, \n    activation=ACTIVATION,\n)\npreprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_workers = 4\nbs = 32\ntrain_dataset = CloudDataset(df=train, datatype='train', img_ids=train_ids, transforms = get_training_augmentation(), preprocessing=get_preprocessing(preprocessing_fn))\nvalid_dataset = CloudDataset(df=train, datatype='valid', img_ids=valid_ids, transforms = get_validation_augmentation(), preprocessing=get_preprocessing(preprocessing_fn))\n\ntrain_loader = DataLoader(train_dataset, batch_size=bs, shuffle=True, num_workers=num_workers)\nvalid_loader = DataLoader(valid_dataset, batch_size=bs, shuffle=False, num_workers=num_workers)\n\nloaders = {\n    \"train\": train_loader,\n    \"valid\": valid_loader\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# import apex\nnum_epochs = 15\nlogdir = \"./logs/segmentation_unet\"\n\n# model, criterion, optimizer\noptimizer = torch.optim.Adam([\n    {'params': model.decoder.parameters(), 'lr': 1e-2}, \n    {'params': model.encoder.parameters(), 'lr': 1e-3},  \n])\nopt_level = 'O1'\nmodel.cuda()\nmodel, optimizer = amp.initialize(model, optimizer, opt_level=opt_level)\nscheduler = ReduceLROnPlateau(optimizer, factor=0.5, patience=2)\ncriterion = smp.utils.losses.BCEDiceLoss(eps=1.)\nrunner = SupervisedRunner()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"runner.train(\n    model=model,\n    criterion=criterion,\n    optimizer=optimizer,\n    scheduler=scheduler,\n    loaders=loaders,\n    callbacks=[DiceCallback(), EarlyStoppingCallback(patience=5, min_delta=0.001)],\n    logdir=logdir,\n    num_epochs=num_epochs,\n    verbose=True\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"utils.plot_metrics(\n    logdir=logdir, \n    metrics=[\"loss\", \"dice\", 'lr', '_base/lr']\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"encoded_pixels = []\nloaders = {\"infer\": valid_loader}\nrunner.infer(\n    model=model,\n    loaders=loaders,\n    callbacks=[\n        CheckpointCallback(\n            resume=f\"{logdir}/checkpoints/best.pth\"),\n        InferCallback()\n    ],\n)\nvalid_masks = []\nprobabilities = np.zeros((2220, 350, 525), dtype = np.float32)\nfor i, (batch, output) in enumerate(tqdm.tqdm(zip(valid_dataset, runner.callbacks[0].predictions[\"logits\"]))):\n    image, mask = batch\n    for m in mask:\n        if m.shape != (350, 525):\n            m = cv2.resize(m, dsize=(525, 350), interpolation=cv2.INTER_LINEAR)\n        valid_masks.append(m)\n\n    for j, probability in enumerate(output):\n        if probability.shape != (350, 525):\n            probability = cv2.resize(probability, dsize=(525, 350), interpolation=cv2.INTER_LINEAR)\n        probabilities[i * 4 + j, :, :] = probability","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\ntorch.cuda.empty_cache()\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_params = {}\nfor class_id in range(4):\n    print(class_id)\n    attempts = []\n    for t in range(0, 100, 5):\n        print(t)\n        t /= 100\n        for ms in [5000, 10000, 15000, 20000, 25000]:\n            masks = []\n            for i in range(class_id, len(probabilities), 4):\n                probability = probabilities[i]\n                predict, num_predict = post_process(sigmoid(probability), t, ms)\n                masks.append(predict)\n            d = []\n            for i, j in zip(masks, valid_masks[class_id::4]):\n                if (i.sum() == 0) & (j.sum() == 0):\n                    d.append(1)\n                else:\n                    d.append(dice(i, j))\n            attempts.append((t, ms, np.mean(d)))\n    attempts_df = pd.DataFrame(attempts, columns=['threshold', 'size', 'dice'])\n    attempts_df = attempts_df.sort_values('dice', ascending=False)\n    print(attempts_df.head())\n    best_threshold = attempts_df['threshold'].values[0]\n    best_size = attempts_df['size'].values[0]\n    class_params[class_id] = (best_threshold, best_size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, (input, output) in enumerate(zip(valid_dataset, runner.callbacks[0].predictions[\"logits\"])):\n    image, mask = input\n    image_vis = image.transpose(1, 2, 0)\n    mask = mask.astype('uint8').transpose(1, 2, 0)\n    pr_mask = np.zeros((350, 525, 4))\n    for j in range(4):\n        probability = cv2.resize(output[:, :, j], dsize=(525, 350), interpolation=cv2.INTER_LINEAR)\n        pr_mask[:, :, j], _ = post_process(sigmoid(probability), class_params[j][0], class_params[j][1])    \n    visualize_with_raw(image=image_vis, mask=pr_mask, original_image=image_vis, original_mask=mask, raw_image=image_vis, raw_mask=output.transpose(1, 2, 0))\n    if i >= 2:\n        break","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":1}