{"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":"This notebook is to infer <a href=https://www.kaggle.com/c/sartorius-cell-instance-segmentation>Sartorius Competition</a>\n\n- Train my notebook: https://www.kaggle.com/yoshikuwano/sartorius-tf-train-tfrecords\n- Image : png images -> ndarray -> tfrecord (split into tiles)\n\nRef. https://www.kaggle.com/wrrosa/hubmap-tf-with-tpu-efficientunet-512x512-subm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"<a class='anchor' id='TOC'></a>\n# Table of Contents\n\n1. [Packages](#1)\n1. [Accelarator](#2)\n1. [Parameters](#3)\n1. [Input Data](#4)\n1. [Generate TFRecord (test data)](#5)\n1. [Model](#6)\n1. [Predict](#7)","metadata":{"execution":{"iopub.status.busy":"2021-11-04T08:24:43.094872Z","iopub.execute_input":"2021-11-04T08:24:43.095844Z","iopub.status.idle":"2021-11-04T08:24:43.122676Z","shell.execute_reply.started":"2021-11-04T08:24:43.095697Z","shell.execute_reply":"2021-11-04T08:24:43.121368Z"}}},{"cell_type":"markdown","source":"<a class='anchor' id='1'></a>\n# 1. Packages\n[Back to Table of Contents](#TOC)","metadata":{"execution":{"iopub.status.busy":"2021-11-04T08:24:57.373574Z","iopub.execute_input":"2021-11-04T08:24:57.374062Z","iopub.status.idle":"2021-11-04T08:24:57.379337Z","shell.execute_reply.started":"2021-11-04T08:24:57.374014Z","shell.execute_reply":"2021-11-04T08:24:57.37851Z"}}},{"cell_type":"code","source":"import os, glob, gc, re, yaml, json, pathlib\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport matplotlib.gridspec as gridspec\nimport cv2\nimport tensorflow as tf\nimport rasterio\nfrom rasterio.windows import Window\nfrom datetime import datetime\nfrom pprint import pprint\nfrom tqdm.notebook import tqdm\n\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:42:18.243822Z","iopub.execute_input":"2021-11-10T00:42:18.24417Z","iopub.status.idle":"2021-11-10T00:42:22.577487Z","shell.execute_reply.started":"2021-11-10T00:42:18.244092Z","shell.execute_reply":"2021-11-10T00:42:22.57675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='2'></a>\n# 2. Accelarator\n[Back to Table of Contents](#TOC)","metadata":{"execution":{"iopub.status.busy":"2021-11-05T05:10:00.089711Z","iopub.execute_input":"2021-11-05T05:10:00.090467Z","iopub.status.idle":"2021-11-05T05:10:00.094992Z","shell.execute_reply.started":"2021-11-05T05:10:00.09043Z","shell.execute_reply":"2021-11-05T05:10:00.093977Z"}}},{"cell_type":"code","source":"def set_strategy():\n    try:\n        tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    except ValueError:\n        tpu = None\n        gpus = tf.config.experimental.list_logical_devices(\"GPU\")\n\n    if tpu:\n        strategy = tf.distribute.TPUStrategy(tpu)\n        print('Running on TPU ', tpu.cluster_spec().as_dict()['worker'])\n    elif len(gpus) > 1:\n        strategy = tf.distribute.MirroredStrategy([gpu.name for gpu in gpus])\n        print('Running on multiple GPUs ', [gpu.name for gpu in gpus])\n    elif len(gpus) == 1:\n        strategy = tf.distribute.get_strategy() # default strategy that works on CPU and single GPU\n        print('Running on single GPU ', gpus[0].name)\n    else:\n        strategy = tf.distribute.get_strategy() # default strategy that works on CPU and single GPU\n        print('Running on CPU')\n\n    print(\"Number of accelerators: \", strategy.num_replicas_in_sync)\n\n    return strategy\n\nstrategy = set_strategy()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:42:22.579106Z","iopub.execute_input":"2021-11-10T00:42:22.579329Z","iopub.status.idle":"2021-11-10T00:42:24.684446Z","shell.execute_reply.started":"2021-11-10T00:42:22.579297Z","shell.execute_reply":"2021-11-10T00:42:24.68319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='3'></a>\n# 3. Parameters\n[Back to Table of Contents](#TOC)","metadata":{}},{"cell_type":"code","source":"# Paths\nINPUT_PATH = '../input/sartorius-cell-instance-segmentation/'\nTRAIN_PATH = '../input/sartorius-train-unet/'\n\n# Trained models\nMODEL_PATH = TRAIN_PATH + 'model/'\nprint(f'Model path: {MODEL_PATH}')\nprint(f'Number of models(folds): {len(glob.glob(MODEL_PATH + \"model-fold*\"))}\\n')\n    \n# Load model metrics\nwith open(MODEL_PATH + 'metrics.json') as json_file:\n    M = json.load(json_file)\nprint('Model metrics:')\nprint(f'  train IoU: {round(M[\"mean_train_iou_coef\"], 4)}')\nprint(f'  valid IoU: {round(M[\"mean_valid_iou_coef\"], 4)}\\n')\n\n# Load lodel parameters\nprint('Model paramaters:')\nwith open(MODEL_PATH + 'params.yaml') as file:\n    P = yaml.load(file, Loader=yaml.FullLoader)\n    pprint(P)\n    \n# Parameters for prediction \nTHRESHOLD = 0.5 # When the predicte pixel value > THRESHOLD, the pixel is judged as MASK\nCHECKSUM = True # Compute the sum of mask pixel, orn not\nMIN_SIZE = 5 # Minimum size of predicted mask which regarded as a object\nMAX_SIZE = 500 # Maximum size of predicted mask which regarded as a object\nAUTO = tf.data.experimental.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2021-11-10T01:03:46.185851Z","iopub.execute_input":"2021-11-10T01:03:46.186133Z","iopub.status.idle":"2021-11-10T01:03:46.206895Z","shell.execute_reply.started":"2021-11-10T01:03:46.186103Z","shell.execute_reply":"2021-11-10T01:03:46.206195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='4'></a>\n# 4. Input Data\n[Back to Table of Contents](#TOC)","metadata":{}},{"cell_type":"markdown","source":"## Sample submission","metadata":{}},{"cell_type":"code","source":"sample_subm = pd.read_csv(INPUT_PATH + 'sample_submission.csv')\ndisplay(sample_subm)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:42.476533Z","iopub.execute_input":"2021-11-10T00:43:42.477155Z","iopub.status.idle":"2021-11-10T00:43:42.5062Z","shell.execute_reply.started":"2021-11-10T00:43:42.477118Z","shell.execute_reply":"2021-11-10T00:43:42.505436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image data","metadata":{}},{"cell_type":"code","source":"test_imgs = INPUT_PATH + 'test/' + sample_subm['id'] + '.png'\nprint(f'test images: {len(test_imgs)} files')\ndisplay(test_imgs)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:42.926488Z","iopub.execute_input":"2021-11-10T00:43:42.926875Z","iopub.status.idle":"2021-11-10T00:43:42.936775Z","shell.execute_reply.started":"2021-11-10T00:43:42.926842Z","shell.execute_reply":"2021-11-10T00:43:42.935983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gs = gridspec.GridSpec(1, 3)\nplt.figure(figsize = (25, 20))\nfor i in range(3):\n    img = cv2.imread(test_imgs[i])\n    img_id = test_imgs[i].split('/')[-1].split('.')[0]\n    ax = plt.subplot(gs[i])\n    ax.set_title(f'id: {img_id}')\n    ax.imshow(img)\n    ax.set_aspect('equal')\n    plt.axis('on')   \n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:43.319124Z","iopub.execute_input":"2021-11-10T00:43:43.31979Z","iopub.status.idle":"2021-11-10T00:43:44.034595Z","shell.execute_reply.started":"2021-11-10T00:43:43.319755Z","shell.execute_reply":"2021-11-10T00:43:44.033866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='5'></a>\n# 5. Generate TFRecord\n[Back to Table of Contents](#TOC)","metadata":{"execution":{"iopub.status.busy":"2021-11-05T02:16:54.828323Z","iopub.execute_input":"2021-11-05T02:16:54.828692Z","iopub.status.idle":"2021-11-05T02:16:54.836183Z","shell.execute_reply.started":"2021-11-05T02:16:54.828658Z","shell.execute_reply":"2021-11-05T02:16:54.834861Z"}}},{"cell_type":"code","source":"# Cast datatypes into 1 of the type lists (integer,float and bytes)\ndef _bytes_feature(value):\n    \"\"\"Returns a bytes_list from a string / byte.\"\"\"\n    if isinstance(value, type(tf.constant(0))):\n        value = value.numpy() # BytesList won't unpack a string from an EagerTensor.\n    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n\ndef _float_feature(value):\n    \"\"\"Returns a float_list from a float / double.\"\"\"\n    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n\ndef _int64_feature(value):\n    \"\"\"Returns an int64_list from a bool / enum / int / uint.\"\"\"\n    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))\n\n# Make grid of tiles \ndef make_grid(shape, window=256, min_overlap=32):\n    \"\"\"\n    return: (N,4)-array, \n    N is number of tiles,\n    4 is slice points: (x1, x2, y1, y2), where (x1,y1) is left-up point, (x2, y2) is right-bottom point.\n    \"\"\"\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    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    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)\n\n# Serialization\ndef serialize_example_test(image, x1, y1):\n    feature = {\n        'image': _bytes_feature(image), # tile image\n        'x1': _int64_feature(x1), # x left-up point of tile\n        'y1': _int64_feature(y1)  # y left-up point of tile\n    }\n    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n    return example_proto.SerializeToString()\n\n# Count images in a tfrecord file\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(f).group(1)) for f in filenames]\n    return np.sum(n)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:44.035945Z","iopub.execute_input":"2021-11-10T00:43:44.036168Z","iopub.status.idle":"2021-11-10T00:43:44.053802Z","shell.execute_reply.started":"2021-11-10T00:43:44.036139Z","shell.execute_reply":"2021-11-10T00:43:44.053074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Config of tiles\nBASE = P['BASE_TILE'][0] # Base tile size (Not original image size)\nRESIZE = P['RESIZED_TILE'][0] # Re-sized tile size\nreduce = BASE//RESIZE # Reduce base image size\nMIN_OVERLAP = P['MIN_OVERLAP'] # Overlap width of each tile (Note: Edge image may overlap more than MIN_OVERLAP)\n\n# Path to save tfrecords\nTFREC_PATH = f'./tfrec-{len(sample_subm)}-data_{RESIZE}x{RESIZE}-tile/'\nP['DATASET'] = TFREC_PATH\nif not os.path.exists(TFREC_PATH):\n    os.mkdir(TFREC_PATH)\n    os.mkdir(TFREC_PATH + 'test')\n\n# For statistics\nidentity = rasterio.Affine(1, 0, 0,\n                           0, 1, 0)\n\npath = pathlib.Path(INPUT_PATH + 'test')\nfor i, filename in tqdm(enumerate(path.glob('*.png')), total = len(list(path.glob('*.png')))):\n    \n    dataset = rasterio.open(filename.as_posix(), transform=identity)\n    slices = make_grid(dataset.shape, window=BASE, min_overlap=MIN_OVERLAP)\n    tfrec_filepath = TFREC_PATH + f'/{filename.stem}.tfrec'\n    writer = tf.io.TFRecordWriter(tfrec_filepath) \n    cnt = 0\n    for (x1, x2, y1, y2) in slices:\n        image = dataset.read(window=Window.from_slices((x1,x2), (y1,y2))) # Shape: (color(3), BASE, BASE)\n        image = np.moveaxis(image, 0, -1) # Shape: (BASE, BASE, color(3))\n        image = cv2.resize(image, (RESIZE, RESIZE), interpolation = cv2.INTER_AREA) # Shape: (RESIZE, RESIZE, color(3))\n        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n        # Create tf.Example\n        example = serialize_example_test(image.tobytes(), x1, y1)\n        writer.write(example)\n        cnt+=1\n        \n    writer.close()\n    del writer\n    \n    os.rename(tfrec_filepath, TFREC_PATH + f'/{filename.stem}-{cnt}.tfrec')\n    print(f'Generate TFRecord: {filename.stem + \"-\" + str(cnt) + \".tfrec\"}')\n    gc.collect();\n\nprint(f'Successfully all completed and Saved in {TFREC_PATH}\\n')\n\ntest_tfrecs = glob.glob(TFREC_PATH + '*.tfrec')\nprint(f'Number of TFRecord files: {len(test_tfrecs)}')\nprint(f'Number of total tiles: {count_data_items(test_tfrecs)}')","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:44.252062Z","iopub.execute_input":"2021-11-10T00:43:44.252287Z","iopub.status.idle":"2021-11-10T00:43:45.134029Z","shell.execute_reply.started":"2021-11-10T00:43:44.252259Z","shell.execute_reply":"2021-11-10T00:43:45.133298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from skimage.segmentation import mark_boundaries\n\nDIM = RESIZE\ndef _parse_image(example_proto):\n    image_feature = {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'x1': tf.io.FixedLenFeature([], tf.int64),\n        'y1': tf.io.FixedLenFeature([], tf.int64)\n    }\n    single_example = tf.io.parse_single_example(example_proto, image_feature)\n    image = tf.reshape(tf.io.decode_raw(single_example['image'], out_type=np.dtype('uint8')),\n                       (DIM, DIM, 3))\n    x1 = single_example['x1']\n    y1 = single_example['y1']\n    return image, x1, y1\n\ndef load_dataset(filenames, ordered=True):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(_parse_image)\n    return dataset\n\ndef get_dataset(filename, n):\n    dataset = load_dataset(filename)\n    dataset  = dataset.batch(n)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\n# Confirm 3 tfrecords \nfor idx in range(3):\n    num_tiles = count_data_items([test_tfrecs[idx]])\n    for imgs, x1, y1 in get_dataset(test_tfrecs[idx], num_tiles).take(1):\n        break\n    print(f'{idx+1}. Sample image: {test_tfrecs[idx].split(\"/\")[-1]}')\n    print(f'image shape: {imgs.shape}')\n    \n    gs = gridspec.GridSpec(6, num_tiles//6)\n    plt.figure(figsize = (8, 6))\n    for i in range(num_tiles):\n        ax1 = plt.subplot(gs[i])\n        ax1.set_xticklabels([])\n        ax1.set_yticklabels([])\n        ax1.set_aspect('equal')\n        ax1.set_axis_off()\n        ax1.imshow(imgs[i])\n    plt.subplots_adjust(wspace=0.02, hspace=0.02)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:45.135793Z","iopub.execute_input":"2021-11-10T00:43:45.136259Z","iopub.status.idle":"2021-11-10T00:43:52.421069Z","shell.execute_reply.started":"2021-11-10T00:43:45.136223Z","shell.execute_reply":"2021-11-10T00:43:52.42037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='6'></a>\n# 6. Model\n[Back to Table of Contents](#TOC)","metadata":{"execution":{"iopub.status.busy":"2021-11-05T05:02:57.752648Z","iopub.execute_input":"2021-11-05T05:02:57.752946Z","iopub.status.idle":"2021-11-05T05:02:57.759082Z","shell.execute_reply.started":"2021-11-05T05:02:57.752915Z","shell.execute_reply":"2021-11-05T05:02:57.757888Z"}}},{"cell_type":"code","source":"from tensorflow.keras import backend as K\n\n# Metric\ndef iou_coef(y_true, y_pred, smooth=1):\n    intersection = K.sum(K.abs(y_true * y_pred), axis=[1,2,3])\n    union = K.sum(y_true,[1,2,3]) + K.sum(y_pred,[1,2,3]) - intersection\n    iou = K.mean((intersection + smooth) / (union + smooth), axis=0)\n    return iou\n\n# Loss\ndef bce_dice_loss(y_true, y_pred):\n    \n    def dice_loss(y_true, y_pred):\n        smooth = 1.\n        y_true_f = K.flatten(y_true)\n        y_pred_f = K.flatten(y_pred)\n        intersection = y_true_f * y_pred_f\n        score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n        return 1. - score\n    \n    bce_loss_ = tf.keras.losses.binary_crossentropy(tf.cast(y_true, tf.float32), y_pred)\n    dice_loss_ = dice_loss(tf.cast(y_true, tf.float32), y_pred)\n    return bce_loss_ * 0.5 + dice_loss_*0.5\n\n\n# Load model\nmodel_paths = sorted(glob.glob(MODEL_PATH + 'model-fold*' ))\nmodels = []\nfor model_path in tqdm(model_paths, total=len(model_paths)):\n    print(f'Model loading from {model_path}')\n    with strategy.scope():\n        model = tf.keras.models.load_model(model_path,\n                                           custom_objects = {'iou_coef'     : iou_coef,\n                                                             'bce_dice_loss': bce_dice_loss})\n    models.append(model)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:43:52.422538Z","iopub.execute_input":"2021-11-10T00:43:52.423667Z","iopub.status.idle":"2021-11-10T00:45:51.885077Z","shell.execute_reply.started":"2021-11-10T00:43:52.423624Z","shell.execute_reply":"2021-11-10T00:45:51.884433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a class='anchor' id='7'></a>\n# 7. Predict\n[Back to Table of Contents](#TOC)","metadata":{"execution":{"iopub.status.busy":"2021-11-05T05:03:46.714991Z","iopub.execute_input":"2021-11-05T05:03:46.715644Z","iopub.status.idle":"2021-11-05T05:03:46.722202Z","shell.execute_reply.started":"2021-11-05T05:03:46.715588Z","shell.execute_reply":"2021-11-05T05:03:46.720889Z"}}},{"cell_type":"code","source":"# RLE Encoder: ndarry -> string\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    returns: run length as string\n    '''\n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n# RLE Decoder: string -> ndarry\ndef rle_decode(annotation, shape):\n    '''\n    annotation: string\n    shape: (height, width)\n    return: ndarray, mask: 1, background: 0\n    '''\n    rle = annotation.split() # Even elements are starts, odd elements are the lengths.\n    starts  = np.asarray(rle[0:][::2], dtype=int)\n    lengths = np.asarray(rle[1:][::2], dtype=int)\n    starts -= 1 # Run-length start is numbered from one, on the other hand, list is numbered from zero.\n    ends = starts + lengths\n    \n    mask = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for start, end in zip(starts, ends):\n        mask[start:end] = 1\n\n    return mask.reshape(shape)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:45:51.888977Z","iopub.execute_input":"2021-11-10T00:45:51.891666Z","iopub.status.idle":"2021-11-10T00:45:51.90469Z","shell.execute_reply.started":"2021-11-10T00:45:51.891625Z","shell.execute_reply":"2021-11-10T00:45:51.903805Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = pathlib.Path(INPUT_PATH + 'test/')\n\npredictions = {}\nfor i, filename in tqdm(enumerate(path.glob('*.png')), total = len(list(path.glob('*.png')))):\n    dataset = rasterio.open(filename.as_posix(), transform=identity)\n    test_tfrec = glob.glob(TFREC_PATH + filename.stem + '*.tfrec')[0]\n    num_tiles = count_data_items([test_tfrec])\n    print(f'{i+1}. Predicting {test_tfrec.split(\"/\")[-1]}')\n        \n    # Predict per tile\n    with strategy.scope():\n        for fold, model in enumerate(models):\n            if fold == 0:\n                pred_tile  = model.predict(get_dataset(filename=test_tfrec, n=num_tiles))\n            else:\n                pred_tile += model.predict(get_dataset(filename=test_tfrec, n=num_tiles))\n    # Average over folds and Resize\n    pred_tile = pred_tile/len(models)\n    pred_tile = tf.image.resize(pred_tile, (BASE,BASE))\n    # Mask probability is converted to boolean by THRESHOLD \n    pred_tile = tf.cast(pred_tile>THRESHOLD, tf.bool).numpy().squeeze() # shape: (#tiles, BASE, BASE)\n\n    # Put together tiles to be the shape of original image\n    pred_together = np.zeros(dataset.shape, dtype=np.uint8)    \n    idx = 0\n    for img, X1, Y1 in get_dataset(filename=test_tfrec, n=num_tiles):\n        for fi in range(X1.shape[0]):\n            x1 = X1[fi].numpy()\n            y1 = Y1[fi].numpy()\n            pred_together[x1:(x1+BASE), y1:(y1+BASE)] += pred_tile[idx]\n            idx += 1\n\n    pred_together = (pred_together>0.5).astype(np.uint8)\n    if CHECKSUM:\n        print(f'   Checksum: {str(np.sum(pred_together))}')\n   \n    predictions[i] = {'id': filename.stem, 'predicted': rle_encode(pred_together)}\n    ","metadata":{"execution":{"iopub.status.busy":"2021-11-10T00:45:51.910239Z","iopub.execute_input":"2021-11-10T00:45:51.912702Z","iopub.status.idle":"2021-11-10T00:46:11.312646Z","shell.execute_reply.started":"2021-11-10T00:45:51.912609Z","shell.execute_reply":"2021-11-10T00:46:11.311811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split the mask of semantic segmention into instance segments\ndef instance_segment(mask, min_size=MIN_SIZE, max_size=MAX_SIZE):\n    num_component, component, stats, centroids = cv2.connectedComponentsWithStats(mask)\n    instance_segments = []\n    for c in range(num_component):\n        mask_pixels = (component == c)\n        mask_size = max(stats[c][cv2.CC_STAT_HEIGHT], stats[c][cv2.CC_STAT_WIDTH])\n        if (mask_size > min_size) & (mask_size < max_size): # judge object or not\n            instance_segment = np.zeros((520, 704), np.float32)\n            instance_segment[mask_pixels] = 1\n            instance_segments.append(instance_segment)\n    return instance_segments\n\n\nsubmission = {}\ncount_all_instances = 0\nfor pred in predictions.values():\n    id_ = pred['id']\n    pred_segmentation = rle_decode(pred['predicted'], (520, 704))\n    \n    # Split into instance segments\n    pred_instances = instance_segment(pred_segmentation)\n    pred_rles = [rle_encode(pred_instance) for pred_instance in pred_instances]\n    count_instances_per_img = 0\n    for pred_rle in pred_rles:\n        submission[count_all_instances] = {'id': id_, 'predicted': pred_rle}\n        count_instances_per_img += 1\n        count_all_instances += 1\n    \n    print(f'ID: {id_},   Number of instances: {count_instances_per_img}')","metadata":{"execution":{"iopub.status.busy":"2021-11-10T01:09:22.639237Z","iopub.execute_input":"2021-11-10T01:09:22.639522Z","iopub.status.idle":"2021-11-10T01:09:22.789944Z","shell.execute_reply.started":"2021-11-10T01:09:22.639478Z","shell.execute_reply":"2021-11-10T01:09:22.789171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.DataFrame.from_dict(submission, orient='index').to_csv('submission.csv', index=False)\ndisplay(pd.read_csv('submission.csv'))","metadata":{"execution":{"iopub.status.busy":"2021-11-10T01:09:38.09117Z","iopub.execute_input":"2021-11-10T01:09:38.091432Z","iopub.status.idle":"2021-11-10T01:09:38.112926Z","shell.execute_reply.started":"2021-11-10T01:09:38.091402Z","shell.execute_reply":"2021-11-10T01:09:38.112271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Confirm image and predicted mask","metadata":{}},{"cell_type":"code","source":"# Build mask image from all annotations with same id\ndef build_masks(annotations, shape, distinguish_objects=False):\n    '''\n    annotation_list: List[string]\n    shape: (height, width)\n    return: ndarray, mask: integer 1,2,3,..., background:0\n    '''\n    masks = np.zeros(shape, dtype=np.uint8)\n    for i, annotation in enumerate(annotations):\n        mask = rle_decode(annotation, shape)\n        if distinguish_objects:\n            masks = np.where(mask==0, masks, i+1)\n        else:\n            masks = np.where(mask==0, masks, 1)\n    \n    return masks\n\nfrom PIL import Image, ImageEnhance\ndef plot_image_and_mask(img, mask, title=None):\n      \n    fig, ax = plt.subplots(1, 4, figsize=(20,4))\n    \n    ax[0].set_title('Original image')\n    ax[0].imshow(img)\n    \n    ax[1].set_title('High contrasted img')\n    img_hc = img.max() - img\n    img_hc = np.asarray(ImageEnhance.Contrast(Image.fromarray(img_hc)).enhance(24))\n    ax[1].imshow(img_hc)\n       \n    ax[2].set_title('Prediction')\n    ax[2].imshow(mask, cmap='inferno')\n    \n    ax[3].set_title('Image + Prediction')\n    mask_ = np.tile(np.expand_dims(mask, 2), 3) # shape: (height, width) -> (height, width, 3) \n    mask_ = np.clip(mask_, 0, 1)*255 # mask: (255,255,255), background: (0,0,0)\n    mask_[:,:,2] = 0 # mask: (255,255,0): yellow\n    mask_ = mask_.astype(np.uint8) # type: np.uint16 -> np.unit8\n    merge_img_mask = cv2.addWeighted(img_hc, 0.80, mask_, 0.20, gamma=0.0)\n    ax[3].imshow(merge_img_mask)\n    \n    fig.suptitle(title, fontsize=14)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T01:09:38.605631Z","iopub.execute_input":"2021-11-10T01:09:38.606145Z","iopub.status.idle":"2021-11-10T01:09:38.618731Z","shell.execute_reply.started":"2021-11-10T01:09:38.606108Z","shell.execute_reply":"2021-11-10T01:09:38.617589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_subm = pd.read_csv('submission.csv').sort_values('id')\n# Anotations are grouped by id\ndf_tmp = df_subm.drop_duplicates('id').reset_index(drop=True)\ndf_tmp[\"predicted\"] = df_subm.groupby('id')['predicted'].agg(list).reset_index(drop=True)\ndf_subm = df_tmp.copy()\nfor i in range(3):\n    img_id = df_subm.loc[i, 'id']\n    img = cv2.imread(INPUT_PATH + 'test/' + img_id + '.png')\n    mask = build_masks(annotations = df_subm.loc[i, 'predicted'],\n                       shape=(520, 704),\n                       distinguish_objects=True)\n    plot_image_and_mask(img, mask, title=f'id: {img_id}')","metadata":{"execution":{"iopub.status.busy":"2021-11-10T01:09:38.79398Z","iopub.execute_input":"2021-11-10T01:09:38.794196Z","iopub.status.idle":"2021-11-10T01:09:41.307349Z","shell.execute_reply.started":"2021-11-10T01:09:38.794171Z","shell.execute_reply":"2021-11-10T01:09:41.30657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}