{"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":"from fastai.vision.all import *\nfrom skimage import measure\nfrom skimage.transform import rescale, resize\nfrom skimage.util import crop, montage\nfrom skimage.morphology import label, square, dilation, watershed\nfrom skimage.io import imsave\n\n\nfrom tqdm import tqdm\nfrom PIL import Image\nimport torch\nimport torch.nn.functional as F","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#path = Path('../input/cropped-imges-and-masks')\n#hide\n#Path.BASE_PATH = path\ndf= pd.read_csv('../input/cropped-imges-and-masks-without-val-leak/crops_with_ships.csv')\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Tratamiento del CSV\nComo podemos observar, en el CSV va a aparecer una entrada por barco, por lo tanto, cuando aparece más de un barco en la misma imagen, ésta aparecerá tantas veces en el csv como barcos contenga. Vamos a agrupar todos los barcos de la imagen en una sola entrada, agrupando por ImageId y uniendo encoded pixels con un espacio de separación. Además de ello, para mayor facilidad en el entrenamiento posterior añadiremos un nuevo campo que llamaremos \"has_ship\" que vale 1 en caso de contener barcos y 0 en caso de no contenerlos. https://blog.softhints.com/python-detect-prevent-typeerror/","metadata":{}},{"cell_type":"code","source":"#Para el segmentador nos quedamos sólo con las imagenes con barcos\ndf.drop(df[df['has_ships'] == False].index, inplace=True)\ndf.head()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef image_open(img_path):\n    return np.array(Image.open(img_path))\n\ndef apply_mask(image,mask):\n    imax,jmax=mask.shape\n    image_masked=np.copy(image)\n    for i in range(imax):\n        for j in range(jmax):\n            if mask[i,j]==1:\n                image_masked[i,j,[0,0]]=170\n    return image_masked\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mascara= image_open('../input/airbus-ship-detection/train_v2/00003e153.jpg')\nmascara.reshape(3,768,768)\nnp.shape(mascara)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Segmentacion\n","metadata":{"trusted":true}},{"cell_type":"code","source":"class Dice(Metric):\n    \"Dice coefficient metric for binary target in segmentation\"\n    def __init__(self, axis=1): self.axis = axis\n    def reset(self): self.inter,self.union = 0,0\n    def accumulate(self, learn):\n        pred,targ = flatten_check(learn.pred.argmax(dim=self.axis), learn.y)\n        pred, targ = TensorBase(pred), TensorBase(targ)\n        self.inter += (pred*targ).float().sum().item()\n        self.union += (pred+targ).float().sum().item()\n\n    @property\n    def value(self): return 2. * self.inter/self.union if self.union > 0 else None\n\ndef IoU(input, target):\n    \"\"\"Intersection over Union (IoU) metric.\"\"\"\n    input = input.argmax(dim=1).float()\n    target = target.squeeze(1).float()\n    \n    smooth = 1.\n    intersection = (input * target).sum()\n    union = (input + target).sum() - intersection\n    return (intersection + smooth) / (union + smooth)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import albumentations\n\nclass AlbumentationsTransform(RandTransform):\n    \"A transform handler for multiple `Albumentation` transforms\"\n    split_idx,order=None,2\n    def __init__(self, train_aug, valid_aug): store_attr()\n    \n    def before_call(self, b, split_idx):\n        self.idx = split_idx\n    \n    def encodes(self, img: PILImage):\n        if self.idx == 0:\n            aug_img = self.train_aug(image=np.array(img))['image']\n        else:\n            aug_img = self.valid_aug(image=np.array(img))['image']\n        return PILImage.create(aug_img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_train_aug(): return albumentations.Compose([\n            albumentations.Resize(256,256),\n            albumentations.Transpose(p=0.5),\n            albumentations.VerticalFlip(p=0.5),\n            albumentations.ShiftScaleRotate(p=0.5),\n])\ndef get_valid_aug(): return albumentations.Compose([\n    \n    albumentations.Resize(256,256)\n], p=1.)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"item_tfms =  AlbumentationsTransform(get_train_aug(), get_valid_aug())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Función de perdidas\n","metadata":{}},{"cell_type":"code","source":"def get_y(r): \n    fname=r.stem\n    mascara= image_open(os.path.join('../input/cropped-imges-and-masks-without-val-leak/masks','{0}.tif'.format(fname)))\n    barcos=mascara[:,:,0]/255\n    bordes=2*(mascara[:,:,1]/255)\n    return barcos+bordes\n    \n#cambiar item_tfms para hacer data augmentation\n#dblock = DataBlock(blocks=(ImageBlock,MaskBlock), get_x=get_x, get_y=get_y, item_tfms=Resize(256))\n#dsets = dblock.datasets(masks_df)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fnames=[]\nfor index, row in tqdm(df.iterrows()):\n    fnames.append(Path(os.path.join('../input/cropped-imges-and-masks-without-val-leak/crops',row['img_name'])))\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#dls=dblock.dataloaders(masks_df,bs=32)\n#mult=1.0, do_flip=True, flip_vert=False, max_rotate=10.0, min_zoom=1.0, max_zoom=1.1, max_lighting=0.2, max_warp=0.2, p_affine=0.75, p_lighting=0.75, xtra_tfms=None, size=None, mode='bilinear', pad_mode='reflection', align_corners=True, batch=False, min_scale=1.0\ndls = SegmentationDataLoaders.from_label_func(\n    \"crops\", bs=32, fnames = fnames, label_func = get_y,splitter=RandomSplitter(), batch_tfms=[*aug_transforms(mult=2,flip_vert=True,max_warp=0)])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls.show_batch(max_n=8,unique=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FocalLossFlat(CrossEntropyLossFlat):\n    \"\"\"\n    Same as CrossEntropyLossFlat but with focal paramter, `gamma`. Focal loss is introduced by Lin et al.\n    https://arxiv.org/pdf/1708.02002.pdf. Note the class weighting factor in the paper, alpha, can be\n    implemented through pytorch `weight` argument in nn.CrossEntropyLoss.\n    \"\"\"\n    y_int = True\n    @use_kwargs_dict(keep=True, weight=None, ignore_index=-100, reduction='mean')\n    def __init__(self, *args, gamma=2, axis=-1, **kwargs):\n        self.gamma = gamma\n        self.reduce = kwargs.pop('reduction') if 'reduction' in kwargs else 'mean'\n        super().__init__(*args, reduction='none', axis=axis, **kwargs)\n    def __call__(self, inp, targ, **kwargs):\n        ce_loss = super().__call__(inp, targ, **kwargs)\n        pt = torch.exp(-ce_loss)\n        fl_loss = (1-pt)**self.gamma * ce_loss\n        return fl_loss.mean() if self.reduce == 'mean' else fl_loss.sum() if self.reduce == 'sum' else fl_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nlearn= unet_learner(dls,resnet18,n_out=3, metrics=[Dice()],lr=1e-2,loss_func=FocalLossFlat(axis=1))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.fine_tune(90,freeze_epochs=4)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.save('Unet_resnet16_seg_cropped_256_bs32_3channels_wo_leakage_80epochs')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.recorder.plot_loss()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.show_results(max_n=20,figsize=(15,15))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls crops/models","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}