{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Cloud Comp Solution - LB 0.670\nMy Cloud Comp solution is the ensemble of a segmentation model and classifier model. In this kernel, we show the segmentation model. The classifier model is an ensemble of 4 models that achieves 78% accuracy and is shown elsewhere. My submitted solution is a pixel voting ensemble of 7 of these segmentation models with different seeds on the 3-Fold split and achieves CV 0.663 and LB 0.670. The main details of the segmentation model are as follows:\n  \n* Unet Architecture\n* EfficientnetB2 backbone\n* Train on 352x544 random crops from 384x576 size images\n* Augmentation of flips and rotate\n* Adam Accumulate optimizer\n* Jaccard loss\n* Kaggle Dice metric, Kaggle accuracy metric\n* Reduce LR on plateau and early stopping\n* Remove small masks\n* TTA of flips and shifts\n* Remove false positive masks with classifier\n* 3-Fold CV and prediction"},{"metadata":{},"cell_type":"markdown","source":"# Load Data"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"import time\nkernel_start = time.time()\nLIMIT = 8.8\n\nDO_TRAIN = True\nDO_TEST = True\nUSE_TTA = True\n\nRAND = 12345\n\n!pip install tensorflow-gpu==1.14.0 --quiet\n!pip install keras==2.2.4 --quiet\n!pip install segmentation-models --quiet\n\nimport albumentations as albu\nimport cv2, gc, os\nimport keras\nfrom keras import backend as K\nfrom keras.models import Model\nfrom keras.layers import Input\nfrom keras import layers\nfrom keras.layers.convolutional import Conv2D, Conv2DTranspose\nfrom keras.layers.pooling import MaxPooling2D\nfrom keras.layers.merge import concatenate\nfrom keras.losses import binary_crossentropy\nfrom keras.callbacks import Callback, ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom skimage.exposure import adjust_gamma\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.model_selection import KFold\nfrom keras.models import load_model\nimport segmentation_models as sm\nfrom sklearn.metrics import roc_auc_score, accuracy_score\n\nimport keras.backend as K\nfrom keras.legacy import interfaces\nfrom keras.optimizers import Optimizer","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = pd.read_csv('../input/understanding_cloud_organization/sample_submission.csv')\nsub['Image'] = sub['Image_Label'].map(lambda x: x.split('.')[0])\nsub['Label'] = sub['Image_Label'].map(lambda x: x.split('_')[1])\n# LOAD TEST CLASSIFIER PREDICTIONS\nsub['p'] = pd.read_csv('../input/cloud-classifiers/pred_cls.csv').p.values\nsub['p'] += np.load('../input/cloud-classifiers/pred_cls0.npy').reshape((-1)) * 0.5\nsub['p'] += pd.read_csv('../input/cloud-classifiers/pred_cls3.csv').p.values * 3.0\nsub['p'] += np.load('/kaggle/input/cloud-classifiers/pred_cls4b.npy') * 0.6\nsub['p'] /= 5.1\n\ntrain = pd.read_csv('../input/cloud-images-resized/train_384x576.csv')\ntrain['Image'] = train['Image_Label'].map(lambda x: x.split('.')[0])\ntrain['Label'] = train['Image_Label'].map(lambda x: x.split('_')[1])\ntrain2 = pd.DataFrame({'Image':train['Image'][::4]})\ntrain2['e1'] = train['EncodedPixels'][::4].values\ntrain2['e2'] = train['EncodedPixels'][1::4].values\ntrain2['e3'] = train['EncodedPixels'][2::4].values\ntrain2['e4'] = train['EncodedPixels'][3::4].values\ntrain2.set_index('Image',inplace=True,drop=True)\ntrain2.fillna('',inplace=True); train2.head()\ntrain2[['d1','d2','d3','d4']] = (train2[['e1','e2','e3','e4']]!='').astype('int8')\nfor k in range(1,5): train2['o'+str(k)] = 0\n# LOAD TRAIN CLASSIFIER PREDICTIONS\ntrain2[['o1','o2','o3','o4']] = np.load('../input/cloud-classifiers/oof_cls.npy')\ntrain2[['o1','o2','o3','o4']] += np.load('../input/cloud-classifiers/oof_cls0.npy') * 0.5\ntrain2[['o1','o2','o3','o4']] += np.load('../input/cloud-classifiers/oof_cls3.npy') * 3.0\ntrain2[['o1','o2','o3','o4']] += np.load('../input/cloud-classifiers/oof_cls4b.npy') * 0.6\ntrain2[['o1','o2','o3','o4']] /= 5.1\ntrain2.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Mask Functions"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def mask2rleXXX(img0, shape=(576,384), grow=(525,350)):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    a = (shape[1]-img0.shape[0])//2\n    b = (shape[0]-img0.shape[1])//2\n    img = np.zeros((shape[1],shape[0]),dtype=np.uint8)\n    img[a:a+img0.shape[0],b:b+img0.shape[1]] = img0\n    img = cv2.resize(img,grow)\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\ndef rle2maskX(mask_rle, shape=(2100,1400), shrink=1):\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).T[::shrink,::shrink]\n\ndef dice_coef(y_true, y_pred, smooth=1):\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = K.sum(y_true_f * y_pred_f)\n    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DataGenerator2(keras.utils.Sequence):\n    # USES GLOBAL VARIABLE TRAIN2 COLUMNS E1, E2, E3, E4\n    'Generates data for Keras'\n    def __init__(self, list_IDs, batch_size=24, shuffle=False, width=544, height=352, scale=1/128., sub=1., mode='train_seg',\n                 path='../input/cloud-images-resized/train_images_384x576/', flips=False, augment=False, shrink1=1,\n                 shrink2=1, dim=(576,384), clean=False):\n        'Initialization'\n        self.list_IDs = list_IDs\n        self.shuffle = shuffle\n        self.batch_size = batch_size\n        self.path = path\n        self.scale = scale\n        self.sub = sub\n        self.path = path\n        self.width = width\n        self.height = height\n        self.mode = mode\n        self.flips = flips\n        self.augment = augment\n        self.shrink1 = shrink1\n        self.shrink2 = shrink2\n        self.dim = dim\n        self.clean = clean\n        self.on_epoch_end()\n        \n    def __len__(self):\n        'Denotes the number of batches per epoch'\n        ct = int(np.floor( len(self.list_IDs) / self.batch_size))\n        if len(self.list_IDs)>ct*self.batch_size: ct += 1\n        return int(ct)\n\n    def __getitem__(self, index):\n        'Generate one batch of data'\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n        X, msk = self.__data_generation(indexes)\n        if self.augment: X, msk = self.__augment_batch(X, msk)\n        if (self.mode=='train_seg')|(self.mode=='validate_seg'): return X, msk\n        else: return X\n\n    def on_epoch_end(self):\n        'Updates indexes after each epoch'\n        self.indexes = np.arange(int( len(self.list_IDs) ))\n        if self.shuffle: np.random.shuffle(self.indexes)\n\n    def __data_generation(self, indexes):\n        'Generates data containing batch_size samples' \n        # Initialization\n        lnn = len(indexes); ex = self.shrink1; ax = self.shrink2\n        X = np.empty((lnn,self.height,self.width,3),dtype=np.float32)\n        msk = np.empty((lnn,self.height//ax,self.width//ax,4),dtype=np.int8)\n        \n        # Generate data\n        for k in range(lnn):\n            img = cv2.imread(self.path + self.list_IDs[indexes[k]]+'.jpg')\n            #img = cv2.resize(img,(self.dim[0]//ex,self.dim[1]//ex),interpolation = cv2.INTER_AREA)\n            img = img[::ex,::ex,:]\n            # AUGMENTATION FLIPS\n            hflip = False; vflip = False\n            if (self.flips):\n                if np.random.uniform(0,1)>0.5: hflip=True\n                if np.random.uniform(0,1)>0.5: vflip=True\n            if vflip: img = cv2.flip(img,0) # vertical\n            if hflip: img = cv2.flip(img,1) # horizontal\n            # AUGMENTATION SHAKE\n            a = np.random.randint(0,self.dim[0]//ex//ax-self.width//ax+1)\n            b = np.random.randint(0,self.dim[1]//ex//ax-self.height//ax+1)\n            if (self.mode=='predict'):\n                a = (self.dim[0]//ex//ax-self.width//ax)//2\n                b = (self.dim[1]//ex//ax-self.height//ax)//2\n            img = img[b*ax:self.height+b*ax,a*ax:self.width+a*ax]\n            # NORMALIZE IMAGES\n            X[k,] = img*self.scale - self.sub      \n            # LABELS\n            if (self.mode!='predict'):\n                for j in range(1,5):\n                    rle = train2.loc[self.list_IDs[indexes[k]],'e'+str(j)]\n                    if self.clean:\n                        if  train2.loc[self.list_IDs[indexes[k]],'o'+str(j)]<0.4:\n                            rle = ''\n                    mask = rle2maskX(rle,shrink=ex*ax,shape=self.dim)\n                    if vflip: mask = np.flip(mask,axis=0)\n                    if hflip: mask = np.flip(mask,axis=1)\n                    msk[k,:,:,j-1] = mask[b:self.height//ax+b,a:self.width//ax+a]\n\n        return X, msk\n    \n    def __random_transform(self, img, masks):\n        composition = albu.Compose([\n            #albu.HorizontalFlip(p=0.5),\n            #albu.VerticalFlip(p=0.5),\n            albu.ShiftScaleRotate(rotate_limit=30, scale_limit=0.1, p=0.5)\n        ])\n        \n        composed = composition(image=img, mask=masks)\n        aug_img = composed['image']\n        aug_masks = composed['mask']\n        \n        return aug_img, aug_masks\n    \n    def __augment_batch(self, img_batch, masks_batch):\n        for i in range(img_batch.shape[0]):\n            img_batch[i, ], masks_batch[i, ] = self.__random_transform(\n                img_batch[i, ], masks_batch[i, ])\n        \n        return img_batch, masks_batch","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Optimizer"},{"metadata":{"trusted":true},"cell_type":"code","source":"class AdamAccumulate(Optimizer):\n\n    def __init__(self, lr=0.001, beta_1=0.9, beta_2=0.999,\n                 epsilon=None, decay=0., amsgrad=False, accum_iters=8, **kwargs):\n        if accum_iters < 1:\n            raise ValueError('accum_iters must be >= 1')\n        super(AdamAccumulate, self).__init__(**kwargs)\n        with K.name_scope(self.__class__.__name__):\n            self.iterations = K.variable(0, dtype='int64', name='iterations')\n            self.lr = K.variable(lr, name='lr')\n            self.beta_1 = K.variable(beta_1, name='beta_1')\n            self.beta_2 = K.variable(beta_2, name='beta_2')\n            self.decay = K.variable(decay, name='decay')\n        if epsilon is None:\n            epsilon = K.epsilon()\n        self.epsilon = epsilon\n        self.initial_decay = decay\n        self.amsgrad = amsgrad\n        self.accum_iters = K.variable(accum_iters, K.dtype(self.iterations))\n        self.accum_iters_float = K.cast(self.accum_iters, K.floatx())\n\n    @interfaces.legacy_get_updates_support\n    def get_updates(self, loss, params):\n        grads = self.get_gradients(loss, params)\n        self.updates = [K.update_add(self.iterations, 1)]\n\n        lr = self.lr\n\n        completed_updates = K.cast(K.tf.floordiv(self.iterations, self.accum_iters), K.floatx())\n\n        if self.initial_decay > 0:\n            lr = lr * (1. / (1. + self.decay * completed_updates))\n\n        t = completed_updates + 1\n\n        lr_t = lr * (K.sqrt(1. - K.pow(self.beta_2, t)) / (1. - K.pow(self.beta_1, t)))\n\n        # self.iterations incremented after processing a batch\n        # batch:              1 2 3 4 5 6 7 8 9\n        # self.iterations:    0 1 2 3 4 5 6 7 8\n        # update_switch = 1:        x       x    (if accum_iters=4)  \n        update_switch = K.equal((self.iterations + 1) % self.accum_iters, 0)\n        update_switch = K.cast(update_switch, K.floatx())\n\n        ms = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        vs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        gs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n\n        if self.amsgrad:\n            vhats = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        else:\n            vhats = [K.zeros(1) for _ in params]\n\n        self.weights = [self.iterations] + ms + vs + vhats\n\n        for p, g, m, v, vhat, tg in zip(params, grads, ms, vs, vhats, gs):\n\n            sum_grad = tg + g\n            avg_grad = sum_grad / self.accum_iters_float\n\n            m_t = (self.beta_1 * m) + (1. - self.beta_1) * avg_grad\n            v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(avg_grad)\n\n            if self.amsgrad:\n                vhat_t = K.maximum(vhat, v_t)\n                p_t = p - lr_t * m_t / (K.sqrt(vhat_t) + self.epsilon)\n                self.updates.append(K.update(vhat, (1 - update_switch) * vhat + update_switch * vhat_t))\n            else:\n                p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon)\n\n            self.updates.append(K.update(m, (1 - update_switch) * m + update_switch * m_t))\n            self.updates.append(K.update(v, (1 - update_switch) * v + update_switch * v_t))\n            self.updates.append(K.update(tg, (1 - update_switch) * sum_grad))\n            new_p = p_t\n\n            # Apply constraints.\n            if getattr(p, 'constraint', None) is not None:\n                new_p = p.constraint(new_p)\n\n            self.updates.append(K.update(p, (1 - update_switch) * p + update_switch * new_p))\n        return self.updates\n\n    def get_config(self):\n        config = {'lr': float(K.get_value(self.lr)),\n                  'beta_1': float(K.get_value(self.beta_1)),\n                  'beta_2': float(K.get_value(self.beta_2)),\n                  'decay': float(K.get_value(self.decay)),\n                  'epsilon': self.epsilon,\n                  'amsgrad': self.amsgrad}\n        base_config = super(AdamAccumulate, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Metrics"},{"metadata":{"trusted":true},"cell_type":"code","source":"def kaggle_acc(y_true, y_pred0, pix=0.5, area=24000, dim=(352,544)):\n \n    # PIXEL THRESHOLD\n    y_pred = K.cast( K.greater(y_pred0,pix), K.floatx() )\n    \n    # MIN AREA THRESHOLD\n    s = K.sum(y_pred, axis=(1,2))\n    s = K.cast( K.greater(s, area), K.floatx() )\n\n    # REMOVE MIN AREA\n    s = K.reshape(s,(-1,1))\n    s = K.repeat(s,dim[0]*dim[1])\n    s = K.reshape(s,(-1,1))\n    y_pred = K.permute_dimensions(y_pred,(0,3,1,2))\n    y_pred = K.reshape(y_pred,shape=(-1,1))\n    y_pred = s*y_pred\n    y_pred = K.reshape(y_pred,(-1,y_pred0.shape[3],dim[0],dim[1]))\n    y_pred = K.permute_dimensions(y_pred,(0,2,3,1))\n\n    # COMPUTE KAGGLE ACC\n    total_y_true = K.sum(y_true, axis=(1,2))\n    total_y_true = K.cast( K.greater(total_y_true, 0), K.floatx() )\n\n    total_y_pred = K.sum(y_pred, axis=(1,2))\n    total_y_pred = K.cast( K.greater(total_y_pred, 0), K.floatx() )\n\n    return 1 - K.mean( K.abs( total_y_pred - total_y_true ) )\n\n\ndef kaggle_dice(y_true, y_pred0, pix=0.5, area=24000, dim=(352,544)):\n \n    # PIXEL THRESHOLD\n    y_pred = K.cast( K.greater(y_pred0,pix), K.floatx() )\n    \n    # MIN AREA THRESHOLD\n    s = K.sum(y_pred, axis=(1,2))\n    s = K.cast( K.greater(s, area), K.floatx() )\n\n    # REMOVE MIN AREA\n    s = K.reshape(s,(-1,1))\n    s = K.repeat(s,dim[0]*dim[1])\n    s = K.reshape(s,(-1,1))\n    y_pred = K.permute_dimensions(y_pred,(0,3,1,2))\n    y_pred = K.reshape(y_pred,shape=(-1,1))\n    y_pred = s*y_pred\n    y_pred = K.reshape(y_pred,(-1,y_pred0.shape[3],dim[0],dim[1]))\n    y_pred = K.permute_dimensions(y_pred,(0,2,3,1))\n\n    # COMPUTE KAGGLE DICE\n    intersection = K.sum(y_true * y_pred, axis=(1,2))\n    total_y_true = K.sum(y_true, axis=(1,2))\n    total_y_pred = K.sum(y_pred, axis=(1,2))\n    return K.mean( (2*intersection+1e-9) / (total_y_true+total_y_pred+1e-9) )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# K-Fold Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"filters = [256, 128, 64, 32, 16]\nREDUCTION = 0; RED = 2**REDUCTION\nfilters = filters[:5-REDUCTION]\n\nBATCH_SIZE = 16\njaccard_loss = sm.losses.JaccardLoss() \n\nskf = KFold(n_splits=3, shuffle=True, random_state=RAND)\nfor k, (idxT0, idxV0) in enumerate( skf.split(train2) ):\n\n    train_idx = train2.index[idxT0]\n    val_idx = train2.index[idxV0]\n\n    if k==0: idx_oof_0 = val_idx.copy()\n    elif k==1: idx_oof_1 = val_idx.copy()\n    elif k==2: idx_oof_2 = val_idx.copy()\n\n    print('#'*20)\n    print('### Fold',k,'###')\n    print('#'*20)\n\n    if not DO_TRAIN: continue\n\n    train_generator = DataGenerator2(\n        train_idx, flips=True, augment=True, shuffle=True, shrink2=RED, batch_size=BATCH_SIZE,\n    )\n\n    val_generator = DataGenerator2(\n        val_idx, shrink2=RED, batch_size=BATCH_SIZE\n    )\n\n    opt = AdamAccumulate(lr=0.001, accum_iters=8)\n    model = sm.Unet(\n        'efficientnetb2', \n        classes=4,\n        encoder_weights='imagenet',\n        decoder_filters = filters,\n        input_shape=(None, None, 3),\n        activation='sigmoid'\n    )\n    model.compile(optimizer=opt, loss=jaccard_loss, metrics=[dice_coef,kaggle_dice,kaggle_acc])\n\n    checkpoint = ModelCheckpoint('model_'+str(k)+'.h5', save_best_only=True)\n    es = EarlyStopping(monitor='val_dice_coef', min_delta=0.001, patience=5, verbose=1, mode='max')\n    rlr = ReduceLROnPlateau(monitor='val_dice_coef', factor=0.5, patience=2, verbose=1, mode='max', min_delta=0.001)\n    \n    history = model.fit_generator(\n         train_generator,\n         validation_data=val_generator,\n         callbacks=[rlr, es, checkpoint],\n         epochs=30,\n         verbose=2, workers=2\n    )\n    history_df = pd.DataFrame(history.history)\n    history_df.to_csv('history_'+str(k)+'.csv', index=False)\n\n    del train_idx, val_idx, train_generator, val_generator, opt, model, checkpoint, es, rlr, history, history_df\n    K.clear_session(); x=gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test Time Augmentation (TTA)"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install tta-wrapper --quiet\nfrom tta_wrapper import tta_segmentation\n\nif DO_TEST:\n    model1 = load_model('model_0.h5',custom_objects={'dice_coef':dice_coef,\n            'jaccard_loss':jaccard_loss,'AdamAccumulate':AdamAccumulate,\n            'kaggle_dice':kaggle_dice,'kaggle_acc':kaggle_acc})\n    if USE_TTA:\n        model1 = tta_segmentation(model1, h_flip=True, h_shift=(-10, 10), v_flip=True, v_shift=(-10, 10), merge='mean')\n    model2 = load_model('model_1.h5',custom_objects={'dice_coef':dice_coef,\n            'jaccard_loss':jaccard_loss,'AdamAccumulate':AdamAccumulate,\n            'kaggle_dice':kaggle_dice,'kaggle_acc':kaggle_acc})\n    if USE_TTA:\n        model2 = tta_segmentation(model2, h_flip=True, h_shift=(-10, 10), v_flip=True, v_shift=(-10, 10), merge='mean')\n    model3 = load_model('model_2.h5',custom_objects={'dice_coef':dice_coef,\n            'jaccard_loss':jaccard_loss,'AdamAccumulate':AdamAccumulate,        \n            'kaggle_dice':kaggle_dice,'kaggle_acc':kaggle_acc})\n    if USE_TTA:\n        model3 = tta_segmentation(model3, h_flip=True, h_shift=(-10, 10), v_flip=True, v_shift=(-10, 10), merge='mean')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Predict Masks for Test Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Computing masks for',len(sub)//4,'test images with 3 models'); sub.EncodedPixels = ''\nPTH = '../input/cloud-images-resized/test_images_384x576/'; bs = 4\nif USE_TTA: bs=1\ntest_gen = DataGenerator2(sub.Image[::4].values, width=576, height=384, batch_size=bs, mode='predict',path=PTH)\n\nsz = 20000.*(576/525)*(384/350)/RED/RED\n\npixt = [0.5,0.5,0.5,0.35] #; pixt = [0.4,0.4,0.4,0.4]\nszt = [25000., 20000., 22500., 15000.] #; szt = [20000., 20000., 20000., 20000.]\nfor k in range(len(szt)): szt[k] = szt[k]*(576./525.)*(384./350.)/RED/RED\n\nif DO_TEST:\n    for b,batch in enumerate(test_gen):\n        btc = model1.predict_on_batch(batch)\n        btc += model2.predict_on_batch(batch)\n        btc += model3.predict_on_batch(batch)\n        btc /= 3.0\n\n        for j in range(btc.shape[0]):\n            for i in range(btc.shape[-1]):\n                mask = (btc[j,:,:,i]>pixt[i]).astype(int); rle = ''\n                if np.sum(mask)>szt[i]: rle = mask2rleXXX( mask ,shape=(576//RED,384//RED))\n                sub.iloc[4*(bs*b+j)+i,1] = rle\n        if b%(100//bs)==0: print(b*bs,', ',end='')\n        t = np.round( (time.time() - kernel_start)/60,1 )\n        if t > LIMIT*60:\n            print('#### EXCEEDED TIME LIMIT. STOPPING NOW ####')\n            break\n\n    sub[['Image_Label','EncodedPixels']].to_csv('sub_seg.csv',index=False)\n    sub.loc[(sub.p<0.5)&(sub.Label=='Fish'),'EncodedPixels'] = ''\n    sub.loc[(sub.p<0.3)&(sub.Label=='Flower'),'EncodedPixels'] = ''\n    sub.loc[(sub.p<0.5)&(sub.Label=='Gravel'),'EncodedPixels'] = ''\n    sub.loc[(sub.p<0.5)&(sub.Label=='Sugar'),'EncodedPixels'] = ''\n    sub[['Image_Label','EncodedPixels']].to_csv('submission.csv',index=False)\n\nsub.head(10)","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}