{"metadata":{"colab":{"name":"lesson-3-rsna-pneumonia-detection-challenge-kaggle","version":"0.3.2","provenance":[],"collapsed_sections":[]},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"accelerator":"GPU","language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":10338,"databundleVersionId":862042,"sourceType":"competition"},{"sourceId":16606520,"sourceType":"kernelVersion"}],"dockerImageVersionId":27938,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Mask-RCNN Starter Model for the RSNA Pneumonia Detection Challenge with transfer learning **\n\nUsing pre-trained COCO weights trained on http://cocodataset.org as in https://github.com/matterport/Mask_RCNN/tree/master/samples/balloon\n\nWe get the best public kernel performance so far, and also training only within the 6hrs kaggle limit.","metadata":{"id":"KBeAf8WgaeSk","_uuid":"7c1fce19a11f95416168ced03c2c70fa818b21a5"}},{"cell_type":"code","source":"import os \nimport sys\nimport random\nimport math\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nimport matplotlib\nimport json\nimport imgaug\nimport pydicom\nfrom imgaug import augmenters as iaa\nfrom tqdm import tqdm\nimport pandas as pd \nimport glob\nfrom sklearn.model_selection import KFold\nimport tensorflow as tf\nimport keras","metadata":{"id":"4kjcC6QqywWl","_uuid":"40c67b3ff0fa04587dec508363308adaa3ceaf34","execution":{"iopub.status.busy":"2024-03-07T11:12:59.357685Z","iopub.execute_input":"2024-03-07T11:12:59.358083Z","iopub.status.idle":"2024-03-07T11:12:59.365434Z","shell.execute_reply.started":"2024-03-07T11:12:59.358021Z","shell.execute_reply":"2024-03-07T11:12:59.364263Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python --version\n\nprint(\"\\nNumPy version:\", np.__version__)\nprint(\"OpenCV version:\", cv2.__version__)\nprint(\"Matplotlib version:\", matplotlib.__version__)\nprint(\"imgaug version:\", imgaug.__version__)\nprint(\"PyDicom version:\", pydicom.__version__)\nprint(\"Pandas version:\", pd.__version__)\nprint(\"Tensorflow version: \", tf.__version__)\nprint(\"Keras version: \", keras.__version__)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_DIR = '/kaggle/input/rsna-pneumonia-detection-challenge'\n# /kaggle/input/rsna-pneumonia-detection-challenge\n\n# Directory to save logs and trained model\nROOT_DIR = '/kaggle/working'","metadata":{"id":"yP0XLJx_x_6o","_uuid":"6e5764759e6a0a9b698b44645658f66873edd807","execution":{"iopub.status.busy":"2024-03-07T11:18:37.417562Z","iopub.execute_input":"2024-03-07T11:18:37.417933Z","iopub.status.idle":"2024-03-07T11:18:37.422457Z","shell.execute_reply.started":"2024-03-07T11:18:37.417889Z","shell.execute_reply":"2024-03-07T11:18:37.421230Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Install Matterport's Mask-RCNN model from github.\nSee the [Matterport's implementation of Mask-RCNN](https://github.com/matterport/Mask_RCNN).","metadata":{"id":"kdYzLq1zfKL4","_uuid":"576df4c47a23d08b1bdb384245e09aa69f88bbd3"}},{"cell_type":"code","source":"!git clone https://www.github.com/matterport/Mask_RCNN.git\nos.chdir('Mask_RCNN')\n#!python setup.py -q install","metadata":{"id":"KgllzLnDr7kF","outputId":"6c978df7-2013-437e-acd1-5011048dfb53","_uuid":"b37d22551d332f0f7b722cc7204eb614524b6c21","execution":{"iopub.status.busy":"2024-03-07T11:13:11.632283Z","iopub.execute_input":"2024-03-07T11:13:11.632667Z","iopub.status.idle":"2024-03-07T11:13:17.604190Z","shell.execute_reply.started":"2024-03-07T11:13:11.632608Z","shell.execute_reply":"2024-03-07T11:13:17.603336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import Mask RCNN\nsys.path.append(os.path.join(ROOT_DIR, 'Mask_RCNN'))  # To find local version of the library\nfrom mrcnn.config import Config\nfrom mrcnn import utils\nimport mrcnn.model as modellib\nfrom mrcnn import visualize\nfrom mrcnn.model import log\nimport mrcnn","metadata":{"id":"-KZXyWwhzOVU","outputId":"2576cc17-7484-4311-ad72-3c5643dcb5bb","_uuid":"3acbbbe055b6a409d3c50ae0f893acf51b5ae7ba","execution":{"iopub.status.busy":"2024-03-07T11:13:22.567822Z","iopub.execute_input":"2024-03-07T11:13:22.568148Z","iopub.status.idle":"2024-03-07T11:13:22.574681Z","shell.execute_reply.started":"2024-03-07T11:13:22.568095Z","shell.execute_reply":"2024-03-07T11:13:22.573610Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip list\nprint(\"\\nNumPy version:\", np.__version__)\nprint(\"OpenCV version:\", cv2.__version__)\nprint(\"Matplotlib version:\", matplotlib.__version__)\nprint(\"imgaug version:\", imgaug.__version__)\nprint(\"PyDicom version:\", pydicom.__version__)\nprint(\"Pandas version:\", pd.__version__)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dicom_dir = os.path.join(DATA_DIR, 'stage_2_train_images')\ntest_dicom_dir = os.path.join(DATA_DIR, 'stage_2_test_images')","metadata":{"id":"FghMmiMjzOX2","_uuid":"50089cc61791871cdf6a5c0037dc4f28b7b7d7cc","execution":{"iopub.status.busy":"2024-03-07T11:19:05.399201Z","iopub.execute_input":"2024-03-07T11:19:05.399553Z","iopub.status.idle":"2024-03-07T11:19:05.404372Z","shell.execute_reply.started":"2024-03-07T11:19:05.399497Z","shell.execute_reply":"2024-03-07T11:19:05.403382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Download COCO pre-trained weights","metadata":{"_uuid":"f108beef7838be8a64dd512d395c5dc0ad952790"}},{"cell_type":"code","source":"!wget --quiet https://github.com/matterport/Mask_RCNN/releases/download/v2.0/mask_rcnn_coco.h5\n!ls -lh mask_rcnn_coco.h5\n\nCOCO_WEIGHTS_PATH = \"mask_rcnn_coco.h5\"","metadata":{"_uuid":"c3ee0cd0ee0b1defdec97b94bc736587c1f7631f","execution":{"iopub.status.busy":"2024-03-07T11:13:41.425062Z","iopub.execute_input":"2024-03-07T11:13:41.425469Z","iopub.status.idle":"2024-03-07T11:13:45.250919Z","shell.execute_reply.started":"2024-03-07T11:13:41.425387Z","shell.execute_reply":"2024-03-07T11:13:45.249952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Some setup functions and classes for Mask-RCNN\n\n- dicom_fps is a list of the dicom image path and filenames \n- image_annotions is a dictionary of the annotations keyed by the filenames\n- parsing the dataset returns a list of the image filenames and the annotations dictionary","metadata":{"id":"gj-tvDvEaDiC","_uuid":"032cc5fe4baa051108106675e6ca4f4fdb2846ed"}},{"cell_type":"code","source":"def get_dicom_fps(dicom_dir):\n    dicom_fps = glob.glob(dicom_dir+'/'+'*.dcm')\n    return list(set(dicom_fps))\n\ndef parse_dataset(dicom_dir, anns): \n    image_fps = get_dicom_fps(dicom_dir)\n    image_annotations = {fp: [] for fp in image_fps}\n    for index, row in anns.iterrows(): \n        fp = os.path.join(dicom_dir, row['patientId']+'.dcm')\n        image_annotations[fp].append(row)\n    return image_fps, image_annotations ","metadata":{"id":"ivqC4cnszOaM","_uuid":"778cb19865d7cc63440491aef9202b71c61e8bb2","execution":{"iopub.status.busy":"2024-03-07T11:15:21.060587Z","iopub.execute_input":"2024-03-07T11:15:21.060899Z","iopub.status.idle":"2024-03-07T11:15:21.068644Z","shell.execute_reply.started":"2024-03-07T11:15:21.060856Z","shell.execute_reply":"2024-03-07T11:15:21.067585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# The following parameters have been selected to reduce running time for demonstration purposes \n# These are not optimal \n\nclass DetectorConfig(Config):\n    \"\"\"Configuration for training pneumonia detection on the RSNA pneumonia dataset.\n    Overrides values in the base Config class.\n    \"\"\"\n    \n    # Give the configuration a recognizable name  \n    NAME = 'pneumonia'\n    \n    # Train on 1 GPU and 8 images per GPU. We can put multiple images on each\n    # GPU because the images are small. Batch size is 8 (GPUs * images/GPU).\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 8\n    \n    BACKBONE = 'resnet50'\n    \n    NUM_CLASSES = 2  # background + 1 pneumonia classes\n    \n    IMAGE_MIN_DIM = 256\n    IMAGE_MAX_DIM = 256\n    RPN_ANCHOR_SCALES = (16, 32, 64, 128)\n    TRAIN_ROIS_PER_IMAGE = 32\n    MAX_GT_INSTANCES = 4\n    DETECTION_MAX_INSTANCES = 3\n    DETECTION_MIN_CONFIDENCE = 0.78  ## match target distribution\n    DETECTION_NMS_THRESHOLD = 0.01\n\n    STEPS_PER_EPOCH = 200\n\nconfig = DetectorConfig()\nconfig.display()","metadata":{"id":"_SfzTa-1zOck","outputId":"91ae8935-bccb-4b8e-9a7e-aa690f95fd9b","_uuid":"dfcffc4eaa94a41497717851dee9f702d8a2a73b","execution":{"iopub.status.busy":"2024-03-07T11:14:40.801917Z","iopub.execute_input":"2024-03-07T11:14:40.802256Z","iopub.status.idle":"2024-03-07T11:14:40.815220Z","shell.execute_reply.started":"2024-03-07T11:14:40.802207Z","shell.execute_reply":"2024-03-07T11:14:40.814297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DetectorDataset(utils.Dataset):\n    \"\"\"Dataset class for training pneumonia detection on the RSNA pneumonia dataset.\n    \"\"\"\n\n    def __init__(self, image_fps, image_annotations, orig_height, orig_width):\n        super().__init__(self)\n        \n        # Add classes\n        self.add_class('pneumonia', 1, 'Lung Opacity')\n        \n        # add images \n        for i, fp in enumerate(image_fps):\n            annotations = image_annotations[fp]\n            self.add_image('pneumonia', image_id=i, path=fp, \n                           annotations=annotations, orig_height=orig_height, orig_width=orig_width)\n            \n    def image_reference(self, image_id):\n        info = self.image_info[image_id]\n        return info['path']\n\n    def load_image(self, image_id):\n        info = self.image_info[image_id]\n        fp = info['path']\n        ds = pydicom.read_file(fp)\n        image = ds.pixel_array\n        # If grayscale. Convert to RGB for consistency.\n        if len(image.shape) != 3 or image.shape[2] != 3:\n            image = np.stack((image,) * 3, -1)\n        return image\n\n    def load_mask(self, image_id):\n        info = self.image_info[image_id]\n        annotations = info['annotations']\n        count = len(annotations)\n        if count == 0:\n            mask = np.zeros((info['orig_height'], info['orig_width'], 1), dtype=np.uint8)\n            class_ids = np.zeros((1,), dtype=np.int32)\n        else:\n            mask = np.zeros((info['orig_height'], info['orig_width'], count), dtype=np.uint8)\n            class_ids = np.zeros((count,), dtype=np.int32)\n            for i, a in enumerate(annotations):\n                if a['Target'] == 1:\n                    x = int(a['x'])\n                    y = int(a['y'])\n                    w = int(a['width'])\n                    h = int(a['height'])\n                    mask_instance = mask[:, :, i].copy()\n                    cv2.rectangle(mask_instance, (x, y), (x+w, y+h), 255, -1)\n                    mask[:, :, i] = mask_instance\n                    class_ids[i] = 1\n        return mask.astype(np.bool), class_ids.astype(np.int32)","metadata":{"id":"8EBVA1M60yAj","_uuid":"52bd3ffbdde0173a363055482d675da51c2aba99","execution":{"iopub.status.busy":"2024-03-07T11:19:17.127945Z","iopub.execute_input":"2024-03-07T11:19:17.128310Z","iopub.status.idle":"2024-03-07T11:19:17.150696Z","shell.execute_reply.started":"2024-03-07T11:19:17.128261Z","shell.execute_reply":"2024-03-07T11:19:17.149924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Examine the annotation data, parse the dataset, and view dicom fields","metadata":{"id":"9RlMo04ckd98","_uuid":"1cb852e262b69d348743767d675573368ab672c9"}},{"cell_type":"code","source":"# training dataset\nanns = pd.read_csv(os.path.join(DATA_DIR, 'stage_2_train_labels.csv'))\nanns.head()","metadata":{"id":"EdhUEFDr0yDA","outputId":"1715a5df-a577-41fd-bf20-f1a27aadb28c","_uuid":"793b1c6c6ba4e5f0d51e130080aa799f230b5ef6","execution":{"iopub.status.busy":"2024-03-07T11:18:45.851528Z","iopub.execute_input":"2024-03-07T11:18:45.851842Z","iopub.status.idle":"2024-03-07T11:18:45.973956Z","shell.execute_reply.started":"2024-03-07T11:18:45.851797Z","shell.execute_reply":"2024-03-07T11:18:45.973021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_fps, image_annotations = parse_dataset(train_dicom_dir, anns=anns)","metadata":{"id":"Mxz-pNbt5txY","_uuid":"7aebc88f910b232e3b8759421914a007c6ffed94","execution":{"iopub.status.busy":"2024-03-07T11:19:27.965365Z","iopub.execute_input":"2024-03-07T11:19:27.965740Z","iopub.status.idle":"2024-03-07T11:19:32.323024Z","shell.execute_reply.started":"2024-03-07T11:19:27.965688Z","shell.execute_reply":"2024-03-07T11:19:32.322141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ds = pydicom.read_file(image_fps[0]) # read dicom image from filepath \nimage = ds.pixel_array # get image array","metadata":{"id":"YPqjEIXWRhSf","_uuid":"6c386dcef041b972f6209dd19e247d547c3c349f","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# show dicom fields \nds","metadata":{"id":"81lovwF2Ro5R","outputId":"e2263fe2-1a32-432a-ec75-b9220a24e697","_uuid":"0ef68a41cf1a5e842e86a219b6392e3695004720","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Original DICOM image size: 1024 x 1024\nORIG_SIZE = 1024","metadata":{"id":"gYNSd1AhRqOV","_uuid":"74277ae9af4a3b044e62b664d10d76b23848bb43","execution":{"iopub.status.busy":"2024-03-07T11:19:37.852741Z","iopub.execute_input":"2024-03-07T11:19:37.853167Z","iopub.status.idle":"2024-03-07T11:19:37.857090Z","shell.execute_reply.started":"2024-03-07T11:19:37.853095Z","shell.execute_reply":"2024-03-07T11:19:37.856016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Split the data into training and validation datasets","metadata":{"id":"4FlRu8ML-ceg","_uuid":"6563bbca143e4bceb1ea850714d7b43bb1e1178d"}},{"cell_type":"code","source":"image_fps_list = list(image_fps)\nrandom.seed(42)\nrandom.shuffle(image_fps_list)\nval_size = 1500\nimage_fps_val = image_fps_list[:val_size]\nimage_fps_train = image_fps_list[val_size:]\n\nprint(len(image_fps_train), len(image_fps_val))\n# print(image_fps_val[:6])","metadata":{"id":"7jByVCZt-ZOC","outputId":"f1aa267d-7530-4620-ffc5-2f7aa39083bb","_uuid":"6175c72e73639e3190e127f67783988eadced9ba","execution":{"iopub.status.busy":"2024-03-07T13:34:26.396450Z","iopub.execute_input":"2024-03-07T13:34:26.396773Z","iopub.status.idle":"2024-03-07T13:34:26.443835Z","shell.execute_reply.started":"2024-03-07T13:34:26.396726Z","shell.execute_reply":"2024-03-07T13:34:26.443020Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create and prepare the training dataset using the DetectorDataset class.","metadata":{"id":"9KUvacUbgiEX","_uuid":"a5143c19dc22bc00d318a3b28cb7e13c7fbacc8a"}},{"cell_type":"code","source":"# prepare the training dataset\ndataset_train = DetectorDataset(image_fps_train, image_annotations, ORIG_SIZE, ORIG_SIZE)\ndataset_train.prepare()","metadata":{"id":"jwMkhotP0yFf","_uuid":"86c3333d4dfb8b7d00ce1f401693d0df4e6254e1","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Let's look at a sample annotation. We see a bounding box with (x, y) of the the top left corner as well as the width and height.","metadata":{"id":"wPDQ9EVDgxa6","_uuid":"4f69286e6b0b640827a3de166326d157a6d86668"}},{"cell_type":"code","source":"# Show annotation(s) for a DICOM image \ntest_fp = random.choice(image_fps_train)\nimage_annotations[test_fp]","metadata":{"id":"0xEc47Jz59x5","outputId":"129edfbc-cf9d-46c7-b569-d804a50cd12d","_uuid":"93da5a58731ad483a4bd2b20543f2b1df4b8ad74","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# prepare the validation dataset\ndataset_val = DetectorDataset(image_fps_val, image_annotations, ORIG_SIZE, ORIG_SIZE)\ndataset_val.prepare()","metadata":{"id":"K1TkWuGP0yHl","_uuid":"313347d838fa8321a714858c8073f98c50c5be26","execution":{"iopub.status.busy":"2024-03-07T11:57:35.244221Z","iopub.execute_input":"2024-03-07T11:57:35.244548Z","iopub.status.idle":"2024-03-07T11:57:35.249605Z","shell.execute_reply.started":"2024-03-07T11:57:35.244502Z","shell.execute_reply":"2024-03-07T11:57:35.248528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Display a random image with bounding boxes","metadata":{"id":"pEXEt8fygWuC","_uuid":"600a8135d4e382f62797d69e9358f5697873c8f9"}},{"cell_type":"code","source":"# Load and display random sample and their bounding boxes\n\nclass_ids = [0]\nwhile class_ids[0] == 0:  ## look for a mask\n    image_id = random.choice(dataset_train.image_ids)\n    image_fp = dataset_train.image_reference(image_id)\n    image = dataset_train.load_image(image_id)\n    mask, class_ids = dataset_train.load_mask(image_id)\n\nprint(image.shape)\n\nplt.figure(figsize=(10, 10))\nplt.subplot(1, 2, 1)\nplt.imshow(image)\nplt.axis('off')\n\nplt.subplot(1, 2, 2)\nmasked = np.zeros(image.shape[:2])\nfor i in range(mask.shape[2]):\n    masked += image[:, :, 0] * mask[:, :, i]\nplt.imshow(masked, cmap='gray')\nplt.axis('off')\n\nprint(image_fp)\nprint(class_ids)","metadata":{"id":"4xwsrf9G1lHR","outputId":"a13386d3-a918-41fe-8824-13625c9d7b08","_uuid":"491b78ec96d28fcdbbf8e2d7f9320a05d64c9249","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Image Augmentation. Try finetuning some variables to custom values","metadata":{"id":"ustAIH78hZI_","_uuid":"342b6008873fe7a6a0870a712ee47a87f0d2828d"}},{"cell_type":"code","source":"# Image augmentation (light but constant)\naugmentation = iaa.Sequential([\n    iaa.OneOf([ ## geometric transform\n        iaa.Affine(\n            scale={\"x\": (0.98, 1.02), \"y\": (0.98, 1.04)},\n            translate_percent={\"x\": (-0.02, 0.02), \"y\": (-0.04, 0.04)},\n            rotate=(-2, 2),\n            shear=(-1, 1),\n        ),\n        iaa.PiecewiseAffine(scale=(0.001, 0.025)),\n    ]),\n    iaa.OneOf([ ## brightness or contrast\n        iaa.Multiply((0.9, 1.1)),\n        iaa.ContrastNormalization((0.9, 1.1)),\n    ]),\n    iaa.OneOf([ ## blur or sharpen\n        iaa.GaussianBlur(sigma=(0.0, 0.1)),\n        iaa.Sharpen(alpha=(0.0, 0.1)),\n    ]),\n])\n\n# test on the same image as above\nimggrid = augmentation.draw_grid(image[:, :, 0], cols=5, rows=2)\nplt.figure(figsize=(30, 12))\n_ = plt.imshow(imggrid[:, :, 0], cmap='gray')","metadata":{"id":"STZnQTE61lME","_uuid":"4ab9d6086ce611a46f189c047956c43b29783e6d","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Now it's time to train the model. Note that training even a basic model can take a few hours. \n\nNote: the following model is for demonstration purpose only. We have limited the training to one epoch, and have set nominal values for the Detector Configuration to reduce run-time. \n\n- dataset_train and dataset_val are derived from DetectorDataset \n- DetectorDataset loads images from image filenames and  masks from the annotation data\n- model is Mask-RCNN","metadata":{"id":"M4kt7LKuc78e","_uuid":"7e65d2cecb283f446f34cdde19b663a8a8e9590f"}},{"cell_type":"code","source":"model = modellib.MaskRCNN(mode='training', config=config, model_dir=ROOT_DIR)\n\n# Exclude the last layers because they require a matching\n# number of classes\nmodel.load_weights(COCO_WEIGHTS_PATH, by_name=True, exclude=[\n    \"mrcnn_class_logits\", \"mrcnn_bbox_fc\",\n    \"mrcnn_bbox\", \"mrcnn_mask\"])","metadata":{"_uuid":"138d6197fc8dce9f1f8a7b5a6c27aa2069698e03","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LEARNING_RATE = 0.006\n\n# Train Mask-RCNN Model \nimport warnings \nwarnings.filterwarnings(\"ignore\")","metadata":{"id":"RVgNhHjl1lOS","outputId":"2cba9efc-eeea-472d-d155-3c3d856585bf","_uuid":"64cce2581ffdb8c2b1cb07948ada4a93f64874b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n## train heads with higher lr to speedup the learning\nmodel.train(dataset_train, dataset_val,\n            learning_rate=LEARNING_RATE*2,\n            epochs=2,\n            layers='heads',\n            augmentation=None)  ## no need to augment yet\n\nhistory = model.keras_model.history.history","metadata":{"_uuid":"cf339a499519d174bcdf2311a1802f0e3acb1758","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nmodel.train(dataset_train, dataset_val,\n            learning_rate=LEARNING_RATE,\n            epochs=6,\n            layers='all',\n            augmentation=augmentation)\n\nnew_history = model.keras_model.history.history\nfor k in new_history: history[k] = history[k] + new_history[k]","metadata":{"_uuid":"8004790d27f041793562e994bbe95edf67f8978b","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nmodel.train(dataset_train, dataset_val,\n            learning_rate=LEARNING_RATE/5,\n            epochs=16,\n            layers='all',\n            augmentation=augmentation)\n\nnew_history = model.keras_model.history.history\nfor k in new_history: history[k] = history[k] + new_history[k]","metadata":{"_uuid":"ccea214a520c686735e138f64977dcd7f3e3330a","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = range(1,len(next(iter(history.values())))+1)\npd.DataFrame(history, index=epochs)","metadata":{"_uuid":"eda9047f485f1d2e0b32b48ec2cec54a38c8535e","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(17,5))\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 ce\")\nplt.plot(epochs, history[\"val_mrcnn_class_loss\"], label=\"Valid class ce\")\nplt.legend()\nplt.subplot(133)\nplt.plot(epochs, history[\"mrcnn_bbox_loss\"], label=\"Train box loss\")\nplt.plot(epochs, history[\"val_mrcnn_bbox_loss\"], label=\"Valid box loss\")\nplt.legend()\n\nplt.show()","metadata":{"_uuid":"fb3b69242b91dcc49697ff076ceeb957347372e1","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_epoch = np.argmin(history[\"val_loss\"])\nprint(\"Best Epoch:\", best_epoch + 1, history[\"val_loss\"][best_epoch])","metadata":{"_uuid":"a6a00c25dfd023d27b54de963d785ca7f5f740d8","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# select trained model \ndir_names = next(os.walk(model.model_dir))[1]\nkey = config.NAME.lower()\ndir_names = filter(lambda f: f.startswith(key), dir_names)\ndir_names = sorted(dir_names)\n\nif not dir_names:\n    import errno\n    raise FileNotFoundError(\n        errno.ENOENT,\n        \"Could not find model directory under {}\".format(self.model_dir))\n    \nfps = []\n# Pick last directory\nfor d in dir_names: \n    dir_name = os.path.join(model.model_dir, d)\n    # Find the last checkpoint\n    checkpoints = next(os.walk(dir_name))[2]\n    checkpoints = filter(lambda f: f.startswith(\"mask_rcnn\"), checkpoints)\n    checkpoints = sorted(checkpoints)\n    if not checkpoints:\n        print('No weight files in {}'.format(dir_name))\n    else:\n        checkpoint = os.path.join(dir_name, checkpoints[best_epoch])\n        fps.append(checkpoint)\n\nmodel_path = sorted(fps)[-1]\nprint('Found model {}'.format(model_path))","metadata":{"id":"eraRlzgPmmIZ","outputId":"de9e688c-ba4f-4b62-f842-dbcf00ce397c","_uuid":"db5c10d3f7da099e5751a04a6e6d49819882ecd4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class InferenceConfig(DetectorConfig):\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 1\n\ninference_config = InferenceConfig()\n\n# Recreate the model in inference mode\nmodel = modellib.MaskRCNN(mode='inference', \n                          config=inference_config,\n                          model_dir=ROOT_DIR)\n\n# Load trained weights (fill in path to trained weights here)\nmodel_path  = \"/kaggle/input/mask-rcnn-and-coco-transfer-learning-lb-0-155/pneumonia20190702T0812/mask_rcnn_pneumonia_0013.h5\"\nassert model_path != \"\", \"Provide path to trained weights\"\nprint(\"Loading weights from \", model_path)\nmodel.load_weights(model_path, by_name=True)","metadata":{"id":"TgpT9AzC2Bgz","outputId":"60f5a175-4666-497d-b4e8-0bdab39a92d0","_uuid":"52138636b2ae5bf444bba808518cd8313bde65cd","execution":{"iopub.status.busy":"2024-03-07T11:22:01.934291Z","iopub.execute_input":"2024-03-07T11:22:01.934723Z","iopub.status.idle":"2024-03-07T11:22:12.573855Z","shell.execute_reply.started":"2024-03-07T11:22:01.934668Z","shell.execute_reply":"2024-03-07T11:22:12.572801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# set color for class\ndef get_colors_for_class_ids(class_ids):\n    colors = []\n    for class_id in class_ids:\n        if class_id == 1:\n            colors.append((.941, .204, .204))\n    return colors","metadata":{"id":"9mTBig7D2BjU","_uuid":"e13c61bee23b791c61ecf1256f7512295cd4d9ab","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### How does the predicted box compared to the expected value? Let's use the validation dataset to check. \n\nNote that we trained only one epoch for **demonstration purposes ONLY**. You might be able to improve performance running more epochs. ","metadata":{"id":"A8EiL2LOiCr_","_uuid":"f99fbd3f31ff1a2bd66764835c9b646375364598"}},{"cell_type":"code","source":"print(image_fps_val)","metadata":{"execution":{"iopub.status.busy":"2024-03-07T13:06:10.492176Z","iopub.execute_input":"2024-03-07T13:06:10.492553Z","iopub.status.idle":"2024-03-07T13:06:10.497759Z","shell.execute_reply.started":"2024-03-07T13:06:10.492499Z","shell.execute_reply":"2024-03-07T13:06:10.496583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"My evaluation suite","metadata":{}},{"cell_type":"code","source":"import csv\n\ndef calculate_iou(ground_truth_boxes, predictions):\n    if len(ground_truth_boxes) == 0:\n        return 0  # If no ground truth boxes, return IoU of 0\n    iou_total = 0\n    if not isinstance(ground_truth_boxes, list):\n        ground_truth_boxes = [ground_truth_boxes]\n    for gt_box in ground_truth_boxes:\n        print(\"here is the boxes:\", ground_truth_boxes)\n        gt_x, gt_y, gt_w, gt_h = gt_box\n        gt_area = gt_w * gt_h\n        for pred_box in predictions:\n            pred_x1, pred_y1, pred_x2, pred_y2 = pred_box['box']['x1'], pred_box['box']['y1'], pred_box['box']['x2'], pred_box['box']['y2']\n            pred_area = (pred_x2 - pred_x1) * (pred_y2 - pred_y1)\n            inter_x1 = max(gt_x, pred_x1)\n            inter_y1 = max(gt_y, pred_y1)\n            inter_x2 = min(gt_x + gt_w, pred_x2)\n            inter_y2 = min(gt_y + gt_h, pred_y2)\n            inter_area = max(0, inter_x2 - inter_x1) * max(0, inter_y2 - inter_y1)\n            iou = inter_area / (gt_area + pred_area - inter_area)\n            iou_total += iou\n    return iou_total / len(ground_truth_boxes) if len(ground_truth_boxes) > 0 else 0\n\ndef calculate_mAP(ground_truth_boxes, predictions):\n    ious = []\n    for gt_box in ground_truth_boxes:\n        max_iou = 0\n        for pred_box in predictions:\n            iou = calculate_iou(gt_box, pred_box['box'])\n            max_iou = max(max_iou, iou)\n        ious.append(max_iou)\n    \n    # Check if ious is empty to prevent division by zero\n    if len(ious) == 0:\n        return 0\n    \n    mAP = sum(ious) / len(ious)\n    return mAP\n\nclass DetectorConfig(Config):\n    \"\"\"Configuration for training pneumonia detection on the RSNA pneumonia dataset.\n    Overrides values in the base Config class.\n    \"\"\"  \n    NAME = 'pneumonia'\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 8\n    BACKBONE = 'resnet50'\n    NUM_CLASSES = 2  # background + 1 pneumonia classes\n    IMAGE_MIN_DIM = 256\n    IMAGE_MAX_DIM = 256\n    # RPN_ANCHOR_SCALES = (16, 32, 64, 128)\n    TRAIN_ROIS_PER_IMAGE = 32\n    MAX_GT_INSTANCES = 4\n    DETECTION_MAX_INSTANCES = 3\n    DETECTION_MIN_CONFIDENCE = 0.78  ## match target distribution\n    DETECTION_NMS_THRESHOLD = 0.01\n    STEPS_PER_EPOCH = 200\n\nclass InferenceConfig(DetectorConfig):\n    GPU_COUNT = 1\n    IMAGES_PER_GPU = 1\n\n\nconfig = DetectorConfig()\ninference_config = InferenceConfig()\n\n\ndef process_image(image):\n    if len(image.shape) != 3 or image.shape[2] != 3:\n        image = np.stack((image,) * 3, -1)\n    resized_image, _, _, _, _ = utils.resize_image(\n        image,\n        min_dim=config.IMAGE_MIN_DIM,\n        min_scale=config.IMAGE_MIN_SCALE,\n        max_dim=config.IMAGE_MAX_DIM,\n        mode=config.IMAGE_RESIZE_MODE)\n\n    return resized_image\ndef calculate_precision(ground_truth_boxes, predictions, iou_threshold=0.5):\n    # Initialize counters\n    true_positives = 0\n    false_positives = 0\n\n    # Check each predicted box\n    for pred_box in predictions:\n        # Initialize flag to keep track of whether a prediction is a true positive\n        is_true_positive = False\n        \n        # Check if the prediction has an IoU greater than the threshold with any ground truth box\n        for gt_box in ground_truth_boxes:\n            iou = calculate_iou(gt_box, pred_box['box'])\n            if iou >= iou_threshold:\n                is_true_positive = True\n                break  # Exit the loop if a true positive is found\n        \n        # Update counters based on whether the prediction is a true positive or false positive\n        if is_true_positive:\n            true_positives += 1\n        else:\n            false_positives += 1\n    \n    # Calculate precision\n    if true_positives + false_positives == 0:\n        precision = 0  # Avoid division by zero\n    else:\n        precision = true_positives / (true_positives + false_positives)\n    \n    return precision\n","metadata":{"execution":{"iopub.status.busy":"2024-03-07T15:05:00.194762Z","iopub.execute_input":"2024-03-07T15:05:00.195170Z","iopub.status.idle":"2024-03-07T15:05:00.223514Z","shell.execute_reply.started":"2024-03-07T15:05:00.195110Z","shell.execute_reply":"2024-03-07T15:05:00.222591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = modellib.MaskRCNN(mode='inference',\n                              config=inference_config,\n                              model_dir=ROOT_DIR)\nmodel.load_weights('/kaggle/input/mask-rcnn-and-coco-transfer-learning-lb-0-155/pneumonia20190702T0812/mask_rcnn_pneumonia_0013.h5', by_name=True)","metadata":{"execution":{"iopub.status.busy":"2024-03-07T14:55:10.904683Z","iopub.execute_input":"2024-03-07T14:55:10.905240Z","iopub.status.idle":"2024-03-07T14:55:48.097305Z","shell.execute_reply.started":"2024-03-07T14:55:10.905006Z","shell.execute_reply":"2024-03-07T14:55:48.096142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef combined_function(filepaths, csv_path, model_path):\n    # Load model\n    \n    \n    # Initialize variables to store aggregated scores\n    total_iou = 0\n    total_mAP = 0\n    total_precision = 0\n    total_images = 0\n    \n    # Load CSV data\n    with open('/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv', 'r') as csvfile:\n        csv_reader = csv.DictReader(csvfile)\n        print(csv_reader.fieldnames)\n        \n        for image_path in filepaths:\n            # Process image\n            csvfile.seek(0)\n            ds = pydicom.dcmread(image_path)\n            image = ds.pixel_array.astype(np.uint8)\n            \n            # Process image\n            if image is None:\n                print(f\"Failed to read image: {image_path}\")\n                continue\n            \n            resized_image = process_image(image)\n            resized_image = process_image(image)\n            results = model.detect([resized_image])\n            r = results[0]\n            if len(r['rois']) == 0:\n                print(f\"No predictions made for {image_title}\")\n                continue\n                \n            total_images += 1\n            \n            # Predicted coordinates\n            predictions = []\n            for i, bbox in enumerate(r['rois']):\n                x1 = int(bbox[1] / config.IMAGE_SHAPE[1] * image.shape[1])\n                y1 = int(bbox[0] / config.IMAGE_SHAPE[0] * image.shape[0])\n                x2 = int(bbox[3] / config.IMAGE_SHAPE[1] * image.shape[1])\n                y2 = int(bbox[2] / config.IMAGE_SHAPE[0] * image.shape[0])\n                prediction = {\n                    'box': {'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2},\n                    'class_id': int(r['class_ids'][i]),\n                    'score': float(r['scores'][i])\n                }\n                predictions.append(prediction)\n            \n            # Ground truth coordinates\n            image_title = ds.PatientID\n            print(image_title)\n#             if image_title.endswith('.dcm'):\n#                 image_title = image_title[:-4] \n            \n            ground_truth_boxes = [(float(row['x']), float(row['y']), float(row['width']), float(row['height']))\n                      for row in csv_reader if row['patientId'] == image_title and row['x'] and row['x'].replace('.', '', 1).isdigit()]\n            print('the boxes are',ground_truth_boxes)\n            # Calculate IoU, mAP, and precision\n            iou = calculate_iou(ground_truth_boxes, predictions)\n            mAP = calculate_mAP(ground_truth_boxes, predictions)\n            precision = calculate_precision(ground_truth_boxes, predictions)\n            \n            # Accumulate scores\n            total_iou += iou\n            total_mAP += mAP\n            total_precision += precision\n            \n            # Print scores for each image\n            print(f\"IoU for {image_title}: {iou}\")\n            print(f\"mAP for {image_title}: {mAP}\")\n            print(f\"Precision for {image_title}: {precision}\")\n    \n    # Calculate overall scores\n    overall_iou = total_iou / total_images if total_images > 0 else 0\n    overall_mAP = total_mAP / total_images if total_images > 0 else 0\n    overall_precision = total_precision / total_images if total_images > 0 else 0\n    \n    # Print overall scores\n    print(f\"Overall IoU: {overall_iou}\")\n    print(f\"Overall mAP: {overall_mAP}\")\n    print(f\"Overall Precision: {overall_precision}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-03-07T14:55:59.305713Z","iopub.execute_input":"2024-03-07T14:55:59.306092Z","iopub.status.idle":"2024-03-07T14:55:59.341384Z","shell.execute_reply.started":"2024-03-07T14:55:59.306034Z","shell.execute_reply":"2024-03-07T14:55:59.340084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#simple test\nwith open('/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv', 'r') as csvfile:\n        csv_reader = csv.DictReader(csvfile)\n        print(csv_reader.fieldnames)\n        image_title = ds.PatientID\n        ground_truth_boxes = [(float(row['x']), float(row['y']), float(row['width']), float(row['height']))\n                      for row in csv_reader if row['patientId'] == image_title and row['x'] and row['x'].replace('.', '', 1).isdigit()]\n        print(ground_truth_boxes)","metadata":{"execution":{"iopub.status.busy":"2024-03-07T14:40:35.350620Z","iopub.execute_input":"2024-03-07T14:40:35.351005Z","iopub.status.idle":"2024-03-07T14:40:35.605242Z","shell.execute_reply.started":"2024-03-07T14:40:35.350945Z","shell.execute_reply":"2024-03-07T14:40:35.604283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"combined_function(image_fps_val, '/kaggle/input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv', '/kaggle/input/mask-rcnn-and-coco-transfer-learning-lb-0-155/pneumonia20190702T0812/mask_rcnn_pneumonia_0013.h5')","metadata":{"execution":{"iopub.status.busy":"2024-03-07T15:05:06.701830Z","iopub.execute_input":"2024-03-07T15:05:06.702160Z","iopub.status.idle":"2024-03-07T15:05:07.768268Z","shell.execute_reply.started":"2024-03-07T15:05:06.702113Z","shell.execute_reply":"2024-03-07T15:05:07.765958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def calculate_metrics(dataset, inference_config, model_path):\n#     \"\"\"Calculates evaluation metrics for a detection model over the whole dataset.\"\"\"\n\n#     APs = []\n#     precisions, recalls = [], []\n\n#     for class_id in dataset.class_ids:\n#         print(f\"Processing class ID: {class_id}\")\n\n#         # ... (Load the model as before)\n\n#         true_positives = 0  \n#         false_positives = 0\n#         scores = []\n#         num_gt_instances = 0 \n\n#         for image_id in dataset.image_ids:\n#             print(f\"  Processing image ID: {image_id}\")\n\n#             # ... (Load image, get ground truth and predictions as before)\n#             original_image, image_meta, gt_class_id, gt_bbox, gt_mask = \\\n#                 modellib.load_image_gt(dataset, inference_config, image_id) \n#             results = model.detect([original_image], verbose=0)[0]\n\n#             true_pos = np.zeros(len(results['rois']))  \n#             false_pos = np.ones(len(results['rois']))\n#             pred_scores = results['scores']\n\n#             for i in range(len(gt_bbox)):\n#                 for j in range(len(results['rois'])):\n#                     iou = calculate_iou(gt_bbox[i], results['rois'][j])\n#                     if iou > 0.5 and results['class_ids'][j] == gt_class_id[i]:\n#                         true_positives += 1 \n#                         false_positives -= 1 \n\n#             true_positives += np.sum(true_pos)  \n#             false_positives += np.sum(false_pos)\n#             scores.extend(pred_scores) \n#             num_gt_instances += len(gt_bbox) \n\n#         # Calculate precision and recall across the whole dataset\n#         precision = true_positives / (true_positives + false_positives)\n#         recall = true_positives / num_gt_instances\n#         precisions.append(precision)\n#         recalls.append(recall)\n\n#         # Calculate AP \n#         AP = calculate_ap(precisions[-1], recalls[-1]) \n#         APs.append(AP)\n\n#         print(f\"  Class ID {class_id} metrics: Precision={precision:.3f}, Recall={recall:.3f}, AP={AP:.3f}\")\n\n#     mAP = np.mean(APs)\n#     print(f\"\\nOverall mAP: {mAP:.3f}\")\n\n#     return {'precision': precisions, 'recall': recalls, 'AP': APs, 'mAP': mAP}\ndef calculate_metrics(dataset, inference_config, model_path):\n    \"\"\"Calculates evaluation metrics for a detection model over the whole dataset.\n\n    Args:\n        dataset: The DetectorDataset object.\n        inference_config: The InferenceConfig object.\n        model_path:  Path to the trained model weights.\n\n    Returns:\n        dict: A dictionary containing the following evaluation metrics:\n            * 'precision': Overall precision across all classes.\n            * 'recall': Overall recall across all classes.\n            * 'AP': Overall Average Precision across all classes.\n            * 'mAP': Overall mean Average Precision across all classes.\n            * 'class_precisions': Precision for each individual class.\n            * 'class_recalls': Recall for each individual class.\n            * 'class_APs': Average Precision for each individual class. \n    \"\"\"\n\n    APs = []\n    class_precisions, class_recalls = [], []\n\n    for class_id in dataset.class_ids:\n        print(f\"Processing class ID: {class_id}\")\n\n        true_positives = 0 \n        false_positives = 0\n        scores = []\n        num_gt_instances = 0 \n\n        for image_id in dataset.image_ids:\n            # ... (Load image, get ground truth and predictions as before)\n            # Load and preprocess the image \n            image, image_meta, gt_class_id, gt_bbox, gt_mask = \\\n                modellib.load_image_gt(dataset, inference_config, image_id, use_mini_mask=False)\n\n            # Generate predictions\n            results = model.detect([image], verbose=0)[0] \n\n            # Confidence score sorting\n#             results = model.detect([original_image], verbose=0)[0] # Add this line\n\n            # Confidence score sorting\n            order = np.argsort(results['scores'])[::-1]  # Descending order\n            results['rois'] = results['rois'][order]\n            results['class_ids'] = results['class_ids'][order]\n            results['scores'] = results['scores'][order]\n\n            true_pos = np.zeros(len(results['rois']))\n            false_pos = np.ones(len(results['rois']))\n\n            for i in range(len(gt_bbox)):\n                for j in range(len(results['rois'])):\n                    iou = calculate_iou(gt_bbox[i], results['rois'][j])\n                    if iou > 0.5 and results['class_ids'][j] == gt_class_id[i]:\n                        true_positives += 1 \n                        false_positives -= 1 \n\n            true_positives += np.sum(true_pos)  \n            false_positives += np.sum(false_pos)\n            scores.extend(results['scores']) \n            num_gt_instances += len(gt_bbox) \n\n        # Calculate class-specific metrics\n#         class_precision = true_positives / (true_positives + false_positives)\n#         class_recall = true_positives / num_gt_instances\n#         class_AP = calculate_ap(class_precision, class_recall)  # Assuming single values\n#         class_precisions.append(class_precision)\n#         class_recalls.append(class_recall) \n#         APs.append(class_AP)\n            \n\n        print(f\"  Class ID {class_id} metrics: Precision={class_precision:.3f}, Recall={class_recall:.3f}, AP={class_AP:.3f}\")\n\n    # Calculate overall metrics\n    precision = np.sum(true_positives) / (np.sum(true_positives) + np.sum(false_positives)) \n    recall = np.sum(true_positives) / np.sum(num_gt_instances)\n    AP = calculate_ap(precision, recall)  # Assuming single values\n    mAP = np.mean(APs)  \n\n    print(f\"\\nOverall metrics: Precision={precision:.3f}, Recall={recall:.3f}, AP={AP:.3f}, mAP={mAP:.3f}\")\n\n    return {'precision': precision, 'recall': recall, 'AP': AP, 'mAP': mAP,\n            'class_precisions': class_precisions, 'class_recalls': class_recalls, 'class_APs': APs}\n","metadata":{"execution":{"iopub.status.busy":"2024-03-07T12:52:51.000251Z","iopub.execute_input":"2024-03-07T12:52:51.000647Z","iopub.status.idle":"2024-03-07T12:52:51.024727Z","shell.execute_reply.started":"2024-03-07T12:52:51.000591Z","shell.execute_reply":"2024-03-07T12:52:51.023803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"metrics = calculate_metrics(dataset_val, inference_config, model_path)\nprint(metrics)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# Get filenames of test dataset DICOM images\ntest_image_fps = get_dicom_fps(test_dicom_dir)","metadata":{"id":"qRWBVJKYNdWM","_uuid":"fd9f53fa319a425693e07fe4898ddeeaa5d07f99","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Final steps - Create the submission file","metadata":{"id":"WcV1cL_aiSc4","_uuid":"164e18701a830bc6c42a791feea13549de37289b"}},{"cell_type":"code","source":"# Make predictions on test images, write out sample submission\ndef predict(image_fps, filepath='submission.csv', min_conf=0.95):\n    # assume square image\n    resize_factor = ORIG_SIZE / config.IMAGE_SHAPE[0]\n    #resize_factor = ORIG_SIZE\n    with open(filepath, 'w') as file:\n        file.write(\"patientId,PredictionString\\n\")\n\n        for image_id in tqdm(image_fps):\n            ds = pydicom.read_file(image_id)\n            image = ds.pixel_array\n            # If grayscale. Convert to RGB for consistency.\n            if len(image.shape) != 3 or image.shape[2] != 3:\n                image = np.stack((image,) * 3, -1)\n            image, window, scale, padding, crop = utils.resize_image(\n                image,\n                min_dim=config.IMAGE_MIN_DIM,\n                min_scale=config.IMAGE_MIN_SCALE,\n                max_dim=config.IMAGE_MAX_DIM,\n                mode=config.IMAGE_RESIZE_MODE)\n\n            patient_id = os.path.splitext(os.path.basename(image_id))[0]\n\n            results = model.detect([image])\n            r = results[0]\n\n            out_str = \"\"\n            out_str += patient_id\n            out_str += \",\"\n            assert( len(r['rois']) == len(r['class_ids']) == len(r['scores']) )\n            if len(r['rois']) == 0:\n                pass\n            else:\n                num_instances = len(r['rois'])\n\n                for i in range(num_instances):\n                    if r['scores'][i] > min_conf:\n                        out_str += ' '\n                        out_str += str(round(r['scores'][i], 2))\n                        out_str += ' '\n\n                        # x1, y1, width, height\n                        x1 = r['rois'][i][1]\n                        y1 = r['rois'][i][0]\n                        width = r['rois'][i][3] - x1\n                        height = r['rois'][i][2] - y1\n                        bboxes_str = \"{} {} {} {}\".format(x1*resize_factor, y1*resize_factor, \\\n                                                           width*resize_factor, height*resize_factor)\n                        out_str += bboxes_str\n\n            file.write(out_str+\"\\n\")","metadata":{"id":"C6UWVrbM2Bob","_uuid":"4a5c0c6134408ddbf5a34496d7e9d7be5692e9a1","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_fp = os.path.join(ROOT_DIR, 'submission.csv')\npredict(test_image_fps, filepath=submission_fp)\nprint(submission_fp)","metadata":{"id":"C5cBpNka2Bsv","outputId":"a2af9176-d9d6-49f6-f22a-5a1c455d144f","_uuid":"0406e7f5aaa4867782c4f9c064f90bba386128e7","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output = pd.read_csv(submission_fp)\noutput.head(60)","metadata":{"id":"_BjPE_Ee9rbA","outputId":"67b5f053-112b-494a-9ab3-d017bfb440c2","_uuid":"3fd8d178fc51ef0bca94fbb3f423160f08a77edc","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# show a few test image detection example\ndef visualize(): \n    image_id = random.choice(test_image_fps)\n    ds = pydicom.read_file(image_id)\n    \n    # original image \n    image = ds.pixel_array\n    \n    # assume square image \n    resize_factor = ORIG_SIZE / config.IMAGE_SHAPE[0]\n    \n    # If grayscale. Convert to RGB for consistency.\n    if len(image.shape) != 3 or image.shape[2] != 3:\n        image = np.stack((image,) * 3, -1) \n    resized_image, window, scale, padding, crop = utils.resize_image(\n        image,\n        min_dim=config.IMAGE_MIN_DIM,\n        min_scale=config.IMAGE_MIN_SCALE,\n        max_dim=config.IMAGE_MAX_DIM,\n        mode=config.IMAGE_RESIZE_MODE)\n\n    patient_id = os.path.splitext(os.path.basename(image_id))[0]\n    print(patient_id)\n\n    results = model.detect([resized_image])\n    r = results[0]\n    for bbox in r['rois']: \n        print(bbox)\n        x1 = int(bbox[1] * resize_factor)\n        y1 = int(bbox[0] * resize_factor)\n        x2 = int(bbox[3] * resize_factor)\n        y2 = int(bbox[2]  * resize_factor)\n        cv2.rectangle(image, (x1,y1), (x2,y2), (77, 255, 9), 3, 1)\n        width = x2 - x1 \n        height = y2 - y1 \n        print(\"x {} y {} h {} w {}\".format(x1, y1, width, height))\n    plt.figure() \n    plt.imshow(image, cmap=plt.cm.gist_gray)\n\nvisualize()\nvisualize()\nvisualize()\nvisualize()","metadata":{"_uuid":"ea110f197abc2acb1c3435383f7259079dc0eb0e","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# remove files to allow committing (hit files limit otherwise)\n!rm -rf /kaggle/working/Mask_RCNN","metadata":{"_uuid":"835a15c9d018acd5deb16e9e02f9b765f68d0e78","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"random.seed(42)  # Use the same seed for reproducibility\nrandom.shuffle(image_fps)  # Assuming image_fps is a list of image names/paths\nval_size = 1500\nimage_fps_val = image_fps[:val_size]\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset_val = DetectorDataset(image_fps_val, image_annotations, ORIG_SIZE, ORIG_SIZE)\ndataset_val.prepare()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}