{"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":"### 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 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_file = '../input/hubmap-efficient-sampling-deepflash2-train/unet_efficientnet-b4.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_name = \"efficientnet-b4\"\n    encoder_weights = None\n    in_channels = 3\n    classes = 2\n    \n    # dataloader \n    batch_size = 16\n    \n    # prediction threshold\n    threshold = 0.5\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)\nmodel = smp.Unet(encoder_name=cfg.encoder_name, \n                 encoder_weights=cfg.encoder_weights, \n                 in_channels=cfg.in_channels, \n                 classes=cfg.classes)\nmodel, stats = load_model_weights(model, cfg.model_file)\nbatch_tfms = [Normalize.from_stats(*stats)]","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    # 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(dl=dls.train, path='/kaggle/temp/', use_tta=False, uncertainty_estimates=False)\n    \n    # Load mask from softmax prediction > threshold\n    msk = (res[0][f.name][..., 1]>cfg.threshold).astype(np.uint8)\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=(12,12))\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":[]}]}