{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n#for dirname, _, filenames in os.walk('/kaggle/input/siim-covid19-detection/train'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\nimport torch \nimport cv2\nimport torch.nn as nn\nimport glob\nimport pydicom\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nfrom pydicom import dcmread\nimport ast\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-23T14:35:58.313563Z","iopub.execute_input":"2021-06-23T14:35:58.313870Z","iopub.status.idle":"2021-06-23T14:35:59.520372Z","shell.execute_reply.started":"2021-06-23T14:35:58.313843Z","shell.execute_reply":"2021-06-23T14:35:59.519449Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## In this notebook, I will apply the following method to solve the problem.\n###    1- Join the two train csv\n###    2- Have a look at the annotated boxes and what class is inside\nBy collecting the boxes, find the interesting part of the photos for the analysis.\nSelect and keep this parts of the image, and then mirror the right image to get a similar support.\n\n###    3- Convert every train dcm images to np.array\n###    4- Separate and mirror the right lung.\n###    5- First try transfer learning with ResNet to classify the images.\n###    6- If the performance is good enough with the training and validation dataset, I will create a test dataset by clipping the test images. \n    ","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"\n# 0- Import the Data\n\nFiles\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 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\ntrain_study_level.csv\n\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\ntrain_image_level.csv\n\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\n\nPath\n","metadata":{}},{"cell_type":"code","source":"DIR_PATH = \"../input/siim-covid19-detection\"\ntrain_img_path = f\"{DIR_PATH}/train_image_level.csv\"\ntrain_stdy_path = f\"{DIR_PATH}/train_study_level.csv\"\ntrain_path = f\"{DIR_PATH}/train\"\n\n#loading csv file using pandas \n\ntrain_img_df = pd.read_csv(train_img_path)\ntrain_stdy_df = pd.read_csv(train_stdy_path)\n#train_df.sample(5)\n\n","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:35:59.521849Z","iopub.execute_input":"2021-06-23T14:35:59.522148Z","iopub.status.idle":"2021-06-23T14:35:59.581577Z","shell.execute_reply.started":"2021-06-23T14:35:59.522119Z","shell.execute_reply":"2021-06-23T14:35:59.580658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lets have a look to the data","metadata":{}},{"cell_type":"code","source":"train_img_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:36:17.581286Z","iopub.execute_input":"2021-06-23T14:36:17.581632Z","iopub.status.idle":"2021-06-23T14:36:17.604429Z","shell.execute_reply.started":"2021-06-23T14:36:17.581596Z","shell.execute_reply":"2021-06-23T14:36:17.603701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_stdy_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:36:22.040694Z","iopub.execute_input":"2021-06-23T14:36:22.041227Z","iopub.status.idle":"2021-06-23T14:36:22.051468Z","shell.execute_reply.started":"2021-06-23T14:36:22.041183Z","shell.execute_reply":"2021-06-23T14:36:22.050707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1- Join the two training dataframes on the StudyInstanceUID.","metadata":{}},{"cell_type":"code","source":"#Remove the study suffix\ntrain_stdy_df['StudyInstanceUID'] = train_stdy_df['id'].apply(lambda x: x.replace('_study', ''))\ntrain_img_df['id'] = train_img_df['id'].apply(lambda x: x.replace('_image', ''))\n","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:36:25.951831Z","iopub.execute_input":"2021-06-23T14:36:25.952294Z","iopub.status.idle":"2021-06-23T14:36:25.963951Z","shell.execute_reply.started":"2021-06-23T14:36:25.952262Z","shell.execute_reply":"2021-06-23T14:36:25.963343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_img_df.merge(train_stdy_df, on='StudyInstanceUID')","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:36:29.928154Z","iopub.execute_input":"2021-06-23T14:36:29.928621Z","iopub.status.idle":"2021-06-23T14:36:29.946932Z","shell.execute_reply.started":"2021-06-23T14:36:29.928591Z","shell.execute_reply":"2021-06-23T14:36:29.946199Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:36:31.884115Z","iopub.execute_input":"2021-06-23T14:36:31.884460Z","iopub.status.idle":"2021-06-23T14:36:31.897769Z","shell.execute_reply.started":"2021-06-23T14:36:31.884432Z","shell.execute_reply":"2021-06-23T14:36:31.896810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2- First focus on the boxes","metadata":{}},{"cell_type":"markdown","source":"A few functions to convert à visualize the images","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pydicom\nimport re\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom pydicom.data import get_testdata_file\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\n# Convert the dcm image to np.array\ndef dicom2array(path, voi_lut = True, fix_monochrome = True):\n    \n    dicom = pydicom.read_file(path)\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\n    \n# plot some images\ndef plot_images(imgs, cols=4, size=7, 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()\n    \n# Plot a single image\ndef plot_image(img, title=\"\", figsize=(8,8), cmap=None):\n    plt.figure(figsize=figsize)\n    \n    if cmap:\n        plt.imshow(img, cmap=cmap)\n    else:\n        img\n        plt.imshow(img)\n        \n    plt.title(title, fontweight=\"bold\")\n    plt.axis(False)\n    plt.show()\n    \ndef get_image_id(path):\n    \"\"\" Function to return the image-id from a path \"\"\"\n    return path.rsplit(\"/\", 1)[1].rsplit(\".\", 1)[0]\n\ndef get_coordinates_boxes(box):\n    # separate boxes\n    l_box = box.split(\"}, {\")\n\n    # Read the numeric values\n    df_coord=pd.DataFrame([re.findall(r' [0-9]+\\.*',lb) for lb in l_box], columns=['x','y','width','height'], dtype=float)\n    \n    return df_coord\n\ndef get_rows_boxes(box, infos_img) :\n    a_box = get_coordinates_boxes(box)\n    #Calculate the relative coord (%)\n    a_box['x_rel'] = 100.*a_box['x']/infos_img[0]\n    a_box['y_rel'] = 100.*a_box['y']/infos_img[1]\n    a_box['x_rel_max'] = 100.*(a_box['x']+a_box['width'])/infos_img[0]\n    a_box['y_rel_max'] = 100.*(a_box['y']+a_box['height'])/infos_img[1]\n    a_box['width_rel'] = 100.*a_box['width']/infos_img[0]\n    a_box['height_rel'] = 100.*a_box['height']/infos_img[1]\n    a_box['img_width'] = infos_img[0]\n    a_box['img_height'] = infos_img[1]\n    a_box['img_id'] = infos_img[2]\n    a_box['study_id'] = infos_img[3]\n    a_box['img_path'] = infos_img[4]\n    #print(a_box)\n    return a_box\n  \n\ndef draw_bboxes(img, tl, br, rgb, label=\"\", label_location=\"tl\", opacity=0.1, line_thickness=0):\n    \"\"\" TBD \n    \n    Args:\n        TBD\n        \n    Returns:\n        TBD \n    \"\"\"\n    rect = np.uint8(np.ones((br[1]-tl[1], br[0]-tl[0], 3))*rgb)\n    sub_combo = cv2.addWeighted(img[tl[1]:br[1],tl[0]:br[0],:], 1-opacity, rect, opacity, 1.0)    \n    img[tl[1]:br[1],tl[0]:br[0],:] = sub_combo\n\n    if line_thickness>0:\n        img = cv2.rectangle(img, tuple(tl), tuple(br), rgb, line_thickness)\n        \n    if label:\n        # DEFAULTS\n        FONT = cv2.FONT_HERSHEY_SIMPLEX\n        FONT_SCALE = 1.666\n        FONT_THICKNESS = 3\n        FONT_LINE_TYPE = cv2.LINE_AA\n        \n        if type(label)==str:\n            LABEL = label.upper().replace(\" \", \"_\")\n        else:\n            LABEL = f\"CLASS_{label:02}\"\n        \n        text_width, text_height = cv2.getTextSize(LABEL, FONT, FONT_SCALE, FONT_THICKNESS)[0]\n        \n        label_origin = {\"tl\":tl, \"br\":br, \"tr\":(br[0],tl[1]), \"bl\":(tl[0],br[1])}[label_location]\n        label_offset = {\n            \"tl\":np.array([0, -10]), \"br\":np.array([-text_width, text_height+10]), \n            \"tr\":np.array([-text_width, -10]), \"bl\":np.array([0, text_height+10])\n        }[label_location]\n        img = cv2.putText(img, LABEL, tuple(label_origin+label_offset), \n                          FONT, FONT_SCALE, rgb, FONT_THICKNESS, FONT_LINE_TYPE)\n    \n    return img","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:37:11.246677Z","iopub.execute_input":"2021-06-23T14:37:11.247029Z","iopub.status.idle":"2021-06-23T14:37:11.281075Z","shell.execute_reply.started":"2021-06-23T14:37:11.246990Z","shell.execute_reply":"2021-06-23T14:37:11.279959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Infos about the dcm image.\nHow to get access to the patient id for example.","metadata":{}},{"cell_type":"code","source":"row_0 = train_df.iloc[0]\ndcm_path = \"{}/{}/*/{}.dcm\".format(train_path,row_0['StudyInstanceUID'], row_0['id_x'])\nfor filename in glob.glob(dcm_path):\n    print(filename)\n    img_array = dicom2array(filename)\n    ds = pydicom.dcmread(filename)\n    print(ds)","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:37:18.472631Z","iopub.execute_input":"2021-06-23T14:37:18.472955Z","iopub.status.idle":"2021-06-23T14:37:19.814507Z","shell.execute_reply.started":"2021-06-23T14:37:18.472926Z","shell.execute_reply":"2021-06-23T14:37:19.813513Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create new dataframe with every box","metadata":{}},{"cell_type":"code","source":"l_boxes = []\nl_images_no_box = []\nboxes_df = pd.DataFrame(l_boxes, columns=['x', 'y', 'width', 'height', 'x_rel', 'x_rel_max', \n                                          'y_rel', 'y_rel_max','width_rel', 'height_rel', \n                                          'img_width', 'img_height','img_id', 'study_id',\n                                          'img_path'])\n\n\n\nfor i, row in train_df.iloc[:].iterrows():\n    dcm_path = \"{}/{}/*/{}.dcm\".format(train_path,row['StudyInstanceUID'], row['id_x'])\n    #print(i)\n    for filename in glob.glob(dcm_path):\n        ds = pydicom.dcmread(filename)\n        \n    infos_img = [ds.Columns, ds.Rows, row['id_x'], row['StudyInstanceUID'], filename]\n    box = str(row['boxes'])\n    #l_except = [771]\n    if box != 'nan'  :\n        box_df = get_rows_boxes(box, infos_img)\n        #print(box_df.head())\n        boxes_df = pd.concat([boxes_df, box_df], ignore_index=True)\n    else :\n        l_images_no_box.append(infos_img)\n\nno_boxes_df = pd.DataFrame(l_images_no_box, columns=['img_width', 'img_height',\n                                                     'img_id', 'study_id','img_path'])\n# Save the df in a csv \nboxes_df.to_csv('/kaggle/working/boxes_df.csv')\nno_boxes_df.to_csv('/kaggle/working/no_boxes_df.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-23T09:30:47.548489Z","iopub.execute_input":"2021-06-23T09:30:47.548913Z","iopub.status.idle":"2021-06-23T09:55:24.647113Z","shell.execute_reply.started":"2021-06-23T09:30:47.548881Z","shell.execute_reply":"2021-06-23T09:55:24.645621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Samples of chest images.","metadata":{}},{"cell_type":"code","source":"# Save the df in a csv \nboxes_df = pd.read_csv('../input/boxes-infos/boxes_df.csv')\nno_boxes_df = pd.read_csv('../input/boxes-infos/no_boxes_df.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:42:15.896113Z","iopub.execute_input":"2021-06-23T14:42:15.896601Z","iopub.status.idle":"2021-06-23T14:42:15.974483Z","shell.execute_reply.started":"2021-06-23T14:42:15.896558Z","shell.execute_reply":"2021-06-23T14:42:15.973840Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Show the values distribution\nI worked with relative values, x and y are the pourcentage of the width or height of the image.\n","metadata":{}},{"cell_type":"code","source":"# some random data\nx_min = boxes_df['x_rel'].to_numpy()\ny_min = boxes_df['y_rel'].to_numpy()\nx_max = boxes_df['x_rel_max'].to_numpy()\ny_max = boxes_df['y_rel_max'].to_numpy()\n\ndef scatter_hist_1d(x_1, y_1, ax, ax_histx, ax_histy, color = 'b'):\n    # no labels\n    ax_histx.tick_params(axis=\"x\", labelbottom=False)\n    ax_histy.tick_params(axis=\"y\", labelleft=False)\n\n    # the scatter plot:\n    ax.scatter(x_1, y_1, c = color)\n\n    # now determine nice limits by hand:\n    binwidth = 2\n    #xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))\n    #lim = (int(xymax/binwidth) + 1) * binwidth\n\n    bins = np.arange(0, 100+1, binwidth)\n    ax_histx.hist(x_1, bins=bins)\n    ax_histy.hist(y_1, bins=bins, orientation='horizontal')","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:47:06.298798Z","iopub.execute_input":"2021-06-23T14:47:06.299132Z","iopub.status.idle":"2021-06-23T14:47:06.305982Z","shell.execute_reply.started":"2021-06-23T14:47:06.299103Z","shell.execute_reply":"2021-06-23T14:47:06.304858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# definitions for the axes\nleft, width = 0.1, 0.65\nbottom, height = 0.1, 0.65\nspacing = 0.005\n\n\nrect_scatter = [left, bottom, width, height]\nrect_histx = [left, bottom + height + spacing, width, 0.2]\nrect_histy = [left + width + spacing, bottom, 0.2, height]\n\n# start with a square Figure\nfig = plt.figure(figsize=(15, 15))\n\nax = fig.add_axes(rect_scatter)\nax_histx = fig.add_axes(rect_histx, sharex=ax)\nax_histy = fig.add_axes(rect_histy, sharey=ax)\n\n# use the previously defined function\nscatter_hist_1d(x_min, y_min,  ax, ax_histx, ax_histy)\nscatter_hist_1d(x_max, y_max, ax, ax_histx, ax_histy, color='r')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:47:58.787281Z","iopub.execute_input":"2021-06-23T14:47:58.787608Z","iopub.status.idle":"2021-06-23T14:47:59.644691Z","shell.execute_reply.started":"2021-06-23T14:47:58.787580Z","shell.execute_reply":"2021-06-23T14:47:59.643707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.patches as patches\n\nfig = plt.figure(figsize=(15, 15))\nfig, ax = plt.subplots()\n\n\nfor i in range(x_min.shape[0]) :\n    ax.add_patch(\n         patches.Rectangle(\n            (x_min[i], y_min[i]),\n            x_max[i] - x_min[i],\n            y_max[i] - y_min[i],\n            edgecolor = 'blue',\n            facecolor = 'red',\n            fill=True\n         ) )\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:48:39.659344Z","iopub.execute_input":"2021-06-23T14:48:39.659661Z","iopub.status.idle":"2021-06-23T14:48:51.252212Z","shell.execute_reply.started":"2021-06-23T14:48:39.659634Z","shell.execute_reply":"2021-06-23T14:48:51.251498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### At first, I thought it possible to focus on each lung and also to resize the image to keep only its useful part\n### It seems the whole image is used by the annotation boxes.","metadata":{}},{"cell_type":"markdown","source":"### following that, here are represented the dimensions of the boxes","metadata":{}},{"cell_type":"code","source":"delta_x = x_max - x_min\ndelta_y = y_max - y_min\n\n# start with a square Figure\nfig = plt.figure(figsize=(15, 15))\n\nax = fig.add_axes(rect_scatter)\nax_histx = fig.add_axes(rect_histx, sharex=ax)\nax_histy = fig.add_axes(rect_histy, sharey=ax)\n\n# use the previously defined function\nscatter_hist_1d(delta_x, delta_y, ax, ax_histx, ax_histy)\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T14:52:49.312878Z","iopub.execute_input":"2021-06-23T14:52:49.313254Z","iopub.status.idle":"2021-06-23T14:52:49.821800Z","shell.execute_reply.started":"2021-06-23T14:52:49.313221Z","shell.execute_reply":"2021-06-23T14:52:49.820946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}}]}