{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"from fastai.vision.all import *\nimport gc","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datapath = Path(\"/kaggle/input/hubmap-kidney-segmentation/\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_files = get_files(datapath/'train', extensions=['.tiff'])\ntest_img_files = get_files(datapath/'test', extensions=['.tiff'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_map = dict(zip(img_files.map(lambda o:o.name), ['train']*len(img_files)))\ntest_map = dict(zip(test_img_files.map(lambda o:o.name), ['test']*len(img_files)))\ntrn_test_map = {**trn_map, **test_map}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_ids = img_files.map(lambda o: o.stem.split(\"_\")[0]).unique(); unique_ids","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(datapath/'train.csv')\nmeta_df = pd.read_csv(datapath/'HuBMAP-20-dataset_information.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"meta_df['split'] = meta_df['image_file'].map(trn_test_map)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"meta_df.sort_values('patient_number')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"meta_df.groupby(['patient_number','image_file', 'split'])[['split']].count()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Utils"},{"metadata":{"trusted":true},"cell_type":"code","source":"def enc2mask(encs, shape):\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for m,enc in enumerate(encs):\n        if isinstance(enc,np.float) and np.isnan(enc): continue\n        s = enc.split()\n        for i in range(len(s)//2):\n            start = int(s[2*i]) - 1\n            length = int(s[2*i+1])\n            img[start:start+length] = 1 + m\n    return img.reshape(shape).T\n\ndef rle_encode_less_memory(img):\n    pixels = img.T.flatten()\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    return ' '.join(str(x) for x in runs)\n\ndef make_grid(shape, window=1024, min_overlap=32):\n    \"\"\"\n        Return Array of size (N,4), where N - number of tiles,\n        2nd axis represente slices: x1,x2,y1,y2 \n    \"\"\"\n#     import pdb; pdb.set_trace()\n    x, y = shape\n    nx = x // (window - min_overlap) + 1\n    x1 = np.linspace(0, x, num=nx, endpoint=False, dtype=np.int64)\n    x1[-1] = x - window\n    x2 = (x1 + window).clip(0, x)\n    \n    ny = y // (window - min_overlap) + 1\n    y1 = np.linspace(0, y, num=ny, endpoint=False, dtype=np.int64)\n    y1[-1] = y - window\n    y2 = (y1 + window).clip(0, y)\n    \n    slices = np.zeros((nx,ny, 4), dtype=np.int64)\n    \n    for i in range(nx):\n        for j in range(ny):\n            slices[i,j] = x1[i], x2[i], y1[j], y2[j]    \n    return slices.reshape(nx*ny,4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Datasets"},{"metadata":{"trusted":true},"cell_type":"code","source":"import rasterio\nfrom rasterio.windows import Window\nimport cv2\n\nWINDOW = 1536\nMIN_OVERLAP = 128\nNEW_SIZE = 512","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# image datasets\nidentity = rasterio.Affine(1, 0, 0, 0, 1, 0)\nid2dataset = {_id : rasterio.open(datapath/'train'/f\"{_id}.tiff\", transform=identity) for _id in unique_ids}\n\n# image masks\nid2rle = dict(zip(train_df['id'], train_df['encoding']))\nid2mask = {_id:enc2mask([rle], id2dataset[_id].shape[::-1]) for _id,rle in id2rle.items()}\n\n# (dataset id, slices array)\nid_slices = []\nfor _id, dataset in id2dataset.items():\n    slices = make_grid(dataset.shape, window=WINDOW, min_overlap=MIN_OVERLAP)\n    id_slices += list(zip([_id]*len(slices), slices))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(id2mask['2f6ecfcdf'][7000:8500, 15000:16500])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = id2dataset['2f6ecfcdf'].read([1,2,3], window=Window.from_slices((7000,8500), (15000,16500)))\nTensorImage(tensor(image)).show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"id_slices[:10], len(id_slices)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# tfms\ndef read_tile(i, id_slices):\n    _id, (x1,x2,y1,y2) = id_slices[i]\n    image = id2dataset[_id].read([1,2,3], window=Window.from_slices((x1,x2),(y1,y2)))\n    image = np.moveaxis(image, 0, -1)\n    image = cv2.resize(image, (NEW_SIZE, NEW_SIZE),interpolation = cv2.INTER_AREA)\n    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n    image = (image2tensor(image)/255.)\n    return TensorImage(image)\n\n\ndef read_mask(i, id_slices):\n    _id, (x1,x2,y1,y2) = id_slices[i]\n    mask = id2mask[_id][x1:x2, y1:y2]\n    mask = cv2.resize(mask, (NEW_SIZE, NEW_SIZE), interpolation=cv2.INTER_NEAREST)\n    return TensorMask(mask)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"items = range(len(id_slices))\ndsets = Datasets(items, \n                 tfms=[[partial(read_tile, id_slices=id_slices)], \n                       [partial(read_mask, id_slices=id_slices)]]\n                )\nlen(dsets)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dls = dsets.dataloaders(bs=4,     \n                        batch_tfms=[Dihedral(p=0.5), \n                            Rotate(p=0.5, max_deg=30), \n                            Brightness(p=0.5, max_lighting=0.3, batch=False)],\n                        splits=RandomSplitter(0.1)(items))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"xb,yb = dls.one_batch()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"xb.shape, yb.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# https://github.com/fastai/fastai/issues/3041\ndef flatten_check(inp, targ):\n    \"Check that `out` and `targ` have the same number of elements and flatten them.\"\n    inp,targ = inp.contiguous().view(-1),targ.contiguous().view(-1)\n    test_eq(len(inp), len(targ))\n    return inp,targ\n    \nclass Dice(Metric):\n    \"Dice coefficient metric for binary target in segmentation\"\n    def __init__(self, thresh=0.5): store_attr()\n    def reset(self): self.inter,self.union = 0,0\n    def accumulate(self, learn):\n        pred,targ = flatten_check(learn.pred.sigmoid().squeeze(1)>self.thresh, 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","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here I preferred softmax since using argmax is easier than setting a threhsold after sigmoid. To my knowledge and the papers I have seen in medical domain says that ImageNet transfer learning help close to nothing, so I will ignore it here to keep things clean and simple."},{"metadata":{"trusted":true},"cell_type":"code","source":"loss_func = BCEWithLogitsLossFlat()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sqrmom=0.99\nmom=0.95\nbeta=0.\neps=1e-4\nopt_func = partial(ranger, mom=mom, sqr_mom=sqrmom, eps=eps, beta=beta)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learner = unet_learner(dls,\n                       xresnet34,\n                       loss_func=loss_func,\n                       opt_func=opt_func,\n                       metrics=[Dice(thresh=0.5)], \n                       normalize=False, \n                       pretrained=False,\n                       n_out=1)\nlearner.to_native_fp16(); # little bit faster compared to fp_16() - thanks to ilovescience's experiments","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learner.fit_flat_cos(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%debug","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ds = rasterio.open(\"/kaggle/input/hubmap-kidney-segmentation/train/cb2d976f4.tiff\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ds.read([1,2,3], window=((0,100), (0,100)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for FOLD in range(8):\n    dls = get_dls(FOLD)\n    learner = get_learner(dls)\n    learner.fit_flat_cos(30, lr=1e-3, cbs=[SaveModelCallback(\"dice\", fname=f'xresunet34_fold{FOLD}')])\n    del learner\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true,"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":false},"cell_type":"code","source":"","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":4}