{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: center; font-family: Verdana; font-size: 32px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; font-variant: small-caps; letter-spacing: 3px; color: #7b4f44; background-color: #ffffff;\">VinBigData Chest X-ray Abnormalities Detection</h1>"},{"metadata":{},"cell_type":"markdown","source":"### About Competation\n\nWhen we have a broken arm, radiologists help save the day—and the bone. These doctors diagnose and treat medical conditions using imaging techniques like CT and PET scans, MRIs, and, of course, X-rays. Yet, as it happens when working with such a wide variety of medical tools, radiologists face many daily challenges, perhaps the most difficult being the chest radiograph. The interpretation of chest X-rays can lead to medical misdiagnosis, even for the best practicing doctor. Computer-aided detection and diagnosis systems (CADe/CADx) would help reduce the pressure on doctors at metropolitan hospitals and improve diagnostic quality in rural areas.\n\nExisting methods of interpreting chest X-ray images classify them into a list of findings. There is currently no specification of their locations on the image which sometimes leads to inexplicable results. A solution for localizing findings on chest X-ray images is needed for providing doctors with more meaningful diagnostic assistance.\n\nIn this competition, we’ll automatically localize and classify 14 types of thoracic abnormalities from chest radiographs. We'll work with a dataset consisting of 18,000 scans that have been annotated by experienced radiologists. train model with 15,000 independently-labeled images and will be evaluated on a test set of 3,000 images. "},{"metadata":{},"cell_type":"markdown","source":"# 1: IMPORTING LIBRARIES AND DATA"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\n\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport seaborn as sns\nimport plotly.express as px\nimport plotly.graph_objects as go\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport cv2\nimport pydicom\nfrom pydicom import dcmread\nfrom pathlib import Path","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\ndf.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.info()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1.1: DATA DESCRIPTION\n\n**Columns**\n- `image_id` - unique image identifier\n- `class_name` - the name of the class of detected object\n- `class_id` - the ID of the class of detected object\n- `rad_id` - the ID of the radiologist that made the observation\n- `x_min` - minimum X coordinate of the object's bounding box\n- `y_min` - minimum Y coordinate of the object's bounding box\n- `x_max` - maximum X coordinate of the object's bounding box\n- `y_max` - maximum Y coordinate of the object's bounding box\n\n\n**LABELS**\n- `0` - Aortic enlargement\n- `1` - Atelectasis\n- `2` - Calcification\n- `3` - Cardiomegaly\n- `4` - Consolidation\n- `5` - ILD\n- `6` - Infiltration\n- `7` - Lung Opacity\n- `8` - Nodule/Mass\n- `9` - Other lesion\n- `10` - Pleural effusion\n- `11` - Pleural thickening\n- `12` - Pneumothorax\n- `13` - Pulmonary fibrosis\n- `14` - No Findings\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Aortic enlargement</b>\n* Aortic enlargement is known as a sign of an aortic aneurysm. This condition often occurs in the ascending aorta. \n* In general, the term aneurysm is used when the axial diameter is >5.0 cm for the ascending aorta and >4.0 cm for the descending aorta.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Atelectasis</b>\n* Atelectasis is a condition where there is no air in part or all of the lungs and they have collapsed. \n* A common cause of atelectasis is obstruction of the bronchi.\n* In atelectasis, there is an increase in density on chest x-ray (usually whiter; black on black-and-white inversion images).\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Calcification</b>\n* Calcium (calcification) may be deposited in areas where previous inflammation of the lungs or pleura has healed. \n* Many diseases or conditions can cause calcification on chest x-ray. \n* Calcification may occur in the Aorta (as with atherosclerosis) or it may occur in mediastinal lymph nodes (as with previous infection, tuberculosis, or histoplasmosis).\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Cardiomegaly</b>\n* Cardiomegaly is usually diagnosed when the ratio of the heart's width to the width of the chest is more than 50%. This diagnostic criterion may be an essential basis for this competition.\n* Cardiomegaly can be caused by many conditions, including hypertension, coronary artery disease, infections, inherited disorders, and cardiomyopathies.\n* The heart-to-lung ratio criterion for the diagnosis of cardiomegaly is a ratio of greater than 0.5. However, this is only valid if the XRay is performed while the patient is standing. If the patient is sitting or in bed, this criterion cannot be used. To determine whether a patient is sitting or standing (and consequently whether this criteron is valid), we will detect the presence of air in the stomach (if there is no air in it, the patient is not standing and the criterion cannot be used)\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Consolidation</b>\n* Consolidation is a decrease in lung permeability due to infiltration of fluid, cells, or tissue replacing the air-containing spaces in the alveoli.\n* Consolidation is officially referred to as air space consolidation. \n* On X-rays displaying air space consolidation, the lung field's density is increased, and pulmonary blood vessels are not seen, but black bronchi can be seen in the white background, which is called <i>\"air bronchogram\"</i>. Since air remains in the bronchial tubes, they do not absorb X-rays and appear black, and the black and white are reversed from normal lung fields.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">ILD</b>\n* ILD stands for <i>\"Interstitial Lung Disease\"</i>.\n* Interstitial Lung Disease is a general term for many conditions in which the interstitial space is injured. \n* The interstitial space refers to the walls of the alveoli (air sacs in the lungs) and the space around the blood vessels and small airways.\n* Chest radiographic findings include ground-glass opacities (i.e., an area of hazy opacification), linear reticular shadows, and granular shadows.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Infiltration</b>\n* The infiltration of some fluid component into the alveoli causes an infiltrative shadow (Infiltration).\n* It is difficult to distinguish from consolidation and, in some cases, impossible to distinguish. Please see [this link](https://allnurses.com/consolidation-vs-infiltrate-vs-opacity-t483538/) for more information.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Lung Opacity</b>\n* Lung opacity is a loose term with many potential interpretations/meanings. Please see this [kaggle discussion](https://www.kaggle.com/zahaviguy/what-are-lung-opacities) for more information.\n* Lung opacity can often be identified as any area in the chest radiograph that is <b>more white than it should be.</b>\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nodule/Mass</b>\n* Nodules and masses are seen primarily in lung cancer, and metastasis from other parts of the body such as colon cancer and kidney cancer, tuberculosis, pulmonary mycosis, non-tuberculous mycobacterium, obsolete pneumonia, and benign tumors.\n* A nodule/mass is a round shade (typically less than 3 cm in diameter – resulting in much smaller than average bounding boxes) that appears on a chest X-ray image. \n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Other lesion</b>\n* Others include all abnormalities that do not fall into any other category. This includes bone penetrating images, fractures, subcutaneous emphysema, etc.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Pleural effusion</b>\n* Pleural effusion is the accumulation of water outside the lungs in the chest cavity. \n* The outside of the lungs is covered by a thin membrane consisting of two layers known as the pleura. Fluid accumulation between these two layers (chest-wall/parietal-pleura and the lung-tissue/visceral-pleura) is called pleural effusion.\n* The findings of pleural effusion vary widely and vary depending on whether the radiograph is taken in the upright or supine position.\n* The most common presentation of pleural effusion is <b>elevation of the diaphragm on one side, flattening the diaphragm, or blunting the angle between rib and diaphragm (typically more than 30 degrees)</b>\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Pleural thickening</b>\n* The pleura is the membrane that covers the lungs, and the change in the thickness of the pleura is called pleural thickening. \n* It is often seen in the uppermost part of the lung field (the apex of the lung).\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Pneumothorax</b>\n* A pneumothorax is a condition in which air leaks from the lungs and accumulates in the chest cavity. \n* When air leaks and accumulates in the chest, it cannot expand outward like a balloon due to the ribs' presence. Instead, the lungs are pushed by the air and become smaller. In other words, a pneumothorax is a situation where air leaks from the lungs and the lungs become smaller (collapsed).\n* In a chest radiograph of a pneumothorax, the collapsed lung is whiter than normal, and the area where the lung is gone is uniformly black. Besides, the edges of the lung may appear linear.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Pulmonary fibrosis</b>\n* Pulmonary Fibrosis is inflammation of the lung interstitium due to various causes, resulting in thickening and hardening of the walls, fibrosis, and scarring.\n* The fibrotic areas lose their air content, which often results in dense cord shadows or granular shadows.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">No finding</b>\n* There are no findings on x-ray images. This is the normal image and is the baseline image needed to differentiate from the abnormal image."},{"metadata":{},"cell_type":"markdown","source":"# 2: DATA VIZUALIZATION & ANALYSIS"},{"metadata":{"trusted":true},"cell_type":"code","source":"df['rad_id'].unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df['class_name'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.histogram(df, x=\"class_name\", color=\"class_name\")\n\nfig.update_layout(\n    yaxis=dict(title_text='Count', titlefont=dict(size=20)),\n    xaxis=dict(title_text='Abnormality Label Name', titlefont=dict(size=20)),\n    title_text='Abnormalities Count Plot'\n)\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def percent_distribution(data):\n    # Get the count for each label\n    label_counts = data.class_name.value_counts()\n\n    # Get total number of samples\n    total_samples = len(data)\n\n    # Count the number of items in each class\n    for i in range(len(label_counts)):\n        label = label_counts.index[i]\n        count = label_counts.values[i]\n        percent = int((count / total_samples) * 10000) / 100\n        print(\"{:<30s}:   {} or {}%\".format(label, count, percent))\n\npercent_distribution(df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.pairplot(df, hue='class_id');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_width_of__bounding_boxes(data):\n    \n    fig, axes = plt.subplots(7, 2, figsize=(16,20), sharex=True)\n    fig.suptitle(\"width of bounding box for different categories\", fontsize=16)\n    \n    classes = data.class_name.unique()\n    for j, i in enumerate(classes[~np.isin(classes, 'No finding')]):\n        data_ = data[data['class_name']==i]\n        sns.distplot(data_['x_max'] - data_['x_min'], ax=axes[j%7, j//7]);\n        axes[j%7, j//7].title.set_text(i);\n    plt.show()\n\nplot_width_of__bounding_boxes(df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_height_of_bounding_boxes(data):\n    \n    fig, axes = plt.subplots(7, 2, figsize=(16,20), sharex=True)\n    fig.suptitle(\"height of bounding box for different categories\", fontsize=16)\n    \n    classes = data.class_name.unique()\n    for j, i in enumerate(classes[~np.isin(classes, 'No finding')]):\n        data_ = data[data['class_name']==i]\n        sns.distplot(data_['y_max'] - data_['y_min'], ax=axes[j%7, j//7]);\n        axes[j%7, j//7].title.set_text(i);\n    plt.show()\n\nplot_height_of_bounding_boxes(df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_area_of_bounding_boxes(data):\n    \n    fig, axes = plt.subplots(7, 2, figsize=(16,20), sharex=True)\n    fig.suptitle(\"area of bounding box for different categories\", fontsize=16)\n    \n    classes = data.class_name.unique()\n    for j, i in enumerate(classes[~np.isin(classes, 'No finding')]):\n        data_ = data[data['class_name']==i]\n        sns.distplot((data_['y_max'] - data_['y_min'])*(data_['x_max'] - data_['x_min']), \n                     ax=axes[j%7, j//7]);\n        axes[j%7, j//7].title.set_text(i);\n    plt.show()\n\nplot_area_of_bounding_boxes(df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3: IMAGE DATA VIZUALIZTION"},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_xray(path, voi_lut = True, fix_monochrome = True):\n    \"\"\" Convert dicom file to numpy array \n    \n    Args:\n        path (str): Path to the dicom file to be converted\n        voi_lut (bool): Whether or not VOI LUT is available\n        fix_monochrome (bool): Whether or not to apply monochrome fix\n        \n    Returns:\n        Numpy array of the respective dicom file \n        \n    \"\"\"\n    dicom = pydicom.read_file(path)      # Use the pydicom library to read the dicom file\n    \n    # VOI LUT (if available by DICOM device) 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\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n        \n    return data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"root = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/'\next = '.dicom'\n\ndata = read_xray(root + df.image_id[0] + ext)\nplt.imshow(data, 'gray');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_xray(data, root=root, ext=ext):\n    fig, axs = plt.subplots(3,3, figsize=(16,18))\n    \n    for i in range(9):\n        k = np.random.randint(0,len(data))  #selecting random integer for plotting XRAY\n        img_id = data.image_id[k]\n        class_name = data.class_name[k]\n        \n        img_path = os.path.join(root, img_id+ext)\n        dicom_pixel = read_xray(img_path)\n        \n        axs[i//3,i%3].imshow(dicom_pixel, cmap='gray')\n        axs[i//3,i%3].title.set_text(class_name)\n    plt.show()\n\nshow_xray(df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.1 Vizualising Anchor Boxes"},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_xray_withAnchorBoxs(data, root=root, ext=ext, n=3):\n    fig, axs = plt.subplots(n,n, figsize=(40,40))\n\n    un_xrays = data.image_id.unique()\n    for i in range(n*n):\n        k = np.random.randint(0,len(un_xrays))  #selecting random integer for plotting XRAY\n        xray_id = un_xrays[k] #taking xray name (image_id)\n        \n        abnorm_df = data[data.image_id==xray_id] #this df contains all anchor boxes loaction for a particula image_id\n        abnorm_df.reset_index(drop=True, inplace=True) #reseting index\n   \n        xray_path = os.path.join(root, xray_id+ext) #path of that image\n        img = read_xray(xray_path) #reading dicom file\n        \n        axs[i//n,i%n].imshow(img, cmap='gray')#plotting the image\n        \n        for j in range(len(abnorm_df)):\n\n            axs[i//n,i%n].add_patch(patches.Rectangle(\n                (abnorm_df['x_min'][j], abnorm_df['y_min'][j]), \n                abnorm_df['x_max'][j] - abnorm_df['x_min'][j], \n                abnorm_df['y_max'][j] - abnorm_df['y_min'][j], \n                edgecolor='y',\n                linewidth=1,\n                fill=False)\n            )\n            axs[i//n,i%n].text(\n                abnorm_df['x_min'][j], abnorm_df['y_min'][j], #coordinate\n                abnorm_df.class_name[j],  #sting \n                bbox=dict(fill=True, edgecolor='yellow', linewidth=2)\n            )\n    plt.show()\n\nshow_xray_withAnchorBoxs(df[df.class_name!='No finding']) #filtering no finding images","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"show_xray_withAnchorBoxs(df[df.class_name!='No finding'], n=4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.2 Generating Heatmaps"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# courtsy of https://www.kaggle.com/craigmthomas/localization-of-findings\n\ndef add_image_dimensions_gender(df):\n    path_spec = \"../input/vinbigdata-chest-xray-abnormalities-detection/train/{}.dicom\"\n    height = []\n    width = []\n    gender = []\n    age = []\n    for _, row in df.iterrows():\n        dcm = dcmread(Path(path_spec.format(row[\"image_id\"])), stop_before_pixels=True)\n        height.append(dcm.Rows)\n        width.append(dcm.Columns)\n        gender.append(dcm[0x10, 0x40].value)\n        patient_age = dcm[0x10, 0x1010].value if [0x10, 0x1010] in dcm else \"\"\n        age.append(patient_age)\n    df[\"image_height\"] = height\n    df[\"image_width\"] = width\n    df[\"gender\"] = gender\n    df[\"age\"] = age\n    \n    return df\n\ndef scale_bounding_boxes(df):\n    df[\"x_min_norm\"] = df[\"x_min\"] / df[\"image_width\"]\n    df[\"y_min_norm\"] = df[\"y_min\"] / df[\"image_height\"]\n    df[\"x_max_norm\"] = df[\"x_max\"] / df[\"image_width\"]\n    df[\"y_max_norm\"] = df[\"y_max\"] / df[\"image_height\"]\n    \n    return df\n\ndef draw_box_on_array(row, np_array):\n    xy = [\n        int(row[\"x_min_norm\"] * 400), \n        int(row[\"y_min_norm\"] * 500),\n        int(row[\"x_max_norm\"] * 400),\n        int(row[\"y_max_norm\"] * 500),\n    ]\n    np_array[xy[1]:xy[3], xy[0]:xy[2]] += 1\n    \ndef get_bbox(df, class_id):\n    np_array = np.zeros(shape=(500, 400))\n    if class_id == 14:\n        return np_array\n    for _, row in df[df[\"class_id\"] == class_id].iterrows():\n        draw_box_on_array(row, np_array)\n    return np_array\n\ndef plot_heatmap(data=df):\n    data = add_image_dimensions_gender(data)\n    data = scale_bounding_boxes(data)\n    \n    classes = [\n        \"0 - Aortic enlargement\",\n        \"1 - Atelectasis\",\n        \"2 - Calcification\",\n        \"3 - Cardiomegaly\", \n        \"4 - Consolidation\",\n        \"5 - ILD\",\n        \"6 - Infiltration\",\n        \"7 - Lung Opacity\",\n        \"8 - Nodule/Mass\",\n        \"9 - Other lesion\",\n        \"10 - Pleural effusion\",\n        \"11 - Pleural thickening\",\n        \"12 - Pneumothorax\",\n        \"13 - Pulmonary fibrosis\",\n        \"14 - No finding\",\n    ]\n\n    fig, axs = plt.subplots(nrows=5, ncols=3, figsize=(20, 30))\n    for i, ax in enumerate(axs.flatten()):\n        ax.imshow(get_bbox(df, i), cmap='hot', interpolation='nearest')\n        _ = ax.set_title(classes[i], fontweight=\"bold\", size=15)\n        \nplot_heatmap()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: green; background-color: #ffffff;\"> Work In Progress</h3>\n\n<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: green; background-color: #ffffff;\"> Do upvote if it helped and comment</h3>"}],"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}