{"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":"markdown","source":"# HuBMAP - Efficient Sampling Baseline (deepflash2, pytorch, fastai) [sub]\n\n> Submission kernel for model trained with efficient region based sampling. \n\n# Acknowledgements\n\n- Train Notebook: https://www.kaggle.com/matjes/hubmap-efficient-sampling-deepflash2-train\n- Sampling Notebook: https://www.kaggle.com/matjes/hubmap-labels-pdf-0-5-0-25-0-01\n- Original Inference Notebook: https://www.kaggle.com/matjes/hubmap-efficient-sampling-deepflash2-sub\n\nRequires deepflash2 (git version), zarr, and segmentation-models-pytorch","metadata":{"id":"OcsetTMwKXqC"}},{"cell_type":"code","source":"#!ls ../input/d/khoongweihao/","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls ../input/","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Installation and package loading","metadata":{}},{"cell_type":"code","source":"# Install deepflash2 and dependencies\nimport sys\nsys.path.append(\"../input/zarrkaggleinstall\")\nsys.path.append(\"../input/segmentation-models-pytorch-install\")\n!pip install -q --no-deps ../input/deepflash2-lfs\nimport cv2, torch, zarr, tifffile, pandas as pd, gc\nfrom fastai.vision.all import *\nfrom deepflash2.all import *\nimport deepflash2.tta as tta\nimport segmentation_models_pytorch as smp","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Helper functions and patches","metadata":{}},{"cell_type":"code","source":"#https://www.kaggle.com/bguberfain/memory-aware-rle-encoding\n#with transposed mask\ndef rle_encode_less_memory(img):\n    #the image should be transposed\n    pixels = img.T.flatten()\n    \n    # This simplified method requires first and last pixel to be zero\n    pixels[0] = 0\n    pixels[-1] = 0\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    runs[1::2] -= runs[::2]\n    \n    return ' '.join(str(x) for x in runs)\n\ndef load_model_weights(model, file, strict=True):\n    state = torch.load(file, map_location='cpu')\n    stats = state['stats']\n    model_state = state['model']\n    model.load_state_dict(model_state, strict=strict)\n    return model, stats","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Patches for deepflash2 classes, see https://fastcore.fast.ai/basics.html#patch","metadata":{}},{"cell_type":"code","source":"# https://matjesg.github.io/deepflash2/data.html#BaseDataset\n# Handling of different input shapes\n@patch\ndef read_img(self:BaseDataset, *args, **kwargs):\n    image = tifffile.imread(args[0])\n    if len(image.shape) == 5:\n        image = image.squeeze().transpose(1, 2, 0)\n    elif image.shape[0] == 3:\n        image = image.transpose(1, 2, 0)\n    return image\n\n# https://matjesg.github.io/deepflash2/data.html#DeformationField\n# Adding normalization (divide by 255)\n@patch\ndef apply(self:DeformationField, data, offset=(0, 0), pad=(0, 0), order=1):\n    \"Apply deformation field to image using interpolation\"\n    outshape = tuple(int(s - p) for (s, p) in zip(self.shape, pad))\n    coords = [np.squeeze(d).astype('float32').reshape(*outshape) for d in self.get(offset, pad)]\n    # Get slices to avoid loading all data (.zarr files)\n    sl = []\n    for i in range(len(coords)):\n        cmin, cmax = int(coords[i].min()), int(coords[i].max())\n        dmax = data.shape[i]\n        if cmin<0: \n            cmax = max(-cmin, cmax)\n            cmin = 0 \n        elif cmax>dmax:\n            cmin = min(cmin, 2*dmax-cmax)\n            cmax = dmax\n            coords[i] -= cmin\n        else: coords[i] -= cmin\n        sl.append(slice(cmin, cmax))    \n    if len(data.shape) == len(self.shape) + 1:\n        \n        ## Channel order change in V12\n        tile = np.empty((*outshape, data.shape[-1]))\n        for c in range(data.shape[-1]):\n            # Adding divide\n            tile[..., c] = cv2.remap(data[sl[0],sl[1], c]/255, coords[1],coords[0], interpolation=order, borderMode=cv2.BORDER_REFLECT)\n    else:\n        tile = cv2.remap(data[sl[0], sl[1]], coords[1], coords[0], interpolation=order, borderMode=cv2.BORDER_REFLECT)\n    return tile","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Configuration","metadata":{}},{"cell_type":"code","source":"class CONFIG():\n    \n    # data paths\n    data_path = Path('../input/hubmap-kidney-segmentation')\n    model_file1 = '../input/hubmap-deepflash-weights/unet_efficientnet-b0-morph-iou0.9015-dice0.9482.pth'\n    model_file2 = '../input/hubmap-deepflash-weights/unet_efficientnet-b2-morph-iou0.8993-dice0.9470.pth'\n    model_file3 = '../input/hubmap-deepflash-weights/unet_efficientnet-b5-morph-iou0.9021-dice0.9485.pth'\n    model_file4 = '../input/hubmap-deepflash-weights/unet_timm-resnest101e_iou0.9051_dice0.9502.pth'\n    model_file5 = '../input/hubmap-efficient-sampling-deepflash2-train/unet_efficientnet-b4.pth'\n    model_file6 = '../input/hubmap-deepflash-weights/unet_efficientnet-b1-morph-iou0.8981-dice0.9463.pth'\n    model_file7 = '../input/hubmap-deepflash-weights/unet_efficientnet-b3-morph-iou0.9038-dice0.9494.pth'\n    \n    # deepflash2 dataset (https://matjesg.github.io/deepflash2/data.html#TileDataset)\n    scale = 3 # zoom facor (zoom out)\n    tile_shape = (512, 512)\n    padding = (100,100) # Border overlap for prediction\n\n    # pytorch model (https://github.com/qubvel/segmentation_models.pytorch)\n    encoder_name1 = \"efficientnet-b0\"\n    encoder_name2 = \"efficientnet-b2\"\n    encoder_name3 = \"efficientnet-b5\"\n    encoder_name4 = \"timm-resnest101e\"\n    encoder_name5 = \"efficientnet-b4\"\n    encoder_name6 = \"efficientnet-b1\"\n    encoder_name7 = \"efficientnet-b3\"\n    encoder_weights = None\n    in_channels = 3\n    classes = 2\n    \n    # dataloader \n    batch_size = 16 #16\n    \n    # prediction threshold\n    threshold = 0.4\n    \ncfg = CONFIG()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sample submissions for ids\ndf_sample = pd.read_csv(cfg.data_path/'sample_submission.csv',  index_col='id')\n\n# Model (see https://github.com/qubvel/segmentation_models.pytorch)\nmodel1 = smp.Unet(encoder_name=cfg.encoder_name1, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel2 = smp.Unet(encoder_name=cfg.encoder_name2, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel3 = smp.Unet(encoder_name=cfg.encoder_name3, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel4 = smp.Unet(encoder_name=cfg.encoder_name4, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel5 = smp.Unet(encoder_name=cfg.encoder_name5, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel6 = smp.Unet(encoder_name=cfg.encoder_name6, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel7 = smp.Unet(encoder_name=cfg.encoder_name7, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel1, stats1 = load_model_weights(model1, cfg.model_file1)\nmodel2, stats2 = load_model_weights(model2, cfg.model_file2)\nmodel3, stats3 = load_model_weights(model3, cfg.model_file3)\nmodel4, stats4 = load_model_weights(model4, cfg.model_file4)\nmodel5, stats5 = load_model_weights(model5, cfg.model_file5)\nmodel6, stats6 = load_model_weights(model6, cfg.model_file6)\nmodel7, stats7 = load_model_weights(model7, cfg.model_file7)\nbatch_tfms1 = [Normalize.from_stats(*stats1)]\nbatch_tfms2 = [Normalize.from_stats(*stats2)]\nbatch_tfms3 = [Normalize.from_stats(*stats3)]\nbatch_tfms4 = [Normalize.from_stats(*stats4)]\nbatch_tfms5 = [Normalize.from_stats(*stats5)]\nbatch_tfms6 = [Normalize.from_stats(*stats6)]\nbatch_tfms7 = [Normalize.from_stats(*stats7)]\n\nmodels = [\n    (model1, batch_tfms1),\n    (model2, batch_tfms2),\n    (model3, batch_tfms3),\n    #(model4, batch_tfms4),\n    (model5, batch_tfms5),\n    (model6, batch_tfms6),\n    (model7, batch_tfms7),\n]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(models))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#!ls ../input/d/khoongweihao","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Prediction","metadata":{}},{"cell_type":"code","source":"names,preds = [],[]\n\n\nfor idx, _ in df_sample.iterrows():\n    print(f'###### File {idx} ######')\n    f = cfg.data_path/'test'/f'{idx}.tiff'\n    \n    # Create deepflash2 dataset (including tiling and file conversion)\n    ds = TileDataset([f], scale=cfg.scale, tile_shape=cfg.tile_shape, padding=cfg.padding)\n    shape = ds.data[f.name].shape\n    print('Shape:', shape)\n    \n    msk = None\n    \n    for i, m in enumerate(models):\n        model = m[0]\n        batch_tfms = m[1]\n        \n        # Create fastai dataloader and learner\n        dls = DataLoaders.from_dsets(ds, batch_size=cfg.batch_size, after_batch=batch_tfms, shuffle=False, drop_last=False)\n        if torch.cuda.is_available(): dls.cuda(), model.cuda()\n        learn = Learner(dls, model, loss_func='')\n\n        # Predict tiles, see https://matjesg.github.io/deepflash2/learner.html#Learner.predict_tiles\n        print('Prediction')\n        res = learn.predict_tiles(\n            dl=dls.train, \n            path='/kaggle/temp/', \n            n_times=2,\n            use_tta=True, \n            tta_merge='mean',\n            tta_tfms=[\n                tta.HorizontalFlip(), \n                tta.Rotate90(angles=[90,180,270]),\n            ],\n            uncertainty_estimates=False\n        )\n\n        # Load mask from softmax prediction > threshold\n        th = 0.2 if idx=='d488c759a' else cfg.threshold\n        print(th)\n        \n        if i == 0:\n            msk = res[0][f.name][..., 1]/len(models)\n        else:\n            msk += res[0][f.name][..., 1]/len(models)\n        print(f'Model {i} done!')\n        \n    msk = (msk>th).astype(np.uint8)\n        \n    print('Rezising')\n    msk = cv2.resize(msk, (shape[1], shape[0]))\n    rle = rle_encode_less_memory(msk)\n    names.append(idx)\n    preds.append(rle)\n    \n    # Plot Result\n    print('Plotting')\n    #fig, ax = plt.subplots(figsize=(15,15))\n    #ax.imshow(cv2.resize(res[1][f.name][:].astype(np.uint8), (1024, 1024)))\n    #plt.show()\n\n    # Overwrite store (reduce disk usage)\n    _ = [shutil.rmtree(p, ignore_errors=True) for p in Path('/kaggle/temp/').iterdir()]\n    _ = [shutil.rmtree(p, ignore_errors=True) for p in Path('/tmp/').iterdir() if p.name.startswith('zarr')]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"df = pd.DataFrame({'id':names,'predicted':preds}).set_index('id')\ndf_sample.loc[df.index.values] = df.values  \ndf_sample.to_csv('submission.csv')\ndisplay(df_sample)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}