{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Vinbigdata Chest X-ray Object Detection & Multi-Class Classification 2021"},{"metadata":{},"cell_type":"markdown","source":">  ### Problem Statement: This is an object detection and multi-label classification problem\n\n#### Task \n\nIn this competition, I am trying to localize and classify **14 types of thoracic abnormalities** from chest radiographs (X-rays).\n\n#### Dataset information\n\nThe dataset consisting of 18,000 (X-ray) scans that have been annotated by experienced radiologists.\n\nThe images are in DICOM format, which means they contain 'meta data' that might be useful for visualizing and classifying.\n\nAll images were labeled by a panel of experienced radiologists for the presence of 14 critical radiographic findings as listed below:\n\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### Machine Learning Model\n\nI am buildig objection detection model with **YOLOv4** to train with 15,000 independently-labeled images.\n\nI'd be evaluating it on a given test set of 3,000 images.\n\nFor each test image, I will be predicting a bounding box and class for all findings. \n\nIf the prediction is no findings, I should create a prediction of \"14 1 0 0 1 1\" (14 is the class ID for no finding, and this provides a one-pixel bounding box with a confidence of 1.0).\n\n> ## Let's first start with the preprocessing of the data and do some insightful exploratory analysis!"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# import libraries required\nimport os,sys,PIL,pydicom\nfrom PIL import Image\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom seaborn import countplot\nfrom matplotlib.pyplot import suptitle\nfrom pydicom import dcmread\nfrom pydicom.data import get_testdata_file\nfrom pathlib import Path\nfrom pydicom.datadict import DicomDictionary, keyword_dict\nfrom pydicom.data import get_testdata_file\nimport tempfile","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Preprocessing\n\n### Number of DICOM image files "},{"metadata":{"trusted":true},"cell_type":"code","source":"# path to train and test image dicom files\nsource_dir_train = Path('../input/vinbigdata-chest-xray-abnormalities-detection/train/')\nsource_dir_test = Path('../input/vinbigdata-chest-xray-abnormalities-detection/test/')\n# lists of dicom files\nimgtrain_files  = os.listdir(source_dir_train)\nimgtest_files =os.listdir(source_dir_test)\n# number of files\nprint(\"Number of training image files with meta data:\",len(imgtrain_files))\nprint(\"Number of test image files with meta data    :\",len(imgtest_files))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Load train data"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = pd.read_csv(\"../input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\npd.set_option('display.max_colwidth',None)\ntrain_data.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### NA values replaced  with zero in last 4 columns"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data.fillna(0,inplace=True)\ntrain_data.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Number of Unique Image IDs      :\",len(train_data['image_id'].unique()))\nprint(\"Number of Classes               :\",len(train_data['class_name'].unique()))\nprint(\"Number of Unique Radiologist IDs:\",len(train_data['rad_id'].unique()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data Structure of Dicom file\n\n### Data Element Tag\n\n(Group Number,Element Number) - An ordered pair of 16-bit unsigned integers \n\n### Value Representation (VR)\n\nThe VR for Data Element Tag - Two single byte characters - encoded using only upper case letters from the DICOM default character set\n\n### Value Length\n\n16 or 32-bit unsigned integer containing the Explicit Length of the Value Field as the number of bytes (even) that make up the Value\n\nOR\n\n32-bit Length Field set to Undefined Length (FFFFFFFFH). \n\n### Value Field\n\nAn even number of bytes containing the Value(s) of the Data Element.\n\nThe data type of Value(s) stored in this field is specified by the Data Element's VR. The VR for a given Data Element Tag can be determined using the Data Dictionary. The Value Multiplicity specifies how many Values with this VR can be placed in the Value Field.\n\n\n[Read Michele's Blog to Understand DICOM](https://mscipio.github.io/post/read_dicom_files_in_python/)"},{"metadata":{},"cell_type":"markdown","source":"### Read DICOM File"},{"metadata":{"trusted":true},"cell_type":"code","source":"file_path = source_dir_train\nfile_name = imgtrain_files[9]\nds = pydicom.dcmread(os.path.join(file_path,file_name))\nds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Read DICOM Meta data "},{"metadata":{"trusted":true},"cell_type":"code","source":"print(ds.file_meta)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Read DICOM Dictionary"},{"metadata":{"trusted":true},"cell_type":"code","source":"ds.dir()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Parsing the data elements from the DICOM file"},{"metadata":{"trusted":true},"cell_type":"code","source":"def myprint(dataset, indent=0):\n    dont_print = ['Pixel Data', 'File Meta Information Version']\n    indent_string = \"   \" * indent\n    next_indent_string = \"   \" * (indent + 1)\n    \n    for data_element in dataset:\n        if data_element.VR == \"SQ\":   # a sequence\n            print(indent_string, data_element.name)\n            for sequence_item in data_element.value:\n                myprint(sequence_item, indent + 1)\n                print(next_indent_string + \"---------\")\n        else:\n            if data_element.name in dont_print:\n                print(\"\"\"<item not printed -- in the \"don't print\" list>\"\"\")\n            else:\n                repr_value = repr(data_element.value)\n                if len(repr_value) > 50:\n                    repr_value = repr_value[:50] + \"...\"\n                print(\"{0:s} {1:s} = {2:s}\".format(indent_string,\n                                                   data_element.name,\n                                                   repr_value))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Parsing the data elements from DICOM file"},{"metadata":{"trusted":true},"cell_type":"code","source":"file_path = source_dir_train\nfile_name = imgtrain_files[9]\nds = pydicom.dcmread(os.path.join(file_path,file_name))\nmyprint(ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Function to take care of teh translation and windowing\n\ndef window_image(img, window_center,window_width, intercept, slope, rescale=True):\n    img = (img*slope +intercept) #for translation adjustments given in the dicom file. \n    img_min = window_center - window_width//2 #minimum HU level\n    img_max = window_center + window_width//2 #maximum HU level\n    img[img<img_min] = img_min #set img_min for all HU levels less than minimum HU level\n    img[img>img_max] = img_max #set img_max for all HU levels higher than maximum HU level\n    if rescale: \n        img = (img - img_min) / (img_max - img_min)*255.0 \n    return img\n    \ndef get_first_of_dicom_field_as_int(x):\n    #get x[0] as in int is x is a 'pydicom.multival.MultiValue', otherwise get int(x)\n    if type(x) == dcm.multival.MultiValue: return int(x[0])\n    else: return int(x)\n    \ndef get_windowing(data):\n    dicom_fields = [data[('0028','1050')].value, #window center\n                    data[('0028','1051')].value, #window width\n                    data[('0028','1052')].value, #intercept\n                    data[('0028','1053')].value] #slope\n    return [get_first_of_dicom_field_as_int(x) for x in dicom_fields]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def view_images(files, title = '', aug = None, windowing = True):\n    width = 2\n    height = 2\n    fig, axs = plt.subplots(height, width, figsize=(15,15))\n    \n    for im in range(0, height * width):\n        data = dcm.dcmread(files[im])\n        image = data.pixel_array\n        window_center , window_width, intercept, slope = get_windowing(data)\n        if windowing:\n            output = window_image(image, window_center, window_width, intercept, slope, rescale = False)\n        else:\n            output = image\n        i = im // width\n        j = im % width\n        axs[i,j].imshow(output, cmap=plt.cm.gray) \n        axs[i,j].axis('off')\n        \n    plt.suptitle(title)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### X-ray image"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Function to plot the X-ray images from dicom files\nfile_path = source_dir_train\nfile_name = imgtrain_files[9]\n\ndef show_image(PATH,NAME):\n    ds = pydicom.dcmread(os.path.join(PATH,NAME))\n    plt.imshow(ds.pixel_array,cmap=plt.cm.gray)\n    plt.show()\n    \nshow_image(file_path,file_name)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Down sampling the image"},{"metadata":{"trusted":true},"cell_type":"code","source":"# function to down sample the image and change BitsStored to 16\n\ndef down_sample(PATH,NAME):\n    ds = pydicom.dcmread(os.path.join(PATH,NAME))\n    ds.BitsStored= 16 # BitsStored replace to 16\n    data = ds.pixel_array\n    data_downsampling = data[::16, ::16]\n    # copy the data back to the original data set\n    ds.PixelData = data_downsampling.tobytes()\n    # update the information regarding the shape of the data array\n    ds.Rows, ds.Columns = data_downsampling.shape\n    return ds\n    \nfile_path = source_dir_train\nfile_name = imgtrain_files[9]\ndown_sample(file_path,file_name)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Reading all image files and saving to list"},{"metadata":{"trusted":true},"cell_type":"code","source":"imgtrain_files = os.listdir(Path('../input/vinbigdata-chest-xray-abnormalities-detection/train/'))\nimgtest_files = os.listdir(Path('../input/vinbigdata-chest-xray-abnormalities-detection/test/'))\n\nfile_path = source_dir_train\nfile_path1 = source_dir_test\n\nfor file_name in imgtrain_files:\n    downsampled_tr_files=[]\n    downsampled_tr_files.append(down_sample(file_path,file_name))\n    \nfor file_name in imgtest_files:\n    downsampled_test_files=[]\n    downsampled_test_files.append(down_sample(file_path1,file_name))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"downsampled_tr_files[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Exploratory Data Analysis"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Number of Image ID\",len(train_data['image_id'].unique()))\nprint(\"Number of Class\",len(train_data['class_name'].unique()))\nprint(\"Number of Radiologist ID\",len(train_data['rad_id'].unique()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Names of Class ID  & Class"},{"metadata":{"trusted":true},"cell_type":"code","source":"class_names = train_data.class_name.unique()\nclass_ID = train_data.class_id.unique()\nRadiologist_ID = train_data.rad_id.unique()\n\nfor id, name in sorted(zip(class_ID,class_names)):\n    print(\"ClassID =\",id,\"->\",name)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Distribution of Class"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(20,5))\nsns.kdeplot(data=train_data['x_min'].value_counts())\nplt.suptitle(\"Count values of findings\\n\")\nplt.xlabel(\"Number of findings\\n\")\nplt.show()\nplt.figure(figsize=(20,5))\nsns.kdeplot(data=train_data['x_max'].value_counts())\nplt.suptitle(\"Count values of findings\\n\")\nplt.xlabel(\"Number of findings\\n\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(20,5))\nsns.kdeplot(train_data['x_min'].value_counts())\nplt.suptitle(\"Count valuys of findings\\n\")\nplt.xlabel(\"Number of findings\\n\")\nplt.figure(figsize=(20,5))\nsns.kdeplot(train_data['y_max'].value_counts())\nplt.suptitle(\"Count values of findings\\n\")\nplt.xlabel(\"Number of findings\\n\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(30,10))\nsns.countplot(x=\"class_name\",data=train_data)\nplt.suptitle(\"Count values of findings\")\nplt.xlabel(\"Class\\n\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,10))\nsns.countplot(y=\"rad_id\",data=train_data)\nplt.suptitle(\"Count values of findings\\n\")\nplt.xlabel(\"Number of findings\\n\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,10))\nplt.scatter(train_data.x_min,train_data.x_max,alpha=0.5)\nplt.show()\nplt.figure(figsize=(10,10))\nplt.scatter(train_data.y_min,train_data.y_max,alpha=0.5)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(30,10))\nsns.boxplot(x=train_data.class_name,y=train_data.x_max)\nplt.show()\nplt.figure(figsize=(30,10))\nsns.boxplot(x=train_data.class_name,y=train_data.y_max)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### count plot"},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nsns.set_context('paper',font_scale=1.5)\nplt.figure(figsize=(20,15))\nsns.countplot(y=ifood2.state,hue=ifood2['diet'])\nplt.ylabel('States\\n')\nplt.xlabel('counts\\n')\nplt.title(\"Number of Food Items from Each State - Veg & Non-Veg\\n\")\nplt.show()","execution_count":null,"outputs":[]}],"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}