{"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":"# EDA, Visualization and Some Level of Prediction: SIIM-FISABIO-RSNA COVID-19 Detection\nIn this competition, we will be providing step by step walk through the different approaches how we can contribute on identifying COVID-19 symptoms comparing with other significant bacterial penumonias.","metadata":{}},{"cell_type":"markdown","source":"### Problem Statement:\nCurrently, COVID-19 can be diagnosed via polymerase chain reaction to detect genetic material from the virus or chest radiograph. However, it can take a few hours and sometimes days before the molecular test results are back. By contrast, chest radiographs can be obtained in minutes. While guidelines exist to help radiologists differentiate COVID-19 from other types of infection, their assessments vary. In addition, non-radiologists could be supported with better localization of the disease, such as with a visual bounding box.\n\nIn this competition, you’ll identify and localize COVID-19 abnormalities on chest radiographs. In particular, you'll categorize the radiographs as negative for pneumonia or typical, indeterminate, or atypical for COVID-19. You and your model will work with imaging data and annotations from a group of radiologists.\n\nIf successful, you'll help radiologists diagnose the millions of COVID-19 patients more confidently and quickly. This will also enable doctors to see the extent of the disease and help them make decisions regarding treatment. Depending upon severity, affected patients may need hospitalization, admission into an intensive care unit, or supportive therapies like mechanical ventilation. As a result of better diagnosis, more patients will quickly receive the best care for their condition, which could mitigate the most severe effects of the virus.\n(Copied from Overview Section)","metadata":{}},{"cell_type":"markdown","source":"### Dataset Information: Understand your Data First\nThe train dataset comprises 6,334 chest scans in DICOM format, which were de-identified to protect patient privacy. All images were labeled by a panel of experienced radiologists for the presence of opacities as well as overall appearance.\n\nNote that all images are stored in paths with the form study/series/image. The study ID here relates directly to the study-level predictions, and the image ID is the ID used for image-level predictions.\n\nThe hidden test dataset is of roughly the same scale as the training dataset.","metadata":{}},{"cell_type":"markdown","source":"**Before even we dive into the EDA, it's time to look at the data one step more closer. \nLet's have a quick look at the files that has been provided:**\n\n* train_study_level.csv - the train study-level metadata, with one row for each study, including correct labels.\n* train_image_level.csv - the train image-level metadata, with one row for each image, including both correct labels and any bounding boxes in a dictionary format. Some \n* images in both test and train have multiple bounding boxes.\n* sample_submission.csv - a sample submission file containing all image- and study-level IDs.\n\n**Columns**\n\n**train_study_level.csv**\n* id - unique study identifier\n* Negative for Pneumonia - 1 if the study is negative for pneumonia, 0 otherwise\n* Typical Appearance - 1 if the study has this appearance, 0 otherwise\n* Indeterminate Appearance  - 1 if the study has this appearance, 0 otherwise\n* Atypical Appearance  - 1 if the study has this appearance, 0 otherwise\n\n**train_image_level.csv**\n* id - unique image identifier\n* boxes - bounding boxes in easily-readable dictionary format\n* label - the correct prediction label for the provided bounding boxes","metadata":{}},{"cell_type":"markdown","source":"Here is the evaluation Metrics:\n<br>Standard PASCAL VOC 2010 mean average Precision at IoU > 0.5","metadata":{}},{"cell_type":"markdown","source":"### Time to Start with the Importing the Basic Libraries and Datasets","metadata":{}},{"cell_type":"markdown","source":"References: Took help from: \nhttps://www.kaggle.com/tanlikesmath/siim-covid-19-detection-a-simple-eda","metadata":{}},{"cell_type":"code","source":"!conda install -c conda-forge gdcm -y","metadata":{"execution":{"iopub.status.busy":"2021-06-25T04:51:24.964251Z","iopub.execute_input":"2021-06-25T04:51:24.965153Z","iopub.status.idle":"2021-06-25T04:52:38.49358Z","shell.execute_reply.started":"2021-06-25T04:51:24.965006Z","shell.execute_reply":"2021-06-25T04:52:38.492141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport os\nfrom os import listdir, mkdir\n\nimport pydicom\nimport scipy.ndimage\nimport gdcm\n\nimport glob\n\nfrom skimage import measure\nfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection\nfrom skimage.morphology import disk, opening, closing\nfrom tqdm.notebook import tqdm\n\nfrom IPython.display import HTML\nfrom PIL import Image\n\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom skimage import exposure\nimport cv2\n\nimport warnings\n\nimport vtk\nfrom vtk.util import numpy_support\nfrom fastai.vision.all import *\nfrom fastai.medical.imaging import *\n\nreader = vtk.vtkDICOMImageReader()\n\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:07:30.044661Z","iopub.execute_input":"2021-06-25T05:07:30.045222Z","iopub.status.idle":"2021-06-25T05:07:36.554164Z","shell.execute_reply.started":"2021-06-25T05:07:30.045171Z","shell.execute_reply":"2021-06-25T05:07:36.552829Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"listdir(\"../input/\")","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:08:04.849974Z","iopub.execute_input":"2021-06-25T05:08:04.850392Z","iopub.status.idle":"2021-06-25T05:08:04.858264Z","shell.execute_reply.started":"2021-06-25T05:08:04.850358Z","shell.execute_reply":"2021-06-25T05:08:04.857066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Time to have a quick look at the CSV files that is provided in the competition\ntrain_study_level = pd.read_csv(\"../input/siim-covid19-detection/train_study_level.csv\") # let's see the train_study_level.csv file\ntrain_image_level = pd.read_csv(\"../input/siim-covid19-detection/train_image_level.csv\") # let's see the train_image_level.csv file\n\ntrain_study_level.head(15) # I love seeing little more rows than just typical 5 rows for no reason lol","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:10:40.35155Z","iopub.execute_input":"2021-06-25T05:10:40.352002Z","iopub.status.idle":"2021-06-25T05:10:40.408211Z","shell.execute_reply.started":"2021-06-25T05:10:40.351967Z","shell.execute_reply":"2021-06-25T05:10:40.407013Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In the Above 15 rows and 4 columns of train_study_level.csv, you can see that we are predicting those 4 column values while moving forward in the model\n* **Negative for Pneumonia** - prediction value 1 if the study is negative for pneumonia, 0 for everything else\n* **Typical Appearance** - prediction value 1 if the study has this appearance, 0 for everything else\n* **Indeterminate Appearance** - prediction value 1 if the study has this appearance, 0 for everything else\n* **Atypical Appearance** - prediction value 1 if the study has this appearance, 0 for everything else\n\n* **id** - Unique Study Identifier","metadata":{}},{"cell_type":"code","source":"train_image_level.head(15) # Want to look at the same amount of rows as above, no compromise","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:10:49.147874Z","iopub.execute_input":"2021-06-25T05:10:49.148283Z","iopub.status.idle":"2021-06-25T05:10:49.164956Z","shell.execute_reply.started":"2021-06-25T05:10:49.14825Z","shell.execute_reply":"2021-06-25T05:10:49.16378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here is what we see in above train_image_level.csv file:\n* **id** - Unique image identifier for each image\n* **boxes** - bounding boxes of the image which is kept in easily-readable dictionary format\n* **label** - correction prediction label for the provided bounding boxes\n\n#### Further:\nIn label, we have values for bounding box which you can identify after 'opacity' & 'none'. Basically, There are two classes, opacity & none, so while moving forward we will create independent feature for that.\nAdditionally, we will split the confidence score & bounding box, if 'boxes' has NaN (there is no xmin, xmax, ymin, ymax) then the bounding box value will be 1 0 0 1 1 & vis-a-versa.\n\nLet's continue exploring","metadata":{}},{"cell_type":"code","source":"study_classes = ['Negative for Pneumonia', 'Typical Appearance', 'Indeterminate Appearance', 'Atypical Appearance']\nnp.unique(train_study_level[study_classes].values, axis=0)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:13:21.229053Z","iopub.execute_input":"2021-06-25T05:13:21.229403Z","iopub.status.idle":"2021-06-25T05:13:21.255652Z","shell.execute_reply.started":"2021-06-25T05:13:21.229373Z","shell.execute_reply":"2021-06-25T05:13:21.254352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Checking at the distribution of each Study Classes\nplt.figure(figsize=(10, 8))\nplt.bar([1,2,3,4], train_study_level[study_classes].values.sum(axis=0))\nplt.xticks([1,2,3,4], study_classes)\nplt.ylabel('Frequency')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:16:06.697657Z","iopub.execute_input":"2021-06-25T05:16:06.698077Z","iopub.status.idle":"2021-06-25T05:16:06.849464Z","shell.execute_reply.started":"2021-06-25T05:16:06.698042Z","shell.execute_reply":"2021-06-25T05:16:06.84815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Time to look at the image data\ntrain_image_level.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:17:41.954698Z","iopub.execute_input":"2021-06-25T05:17:41.955181Z","iopub.status.idle":"2021-06-25T05:17:41.970297Z","shell.execute_reply.started":"2021-06-25T05:17:41.955136Z","shell.execute_reply":"2021-06-25T05:17:41.969089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Format of the label column is broken in the way: class ID, confidence score and bounding box\n\nclass ID: either opacity or none\n\nconfidence score: confidence from neural network model, 1 if none\n\nbounding box: typical xmin ymin xmax y max format. if class ID is none, bounding box is 10011","metadata":{}},{"cell_type":"code","source":"# Checking the distribution of opacity vs none\ntrain_image_level['split_label'] = train_image_level.label.apply(lambda x: [x.split()[offs:offs+6] for offs in range(0, len(x.split()), 6)])\n\nclasses_freq = []\nfor i in range(len(train_image_level)):\n    for j in train_image_level.iloc[i].split_label: classes_freq.append(j[0])\nplt.hist(classes_freq)\nplt.ylabel('Frequency')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:27:58.494991Z","iopub.execute_input":"2021-06-25T05:27:58.495426Z","iopub.status.idle":"2021-06-25T05:27:59.400897Z","shell.execute_reply.started":"2021-06-25T05:27:58.495391Z","shell.execute_reply":"2021-06-25T05:27:59.399582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Time to check for the distribution of the bounding box areas","metadata":{}},{"cell_type":"code","source":"bbox_areas = []\n\nfor i in range(len(train_image_level)):\n    for j in train_image_level.iloc[i].split_label:\n        bbox_areas.append((float(j[4])-float(j[2]))*(float(j[5])*float(j[3])))\nplt.hist(bbox_areas)\nplt.ylabel('Frequency')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:30:20.21275Z","iopub.execute_input":"2021-06-25T05:30:20.213358Z","iopub.status.idle":"2021-06-25T05:30:21.183459Z","shell.execute_reply.started":"2021-06-25T05:30:20.213316Z","shell.execute_reply":"2021-06-25T05:30:21.182242Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Let's Start Exploring Image Dataset Now!","metadata":{}},{"cell_type":"markdown","source":"#### Let's Look at the Image Dataset Now","metadata":{}},{"cell_type":"code","source":"def dicom2array(path, voi_lut=True, fix_monochrome=True): # Converting pixel data to array\n    dicom = pydicom.read_file(path)\n    # voi_lut is used to transform raw DICOM data to \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n        \n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data # np.amax returns the maximum of an array or maximum along the axis (if mentioned)\n    data = data - np.min(data) # Actual value - minimum value of pixel array\n    data = data / np.max(data) # Actual value / maximum value of pixel array\n    data = (data * 255).astype(np.uint8)\n    return data\n        \ndef plot_img(img, size=(6, 6), is_rgb=True, title=\"\", cmap='gray'):\n    plt.figure(figsize=size)\n    plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()\n\n\ndef plot_imgs(imgs, cols=4, size=6, is_rgb=True, title=\"\", cmap='gray', img_size=(500,500)):\n    rows = len(imgs)//cols + 1\n    fig = plt.figure(figsize=(cols*size, rows*size))\n    for i, img in enumerate(imgs):\n        if img_size is not None:\n            img = cv2.resize(img, img_size)\n        fig.add_subplot(rows, cols, i+1)\n        plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:31:32.859331Z","iopub.execute_input":"2021-06-25T05:31:32.859799Z","iopub.status.idle":"2021-06-25T05:31:32.872887Z","shell.execute_reply.started":"2021-06-25T05:31:32.859762Z","shell.execute_reply":"2021-06-25T05:31:32.87152Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_path = Path('../input/siim-covid19-detection')\n\n# Let's look at sample 16 records for better understanding of images\ndicom_paths = get_dicom_files(data_path/'train')\nimgs = [dicom2array(path) for path in dicom_paths[:16]]\nplot_imgs(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:32:31.401531Z","iopub.execute_input":"2021-06-25T05:32:31.402039Z","iopub.status.idle":"2021-06-25T05:33:10.497026Z","shell.execute_reply.started":"2021-06-25T05:32:31.402001Z","shell.execute_reply":"2021-06-25T05:33:10.493892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's analyze how many images are available per study","metadata":{}},{"cell_type":"code","source":"# Looking at the image data per study\nimages_per_study = []\nfor i in (data_path/'train').ls():\n    images_per_study.append(len(get_dicom_files(i)))\n    if len(get_dicom_files(i)) > 5:\n        print(f'Study {i} had {len(get_dicom_files(i))} images')\n\nplt.hist(images_per_study)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:33:17.368494Z","iopub.execute_input":"2021-06-25T05:33:17.369117Z","iopub.status.idle":"2021-06-25T05:33:38.803331Z","shell.execute_reply.started":"2021-06-25T05:33:17.369059Z","shell.execute_reply":"2021-06-25T05:33:38.802128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setting up the image path for each images in study\ndef image_path(row):\n    study_path = data_path/'train'/row.StudyInstanceUID\n    for i in get_dicom_files(study_path):\n        if row.id.split('_')[0] == i.stem: return i \n        \ntrain_image_level['image_path'] = train_image_level.apply(image_path, axis=1)\n\n# Have a look at few\ntrain_image_level['image_path'].head()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:37:34.165706Z","iopub.execute_input":"2021-06-25T05:37:34.16619Z","iopub.status.idle":"2021-06-25T05:37:41.847493Z","shell.execute_reply.started":"2021-06-25T05:37:34.166149Z","shell.execute_reply":"2021-06-25T05:37:41.846477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize list for images\nimgs = []\n# set up image path values\nimage_paths = train_image_level['image_path'].values\n\n# Mapping label_id to specify color\nthickness = 10\nscale = 5\n\nfor i in range(8):\n    image_path = random.choice(image_paths)\n    print(image_path)\n    img = dicom2array(path=image_path)\n    img = cv2.resize(img, None, fx=1/scale, fy=1/scale, interpolation=cv2.INTER_CUBIC)\n    img = np.stack([img, img, img], axis=-1)\n    for i in train_image_level.loc[train_image_level['image_path'] == image_path].split_label.values[0]:\n        if i[0] == 'opacity':\n            img = cv2.rectangle(img,\n                                (int(float(i[2])/scale), int(float(i[3])/scale)),\n                                (int(float(i[4])/scale), int(float(i[5])/scale)),\n                                [255,0,0], thickness)\n    \n    img = cv2.resize(img, (500,500))\n    imgs.append(img)\n\n# Plotting Images\nplot_imgs(imgs, cmap=None)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:38:23.272366Z","iopub.execute_input":"2021-06-25T05:38:23.272817Z","iopub.status.idle":"2021-06-25T05:38:29.666398Z","shell.execute_reply.started":"2021-06-25T05:38:23.272775Z","shell.execute_reply":"2021-06-25T05:38:29.665443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It's now all about submission, never worried for now, going to keep improving the notebook every day, will see how far I can go! Learning every day! :)","metadata":{}},{"cell_type":"code","source":"submission_df = pd.read_csv(data_path/'sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:40:33.523434Z","iopub.execute_input":"2021-06-25T05:40:33.523885Z","iopub.status.idle":"2021-06-25T05:40:33.543236Z","shell.execute_reply.started":"2021-06-25T05:40:33.52385Z","shell.execute_reply":"2021-06-25T05:40:33.54231Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:40:34.96923Z","iopub.execute_input":"2021-06-25T05:40:34.969817Z","iopub.status.idle":"2021-06-25T05:40:34.980886Z","shell.execute_reply.started":"2021-06-25T05:40:34.969765Z","shell.execute_reply":"2021-06-25T05:40:34.980116Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.iloc[2000:2010]","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:40:46.040576Z","iopub.execute_input":"2021-06-25T05:40:46.041133Z","iopub.status.idle":"2021-06-25T05:40:46.053705Z","shell.execute_reply.started":"2021-06-25T05:40:46.041095Z","shell.execute_reply":"2021-06-25T05:40:46.052427Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-25T05:40:50.22651Z","iopub.execute_input":"2021-06-25T05:40:50.226888Z","iopub.status.idle":"2021-06-25T05:40:50.244736Z","shell.execute_reply.started":"2021-06-25T05:40:50.226853Z","shell.execute_reply":"2021-06-25T05:40:50.243383Z"},"trusted":true},"execution_count":null,"outputs":[]}]}