{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":24800,"datasetId":1042002,"databundleVersionId":1831594},{"sourceType":"datasetVersion","sourceId":10471582,"datasetId":6483804,"databundleVersionId":10795402},{"sourceType":"datasetVersion","sourceId":1805632,"datasetId":1070494,"databundleVersionId":1843099}],"dockerImageVersionId":30043,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport gc\nimport sys\nimport time\nimport json\nimport glob\nimport random\nfrom pathlib import Path\nimport pandas as pd\n\nfrom PIL import Image\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom imgaug import augmenters as iaa\n\nimport itertools\nfrom tqdm import tqdm\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\n\nimport warnings \nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"67a5bd00-f052-45fb-a129-96575ba2f82c","_cell_guid":"ef70dabf-bd7e-4b55-b349-6e35a9e72af9","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data preparation","metadata":{"_uuid":"62b229df-c9ce-4cde-b02e-3e30eb31a681","_cell_guid":"e0f87e74-4efa-4d74-84bb-d14029967d41","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"training_folder = \"../input/vinbigdata-chest-xray-abnormalities-detection/train/\"\ndf = pd.read_csv(\"../input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\ndf = df.query(\"class_id<14\")\ndf = df.query(\"rad_id=='R9'\")","metadata":{"_uuid":"a2038ddf-76be-4774-bb09-4c4f7e326e2b","_cell_guid":"203663e6-ef49-4b12-b7f2-62fdc769c1ce","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[\"several_issues\"] = df.duplicated(subset=['image_id'])\ndf[\"box_size\"] = [(row.y_max-row.y_min)*(row.x_max-row.x_min) for idx, row in df.iterrows()]","metadata":{"_uuid":"6abe1098-b53c-4da9-84ec-33cf6707c2ef","_cell_guid":"bd30c805-97da-47b3-8bb4-b27ee04f374e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.class_name.unique()","metadata":{"_uuid":"ef24ccdc-1fc9-4d61-b919-899d6cdeb1aa","_cell_guid":"cb83566d-cfb5-4698-ba6c-b5db28cc27c3","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.groupby(\"class_id\")[\"box_size\"].mean()","metadata":{"_uuid":"6bb9b380-740f-4ac3-82fa-57b4c34839a5","_cell_guid":"058c775b-dcb6-48f2-bb53-d6eaebc513d6","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.groupby(\"class_id\")[\"box_size\"].std()","metadata":{"_uuid":"1a10dd1c-e028-42a2-adeb-103fbe334424","_cell_guid":"74ecebdc-fc7c-490f-b971-f97593a9b716","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.groupby(\"class_id\").image_id.count()","metadata":{"_uuid":"93017b92-593c-4984-9a44-2afe31f5c568","_cell_guid":"0dbd8e84-becf-493d-933a-477a702439fb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"selected_classes = [0,3,5,7,10]\ncategory_list = [\"Aortic enlargement\", \"Cardiomegaly\", \"ILD\", \"Lung Opacity\", \"Pleural effusion\"]\nfiltered_df = df.query(\"class_id in @selected_classes\")","metadata":{"_uuid":"fa1f9f1d-1411-4ec7-af14-78640a3cc660","_cell_guid":"daf0a5dd-ab53-49d0-84ef-947da1bc63e0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"selected_classes_dict = {\"0\":0,\"3\":1,\"5\":2,\"7\":3,\"10\":4}\nfiltered_df[\"reclass_id\"] = [selected_classes_dict[str(row.class_id)] for idx, row in filtered_df.iterrows()]","metadata":{"_uuid":"670a5c06-ab2d-42ae-b012-5bdb0c1a02bc","_cell_guid":"d51652d8-c44a-492c-9a51-40609b518b8f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filtered_df","metadata":{"_uuid":"4b9d7dbf-1695-4368-9201-a1161eb20711","_cell_guid":"eba040e6-4ffe-4cbc-9b35-f2e95656f8eb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_mask(img_dimensions, x_min, y_min, x_max, y_max):\n    img_height, img_width = img_dimensions\n    img_mask = np.full((img_height,img_width),0)\n    img_mask[y_min:y_max,x_min:x_max] = 255\n    \n    return img_mask.astype(np.float32)\n\n\ndef rle_encoding(x):\n    dots = np.where(x.T.flatten() == 255)[0]\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if (b>prev+1): run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    return ' '.join([str(x) for x in run_lengths])","metadata":{"_uuid":"77a33b78-72ce-4bf6-804c-0fb42cb03823","_cell_guid":"cc4e0ba8-376b-4e30-b89e-37030189c6e8","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_xray(path, voi_lut = True, fix_monochrome = True):\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n               \n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n        \n    return data","metadata":{"_uuid":"388d0def-304b-4be1-a54e-97c387267d85","_cell_guid":"8738a943-81f1-4cbe-9c84-b71ae9f85e3c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"resized_folder = \"../working/resized_train/\"\nos.mkdir(resized_folder)","metadata":{"_uuid":"89eff296-6707-466e-bd8c-8a0852a69e2b","_cell_guid":"13e3aa92-937b-4213-8cb9-002f7d45a010","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filtered_df.groupby(\"class_id\").image_id.count()","metadata":{"_uuid":"726898a9-eda3-4708-a2a8-4379bf4b0650","_cell_guid":"dfd8e4b5-23e8-4a9e-a60b-a6433aa99335","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"balanced_filtered_df = pd.DataFrame()\nsamples_per_class = 500\nfor class_name in filtered_df.class_name.unique():\n    balanced_filtered_df = balanced_filtered_df.append(filtered_df.query(\"class_name==@class_name\")[:samples_per_class], \n                                                       ignore_index=True)","metadata":{"_uuid":"6082c9ac-070a-4a76-8c6f-bb12a84a77b6","_cell_guid":"bf32551e-b200-4b3d-af8c-671ac2e094db","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"balanced_filtered_df","metadata":{"_uuid":"fda3bb2b-618e-44fc-96b5-94523706ef24","_cell_guid":"2f9e7a93-b225-444e-943a-36250708250a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"diagnostic_per_image = []\n\nimage_size=512\nwith tqdm(total=len(balanced_filtered_df)) as pbar:\n    for idx,row in balanced_filtered_df.iterrows():\n        image_id = row.image_id\n        image_df = balanced_filtered_df.query(\"image_id==@image_id\")\n        class_list = []\n        RLE_list = []\n        \n        for diagnostic_id, diagnostic in image_df.iterrows():\n            class_list.append(diagnostic.reclass_id)\n\n            dicom_image = read_xray(training_folder+image_id+\".dicom\")\n            image_dimensions = dicom_image.shape\n            \n            resized_img = cv2.resize(dicom_image, (image_size,image_size), interpolation = cv2.INTER_AREA)\n            cv2.imwrite(resized_folder+image_id+\".jpg\", resized_img) \n            \n            mask = get_mask(image_dimensions, int(diagnostic.x_min), int(diagnostic.y_min), int(diagnostic.x_max), int(diagnostic.y_max))\n            resized_mask = cv2.resize(mask, (image_size,image_size))\n            RLE_list.append(rle_encoding(resized_mask))\n        diagnostic_per_image.append({\"image_id\":image_id,\n                                     \"CategoryId\":class_list,\n                                     \"EncodedPixels\":RLE_list})\n        pbar.update(1)","metadata":{"_uuid":"76d423ac-ec5d-40e5-bce5-a917af55bd90","_cell_guid":"589bcd41-e630-4831-9cc6-0d5ea1bab88a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"samples_df = pd.DataFrame(diagnostic_per_image)\nsamples_df[\"Height\"] = image_size\nsamples_df[\"Width\"] = image_size","metadata":{"_uuid":"8be32226-4c33-4164-bbf4-b4468f0cf5f7","_cell_guid":"af80d3a7-8d51-4ce3-b2e2-674c951e4415","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"samples_df","metadata":{"_uuid":"c2e7834f-6b07-41f1-b4f8-5e6e410980b5","_cell_guid":"0b4a7ddb-1a1a-4c99-832c-0040cd9d7645","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train a Mask-RCNN model","metadata":{"_uuid":"50d9187a-397e-423b-b118-b036ce004ec7","_cell_guid":"fefb7413-d0f1-4b4b-9388-24564b286a23","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"!cp -r ../input/maskrcnn-tf2-keras ../working/maskrcnn-tf2-keras","metadata":{"_uuid":"b6b95098-71da-4a22-840b-212506f352de","_cell_guid":"48e2ffcb-4777-4d6b-8912-84a0a8b1b797","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = Path('../working/')\nROOT_DIR = \"../../working\"\n\nNUM_CATS = len(selected_classes)\nIMAGE_SIZE = 512\nos.chdir('../working/maskrcnn-tf2-keras')\nsys.path.append(ROOT_DIR+'/maskrcnn-tf2-keras')\nfrom mrcnn.config import Config\n\nfrom mrcnn import utils\nimport mrcnn.model as modellib\nfrom mrcnn import visualize\nfrom mrcnn.model import log","metadata":{"_uuid":"6e0e3992-97ef-4279-805e-cd20c074d9e6","_cell_guid":"0052a98b-39a7-4246-9c22-d36154beb524","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"COCO_WEIGHTS_PATH = '../../input/coco-weights/mask_rcnn_coco.h5'\n\nclass DiagnosticConfig(Config):\n    NAME = \"Diagnostic\"\n    NUM_CLASSES = NUM_CATS + 1 # +1 for the background class\n    \n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 2 #That is the maximum with the memory available on kernels\n    \n    BACKBONE = 'resnet50'\n    \n    IMAGE_MIN_DIM = IMAGE_SIZE\n    IMAGE_MAX_DIM = IMAGE_SIZE    \n    IMAGE_RESIZE_MODE = 'none'\n\n    POST_NMS_ROIS_TRAINING = 250\n    POST_NMS_ROIS_INFERENCE = 150\n    MAX_GROUNDTRUTH_INSTANCES = 5\n    BACKBONE_STRIDES = [4, 8, 16, 32, 64]\n    BACKBONESHAPE = (8, 16, 24, 32, 48)\n    RPN_ANCHOR_SCALES = (8,16,24,32,48)\n    ROI_POSITIVE_RATIO = 0.33\n    DETECTION_MAX_INSTANCES = 300\n    DETECTION_MIN_CONFIDENCE = 0.7    \n    # STEPS_PER_EPOCH should be the number of instances \n    # divided by (GPU_COUNT*IMAGES_PER_GPU), and so should VALIDATION_STEPS;\n    # however, due to the time limit, I set them so that this kernel can be run in 9 hours\n    STEPS_PER_EPOCH = int(len(samples_df)*0.9/IMAGES_PER_GPU)\n    VALIDATION_STEPS = len(samples_df)-int(len(samples_df)*0.9/IMAGES_PER_GPU)\n    \nconfig = DiagnosticConfig()\nconfig.display()","metadata":{"_uuid":"a8b12c52-db73-4661-a495-78bfccd0c2f4","_cell_guid":"b142c7d8-d962-4c82-ba6c-30d609258a67","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DiagnosticDataset(utils.Dataset):\n    def __init__(self, df):\n        super().__init__(self)\n        \n        # Add classes\n        for i, name in enumerate(category_list):\n            self.add_class(\"diagnostic\", i+1, name)\n        \n        # Add images \n        for i, row in df.iterrows():\n            self.add_image(\"diagnostic\", \n                           image_id=row.name,\n                           path=\"../\"+resized_folder+str(row.image_id)+\".jpg\", \n                           labels=row['CategoryId'],\n                           annotations=row['EncodedPixels'], \n                           height=row['Height'], width=row['Width'])\n\n    def image_reference(self, image_id):\n        info = self.image_info[image_id]\n        return info['path'], [category_list[int(x)] for x in info['labels']]\n    \n    def load_image(self, image_id):\n        return cv2.imread(self.image_info[image_id]['path'])\n\n    def load_mask(self, image_id):\n        info = self.image_info[image_id]\n                \n        mask = np.zeros((IMAGE_SIZE, IMAGE_SIZE, len(info['annotations'])), dtype=np.uint8)\n        labels = []\n        \n        for m, (annotation, label) in enumerate(zip(info['annotations'], info['labels'])):\n            sub_mask = np.full(info['height']*info['width'], 0, dtype=np.uint8)\n            annotation = [int(x) for x in annotation.split(' ')]\n            \n            for i, start_pixel in enumerate(annotation[::2]):\n                sub_mask[start_pixel: start_pixel+annotation[2*i+1]] = 1\n\n            sub_mask = sub_mask.reshape((info['height'], info['width']), order='F')\n            sub_mask = cv2.resize(sub_mask, (IMAGE_SIZE, IMAGE_SIZE), interpolation=cv2.INTER_NEAREST)\n            \n            mask[:, :, m] = sub_mask\n            labels.append(int(label)+1)\n        return mask, np.array(labels)","metadata":{"_uuid":"1701ef8b-4946-4404-b1db-f91ced283e6e","_cell_guid":"c85569f7-bd90-4ca4-86b6-50659d44b39b","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"training_percentage = 0.9\n\ntraining_set_size = int(training_percentage*len(samples_df))\nvalidation_set_size = int((1-training_percentage)*len(samples_df))\n\ntrain_dataset = DiagnosticDataset(samples_df[:training_set_size])\ntrain_dataset.prepare()\n\nvalid_dataset = DiagnosticDataset(samples_df[training_set_size:training_set_size+validation_set_size])\nvalid_dataset.prepare()\n\nfor i in range(10):\n    image_id = random.choice(train_dataset.image_ids)\n    image = train_dataset.load_image(image_id)\n    mask, class_ids = train_dataset.load_mask(image_id)\n    \n    visualize.display_top_masks(image, mask, class_ids, train_dataset.class_names, limit=5)","metadata":{"_uuid":"98fe4f43-4778-4d2a-9e20-c950bef13f1c","_cell_guid":"3dadb541-95bd-4565-984a-d954e1f40260","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LR = 1e-4\nEPOCHS = [1,18]\n\nmodel = modellib.MaskRCNN(mode='training', config=config, model_dir=\"\")\nmodel.load_weights(COCO_WEIGHTS_PATH, by_name=True, exclude=['mrcnn_class_logits', 'mrcnn_bbox_fc', 'mrcnn_bbox', 'mrcnn_mask'])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\nmodel.train(train_dataset, valid_dataset,\n            learning_rate=LR,\n            epochs=EPOCHS[0],\n            layers='heads')\n\nhistory = model.keras_model.history.history","metadata":{"_uuid":"72ca45d3-58f2-4ade-8082-1812d06d34bb","_cell_guid":"d4b88868-8c5b-4b9f-825a-25e1ad4cf777","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\nmodel.train(train_dataset, valid_dataset,\n            learning_rate=LR/10,\n            epochs=EPOCHS[1],\n            layers='all')\n\nnew_history = model.keras_model.history.history\nfor k in new_history: history[k] = history[k] + new_history[k]","metadata":{"_uuid":"5125fff7-6466-441b-b611-2c8a0ad9b9c8","_cell_guid":"5e00dcec-e74a-4818-b517-39d22ef1c4ff","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = range(EPOCHS[-1])\n\nplt.figure(figsize=(18, 6))\n\nplt.subplot(131)\nplt.plot(epochs, history['loss'], label=\"train loss\")\nplt.plot(epochs, history['val_loss'], label=\"valid loss\")\nplt.legend()\nplt.subplot(132)\nplt.plot(epochs, history['mrcnn_class_loss'], label=\"train class loss\")\nplt.plot(epochs, history['val_mrcnn_class_loss'], label=\"valid class loss\")\nplt.legend()\nplt.subplot(133)\nplt.plot(epochs, history['mrcnn_mask_loss'], label=\"train mask loss\")\nplt.plot(epochs, history['val_mrcnn_mask_loss'], label=\"valid mask loss\")\nplt.legend()\n\nplt.show()","metadata":{"_uuid":"16b424a4-59ce-465e-98bc-2380d582d5b6","_cell_guid":"e24cff5b-70be-4893-8b15-d5e1f0df8c6f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_epoch = np.argmin(history[\"val_loss\"][1:]) + 1\nprint(\"Best epoch: \", best_epoch)\nprint(\"Valid loss: \", history[\"val_loss\"][1:][best_epoch-1])","metadata":{"_uuid":"2f11db08-e95d-4abc-9793-37d20fc0480e","_cell_guid":"52a25624-2c85-4da7-9bfd-e9bdb1a9ec6d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predict on test images","metadata":{"_uuid":"eb8993be-4a3b-4ec6-9cd5-a15ce5e85411","_cell_guid":"9e7c276e-60cb-4e81-b45c-4a8aee99b5b1","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"resized_test_folder = \"../../working/resized_test/\"\nos.mkdir(resized_test_folder)","metadata":{"_uuid":"8bc48150-48ed-4fa1-9b59-573bb0bcabfa","_cell_guid":"22058e15-0676-4c8c-a74a-d1beaad771c1","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class InferenceConfig(DiagnosticConfig):\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 1\n    IMAGE_MIN_DIM = IMAGE_SIZE\n    IMAGE_MAX_DIM = IMAGE_SIZE    \n    IMAGE_RESIZE_MODE = 'none'\n    DETECTION_MIN_CONFIDENCE = 0.8\n    DETECTION_NMS_THRESHOLD = 0.5\n\ninference_config = InferenceConfig()\n\nmodel = modellib.MaskRCNN(mode='inference', \n                          config=inference_config,\n                          model_dir=\"\")","metadata":{"_uuid":"440a79b3-bcd7-40a6-bf5b-0f9ffec19c71","_cell_guid":"9f129e98-102f-4d63-85d8-91b697e73f07","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"glob_list = glob.glob(f'diagnostic*/mask_rcnn_diagnostic_{best_epoch:04d}.h5')\nmodel_path = glob_list[0] if glob_list else ''\nmodel.load_weights(model_path, by_name=True)","metadata":{"_uuid":"1466e24c-f4d9-4293-8c64-7922c2f0bf7f","_cell_guid":"bdfe6dcb-e836-41c7-b00e-c8b589621cf7","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from skimage.measure import find_contours\nfrom matplotlib.patches import Polygon\n\n\n# Fix overlapping masks\ndef refine_masks(masks, rois):\n    areas = np.sum(masks.reshape(-1, masks.shape[-1]), axis=0)\n    mask_index = np.argsort(areas)\n    union_mask = np.zeros(masks.shape[:-1], dtype=bool)\n    for m in mask_index:\n        masks[:, :, m] = np.logical_and(masks[:, :, m], np.logical_not(union_mask))\n        union_mask = np.logical_or(masks[:, :, m], union_mask)\n    for m in range(masks.shape[-1]):\n        mask_pos = np.where(masks[:, :, m]==True)\n        if np.any(mask_pos):\n            y1, x1 = np.min(mask_pos, axis=1)\n            y2, x2 = np.max(mask_pos, axis=1)\n            rois[m, :] = [y1, x1, y2, x2]\n    return masks, rois\n\ndef decode_rle(rle, height, width):\n    s = 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(height*width, dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape((height, width)).T\n\ndef annotations_to_mask(annotations, height, width):\n    if isinstance(annotations, list):\n        # The annotation consists in a list of RLE codes\n        mask = np.zeros((height, width, len(annotations)))\n        for i, rle_code in enumerate(annotations):\n            mask[:, :, i] = decode_rle(rle_code, height, width)\n    else:\n        error_message = \"{} is expected to be a list or str but received {}\".format(annotation, type(annotation))\n        raise TypeError(error_message)\n    return mask\n\ndef find_anomalies(dicom_image, display=False):\n\n    image_dimensions = dicom_image.shape\n\n    resized_img = cv2.resize(dicom_image, (image_size,image_size), interpolation = cv2.INTER_AREA)\n    saved_filename = resized_test_folder+\"temp_image.jpg\"\n    cv2.imwrite(saved_filename, resized_img) \n    img = cv2.imread(saved_filename)\n\n    result = model.detect([img])\n    r = result[0]\n    \n    if r['masks'].size > 0:\n        masks = np.zeros((img.shape[0], img.shape[1], r['masks'].shape[-1]), dtype=np.uint8)\n        for m in range(r['masks'].shape[-1]):\n            masks[:, :, m] = cv2.resize(r['masks'][:, :, m].astype('uint8'), \n                                        (img.shape[1], img.shape[0]), interpolation=cv2.INTER_NEAREST)\n        \n        y_scale = image_dimensions[0]/IMAGE_SIZE\n        x_scale = image_dimensions[1]/IMAGE_SIZE\n        rois = (r['rois'] * [y_scale, x_scale, y_scale, x_scale]).astype(int)\n        \n        masks, rois = refine_masks(masks, rois)\n    else:\n        masks, rois = r['masks'], r['rois']\n        \n    if display:\n        visualize.display_instances(img, rois, masks, r['class_ids'], \n                                    ['bg']+category_list, r['scores'],\n                                    title=\"prediction\", figsize=(12, 12))\n    return rois, r['class_ids'], r['scores']","metadata":{"_uuid":"21e8dcd5-25a1-4b81-b574-59748e936dce","_cell_guid":"a205ad70-89b6-4bf6-b0b0-9f1489504330","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_folder = \"../../input/vinbigdata-chest-xray-abnormalities-detection/test/\"\ntest_file_list = os.listdir(test_folder)[:5]\n\nfor test_file in test_file_list:\n    dicom_image = read_xray(test_folder+test_file)\n    find_anomalies(dicom_image, display=True)","metadata":{"_uuid":"e52fd358-068f-407d-8210-7d872b427b97","_cell_guid":"dd872fb2-d6fb-428d-8fbd-26cbcfa42fed","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prediction and generating the submission file","metadata":{"_uuid":"315a4c95-bc8a-4b12-8c1c-4a6ce63402e1","_cell_guid":"95282a84-322a-49f4-adc7-fd7987c7bb6f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"def keep_best_cardiomegaly_box(bbox_list, class_list, confidence_list):\n    '''\n    go through the boxes and keep only one box for \n    cardiomegaly with the highest confidence score\n    '''\n    best_cardiomegaly_score = -1\n    best_cardiomegaly_bbox = []\n    clean_bbox_list, clean_class_list, clean_confidence_list = [],[],[]\n    \n    for bbox, class_id, confidence in zip(bbox_list, class_list, confidence_list):\n        #While the class number if 3 in the dataset, it is 2 in the maskrcnn training process\n        # as I have excluded some classes\n        if class_id==2:\n            if confidence>best_cardiomegaly_score:\n                best_cardiomegaly_score = confidence\n                best_cardiomegaly_bbox = bbox\n        else:\n            clean_bbox_list.append(bbox)\n            clean_class_list.append(class_id)\n            clean_confidence_list.append(confidence)\n            \n    if best_cardiomegaly_score>0:\n        clean_bbox_list.append(best_cardiomegaly_bbox)\n        clean_class_list.append(2)\n        clean_confidence_list.append(best_cardiomegaly_score)\n        \n    return clean_bbox_list, clean_class_list, clean_confidence_list","metadata":{"_uuid":"5d63b52b-beed-45ca-ba57-d6da35ad6bf2","_cell_guid":"a35d1cfb-5cc9-4b21-81c4-d88ba7008d71","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = []\ntest_file_list = os.listdir(test_folder)\nwith tqdm(total=len(test_file_list)) as pbar:\n    for image_filename in test_file_list:\n        dicom_image = read_xray(test_folder+image_filename)\n        image_dimensions = dicom_image.shape\n        bbox_list, class_list, confidence_list = find_anomalies(dicom_image, display=False)\n        prediction_string = \"\"\n        \n        if len(bbox_list)>0:\n                    \n            bbox_list, class_list, confidence_list = keep_best_cardiomegaly_box(bbox_list, class_list, confidence_list)\n            \n            for bbox, class_id, confidence in zip(bbox_list, class_list, confidence_list):\n                class_id = next(key for key, value in selected_classes_dict.items() if value == int(class_id)-1)\n                confidence_score = str(round(confidence,3))\n\n                #HACK: I had to rescale the bounding box here. For some reason,\n                #It did not do it in the prediction function.\n                y_scale = image_dimensions[0]/image_size\n                x_scale = image_dimensions[1]/image_size\n                rescaled_bbox = (bbox * [y_scale, x_scale, y_scale, x_scale]).astype(int)\n\n                #organise the bbox into xmin, ymin, xmax, ymax\n                ymin = image_dimensions[0]-rescaled_bbox[2]\n                ymax = image_dimensions[0]-rescaled_bbox[0]\n                xmin = rescaled_bbox[1]\n                xmax = rescaled_bbox[3]\n\n                prediction_string += \"{} {} {} {} {} {} \".format(class_id, confidence_score, xmin, ymin, xmax, ymax)\n            results.append({\"image_id\":image_filename.replace(\".dicom\",\"\"), \"PredictionString\":prediction_string.strip()})\n        else:\n            results.append({\"image_id\":image_filename.replace(\".dicom\",\"\"), \"PredictionString\":\"14 1.0 0 0 1 1\"})\n        pbar.update(1)","metadata":{"_uuid":"7465e934-010c-4a69-9ada-734c0d66919c","_cell_guid":"4f02a453-8e06-4a67-ad9b-201bc53ea34e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.DataFrame(results)","metadata":{"_uuid":"5ffe0ac1-94ec-4853-8166-eee74c37a08e","_cell_guid":"ce5f1693-4c19-4384-8a1d-9d6d4ff57862","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df","metadata":{"_uuid":"72648d04-e8c1-4927-9429-faa91dc472e1","_cell_guid":"c9fa325e-e746-41f5-8266-8f4b79626e8c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df.to_csv('../submission.csv', index=False)","metadata":{"_uuid":"06288a6f-6798-49c9-acb7-abdc4dd85e99","_cell_guid":"1538cb74-df64-4198-91b8-0424c142b649","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#clear all the images from the working directory\n!rm -rf ../../working/resized_train/\n!rm -rf ../../working/resized_test/","metadata":{"_uuid":"209a4b37-166a-47be-a323-849258811e23","_cell_guid":"81c4a10e-5771-4dee-a4a9-5c26018266ab","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null}]}