{"cells":[{"metadata":{},"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/MmfIl9W.png\">\n\n<center><h1>🥼RSNA STR Pulmonary Embolism Detection🥼</h1></center>\n<center><h2>Classify Pulmonary Embolism cases in chest CT scans</h2></center>\n<p></p>\n<p></p>\n\n\n# 1. Introduction - what do I need to know\n\n### #1. What is a Pulmonary Embolism (PE)?\n\n* is a **blood clot** in the *lungs*\n* it damages the lung due to restricted blood flow & decrease oxygen levels in the blood\n* 1/3 people diagnosticated or untreated die from this disease\n* source of info [here](https://www.healthline.com/health/pulmonary-embolus#:~:text=A%20pulmonary%20embolism%20is%20a,blockage%20can%20be%20life%2Dthreatening.)\n\n> However, *correct* medical intervenion greatly increases the avoidance of permanent damage => hence the importance of correctly classifying the disease.\n\n<img src=\"https://i.imgur.com/VBwwaOj.png\" width=350>\n\n### #2. How do we identify it? CTPA\n\n* Chest CTPA - CT Pulmonary Angiography - the most common type of tool used to identify PE\n* Each scan has hundreds of images that require detailed look into it\n\n### #3. Data\n\n> The data structure is as the schema below:\n<img src=\"https://i.imgur.com/F8sLJQQ.png\" width = 350>\n\n* Images are groupes by **study** and **series**\n* *Dicom* images: unique identifier is `SOPInstanceUID` and location looks like `<StudyInstanceUID>/<SeriesInstanceUID>/<SOPInstanceUID>.dcm`\n\n\n* `train.csv` - containes *labels whch are targets* and some *informal* ones\n\n### #4. Prediction\n> Prediction is made at both **image level** and **study level**.\n\n> For `.csv` data, there are labels that are noted as *exam-level*, which need to be predicted, and some that are noted as *informal*, which are NOT required to be predicted (meant as helpers). The informal labels are:\n        * `qa_motion`\n        * `qa_contrast`\n        * `flow_artifact`\n        * `true_filling_defect_not_pe`\n        \n*Note: `acute_pe` is implied by the lack of `chronic_pe` and `acute_and_chronic_pe` labels*\n\n> There is also a *relationship* between labels that MUST be respected (as the below diagram shows - you can find it in the original dataset)\n<img src=\"https://i.imgur.com/SZkdW4t.png\" width=450>\n\n> **SO: By looking only at the image, we need to predict all *exam-level* features, which *need* to also comply with the rules in the above schema. Oh boy.**\n\n### Libraries 📚"},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"# Regular Imports\nimport os\nimport pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport matplotlib.image as mpimg\nfrom tabulate import tabulate\nimport missingno as msno \nfrom IPython.display import display_html\nfrom PIL import Image\nimport gc\nimport cv2\nfrom scipy.stats import pearsonr\nimport tqdm\n\nimport pydicom # for DICOM images\nfrom skimage.transform import resize\nimport copy\nimport re\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\n\n# Color Palette\ncustom_colors = ['#6C4C4D','#95715F','#C4A797','#A9DBC2','#3C887E', '#386B64']\nsns.palplot(sns.color_palette(custom_colors))\n\n# Set Style\nsns.set_style(\"whitegrid\")\nsns.despine(left=True, bottom=True)\n\n# Set tick size\nplt.rc('xtick',labelsize=11)\nplt.rc('ytick',labelsize=11)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# Centers the images\n\nfrom IPython.core.display import HTML\nHTML(\"\"\"\n<style>\n.output_png {\n    display: table-cell;\n    text-align: center;\n    vertical-align: middle;\n}\n</style>\n\"\"\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2. The Metadata - *.csv* files📋\n\n## 2.1 Loading the data"},{"metadata":{"trusted":true},"cell_type":"code","source":"basepath = \"../input/rsna-str-pulmonary-embolism-detection\"\n\n# Import the data\ntrain_csv = pd.read_csv(basepath + \"/train.csv\")\ntest_csv = pd.read_csv(basepath + \"/test.csv\")\n\n# How many lines are in each dataframe?\nprint(\"Train: {:,} lines.\".format(len(train_csv)), \"\\n\" +\n      \"Test: {:,} lines.\".format(len(test_csv)))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"df1_styler = train_csv.head().style.set_table_attributes(\"style='display:inline'\").set_caption('Head Train Data')\ndf2_styler = test_csv.head().style.set_table_attributes(\"style='display:inline'\").set_caption('Test Data (rest Hidden)')\n\ndisplay_html(df1_styler._repr_html_() + df2_styler._repr_html_(), raw=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2.2 Missing Data❓\n\n> No Missing Data, so we can proceed."},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Train Data:\", \"\\n\" + \n      \"Q: Are there any missing values?\", \"\\n\" +\n      \"A: {}\".format(train_csv.isnull().values.any()), \"\\n\")\n\nprint(\"Test Data:\", \"\\n\" + \n      \"Q: Are there any missing values?\", \"\\n\" +\n      \"A: {}\".format(test_csv.isnull().values.any()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2.3 Study, Series, Image\n\n> I needed to understand a bit the format of the data. Below is a schema of how it looks.\n<img src=\"https://i.imgur.com/CBF9g1W.png\" width=\"500\">"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Number of unique studies\nprint(\"--- Train:\", \"\\n\" +\n      \"Total Unique Studies&Series: {:,}\".format(len(train_csv.groupby(\"StudyInstanceUID\")[\"SeriesInstanceUID\"].count())))\n\n# Group by Study\ndata = train_csv.groupby(\"StudyInstanceUID\")[\"SOPInstanceUID\"].count().reset_index()\nprint(\"Min No. Images/Study: {:,}\".format(data.min()[1]), \"\\n\" +\n      \"Max No. Images/Study: {:,}\".format(data.max()[1]), \"\\n\" +\n      \"Avg No. Images/Study: {:,.0f}\".format(round(data.mean()[0], 0)))\n\n# Plot\nplt.figure(figsize=(16, 6))\nsns.boxenplot(x = \"SOPInstanceUID\", data=data, color=custom_colors[4])\n\n\nplt.title(\"TRAIN: No. Images per Study\", fontsize = 17)\nplt.xlabel('No. Images', fontsize=14);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Number of unique studies\nprint(\"--- Test:\", \"\\n\" +\n      \"Total Unique Studies&Series: {:,}\".format(len(test_csv.groupby(\"StudyInstanceUID\")[\"SeriesInstanceUID\"].count())))\n\n# Group by Study\ndata = test_csv.groupby(\"StudyInstanceUID\")[\"SOPInstanceUID\"].count().reset_index()\nprint(\"Min No. Images/Study: {:,}\".format(data.min()[1]), \"\\n\" +\n      \"Max No. Images/Study: {:,}\".format(data.max()[1]), \"\\n\" +\n      \"Avg No. Images/Study: {:,.0f}\".format(round(data.mean()[0], 0)))\n\n# Plot\nplt.figure(figsize=(16, 6))\nsns.boxenplot(x = \"SOPInstanceUID\", data=data, color=custom_colors[2])\n\n\nplt.title(\"TEST: No. Images per Study\", fontsize = 17)\nplt.xlabel('No. Images', fontsize=14);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2.4 Labels that WILL BE Predicted\n\nAs stated before, using the `.dcm` images we'll predict a suite of features:\n* 'pe_present_on_image'\n* 'negative_exam_for_pe'\n* 'rv_lv_ratio_gte_1'\n* 'rv_lv_ratio_lt_1'\n* 'leftsided_pe'\n* 'chronic_pe'\n* 'rightsided_pe'\n* 'acute_and_chronic_pe'\n* 'central_pe'\n* 'indeterminate'\n\n<div class=\"alert alert-block alert-info\">  \nA great discussion on what the features mean <a href=\"https://www.kaggle.com/c/rsna-str-pulmonary-embolism-detection/discussion/183850\">can be found here</a>. \n</div>\n\n> **📌Note**: there are more cases of *right* side PE than *left* side. The least cases are *central*. There is a high percentage of PEs that are present in the study, but not in the particular image."},{"metadata":{"trusted":true},"cell_type":"code","source":"predict_variables = ['pe_present_on_image', 'negative_exam_for_pe', 'rv_lv_ratio_gte_1', \n                     'rv_lv_ratio_lt_1', 'leftsided_pe', 'chronic_pe', 'rightsided_pe', \n                     'acute_and_chronic_pe', 'central_pe', 'indeterminate']\n\n# Melt the prediction variables on a single column - so we can plot more easily the 10 variables\nmelt = train_csv[['SOPInstanceUID'] + predict_variables]\nmelt = pd.melt(melt, id_vars=['SOPInstanceUID'], value_vars=predict_variables)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(16, 6))\n\na = sns.countplot(x=melt[\"variable\"], hue=melt[\"value\"], \n                  palette=\"copper\")\n\nplt.xticks(rotation=40)\nplt.title(\"TRAIN: (to predict) Variables\", fontsize = 16)\nplt.xlabel(\"\")\nplt.ylabel(\"Frequency\", fontsize=14);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2.5 Informational Labels (won't be predicted)\n\nThere are also 4 features that are intended only to be helpers.\n* 'qa_motion'\n* 'qa_contrast'\n* 'flow_artifact'\n* 'true_filling_defect_not_pe'\n\n> **📌Note**: All features hase very low frequencies for *present* (or 1). These represent notes from radiologists, however we can't count on these, as there could be human error involved."},{"metadata":{"trusted":true},"cell_type":"code","source":"bonus_variables = ['qa_motion', 'qa_contrast', 'flow_artifact', 'true_filling_defect_not_pe']\n\n# Melt the variables on a single column - so we can plot more easily the 4 variables\nmelt = train_csv[['SOPInstanceUID'] + bonus_variables]\nmelt = pd.melt(melt, id_vars=['SOPInstanceUID'], value_vars=bonus_variables)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(16, 6))\n\na = sns.countplot(x=melt[\"variable\"], hue=melt[\"value\"], \n                  palette=\"PuBuGn_r\")\n\nplt.xticks(rotation=40)\nplt.title(\"TRAIN: (bonus) Variables\", fontsize = 16)\nplt.xlabel(\"\")\nplt.ylabel(\"Frequency\", fontsize=14);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. DICOM Data📸\n\nNow let's explore the `.dcm` files we were provided and to extract insights about it.\n\n## 3.1 Sanity Check - verify number of *.dcm* files"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create base director for train and test data\nbase_train = \"../input/rsna-str-pulmonary-embolism-detection/train\"\nbase_test = \"../input/rsna-str-pulmonary-embolism-detection/test\"\n\n# --- TRAIN\n# Count total number of files in each subdirectory in train and test\ndcm_train = 0\n\n# dirpath - the directory path in string\n# dirnames - all main directories\n# filenames - all subdirectories\nfor dirpath, dirnames, filenames in tqdm.tqdm(os.walk(base_train)):\n    dcm_train += len(filenames)\n        \n# --- TEST\ndcm_test = 0\n\nfor dirpath, dirnames, filenames in tqdm.tqdm(os.walk(base_test)):\n    dcm_test += len(filenames)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"*The number of .dcm images matches the number of rows in the .csv files.*"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Train: total .dcm files - {:,}\".format(dcm_train), \"\\n\" +\n      \"Test: total .dcm files - {:,}\".format(dcm_test))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.2 Visualize a DICOM image\n\n> **📌Note**: all information regarding the patient has been removed for the images. Therefore, each *study* doesn't necessarly mean that is one person - it could be images from multiple people mixed in only 1 study."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Color of text\nclass bcolors:\n    OKBLUE = '\\033[96m'\n    OKGREEN = '\\033[92m'\n    \npath = \"../input/rsna-str-pulmonary-embolism-detection/train/0003b3d648eb/d2b2960c2bbf/5eb3f6566b0f.dcm\"\ndataset = pydicom.dcmread(path)\n\nprint(bcolors.OKBLUE + \"Image Type.......:\", dataset.ImageType, \"\\n\" +\n      \"Modality.........:\", dataset.Modality, \"\\n\" +\n      \"Rows.............:\", dataset.Rows, \"\\n\" +\n      \"Columns..........:\", dataset.Columns)\n\nplt.figure(figsize = (7, 7))\nplt.imshow(dataset.pixel_array, cmap=\"plasma\")\nplt.axis('off');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Let's see the difference between no present PE and PE present"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Get the base directory\nbase = \"../input/rsna-str-pulmonary-embolism-detection/train\"\n\n# Get a sample of 2 images (1 with pe present and 1 with no pe present)\npe_yes = train_csv[train_csv[\"pe_present_on_image\"] == 1].sample(random_state=33).reset_index()\npe_no = train_csv[train_csv[\"pe_present_on_image\"] == 0].sample(random_state=101).reset_index()\n\n# Get paths of these images\npe_yes_path = base + \"/\" + pe_yes[\"StudyInstanceUID\"] + \"/\" + pe_yes[\"SeriesInstanceUID\"] + \"/\" + pe_yes[\"SOPInstanceUID\"] + \".dcm\"\npe_no_path = base + \"/\" + pe_no[\"StudyInstanceUID\"] + \"/\" + pe_no[\"SeriesInstanceUID\"] + \"/\" + pe_no[\"SOPInstanceUID\"] + \".dcm\"\n\npe_yes_path = pe_yes_path[0]\npe_no_path = pe_no_path[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Positive PE: There is a leftside PE, a righside PE and also a central PE (according with the .csv information)"},{"metadata":{"trusted":true},"cell_type":"code","source":"pe_yes[[\"SOPInstanceUID\", \"pe_present_on_image\", \"leftsided_pe\", \"rightsided_pe\", \"central_pe\"]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset_yes = pydicom.dcmread(pe_yes_path)\ndataset_no = pydicom.dcmread(pe_no_path)\n\n\nf, ax = plt.subplots(1, 2, figsize = (11, 11))\n\nax[0].imshow(dataset_no.pixel_array, cmap=\"plasma\")\nax[1].imshow(dataset_yes.pixel_array, cmap=\"plasma\")\nax[0].title.set_text(\"No PE present\")\nax[1].title.set_text(\"PE present\")\nax[0].axis('off')\nax[1].axis('off');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.3 Visualize a set of images for a Study\n\n> You can see how the lungs are expanding as they inhales air and contracting as they exhale."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Study \"0003b3d648eb\"\nstudy_dir = \"../input/rsna-str-pulmonary-embolism-detection/train/0003b3d648eb/d2b2960c2bbf\"\ndatasets = []\n\n# Read in the Dataset\nfor dcm in os.listdir(study_dir):\n    path = study_dir + \"/\" + dcm\n    datasets.append(pydicom.dcmread(path))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot the images\nfig=plt.figure(figsize=(16, 6))\ncolumns = 10\nrows = 3\n\nfor i in range(1, columns*rows +1):\n    img = datasets[i-1].pixel_array\n    fig.add_subplot(rows, columns, i)\n    plt.imshow(img, cmap=\"plasma\")\n    plt.title(i, fontsize = 9)\n    plt.axis('off');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. Prepping the Data\n\n<div class=\"alert alert-block alert-success\">  \nGreat notebook that was used as inspiration <a href=\"https://www.kaggle.com/allunia/pulmonary-dicom-preprocessing\">was this one</a> by @Laura Fink. \n</div>"},{"metadata":{},"cell_type":"markdown","source":"# Work in Progress⏳"}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}