{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Setups and Imports"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"%%capture\n!pip install \"../input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl\"\n!pip install \"../input/hpapytorchzoozip/pytorch_zoo-master\"\n!pip install \"../input/hpacellsegmentatormaster/HPA-Cell-Segmentation-master\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nprint(tf.__version__)\n\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.models import *\nimport tensorflow_addons as tfa\n\n# Ref: https://www.tensorflow.org/guide/gpu\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n  try:\n    # Currently, memory growth needs to be the same across GPUs\n    for gpu in gpus:\n      tf.config.experimental.set_memory_growth(gpu, True)\n    logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n    print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n  except RuntimeError as e:\n    # Memory growth must be set before GPUs have been initialized\n    print(e)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport re\nimport cv2\nimport glob\nimport imageio\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nfrom skimage.transform import resize\n\n%matplotlib inline\n\n# HPA Segmentation tool related imports\nimport hpacellseg.cellsegmentator as cellsegmentator\nfrom hpacellseg.utils import label_cell, label_nuclei\n\n# Imports for encoding binary masks\nimport base64\nfrom pycocotools import _mask as coco_mask\nimport typing as t\nimport zlib","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Hyperparameters"},{"metadata":{"trusted":true},"cell_type":"code","source":"WORKING_DIR_PATH = '../input/hpa-single-cell-image-classification/'\n\nIMG_WIDTH = 224\nIMG_HEIGHT = 224\nIMG_SIZES = [1728, 2048, 3072] # available image sizes.\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\nLABELS = {\n0: \"Nucleoplasm\",\n1: \"Nuclear membrane\",\n2: \"Nucleoli\",\n3: \"Nucleoli fibrillar center\",\n4: \"Nuclear speckles\",\n5: \"Nuclear bodies\",\n6: \"Endoplasmic reticulum\",\n7: \"Golgi apparatus\",\n8: \"Intermediate filaments\",\n9: \"Actin filaments\",\n10: \"Microtubules\",\n11: \"Mitotic spindle\",\n12: \"Centrosome\",\n13: \"Plasma membrane\",\n14: \"Mitochondria\",\n15: \"Aggresome\",\n16: \"Cytosol\",\n17: \"Vesicles and punctate cytosolic patterns\",\n18: \"Negative\"\n}\n\nPRED_THRESHOLD = 0.1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prepare Dataloader"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_submission = pd.read_csv(WORKING_DIR_PATH+'sample_submission.csv')\ndf_submission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def decode_image(img):\n    # convert the compressed string to a 3D uint8 tensor\n    img = tf.image.decode_png(img, channels=1)\n    # Normalize image\n    img = tf.image.convert_image_dtype(img, dtype=tf.float32)\n\n    return img\n\ndef parse_data(df_dict):\n    # Get image names\n    mt = WORKING_DIR_PATH+'test/'+df_dict['ID']+'_red.png'\n    er = WORKING_DIR_PATH+'test/'+df_dict['ID']+'_yellow.png'\n    nu = WORKING_DIR_PATH+'test/'+df_dict['ID']+'_blue.png'\n    \n    protein = WORKING_DIR_PATH+'test/'+df_dict['ID']+'_green.png'\n    \n    # Red channel\n    red = tf.io.read_file(mt)\n    red = decode_image(red)\n    # Green channel\n    green = tf.io.read_file(protein)\n    green = decode_image(green)\n    # Blue channel\n    blue = tf.io.read_file(nu)\n    blue = decode_image(blue)\n    \n    # Stack channels to make image\n    image_for_classification = tf.experimental.numpy.dstack((red, green, blue))\n    # resize image for classification as per trained model requirement\n    image_for_classification = tf.image.resize(image_for_classification, [IMG_HEIGHT, IMG_WIDTH])\n    \n    return {'id': df_dict['ID'],\n            'mt': mt,\n            'er': er,\n            'nu': nu,\n            'image': image_for_classification}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Consume submission CSV file\ntest_ds = tf.data.Dataset.from_tensor_slices(dict(df_submission))\n\n# Test Dataset\ntest_ds = (\n    test_ds\n    .map(parse_data, num_parallel_calls=AUTOTUNE)\n    .batch(1)\n    .prefetch(AUTOTUNE)\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualize a sample"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_data_dict = next(iter(test_ds))\nplt.imshow(tf.squeeze(test_data_dict['image']));","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Segmentation Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"NUC_MODEL = \"../input/hpacellsegmentatormodelweights/dpn_unet_nuclei_v1.pth\"\nCELL_MODEL = \"../input/hpacellsegmentatormodelweights/dpn_unet_cell_3ch_v1.pth\"\n\nsegmentator = cellsegmentator.CellSegmentator(\n    NUC_MODEL,\n    CELL_MODEL,\n    scale_factor=0.25,\n    device=\"cuda\",\n    padding=True,\n    multi_channel_model=True,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Classification Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"MODEL_PATH = '../input/hpa-models/effnet_multi_label_1.h5'\n\ntf.keras.backend.clear_session()\nclassifier = tf.keras.models.load_model(MODEL_PATH)\nclassifier.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Utilities"},{"metadata":{"trusted":true},"cell_type":"code","source":"def encode_binary_mask(mask, mask_val):\n  \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n  mask = np.where(mask==mask_val, True, False)\n  \n  # check input mask --\n  if mask.dtype != np.bool:\n    raise ValueError(\n        \"encode_binary_mask expects a binary mask, received dtype == %s\" %\n        mask.dtype)\n\n  mask = np.squeeze(mask)\n  if len(mask.shape) != 2:\n    raise ValueError(\n        \"encode_binary_mask expects a 2d mask, received shape == %s\" %\n        mask.shape)\n\n  # convert input mask to expected COCO API input --\n  mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1)\n  mask_to_encode = mask_to_encode.astype(np.uint8)\n  mask_to_encode = np.asfortranarray(mask_to_encode)\n\n  # RLE encode mask --\n  encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n\n  # compress and base64 encoding --\n  binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n  base64_str = base64.b64encode(binary_str)\n  return base64_str","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Run Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Returns true if the nucleus of the cell is on the edge of the image.\ndef is_border_nuclei(contour_points):\n    unique_points = np.unique(contour_points)\n    # basically if any point is 0 that means its touching the edge of the image.\n    if 0 in unique_points:\n        return True\n    return False\n\ndef clean_nuclei_mask_vals(nuclei_mask):\n    nuclei = np.unique(nuclei_mask)\n    \n    nuclei_list = []\n    \n    for nucleus in nuclei:\n        # get inidivual nucleus mask\n        nucleus_mask = np.where(nuclei_mask==nucleus, 1,0).astype('uint8')\n        \n        # get contour for cell and nucleus\n        nucleus_cnts, _ = cv2.findContours(nucleus_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n        \n        if not is_border_nuclei(nucleus_cnts[0]): # If not touching the boundary\n            nuclei_list.append(nucleus)\n        \n    return nuclei_list","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for data_dict in tqdm(test_ds):\n    # Perform segmentation\n    # For nuclei segmentation only blue channel is required.\n    nuc_segmentation = segmentator.pred_nuclei([data_dict['nu'].numpy()[0].decode('UTF8')])\n    # For full cells all the three reference(except green) channels are required.\n    cell_segmentation = segmentator.pred_cells([[data_dict['mt'].numpy()[0].decode('UTF8')], \n                                                [data_dict['er'].numpy()[0].decode('UTF8')], \n                                                [data_dict['nu'].numpy()[0].decode('UTF8')]])\n    # Get cell mask\n    nuclei_mask, cell_mask = label_cell(nuc_segmentation[0], cell_segmentation[0])\n    \n    # Unique cell ids.\n    cells = np.unique(cell_mask)\n    # Get the unique nucleus ids not bordering the image.\n    nuclei = clean_nuclei_mask_vals(nuclei_mask)\n        \n    # Perform classification - **Image level classification**\n    preds = classifier.predict(data_dict['image'])[0]\n    \n    # Post process prediction scores\n    threshold_mask = tf.math.greater(preds, [PRED_THRESHOLD])\n    threshold_mask = tf.where(threshold_mask, x=preds[0], y=[0])\n    vals, idxs = tf.math.top_k(threshold_mask, k=4)\n    \n    prediction_id = \"\"\n    # post process\n    for mask_val in cells[1:]:\n        # Get rle encoded mask\n        rle = encode_binary_mask(cell_mask, mask_val)\n        \n        # The cell with its nucleus bordering the image is discarded.\n        if mask_val in nuclei:\n            # **Assign same image level prediction to each segmented cell**\n            if len(vals)==0: # no label greater than the PRED_THRESHOLD\n                prediction_id += f\"0 1 {rle.decode('utf-8')} \" # assigning label 0 (most probable class) with confidence 1\n            else:\n                for val, idx in zip(vals, idxs): # multi-labeling\n                    if val != 0.:\n                        prediction_id += f\"{idx} {val} {rle.decode('utf-8')} \"            \n\n    # Replace PredictionString in-place\n    df_submission.loc[df_submission['ID'] == data_dict['id'], 'PredictionString'] = prediction_id","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_submission.to_csv(\"/kaggle/working/submission.csv\", index=False)\ndf_submission.head()","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}