{"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":"**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":"markdown","source":"# RSNA Pneumonia Detection Challenge\n![](https://i.pinimg.com/564x/ff/cd/c4/ffcdc4d74eed036d029a84c381604a10.jpg)\n\n# Why to detetct this\n- Pneumonia accounts for over **15% of all deaths of children under 5 years old** internationally. In 2015, **920,000 children under the age of 5 died from the disease. **\n- In the United States,  **pneumonia accounts for over 500,000** visits to emergency departments [[1]](http://www.cdc.gov/nchs/data/nhamcs/web_tables/2015_ed_web_tables.pdf) and over **50,000 deaths in 2015** [[2]](http://www.cdc.gov/nchs/data/nvsr/nvsr66/nvsr66_06_tables.pdf), keeping the ailment on the list of top **10** causes of death in the country.\n\n# Symptoms to detect Pneumonia\n- The Diagnosis of pneumonia on CXR( is complicated because of a number of other **nditions in the lungs**uch as **uid overload (pulmonary edema), bleeding, volume loss (atelectasis or collapse), lung cancer, or post-radiation or surgical changes.**\n\n<table style=\"height: 684px; width: 348px; margin-left: auto; margin-right: auto;\">\n<tr style=\"height: 18px;\">\n<td style=\"height: 18px; width: 167px;\"><strong>Characteristics</strong></td>\n<td style=\"height: 18px; width: 165px;\"><strong>Pneumonia</strong></td>\n</tr>\n<tr style=\"height: 108px;\">\n<td style=\"height: 108px; width: 167px;\"><em>History</em></td>\n<td style=\"height: 108px; width: 165px;\">Underlying lung disease, contact with individuals having upper or lower respiratory infection, contact with birds/animals.</td>\n</tr>\n<tr style=\"height: 36px;\">\n<td style=\"height: 36px; width: 167px;\"><em>Causes</em></td>\n<td style=\"height: 36px; width: 165px;\">Bacteria, virus, fungi; aspiration.</td>\n</tr>\n<tr style=\"height: 72px;\">\n<td style=\"height: 72px; width: 167px;\"><em>Body systems </em></td>\n<td style=\"height: 72px; width: 165px;\">Respiratory system&mdash;lungs.</td>\n</tr>\n<tr style=\"height: 144px;\">\n<td style=\"height: 144px; width: 167px;\"><em>Clinical symptoms</em></td>\n<td style=\"height: 144px; width: 165px;\">High fever (sometimes with chills and rigors), cough, wheezing, breathing difficulty, chest pain.</td>\n</tr>\n<tr style=\"height: 162px;\">\n<td style=\"height: 162px; width: 167px;\"><em>Investigations</em></td>\n<td style=\"height: 162px; width: 165px;\">Blood investigations&mdash;complete blood count, ESR, sputum examination and culture, chest X-ray, CT scan, bronchoscopy, thoracocentesis, pleural fluid aspiration and culture.</td>\n</tr>\n<tr style=\"height: 126px;\">\n<td style=\"height: 126px; width: 167px;\"><em>Treatments</em></td>\n<td style=\"height: 126px; width: 165px;\">Appropriate antimicrobial therapy, expectorant, antipyretics and analgesics, oxygen therapy (if required), fluids.</td>\n</tr>\n</table>\n\n[Table References](http://www.differencebetween.net/science/health/difference-between-pneumonia-and-typhoid/)\n\n# Goal of This Project\n-  The primary endpoint will be the **detection of bounding boxes** corresponding to the **diagnosis of pneumonia** (e.g. **lung infection**) on chest radiographs, a **special 2D high resolution grayscale medical image**. \n- **Note** that pnuemonia is just one of many possible disease processes that can **occur on a chest radiograph**, and that any given **single image** may contain **0, 1** or **many boxes corresponding** to possible pneumonia locations.\n\n\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h2 id=\"Data-Overview\">Data Summary<a class=\"anchor-link\" href=\"#Data-Overview\" target=\"_self\">¶</a></h2><h3 id=\"Stage-2-Images---stage_2_train_images.zip-and-stage_2_test_images.zip\">Stage 2 Images - <code>stage_2_train_images.zip</code> and <code>stage_2_test_images.zip</code><a class=\"anchor-link\" href=\"#Stage-2-Images---stage_2_train_images.zip-and-stage_2_test_images.zip\" target=\"_self\">¶</a></h3><ul>\n<li>images for the current stage. Filenames are also patient names.</li>\n</ul>\n<h3 id=\"Stage-2-Labels---stage_2_train_labels.csv-and-Stage-2-Sample-Submission-stage_2_sample_submission.csv\">Stage 2 Labels - <code>stage_2_train_labels.csv</code> and Stage 2 Sample Submission <code>stage_2_sample_submission.csv</code><a class=\"anchor-link\" href=\"#Stage-2-Labels---stage_2_train_labels.csv-and-Stage-2-Sample-Submission-stage_2_sample_submission.csv\" target=\"_self\">¶</a></h3><ul>\n<li>Which provides the IDs for the test set, as well as a sample of what your submission should look like</li>\n</ul>\n<h3 id=\"Stage-2-Detailed-Info---stage_2_detailed_class_info.csv\">Stage 2 Detailed Info - <code>stage_2_detailed_class_info.csv</code><a class=\"anchor-link\" href=\"#Stage-2-Detailed-Info---stage_2_detailed_class_info.csv\" target=\"_self\">¶</a></h3><ul>\n<li>contains detailed information about the positive and negative classes in the training set, and may be used to build more nuanced models.</li>\n</ul>\n\n</div>\n</div>\n\n<h2>File descriptions</h2>\n<ul>\n<li><strong>stage_2_train.csv</strong>&nbsp;- the training set. Contains&nbsp;<code>patientId</code>s and bounding box / target information.</li>\n<li><strong>stage_2_sample_submission.csv</strong>&nbsp;- a sample submission file in the correct format. Contains&nbsp;<code>patientId</code>s for the test set. Note that the sample submission contains one box per image, but there is no limit to the number of bounding boxes that can be assigned to a given image.</li>\n<li><strong>stage_2_detailed_class_info.csv</strong>&nbsp;- provides detailed information about the type of positive or negative class for each image.</li>\n</ul>\n<h2>Data fields</h2>\n<ul>\n<li><strong>patientId</strong>&nbsp;_- A&nbsp;<code>patientId</code>. Each&nbsp;<code>patientId</code>&nbsp;corresponds to a unique image.</li>\n<li><strong>x</strong>_&nbsp;- the upper-left&nbsp;<code>x</code>&nbsp;coordinate of the bounding box.</li>\n<li><strong>y</strong>_&nbsp;- the upper-left&nbsp;<code>y</code>&nbsp;coordinate of the bounding box.</li>\n<li><strong>width</strong>_&nbsp;- the&nbsp;<code>width</code>&nbsp;of the bounding box.</li>\n<li><strong>height</strong>_&nbsp;- the&nbsp;<code>height</code>&nbsp;of the bounding box.</li>\n<li><strong>Target</strong>_&nbsp;- the binary&nbsp;<code>Target</code>, indicating whether this sample has evidence of pneumonia.</li>\n</ul>","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n\n\n# Required packages\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pylab as plt\nimport pydicom\nimport os\nfrom os import listdir\nfrom os.path import isfile, join\n\n# Any results you write to the current directory are saved as output.\nimport sys\nimport random\nimport math\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nimport json\nimport pydicom\nfrom imgaug import augmenters as iaa\nfrom tqdm import tqdm\nimport pandas as pd \nimport glob\nfrom sklearn.model_selection import KFold","metadata":{"id":"4kjcC6QqywWl","_uuid":"40c67b3ff0fa04587dec508363308adaa3ceaf34","execution":{"iopub.status.busy":"2022-06-06T22:46:46.722777Z","iopub.execute_input":"2022-06-06T22:46:46.723097Z","iopub.status.idle":"2022-06-06T22:46:48.263528Z","shell.execute_reply.started":"2022-06-06T22:46:46.723045Z","shell.execute_reply":"2022-06-06T22:46:48.262487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Images Example\ntrain_images_dir = '../input/stage_2_train_images/'\ntrain_images = [f for f in listdir(train_images_dir) if isfile(join(train_images_dir, f))]\ntest_images_dir = '../input/stage_2_test_images/'\ntest_images = [f for f in listdir(test_images_dir) if isfile(join(test_images_dir, f))]\nprint('5 Training images', train_images[:5]) # Print the first 5","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:13:49.355923Z","iopub.execute_input":"2022-06-06T16:13:49.356264Z","iopub.status.idle":"2022-06-06T16:15:09.93029Z","shell.execute_reply.started":"2022-06-06T16:13:49.356209Z","shell.execute_reply":"2022-06-06T16:15:09.929297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Number of train images:', len(train_images))\nprint('Number of test images:', len(test_images))","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:20:49.765818Z","iopub.execute_input":"2022-06-06T16:20:49.766117Z","iopub.status.idle":"2022-06-06T16:20:49.771802Z","shell.execute_reply.started":"2022-06-06T16:20:49.766065Z","shell.execute_reply":"2022-06-06T16:20:49.77093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ChexNet is SOTA Radiologist-Level Pneumonia Detection on Chest X-Rays with Deep Learning\n[Stanford ChexNet](https://arxiv.org/abs/1711.05225)\n\n### Radiology \n* The algorithm detect pneumonia from chest X-rays at a level exceeding practicing radiologists. CheXNet, is a 121-layer convolutional neural network trained on ChestX-ray14, currently the largest publicly available chest X-ray dataset, containing over 100,000 frontal-view X-ray images with 14 diseases  \n* The ***ChexNet*** paper reviews performance of AI versus 4 trained radiologists in diagnosing pneumonia. \n* **Pneumonia is a clinical diagnosis** — a **patient** will present with **fever and cough** , and can get a **chest Xray(CXR) to identify complications of pneumonia.** Patients will usually get **blood cultures** to **supplement diagnosis. Pneumonia on a CXR** is not easily distinguishable from other findings that fill the **alevolar spaces  —  specifically pus , blood , fluid or collapsed lung called atelectasis.**   \n* The radiologists interpreting these studies can therefore use terms like infiltrates , consolidation and atelectasis interchangeably.","metadata":{}},{"cell_type":"markdown","source":"# Display Images for review","metadata":{}},{"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(20, 10))\ncolumns = 9; rows = 4\nfor i in range(1, columns*rows +1):\n    ds = pydicom.dcmread(train_images_dir + train_images[i])\n    fig.add_subplot(rows, columns, i)\n    plt.imshow(ds.pixel_array, cmap=plt.cm.bone)\n    fig.add_subplot","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:20:54.161283Z","iopub.execute_input":"2022-06-06T16:20:54.161741Z","iopub.status.idle":"2022-06-06T16:21:01.058487Z","shell.execute_reply.started":"2022-06-06T16:20:54.161522Z","shell.execute_reply":"2022-06-06T16:21:01.055942Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Distribution of Positive Case","metadata":{}},{"cell_type":"code","source":"from plotly.offline import init_notebook_mode, iplot\ninit_notebook_mode(connected=True)\nimport plotly.graph_objs as go\nimport plotly.plotly as py\nimport cufflinks as cf\n\n# Number of positive targets\ntrain_labels = pd.read_csv('../input/stage_2_train_labels.csv')\nprint(round((8964 / (8964 + 20025)) * 100, 2), '% of the examples are positive')\ntrained_label = pd.DataFrame(train_labels.groupby('Target')['patientId'].count())\ntrace1  = go.Bar(x=train_labels.index, y=trained_label.patientId,\n                  marker=dict(color='rgb(158,120,200)',line=dict(color='rgb(8,48,107)',width=1.5)))\n\ndata = [trace1]\nlayout = dict(title = 'Classes by Patient',\n              xaxis= dict(title= 'Patient ID',ticklen= 5,zeroline= False),\n             yaxis = dict(title=\"Target\",ticklen= 5,zeroline= False))\n\nfig = dict(data = data, layout = layout)\niplot(fig)\ntrained_label","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:21:43.494499Z","iopub.execute_input":"2022-06-06T16:21:43.494831Z","iopub.status.idle":"2022-06-06T16:21:45.286051Z","shell.execute_reply.started":"2022-06-06T16:21:43.494778Z","shell.execute_reply":"2022-06-06T16:21:45.285253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Size of the Area that Impact","metadata":{}},{"cell_type":"code","source":"cf.set_config_file(offline=False, world_readable=True, theme='ggplot')\ntrain_labels['area'] = train_labels['width'] * train_labels['height']\ntrace1 = go.Histogram(x=train_labels['area'],opacity=0.75,name = \"name\",marker=dict(color='rgba(256, 120, 236, 0.9)'))\nlayout = go.Layout(barmode='overlay',\n                   title='Distribution of Area within Image idenfitying a positive target',\n                   xaxis=dict(title='Area'),\n                   yaxis=dict( title='Count'),\n)\ndata = [trace1]\nfig = go.Figure(data=data, layout=layout)\niplot(fig)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:21:55.616137Z","iopub.execute_input":"2022-06-06T16:21:55.616444Z","iopub.status.idle":"2022-06-06T16:21:56.002352Z","shell.execute_reply.started":"2022-06-06T16:21:55.616389Z","shell.execute_reply":"2022-06-06T16:21:56.001637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plotting Boxes Images","metadata":{}},{"cell_type":"code","source":"# Forked from `https://www.kaggle.com/peterchang77/exploratory-data-analysis`\ndef parse_data(df):\n    \"\"\"\n    Method to read a CSV file (Pandas dataframe) and parse the \n    data into the following nested dictionary:\n\n      parsed = {\n        \n        'patientId-00': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        },\n        'patientId-01': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        }, ...\n\n      }\n\n    \"\"\"\n    # --- Define lambda to extract coords in list [y, x, height, width]\n    extract_box = lambda row: [row['y'], row['x'], row['height'], row['width']]\n\n    parsed = {}\n    for n, row in df.iterrows():\n        # --- Initialize patient entry into parsed \n        pid = row['patientId']\n        if pid not in parsed:\n            parsed[pid] = {\n                'dicom': '../input/stage_2_train_images/%s.dcm' % pid,\n                'label': row['Target'],\n                'boxes': []}\n\n        # --- Add box if opacity is present\n        if parsed[pid]['label'] == 1:\n            parsed[pid]['boxes'].append(extract_box(row))\n\n    return parsed\n\nparsed = parse_data(train_labels)\n\ndef draw(data):\n    \"\"\"\n    Method to draw single patient with bounding box(es) if present \n\n    \"\"\"\n    # --- Open DICOM file\n    d = pydicom.read_file(data['dicom'])\n    im = d.pixel_array\n\n    # --- Convert from single-channel grayscale to 3-channel RGB\n    im = np.stack([im] * 3, axis=2)\n\n    # --- Add boxes with random color if present\n    for box in data['boxes']:\n        #rgb = np.floor(np.random.rand(3) * 256).astype('int')\n        rgb = [255, 251, 204] # Just use yellow\n        im = overlay_box(im=im, box=box, rgb=rgb, stroke=15)\n\n    plt.imshow(im, cmap=plt.cm.gist_gray)\n    plt.axis('off')\n\ndef overlay_box(im, box, rgb, stroke=2):\n    \"\"\"\n    Method to overlay single box on image\n\n    \"\"\"\n    # --- Convert coordinates to integers\n    box = [int(b) for b in box]\n    \n    # --- Extract coordinates\n    y1, x1, height, width = box\n    y2 = y1 + height\n    x2 = x1 + width\n\n    im[y1:y1 + stroke, x1:x2] = rgb\n    im[y2:y2 + stroke, x1:x2] = rgb\n    im[y1:y2, x1:x1 + stroke] = rgb\n    im[y1:y2, x2:x2 + stroke] = rgb\n\n    return im","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:22:03.306003Z","iopub.execute_input":"2022-06-06T16:22:03.30636Z","iopub.status.idle":"2022-06-06T16:22:06.741437Z","shell.execute_reply.started":"2022-06-06T16:22:03.306301Z","shell.execute_reply":"2022-06-06T16:22:06.740614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(20, 10))\ncolumns = 8; rows = 4\nfor i in range(1, columns*rows +1):\n    fig.add_subplot(rows, columns, i)\n    draw(parsed[train_labels['patientId'].unique()[i]])\n    fig.add_subplot","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:22:10.619004Z","iopub.execute_input":"2022-06-06T16:22:10.619311Z","iopub.status.idle":"2022-06-06T16:22:16.04928Z","shell.execute_reply.started":"2022-06-06T16:22:10.61926Z","shell.execute_reply":"2022-06-06T16:22:16.04851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Class Label","metadata":{}},{"cell_type":"code","source":"detailed_class_info = pd.read_csv('../input/stage_2_detailed_class_info.csv')\ndf_label = detailed_class_info.groupby('class').count()\n\ntrace1  = go.Bar(x=df_label.index, y=df_label.patientId,\n                  marker=dict(color='rgb(120,240,160)',line=dict(color='rgb(8,48,107)',width=1.5)))\n\ndata = [trace1]\nlayout = dict(title = 'Count of Class Label',\n              xaxis= dict(title= 'Patient ID',ticklen= 5,zeroline= False),\n             yaxis = dict(title=\"Target\",ticklen= 5,zeroline= False))\n\nfig = dict(data = data, layout = layout)\niplot(fig)\ndf_label","metadata":{"execution":{"iopub.status.busy":"2022-06-06T16:22:30.799875Z","iopub.execute_input":"2022-06-06T16:22:30.800189Z","iopub.status.idle":"2022-06-06T16:22:30.991601Z","shell.execute_reply.started":"2022-06-06T16:22:30.800138Z","shell.execute_reply":"2022-06-06T16:22:30.990801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_DIR = '/kaggle/input'\n\n# Directory to save logs and trained model\nROOT_DIR = '/kaggle/working'","metadata":{"id":"yP0XLJx_x_6o","_uuid":"6e5764759e6a0a9b698b44645658f66873edd807","execution":{"iopub.status.busy":"2022-06-06T16:23:33.132097Z","iopub.execute_input":"2022-06-06T16:23:33.132398Z","iopub.status.idle":"2022-06-06T16:23:33.136585Z","shell.execute_reply.started":"2022-06-06T16:23:33.132346Z","shell.execute_reply":"2022-06-06T16:23:33.135514Z"},"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":"2022-06-06T16:23:36.876363Z","iopub.execute_input":"2022-06-06T16:23:36.876699Z","iopub.status.idle":"2022-06-06T16:23:52.336166Z","shell.execute_reply.started":"2022-06-06T16:23:36.876643Z","shell.execute_reply":"2022-06-06T16:23:52.335332Z"},"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","metadata":{"id":"-KZXyWwhzOVU","outputId":"2576cc17-7484-4311-ad72-3c5643dcb5bb","_uuid":"3acbbbe055b6a409d3c50ae0f893acf51b5ae7ba","execution":{"iopub.status.busy":"2022-06-06T16:23:54.309874Z","iopub.execute_input":"2022-06-06T16:23:54.310239Z","iopub.status.idle":"2022-06-06T16:23:55.134635Z","shell.execute_reply.started":"2022-06-06T16:23:54.310184Z","shell.execute_reply":"2022-06-06T16:23:55.133787Z"},"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":"2022-06-06T16:23:58.171719Z","iopub.execute_input":"2022-06-06T16:23:58.172037Z","iopub.status.idle":"2022-06-06T16:23:58.177176Z","shell.execute_reply.started":"2022-06-06T16:23:58.171984Z","shell.execute_reply":"2022-06-06T16:23:58.176338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h1><span class=\"label label-default\" style=\"background-color:black;border-radius:100px 100px; font-weight: bold; font-family:Garamond; font-size:20px; color:#03e8fc; padding:10px\">Mask R-CNN</span></h1>\n\nMask R-CNN is basically an extension of Faster R-CNN. Faster R-CNN is widely used for object detection tasks. For a given image, it returns the class label and bounding box coordinates for each object in the image. So, let’s say you pass the following image:\n\n<img src=\"https://cdn.analyticsvidhya.com/wp-content/uploads/2019/07/Screenshot-from-2019-07-18-15-52-17.png\">\n\nFaster RCNN will return the above output.\n\nThe Mask R-CNN framework is built on top of Faster R-CNN. So, for a given image, Mask R-CNN, in addition to the class label and bounding box coordinates for each object, will also return the object mask.\n\nLet’s first quickly understand how Faster R-CNN works. This will help us grasp the intuition behind Mask R-CNN as well.\n\n1) Faster R-CNN first uses a ConvNet to extract feature maps from the images<br>\n2) These feature maps are then passed through a Region Proposal Network (RPN) which returns the candidate bounding boxes<br>\n3) We then apply an RoI pooling layer on these candidate bounding boxes to bring all the candidates to the same size<br>\n4) And finally, the proposals are passed to a fully connected layer to classify and output the bounding boxes for objects<br>\n\nOnce you understand how Faster R-CNN works, understanding Mask R-CNN will be very easy. So, let’s understand it step-by-step starting from the input to predicting the class label, bounding box, and object mask.\n\n<h1><span class=\"label label-default\" style=\"background-color:black;border-radius:100px 100px; font-weight: bold; font-family:Garamond; font-size:20px; color:#03e8fc; padding:10px\">Mask RCNN Architecture</span></h1>\n\n<img src=\"https://viso.ai/wp-content/uploads/2021/03/mask-r-cnn-framework-for-instance-segmentation-1.jpg\">\n\n<b>Source : https://viso.ai/deep-learning/mask-r-cnn/ </b>\n\n#### Backbone: \n\nSimilar to the ConvNet that we use in Faster R-CNN to extract feature maps from the image, we use the ResNet 101 architecture to extract features from the images in Mask R-CNN. So, the first step is to take an image and extract features using the ResNet 101 architecture. These features act as an input for the next layer.\n\n#### Region Proposal Network (RPN):\n\nNow, we take the feature maps obtained in the previous step and apply a region proposal network (RPM). This basically predicts if an object is present in that region (or not). In this step, we get those regions or feature maps which the model predicts contain some object.\n\n#### Region of Interest (RoI):\n\nThe regions obtained from the RPN might be of different shapes, right? Hence, we apply a pooling layer and convert all the regions to the same shape. Next, these regions are passed through a fully connected network so that the class label and bounding boxes are predicted.\n\nTill this point, the steps are almost similar to how Faster R-CNN works. Now comes the difference between the two frameworks. In addition to this, Mask R-CNN also generates the segmentation mask.\n\nFor that, we first compute the region of interest so that the computation time can be reduced. For all the predicted regions, we compute the Intersection over Union (IoU) with the ground truth boxes. We can computer IoU like this:\n\nIoU = Area of the intersection / Area of the union\n\nNow, only if the IoU is greater than or equal to 0.5, we consider that as a region of interest. Otherwise, we neglect that particular region. We do this for all the regions and then select only a set of regions for which the IoU is greater than 0.5.\n\n#### Segmentation Mask:\n\nOnce we have the RoIs based on the IoU values, we can add a mask branch to the existing architecture. This returns the segmentation mask for each region that contains an object. It returns a mask of size 28 X 28 for each region which is then scaled up for inference.\n\n<h1><span class=\"label label-default\" style=\"background-color:black;border-radius:100px 100px; font-weight: bold; font-family:Garamond; font-size:20px; color:#03e8fc; padding:10px\">Instruction</span></h1>\n\n1) Clone repository:\nhttps://github.com/matterport/Mask_RCNN.git\n\n2) Install below version of tensorflow and keras to avoid errors\n\n* sudo pip install --no-deps tensorflow==1.15.3\n* sudo pip install --no-deps keras==2.2.4\n\n3) Download pretrained maskrcnn weights on MSCOCO dataset from: https://github.com/matterport/Mask_RCNN/releases/download/v2.0/mask_rcnn_coco.h5\n\n4) Download few sample images and place under working directory \"Mask_RCNN\"","metadata":{}},{"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":"2022-06-06T16:24:05.098041Z","iopub.execute_input":"2022-06-06T16:24:05.098374Z","iopub.status.idle":"2022-06-06T16:25:19.764645Z","shell.execute_reply.started":"2022-06-06T16:24:05.098321Z","shell.execute_reply":"2022-06-06T16:25:19.763748Z"},"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":"2022-06-06T16:26:33.799693Z","iopub.execute_input":"2022-06-06T16:26:33.800044Z","iopub.status.idle":"2022-06-06T16:26:33.807392Z","shell.execute_reply.started":"2022-06-06T16:26:33.799992Z","shell.execute_reply":"2022-06-06T16:26:33.806384Z"},"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":"2022-06-06T16:29:39.230154Z","iopub.execute_input":"2022-06-06T16:29:39.230474Z","iopub.status.idle":"2022-06-06T16:29:39.245595Z","shell.execute_reply.started":"2022-06-06T16:29:39.230414Z","shell.execute_reply":"2022-06-06T16:29:39.244879Z"},"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":"2022-06-06T16:26:39.346859Z","iopub.execute_input":"2022-06-06T16:26:39.347171Z","iopub.status.idle":"2022-06-06T16:26:39.365637Z","shell.execute_reply.started":"2022-06-06T16:26:39.347118Z","shell.execute_reply":"2022-06-06T16:26:39.364746Z"},"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":"2022-06-06T16:26:44.258667Z","iopub.execute_input":"2022-06-06T16:26:44.258976Z","iopub.status.idle":"2022-06-06T16:26:44.319478Z","shell.execute_reply.started":"2022-06-06T16:26:44.258926Z","shell.execute_reply":"2022-06-06T16:26:44.318682Z"},"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":"2022-06-06T16:26:49.881109Z","iopub.execute_input":"2022-06-06T16:26:49.881516Z","iopub.status.idle":"2022-06-06T16:26:52.900558Z","shell.execute_reply.started":"2022-06-06T16:26:49.881441Z","shell.execute_reply":"2022-06-06T16:26:52.899682Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:26:56.677151Z","iopub.execute_input":"2022-06-06T16:26:56.677464Z","iopub.status.idle":"2022-06-06T16:26:56.699277Z","shell.execute_reply.started":"2022-06-06T16:26:56.677413Z","shell.execute_reply":"2022-06-06T16:26:56.697902Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:26:58.493078Z","iopub.execute_input":"2022-06-06T16:26:58.493554Z","iopub.status.idle":"2022-06-06T16:26:58.500374Z","shell.execute_reply.started":"2022-06-06T16:26:58.49334Z","shell.execute_reply":"2022-06-06T16:26:58.499532Z"},"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":"2022-06-06T16:27:02.781178Z","iopub.execute_input":"2022-06-06T16:27:02.781495Z","iopub.status.idle":"2022-06-06T16:27:02.785772Z","shell.execute_reply.started":"2022-06-06T16:27:02.78144Z","shell.execute_reply":"2022-06-06T16:27:02.784866Z"},"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":"2022-06-06T16:27:06.526004Z","iopub.execute_input":"2022-06-06T16:27:06.526306Z","iopub.status.idle":"2022-06-06T16:27:06.565001Z","shell.execute_reply.started":"2022-06-06T16:27:06.526256Z","shell.execute_reply":"2022-06-06T16:27:06.564287Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:27:15.504198Z","iopub.execute_input":"2022-06-06T16:27:15.504505Z","iopub.status.idle":"2022-06-06T16:27:15.580499Z","shell.execute_reply.started":"2022-06-06T16:27:15.504451Z","shell.execute_reply":"2022-06-06T16:27:15.579486Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:27:20.064774Z","iopub.execute_input":"2022-06-06T16:27:20.06509Z","iopub.status.idle":"2022-06-06T16:27:20.071615Z","shell.execute_reply.started":"2022-06-06T16:27:20.065033Z","shell.execute_reply":"2022-06-06T16:27:20.070573Z"},"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":"2022-06-06T16:27:26.625933Z","iopub.execute_input":"2022-06-06T16:27:26.626242Z","iopub.status.idle":"2022-06-06T16:27:26.639307Z","shell.execute_reply.started":"2022-06-06T16:27:26.62619Z","shell.execute_reply":"2022-06-06T16:27:26.636447Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:28:08.474006Z","iopub.execute_input":"2022-06-06T16:28:08.474367Z","iopub.status.idle":"2022-06-06T16:28:09.42629Z","shell.execute_reply.started":"2022-06-06T16:28:08.474309Z","shell.execute_reply":"2022-06-06T16:28:09.425502Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:28:13.622376Z","iopub.execute_input":"2022-06-06T16:28:13.622698Z","iopub.status.idle":"2022-06-06T16:28:19.093342Z","shell.execute_reply.started":"2022-06-06T16:28:13.622645Z","shell.execute_reply":"2022-06-06T16:28:19.092602Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:29:46.265926Z","iopub.execute_input":"2022-06-06T16:29:46.266226Z","iopub.status.idle":"2022-06-06T16:29:57.47733Z","shell.execute_reply.started":"2022-06-06T16:29:46.266178Z","shell.execute_reply":"2022-06-06T16:29:57.476567Z"},"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","execution":{"iopub.status.busy":"2022-06-06T16:30:20.13149Z","iopub.execute_input":"2022-06-06T16:30:20.13184Z","iopub.status.idle":"2022-06-06T16:30:20.136434Z","shell.execute_reply.started":"2022-06-06T16:30:20.131787Z","shell.execute_reply":"2022-06-06T16:30:20.135463Z"},"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=5,\n            layers='heads',\n            augmentation=None)  ## no need to augment yet\n\nhistory = model.keras_model.history.history","metadata":{"_uuid":"cf339a499519d174bcdf2311a1802f0e3acb1758","execution":{"iopub.status.busy":"2022-06-06T16:32:19.877627Z","iopub.execute_input":"2022-06-06T16:32:19.877947Z","iopub.status.idle":"2022-06-06T18:08:09.829748Z","shell.execute_reply.started":"2022-06-06T16:32:19.877893Z","shell.execute_reply":"2022-06-06T18:08:09.824757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nmodel.train(dataset_train, dataset_val,\n            learning_rate=LEARNING_RATE,\n            epochs=5,\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","execution":{"iopub.status.busy":"2022-06-06T18:13:36.14561Z","iopub.execute_input":"2022-06-06T18:13:36.145941Z","iopub.status.idle":"2022-06-06T18:13:59.395086Z","shell.execute_reply.started":"2022-06-06T18:13:36.145891Z","shell.execute_reply":"2022-06-06T18:13:59.393923Z"},"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","execution":{"iopub.status.busy":"2022-06-06T20:11:41.477264Z","iopub.status.idle":"2022-06-06T20:11:41.479489Z"},"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","execution":{"iopub.status.busy":"2022-06-06T20:11:41.471087Z","iopub.status.idle":"2022-06-06T20:11:41.471698Z"},"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","execution":{"iopub.status.busy":"2022-06-06T20:11:41.474659Z","iopub.status.idle":"2022-06-06T20:11:41.475245Z"},"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","execution":{"iopub.status.busy":"2022-06-04T20:07:25.385645Z","iopub.execute_input":"2022-06-04T20:07:25.386617Z","iopub.status.idle":"2022-06-04T20:07:25.397026Z","shell.execute_reply.started":"2022-06-04T20:07:25.386263Z","shell.execute_reply":"2022-06-04T20:07:25.395353Z"},"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","execution":{"iopub.status.busy":"2022-06-04T20:07:32.604172Z","iopub.execute_input":"2022-06-04T20:07:32.604654Z","iopub.status.idle":"2022-06-04T20:07:32.663362Z","shell.execute_reply.started":"2022-06-04T20:07:32.604589Z","shell.execute_reply":"2022-06-04T20:07:32.662156Z"},"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)\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":"2022-06-04T20:07:41.288442Z","iopub.execute_input":"2022-06-04T20:07:41.289352Z","iopub.status.idle":"2022-06-04T20:09:10.662094Z","shell.execute_reply.started":"2022-06-04T20:07:41.288839Z","shell.execute_reply":"2022-06-04T20:09:10.660144Z"},"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","execution":{"iopub.status.busy":"2022-06-04T20:09:46.429278Z","iopub.execute_input":"2022-06-04T20:09:46.429613Z","iopub.status.idle":"2022-06-04T20:09:46.445939Z","shell.execute_reply.started":"2022-06-04T20:09:46.42957Z","shell.execute_reply":"2022-06-04T20:09:46.44248Z"},"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":"# Show few example of ground truth vs. predictions on the validation dataset \ndataset = dataset_val\nfig = plt.figure(figsize=(10, 30))\n\nfor i in range(6):\n\n    image_id = random.choice(dataset.image_ids)\n    \n    original_image, image_meta, gt_class_id, gt_bbox, gt_mask =\\\n        modellib.load_image_gt(dataset_val, inference_config, \n                               image_id, use_mini_mask=False)\n    \n    print(original_image.shape)\n    plt.subplot(6, 2, 2*i + 1)\n    visualize.display_instances(original_image, gt_bbox, gt_mask, gt_class_id, \n                                dataset.class_names,\n                                colors=get_colors_for_class_ids(gt_class_id), ax=fig.axes[-1])\n    \n    plt.subplot(6, 2, 2*i + 2)\n    results = model.detect([original_image]) #, verbose=1)\n    r = results[0]\n    visualize.display_instances(original_image, r['rois'], r['masks'], r['class_ids'], \n                                dataset.class_names, r['scores'], \n                                colors=get_colors_for_class_ids(r['class_ids']), ax=fig.axes[-1])","metadata":{"id":"irheTbrW2Bl0","outputId":"56041ad4-173d-45ab-af67-f54e8333511e","_uuid":"186412199e25b98719f71cfe5e8869abcce516c4","execution":{"iopub.status.busy":"2022-06-04T20:09:50.59665Z","iopub.execute_input":"2022-06-04T20:09:50.597053Z","iopub.status.idle":"2022-06-04T20:10:17.825762Z","shell.execute_reply.started":"2022-06-04T20:09:50.597003Z","shell.execute_reply":"2022-06-04T20:10:17.824666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","execution":{"iopub.status.busy":"2022-06-04T20:10:37.913983Z","iopub.execute_input":"2022-06-04T20:10:37.914652Z","iopub.status.idle":"2022-06-04T20:10:38.179314Z","shell.execute_reply.started":"2022-06-04T20:10:37.914335Z","shell.execute_reply":"2022-06-04T20:10:38.178377Z"},"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","execution":{"iopub.status.busy":"2022-06-04T20:10:51.142382Z","iopub.execute_input":"2022-06-04T20:10:51.142791Z","iopub.status.idle":"2022-06-04T20:10:57.41929Z","shell.execute_reply.started":"2022-06-04T20:10:51.142741Z","shell.execute_reply":"2022-06-04T20:10:57.418371Z"},"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":[]}]}