{"cells":[{"metadata":{"_uuid":"0cbce5cc636ba65b81d858c596e39aa532728780"},"cell_type":"markdown","source":"# Overview\nThe notebook aims to get a better feeling for the data and more importantly the distributions of values. We take the labels and combine them with the detailed class info and try and determine what the biggest challenges of the prediction might be. "},{"metadata":{"trusted":true,"_uuid":"ae448b0ed29194053d40ebd29b2fa03982468552"},"cell_type":"code","source":"%matplotlib inline\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pydicom\nimport pandas as pd\nfrom glob import glob\nimport os\nfrom matplotlib.patches import Rectangle\ndet_class_path = '../input/stage_2_detailed_class_info.csv'\nbbox_path = '../input/stage_2_train_labels.csv'\ndicom_dir = '../input/stage_2_train_images/'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f4ff05105153200d3dc3f382b5a24b6e6dd80291"},"cell_type":"markdown","source":"# Detailed Class Info\nHere we show the image-level labels for the scans. The most interesting group here is the `No Lung Opacity / Not Normal` since they are cases that look like opacity but are not. So the first step might be to divide the test images into clear groups and then only perform the bounding box prediction on the suspicious images."},{"metadata":{"trusted":true,"_uuid":"a0a43187041d2773c035b62f68a9687811f9fcc2"},"cell_type":"code","source":"det_class_df = pd.read_csv(det_class_path)\nprint(det_class_df.shape[0], 'class infos loaded')\nprint(det_class_df['patientId'].value_counts().shape[0], 'patient cases')\ndet_class_df.groupby('class').size().plot.bar()\ndet_class_df.sample(3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4ca820c61b58fe03a613bce38c47786b8a8ef399"},"cell_type":"markdown","source":"# Load the Bounding Box Data\nHere we show the bounding boxes"},{"metadata":{"trusted":true,"_uuid":"cc03f5c79bee9f015c12dc8181f7997df584fbb8"},"cell_type":"code","source":"bbox_df = pd.read_csv(bbox_path)\nprint(bbox_df.shape[0], 'boxes loaded')\nprint(bbox_df['patientId'].value_counts().shape[0], 'patient cases')\nbbox_df.sample(3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c5cc20e2eb8e9cbecf4494fe5264397438fb73a1"},"cell_type":"markdown","source":"# Combine Boxes and Labels\nHere we bring the labels and the boxes together and now we can focus on how the boxes look on the images"},{"metadata":{"trusted":true,"_uuid":"ca18783acf240faea13a23fd06fba7b41f9ea71d"},"cell_type":"code","source":"# we first try a join and see that it doesn't work (we end up with too many boxes)\ncomb_bbox_df = pd.merge(bbox_df, det_class_df, how='inner', on='patientId')\nprint(comb_bbox_df.shape[0], 'combined cases')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"15ab554a46421da28b29d7932dcad07b5146203f"},"cell_type":"markdown","source":"## Concatenate\nWe have to concatenate the two datasets and then we get class and target information on each region"},{"metadata":{"trusted":true,"_uuid":"91447e135cd4525c0869692ab6a6269e5bc9f30f"},"cell_type":"code","source":"comb_bbox_df = pd.concat([bbox_df, \n                        det_class_df.drop('patientId',1)], 1)\nprint(comb_bbox_df.shape[0], 'combined cases')\ncomb_bbox_df.sample(3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6f29b7e35b8f879687bcfeadf8a93c708c94d9a7"},"cell_type":"markdown","source":"# Distribution of Boxes and Labels\nThe values below show the number of boxes and the patients that have that number. "},{"metadata":{"trusted":true,"_uuid":"569f38045b0359ae0635bca97dbbe600785ff565"},"cell_type":"code","source":"box_df = comb_bbox_df.groupby('patientId').\\\n    size().\\\n    reset_index(name='boxes')\ncomb_box_df = pd.merge(comb_bbox_df, box_df, on='patientId')\nbox_df.\\\n    groupby('boxes').\\\n    size().\\\n    reset_index(name='patients')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"277b277b5c63562afa5a66c0714a94bb139979ec"},"cell_type":"markdown","source":"# How are class and target related?\nI assume that all the `Target=1` values fall in the `Lung Opacity` class, but it doesn't hurt to check."},{"metadata":{"trusted":true,"_uuid":"2386013f7461e1407f1e782865228d146264bff0"},"cell_type":"code","source":"comb_bbox_df.groupby(['class', 'Target']).size().reset_index(name='Patient Count')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c5c58ddf2a9a7350650586865166ae0d5ed9a0cf"},"cell_type":"markdown","source":"# Images\nNow that we have the boxes and labels loaded we can examine a few images."},{"metadata":{"trusted":true,"_uuid":"46fe765772e24397a94295a70dbde46254c4cc8b"},"cell_type":"code","source":"image_df = pd.DataFrame({'path': glob(os.path.join(dicom_dir, '*.dcm'))})\nimage_df['patientId'] = image_df['path'].map(lambda x: os.path.splitext(os.path.basename(x))[0])\nprint(image_df.shape[0], 'images found')\nimg_pat_ids = set(image_df['patientId'].values.tolist())\nbox_pat_ids = set(comb_box_df['patientId'].values.tolist())\n# check to make sure there is no funny business\nassert img_pat_ids.union(box_pat_ids)==img_pat_ids, \"Patient IDs should be the same\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"49b1259692d6246459452f80ff2d7e1d4755d28d"},"cell_type":"code","source":"image_bbox_df = pd.merge(comb_box_df, \n                         image_df, \n                         on='patientId',\n                        how='left').sort_values('patientId')\nprint(image_bbox_df.shape[0], 'image bounding boxes')\nimage_bbox_df.head(5)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c6299cf5b16c13e9eb07c69a84a934fec4cf8b6f"},"cell_type":"markdown","source":"# Enrich the image fields\nWe have quite a bit of additional data in the DICOM header we can easily extract to help learn more about the patient like their age, view position and gender which can make the model much more precise"},{"metadata":{"trusted":true,"_uuid":"6fd8a408e2999759d3cc7500f7cb6d03d767103d"},"cell_type":"code","source":"DCM_TAG_LIST = ['PatientAge', 'BodyPartExamined', 'ViewPosition', 'PatientSex']\ndef get_tags(in_path):\n    c_dicom = pydicom.read_file(in_path, stop_before_pixels=False)\n    tag_dict = {c_tag: getattr(c_dicom, c_tag, '') \n         for c_tag in DCM_TAG_LIST}\n    tag_dict['path'] = in_path\n    return pd.Series(tag_dict)\nimage_meta_df = image_df.apply(lambda x: get_tags(x['path']), 1)\n# show the summary\nimage_meta_df['PatientAge'] = image_meta_df['PatientAge'].map(int)\nimage_meta_df['PatientAge'].hist()\nimage_meta_df.drop('path',1).describe(exclude=np.number)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9e42002854e4a06e6bc9ff24337b63bb42af9d2f"},"cell_type":"code","source":"image_full_df = pd.merge(image_df,\n                         image_meta_df,\n                         on='path')\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"96af4bb8a61df3dd753e6b103434b996f3884c13"},"cell_type":"markdown","source":"## Create Sample Data Set\nWe create a sample dataset covering different cases, and number of boxes"},{"metadata":{"trusted":true,"_uuid":"5aa2c0ab7e7970ed26de93d66eb52cd4b08e57b6"},"cell_type":"code","source":"sample_df = image_bbox_df.\\\n    groupby(['Target','class', 'boxes']).\\\n    apply(lambda x: x[x['patientId']==x.sample(1)['patientId'].values[0]]).\\\n    reset_index(drop=True)\nsample_df","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e59e981d122b9a9c1c0f78d2d914775ff74f7c72"},"cell_type":"markdown","source":"## Show the position and bounding box\nHere we can see the position (point) and the bounding box for each of the different image types"},{"metadata":{"trusted":true,"_uuid":"a110097202462adb9042a02cf5ce641584717043"},"cell_type":"code","source":"fig, m_axs = plt.subplots(2, 3, figsize = (20, 10))\nfor c_ax, (c_path, c_rows) in zip(m_axs.flatten(),\n                    sample_df.groupby(['path'])):\n    c_dicom = pydicom.read_file(c_path)\n    c_ax.imshow(c_dicom.pixel_array, cmap='bone')\n    c_ax.set_title('{class}'.format(**c_rows.iloc[0,:]))\n    for i, (_, c_row) in enumerate(c_rows.dropna().iterrows()):\n        c_ax.plot(c_row['x'], c_row['y'], 's', label='{class}'.format(**c_row))\n        c_ax.add_patch(Rectangle(xy=(c_row['x'], c_row['y']),\n                                width=c_row['width'],\n                                height=c_row['height'], \n                                 alpha = 0.5))\n        if i==0: c_ax.legend()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9cf95258eeb58aa070776fca48e620359ebb223d"},"cell_type":"markdown","source":"# Bounding Box Distribution\nHere we just look at the bounding box distribution to get a better idea how this looks over the whole dataset"},{"metadata":{"trusted":true,"_uuid":"a0e9bb4b310379c622055f7a18a5084795ed0e10"},"cell_type":"code","source":"pos_bbox = image_bbox_df.query('Target==1')\npos_bbox.plot.scatter(x='x', y='y')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8e5a67838560247ef72e5669cdf6369b243dc998"},"cell_type":"code","source":"fig, ax1 = plt.subplots(1, 1, figsize = (10, 10))\nax1.set_xlim(0, 1024)\nax1.set_ylim(0, 1024)\nfor _, c_row in pos_bbox.sample(1000).iterrows():\n    ax1.add_patch(Rectangle(xy=(c_row['x'], c_row['y']),\n                 width=c_row['width'],\n                 height=c_row['height'],\n                           alpha=5e-3))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"29a114790d74a2b2794394a85045e9e94782cb3a"},"cell_type":"markdown","source":"# Show the boxes as segmentation\nBy showing them as segmentations we can get a better probability map for where the opacity regions are most likely to occur"},{"metadata":{"trusted":true,"_uuid":"99d9163a94d7991d209e984d9d2b6774fc6d8a77"},"cell_type":"code","source":"# Show the boxes themselves\nX_STEPS, Y_STEPS = 1024, 1024\nxx, yy = np.meshgrid(np.linspace(0, 1024, X_STEPS),\n           np.linspace(0, 1024, Y_STEPS), \n           indexing='xy')\nprob_image = np.zeros_like(xx)\nfor _, c_row in pos_bbox.sample(5000).iterrows():\n    c_mask = (xx>=c_row['x']) & (xx<=(c_row['x']+c_row['width']))\n    c_mask &= (yy>=c_row['y']) & (yy<=c_row['y']+c_row['height'])\n    prob_image += c_mask\nfig, ax1 = plt.subplots(1, 1, figsize = (10, 10))\nax1.imshow(prob_image, cmap='hot')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"963b741fdb177d131af2afc6e0c4f151b014e47b"},"cell_type":"markdown","source":"# Overlay the Probability on a few images\nDoes the probability we calculate seem to make sense? or have we flipped something somewhere?"},{"metadata":{"trusted":true,"_uuid":"f03e4f67dbff68cedf2cc9b29ed489fe2fcdec62"},"cell_type":"code","source":"fig, m_axs = plt.subplots(2, 3, figsize = (20, 10))\nfor c_ax, (c_path, c_rows) in zip(m_axs.flatten(),\n                    sample_df.groupby(['path'])):\n    c_img_arr = pydicom.read_file(c_path).pixel_array\n    # overlay\n    c_img = plt.cm.gray(c_img_arr)\n    c_img += 0.25*plt.cm.hot(prob_image/prob_image.max())\n    c_img = np.clip(c_img, 0, 1)\n    c_ax.imshow(c_img)\n    \n    c_ax.set_title('{class}'.format(**c_rows.iloc[0,:]))\n    for i, (_, c_row) in enumerate(c_rows.dropna().iterrows()):\n        c_ax.plot(c_row['x'], c_row['y'], 's', label='{class}'.format(**c_row))\n        c_ax.add_patch(Rectangle(xy=(c_row['x'], c_row['y']),\n                                width=c_row['width'],\n                                height=c_row['height'], \n                                 alpha = 0.5,\n                                fill=False))\n        if i==0: c_ax.legend()\nfig.savefig('overview.png', figdpi = 600)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"828c1986e2f5527cce11e7d114e166454caef64e"},"cell_type":"markdown","source":"### Save the preprocessed results\nWe can use the preprocessed results with the appropriate DICOM tags to make model training step easier"},{"metadata":{"trusted":true,"_uuid":"497116383a76bd6285edf70c2604924f6e5e1687"},"cell_type":"code","source":"image_bbox_df.to_csv('image_bbox_full.csv', index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"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"}},"nbformat":4,"nbformat_minor":1}