{"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":"## IMPORTS","metadata":{}},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nfrom sklearn.model_selection import train_test_split\n\nfrom PIL import Image\n\nimport numpy as np\nfrom skimage import color\nimport cv2\nimport os\nimport glob\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport time\nimport gc\nimport zipfile\nimport io\nimport itertools\nfrom tqdm.notebook import tqdm_notebook\nfrom concurrent.futures import ThreadPoolExecutor","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-09T09:02:18.659294Z","iopub.execute_input":"2022-08-09T09:02:18.660000Z","iopub.status.idle":"2022-08-09T09:02:23.197620Z","shell.execute_reply.started":"2022-08-09T09:02:18.659946Z","shell.execute_reply":"2022-08-09T09:02:23.195996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DATA LOADING","metadata":{}},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/hubmap-organ-segmentation\"\ntrain_df = pd.read_csv(os.path.join(BASE_PATH, \"train.csv\"))\ntest_df = pd.read_csv(os.path.join(BASE_PATH, \"test.csv\"))\nsubmission_df = pd.read_csv(os.path.join(BASE_PATH, \"sample_submission.csv\"))\ntrain_labels = glob.glob(os.path.join(BASE_PATH,\"train_annotations/*.json\"), recursive=True)\ntrain_images = glob.glob(os.path.join(BASE_PATH, \"train_images/*.tiff\"), recursive=True)\ntrain_label_map = dict((int(x[:-5].rsplit(\"/\",1)[-1]),x) for x in train_labels)\ntrain_image_map = dict((int(x[:-5].rsplit(\"/\",1)[-1]),x) for x in train_images)\ntest_images = glob.glob(os.path.join(BASE_PATH, \"test_images/*.tiff\"), recursive=True)\ntest_image_map = dict((int(x[:-5].rsplit(\"/\",1)[-1]),x) for x in test_images)\n\n# https://www.kaggle.com/code/dschettler8845/eda-hubmap-hpa-organ-segmentation\ndef rgb2hex(rgb_tuple):\n    r,g,b = rgb_tuple\n    def clamp(x): \n        return max(0, min(x, 255))\n    return \"#{0:02x}{1:02x}{2:02x}\".format(clamp(r), clamp(g), clamp(b))\nORGANS = ['kidney', 'largeintestine', 'lung', 'prostate', 'spleen']\n_COLOURS = [(230, 0, 73), (11, 180, 255), (80, 233, 145), (230, 216, 0), (155, 25, 245)]\nO2C_MAP = {_o:_c for _o,_c in zip(ORGANS, _COLOURS)}\nO2C_HEX_MAP = {_o:rgb2hex(_c) for _o,_c in O2C_MAP.items()}","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:23.200013Z","iopub.execute_input":"2022-08-09T09:02:23.200699Z","iopub.status.idle":"2022-08-09T09:02:23.707836Z","shell.execute_reply.started":"2022-08-09T09:02:23.200661Z","shell.execute_reply":"2022-08-09T09:02:23.706402Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def populate_df(df, image_path_map, label_path_map):\n    df[\"image_path\"] = df[\"id\"].map(image_path_map)\n    df[\"label_path\"] = df[\"id\"].map(label_path_map)\n    df[\"age\"] = df[\"age\"]/100.0\n    df[\"sex\"] = df[\"sex\"].map({\"Male\":0, \"Female\":1})\n    df = df[[\"id\", \"organ\", \"data_source\", \"image_path\", \"label_path\", \"img_height\", \"img_width\", \"pixel_size\", \"tissue_thickness\", \"rle\", \"age\", \"sex\"]]\n    return df","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:23.709682Z","iopub.execute_input":"2022-08-09T09:02:23.710410Z","iopub.status.idle":"2022-08-09T09:02:23.718867Z","shell.execute_reply.started":"2022-08-09T09:02:23.710368Z","shell.execute_reply":"2022-08-09T09:02:23.717027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"POPULATING ....\\n\")\ndf = populate_df(train_df, train_image_map, train_label_map)\ndisplay(df.head())","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:23.721911Z","iopub.execute_input":"2022-08-09T09:02:23.722986Z","iopub.status.idle":"2022-08-09T09:02:23.777368Z","shell.execute_reply.started":"2022-08-09T09:02:23.722936Z","shell.execute_reply":"2022-08-09T09:02:23.775622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## HELPER FUNCTIONS","metadata":{}},{"cell_type":"code","source":"def rle_decode(mask_rle, shape, color=1):\n    s = np.array(mask_rle.split(), dtype=int)\n    start, lengths = s[0::2]-1, s[1::2]\n    ends = start + lengths\n    \n    # The image image is actually flattened since RLE is a 1D \"run\"\n    if len(shape)==3:\n        h, w, d = shape\n        img = np.zeros((h * w, d), dtype=np.float32)\n    else:\n        h, w = shape\n        img = np.zeros((h * w,), dtype=np.float32)\n    \n    for lo,hi in zip(start,ends):\n        img[lo:hi] = color\n    \n    return img.reshape(shape).T\n\n# https://www.kaggle.com/namgalielei/which-reshape-is-used-in-rle\ndef rle_decode_top_to_bot_first(mask_rle, shape):\n    \"\"\" TBD\n    \n    Args:\n        mask_rle (str): run-length as string formated (start length)\n        shape (tuple of ints): (height,width) of array to return \n    \n    Returns:\n        Mask (np.array)\n            - 1 indicating mask\n            - 0 indicating background\n\n    \"\"\"\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[1], shape[0]), order='F').T  # Reshape from top -> bottom first\n\n# ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        run length as string formated\n    \"\"\"\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1 #compares where pixels[i]!=pixels[i-1]\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef flatten_l_o_l(nested_list):\n    \"\"\" Flatten a list of lists \"\"\"\n    return [item for sublist in nested_list for item in sublist]\n\ndef load_json_to_dict(json_path):\n    \"\"\" tbd \"\"\"\n    with open(json_path) as json_file:\n        data = json.load(json_file)\n    return data\n\ndef load_tiff(img_path, to_numpy=False, to_rgb=False):\n    img = tf.io.read_file(img_path)\n    img = tfio.experimental.image.decode_tiff(img)\n    \n    # Optionals\n    if to_rgb: img = tfio.experimental.color.rgba_to_rgb(img)\n    if to_numpy: img = img.numpy()\n        \n    return img\n\ndef load_image_mask(row, color=1):\n    image = cv2.imread(row.image_path)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    mask = rle_decode(row.rle, (row.img_width, row.img_height), color=color)\n    return image, mask, row.id","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:23.780010Z","iopub.execute_input":"2022-08-09T09:02:23.780449Z","iopub.status.idle":"2022-08-09T09:02:23.801579Z","shell.execute_reply.started":"2022-08-09T09:02:23.780413Z","shell.execute_reply":"2022-08-09T09:02:23.799855Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CREATING IMAGE TILES","metadata":{}},{"cell_type":"markdown","source":"We need to:\n1. Create Image and Segmentation Tiles\n2. Padding the extra part with background colors for image and segmentation tiles\n3. Augment these tiles\n4. Each tile will have an image_id, augmentation_id as well as a tile_id. This will help us uniquely identify the tile.\n5. We train a CNN using each tile as an independent sample\n6. During inference we perform segmentation at a tile level and reconstruct them back to a full segmentation mask\n7. We can use an ensemble with the global context model","metadata":{}},{"cell_type":"code","source":"IMAGE_TILES_FOLDER = \"image_tiles/\"\nSEGMENTATION_TILES_FOLDER = \"seg_tiles/\"\n\nOUT_TRAIN = 'train.zip'\nOUT_MASKS = 'masks.zip'\n\n!mkdir {IMAGE_TILES_FOLDER}\n!mkdir {SEGMENTATION_TILES_FOLDER}","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:23.804497Z","iopub.execute_input":"2022-08-09T09:02:23.805544Z","iopub.status.idle":"2022-08-09T09:02:26.118215Z","shell.execute_reply.started":"2022-08-09T09:02:23.805486Z","shell.execute_reply":"2022-08-09T09:02:26.116704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BACKGROUND_COLOR_RGB = np.array([244, 245, 245],dtype=np.uint8)","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:26.120179Z","iopub.execute_input":"2022-08-09T09:02:26.120577Z","iopub.status.idle":"2022-08-09T09:02:26.126834Z","shell.execute_reply.started":"2022-08-09T09:02:26.120538Z","shell.execute_reply":"2022-08-09T09:02:26.125915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def tile_image(im, seg, size: int = 768) -> list:\n    w = h = size\n    # https://stackoverflow.com/a/47581978/4521646\n    img_tiles = [im[i:(i + h), j:(j + w), ...] for i in range(0, im.shape[0], h) for j in range(0, im.shape[1], w)]\n    seg_tiles = [seg[i:(i + h), j:(j + w)] for i in range(0, im.shape[0], h) for j in range(0, im.shape[1], w)]\n    idxs = [(i, (i + h), j, (j + w)) for i in range(0, im.shape[0], h) for j in range(0, im.shape[1], w)]\n    tiles_img, tiles_seg = [], []\n    for k, (img_tile, seg_tile) in enumerate(zip(img_tiles,seg_tiles)):\n        if img_tile.shape[:2] != (h, w):\n            img_tile_ = img_tile\n            img_tile = np.tile(BACKGROUND_COLOR_RGB,(size,size,1))\n            img_tile[:img_tile_.shape[0], :img_tile_.shape[1], :] = img_tile_\n            \n            seg_tile_ = seg_tile\n            seg_tile = np.zeros_like(seg_tiles[0])\n            seg_tile[:seg_tile_.shape[0], :seg_tile_.shape[1]] = seg_tile_\n            \n        tiles_img.append(img_tile)\n        tiles_seg.append(seg_tile)\n    return tiles_img, tiles_seg, idxs\n\nrow = df[df[\"id\"]==12233].squeeze()\nim, seg, row_id = load_image_mask(row, color=255) \ntiles_img, tiles_seg, idxs = tile_image(im, seg, size=512)\n# tiles_seg, idxs = tile_image(\"../input/hacking-the-human-body-annotation-masks/train_binary_masks/12233.png\", \"/kaggle/temp/masks\", size=1024)","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:26.130139Z","iopub.execute_input":"2022-08-09T09:02:26.130488Z","iopub.status.idle":"2022-08-09T09:02:26.665428Z","shell.execute_reply.started":"2022-08-09T09:02:26.130457Z","shell.execute_reply":"2022-08-09T09:02:26.664120Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axes = plt.subplots(nrows=6, ncols=6, figsize=(9, 9))\nfor i, (img, seg) in enumerate(zip(tiles_img, tiles_seg)):\n    axes[i // 6, i % 6].imshow(img)\n    axes[i // 6, i % 6].imshow(seg, alpha=0.2)\n    axes[i // 6, i % 6].set_axis_off()\nfig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:26.667011Z","iopub.execute_input":"2022-08-09T09:02:26.667338Z","iopub.status.idle":"2022-08-09T09:02:30.825186Z","shell.execute_reply.started":"2022-08-09T09:02:26.667310Z","shell.execute_reply":"2022-08-09T09:02:30.824070Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### BACK RECONSTRUCTION","metadata":{}},{"cell_type":"code","source":"im = plt.imread(\"../input/hubmap-organ-segmentation/train_images/12233.tiff\")\nimg = np.zeros(im.shape, dtype=np.uint8)\nseg = np.zeros(im.shape[:2], dtype=np.uint8)\n\nfor img_tile, seg_tile, (i1, i2, j1, j2) in zip(tiles_img, tiles_seg, idxs):\n    i2 = min(i2, im.shape[0])\n    j2 = min(j2, im.shape[1])\n    img[i1:i2, j1:j2] = img_tile[:(i2 - i1), :(j2 - j1)]\n    seg[i1:i2, j1:j2] = seg_tile[:(i2 - i1), :(j2 - j1)]\nplt.imshow(img)\nplt.imshow(seg, alpha=0.2)","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:30.826351Z","iopub.execute_input":"2022-08-09T09:02:30.826726Z","iopub.status.idle":"2022-08-09T09:02:33.158504Z","shell.execute_reply.started":"2022-08-09T09:02:30.826690Z","shell.execute_reply":"2022-08-09T09:02:33.157109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Assuming every tile is 1Kb in size, and we have 36 tiles for each image + 80 augmentations for each tile, we will have a total of ~351\\*36\\*80 kbs of data","metadata":{}},{"cell_type":"markdown","source":"## AUGMENTATIONS","metadata":{}},{"cell_type":"code","source":"def get_aug_funcs():\n    hor_trans = A.HorizontalFlip(p=1.0)\n    vert_trans = A.VerticalFlip(p=1.0)\n    rot_trans = A.RandomRotate90(p=1.0)\n    shift_trans = A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=20, p=1.0, \n                                                 border_mode=cv2.BORDER_REFLECT)\n    \n    optical_trans = A.OpticalDistortion(p=1.0)\n    grid_trans = A.GridDistortion(p=1.0)\n    piecewise_trans = A.IAAPiecewiseAffine(p=1.0)\n    \n    saturate_trans = A.HueSaturationValue(10,15,10)\n    clahe_trans = A.CLAHE(clip_limit=3)\n    brightness_trans = A.RandomBrightnessContrast()\n    \n    l1 = [hor_trans, vert_trans, rot_trans, shift_trans]\n    l2 = [optical_trans, grid_trans, piecewise_trans]\n    l3 = [saturate_trans, clahe_trans, brightness_trans]\n    combs = get_all_combinations(l1) + get_all_combinations(l2) + get_all_combinations(l3) +  list(itertools.product(*[l1,l2])) + list(itertools.product(*[l1,l3])) + list(itertools.product(*[l2,l3]))+ list(itertools.product(*[l1,l2,l3]))\n    \n    compose_arrays = [A.Compose(list(x)) for x in combs]\n    \n    return compose_arrays","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:33.161687Z","iopub.execute_input":"2022-08-09T09:02:33.162084Z","iopub.status.idle":"2022-08-09T09:02:33.172966Z","shell.execute_reply.started":"2022-08-09T09:02:33.162049Z","shell.execute_reply":"2022-08-09T09:02:33.171731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_all_combinations(arr):\n    combs = []\n    for subset in itertools.combinations(arr, 1):\n        combs.append(subset)\n    return combs","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:33.174532Z","iopub.execute_input":"2022-08-09T09:02:33.176261Z","iopub.status.idle":"2022-08-09T09:02:33.187928Z","shell.execute_reply.started":"2022-08-09T09:02:33.176201Z","shell.execute_reply":"2022-08-09T09:02:33.186823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def augment_image_mask(image, mask, transform_func, image_id):\n    im, m = transform_func(image=image, mask=mask).values()\n    return im, m, image_id","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:33.189381Z","iopub.execute_input":"2022-08-09T09:02:33.190633Z","iopub.status.idle":"2022-08-09T09:02:33.200790Z","shell.execute_reply.started":"2022-08-09T09:02:33.190580Z","shell.execute_reply":"2022-08-09T09:02:33.199538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def chunkify(iterable, chunk=5):\n    from itertools import islice\n    it = iter(iterable)\n    while True:\n        piece = list(islice(it, chunk))\n        if piece:\n            yield piece\n        else:\n            return","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:02:33.202590Z","iopub.execute_input":"2022-08-09T09:02:33.203118Z","iopub.status.idle":"2022-08-09T09:02:33.213728Z","shell.execute_reply.started":"2022-08-09T09:02:33.203070Z","shell.execute_reply":"2022-08-09T09:02:33.212527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resize_trans = A.Resize(224,224, p=1.0)","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:03:30.806900Z","iopub.execute_input":"2022-08-09T09:03:30.807363Z","iopub.status.idle":"2022-08-09T09:03:30.814249Z","shell.execute_reply.started":"2022-08-09T09:03:30.807327Z","shell.execute_reply":"2022-08-09T09:03:30.812924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"compose_arrays = get_aug_funcs()\n\nprint(f\"Each Image is being Augmented {len(compose_arrays)} times\")\n\nbase_bar = tqdm_notebook(total=df.shape[0], desc='Iterating over all images')\n# t = tqdm_notebook(total=len(compose_arrays), desc='Augmenting current image')\n\nCHUNK_SIZE = 10\n\nimg_out = zipfile.ZipFile(OUT_TRAIN, 'a')\nmask_out = zipfile.ZipFile(OUT_MASKS, 'a')\n\nfor i in range(1):\n    row = df.iloc[i].squeeze()\n    image, mask, id = load_image_mask(row) \n    images = [image]*len(compose_arrays)\n    masks = [mask]*len(compose_arrays)\n    ids = [id]*len(compose_arrays)\n    \n    # We need to chunk it since the thread process is not released until all of them are done, leading to OOM\n    with ThreadPoolExecutor() as executor:\n        for chunk_id, chunk in enumerate(chunkify(zip(images,masks, compose_arrays,ids), chunk=CHUNK_SIZE)):\n            for aug_id, (im,m,image_id) in enumerate(executor.map(lambda p: augment_image_mask(*p),chunk)):\n                img_tiles, seg_tiles, idxs = tile_image(im, m, size=512)\n                for tile_id, (img_tile, seg_tile, idx) in enumerate(zip(img_tiles, seg_tiles, idxs)):\n                    img_tile, seg_tile = resize_trans(image=img_tile,mask=seg_tile).values()\n                    img_tile = cv2.imencode('.png',cv2.cvtColor(img_tile, cv2.COLOR_RGB2BGR))[1]\n                    img_out.writestr(f'{image_id}_{aug_id+(chunk_id*CHUNK_SIZE):02d}_{tile_id:02d}.png', img_tile)\n                    seg_tile = cv2.imencode('.png',seg_tile)[1]\n                    mask_out.writestr(f'{image_id}_{aug_id+(chunk_id*CHUNK_SIZE):02d}_{tile_id:02d}.png', seg_tile)\n            gc.collect()\n        gc.collect()\n\n    base_bar.update()\n\nimg_out.close()\nmask_out.close()","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:06:05.468766Z","iopub.execute_input":"2022-08-09T09:06:05.469289Z","iopub.status.idle":"2022-08-09T09:09:00.966244Z","shell.execute_reply.started":"2022-08-09T09:06:05.469253Z","shell.execute_reply":"2022-08-09T09:09:00.964673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_id=10044\n\nimg_out = zipfile.ZipFile(OUT_TRAIN, 'r')\nmask_out = zipfile.ZipFile(OUT_MASKS, 'r')\nimage_files = img_out.namelist()\nmask_files = mask_out.namelist()\n\nnum_plots = 6\nn_group_cols = 3\nn_group_rows = num_plots//n_group_cols\nfig, axes = plt.subplots(nrows=6*n_group_rows, ncols=6*n_group_cols, figsize=(30*n_group_rows, 10*n_group_cols))\nfor plot_id,aug_id in enumerate(range(num_plots)):\n    indexes = [i for i in range(len(image_files)) if image_files[i].startswith(f\"{image_id}_{aug_id:02d}\")]\n    # samples = np.random.choice(indexes, size=50)\n\n    \n    for i,ix in enumerate(indexes):\n        img = np.array(Image.open(io.BytesIO(img_out.read(image_files[ix]))))\n        seg = np.array(Image.open(io.BytesIO(mask_out.read(mask_files[ix]))))\n        row = i//6 + (plot_id//n_group_cols)*6\n        col = i%6 + (plot_id%n_group_cols)*6\n        axes[row, col].imshow(img)\n        axes[row, col].imshow(seg, alpha=0.2)\n        axes[row, col].set_axis_off()\n    fig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2022-08-09T09:59:14.886728Z","iopub.execute_input":"2022-08-09T09:59:14.887953Z","iopub.status.idle":"2022-08-09T09:59:14.986565Z","shell.execute_reply.started":"2022-08-09T09:59:14.887837Z","shell.execute_reply":"2022-08-09T09:59:14.985260Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}