{"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":"# I. EXPLORATORY DATA ANALYSIS\n- load and study from train sets\n- view distributions of classes\n- view DICOM information (and eventually to be dropped before experiments)","metadata":{}},{"cell_type":"code","source":"#### uncomment below lines if running for first time\n#### install gdcm and related libs which read medical dicom files\n#!conda install -c conda-forge pillow -y\n#!conda install -c conda-forge pydicom -y\n#!conda install -c conda-forge gdcm -y\n#!pip install pylibjpeg pylibjpeg-libjpeg","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:07:39.741323Z","iopub.execute_input":"2021-08-10T14:07:39.741788Z","iopub.status.idle":"2021-08-10T14:07:39.747626Z","shell.execute_reply.started":"2021-08-10T14:07:39.741691Z","shell.execute_reply":"2021-08-10T14:07:39.746017Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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# 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\n\nimport numpy as np \nimport pandas as pd \nfrom pandas import DataFrame\n\nfrom matplotlib.lines import Line2D\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly.express as px\n\nimport os\nimport pydicom\nimport glob\nimport cv2\n\nfrom fastai.vision.all import *\nfrom fastai.medical.imaging import *\n\nfrom tqdm.notebook import tqdm\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom skimage import exposure\n\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-10T14:07:39.779889Z","iopub.execute_input":"2021-08-10T14:07:39.780235Z","iopub.status.idle":"2021-08-10T14:07:45.342526Z","shell.execute_reply.started":"2021-08-10T14:07:39.780207Z","shell.execute_reply":"2021-08-10T14:07:45.341182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 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\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:07:45.344651Z","iopub.execute_input":"2021-08-10T14:07:45.345213Z","iopub.status.idle":"2021-08-10T14:08:21.255110Z","shell.execute_reply.started":"2021-08-10T14:07:45.345170Z","shell.execute_reply":"2021-08-10T14:08:21.253702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"## There are two datasets - study level and image level\n## Study level is classification problem, an Xray may have one of the 4 given classes\n- Negative: No findings, clean lungs\n- Typical: findings common in COVID-19\n- ATypical: findings uncommon in COVID-19\n- Indeterminate: findings may occur in COVID-19 patients but usually seen for other infections\n\nLoading study level dataset ...","metadata":{}},{"cell_type":"code","source":"#read study level data\ntrain_study_df = pd.read_csv('../input/siim-covid19-detection/train_study_level.csv')\ntrain_study_df = train_study_df.rename(columns = {'Negative for Pneumonia': 'Negative', 'Typical Appearance': 'Typical', 'Indeterminate Appearance': 'Indeterminate', 'Atypical Appearance': 'Atypical'}, inplace = False)\n\n#determine \"y\"\ntrain_study_df['y_study'] = 'Typical'\ntrain_study_df.loc[train_study_df['Negative']==1, 'y_study'] = 'Negative'\ntrain_study_df.loc[train_study_df['Indeterminate']==1, 'y_study'] = 'Indeterminate'\ntrain_study_df.loc[train_study_df['Atypical']==1, 'y_study'] = 'Atypical'\n\ntrain_study_df.head(3)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:21.257179Z","iopub.execute_input":"2021-08-10T14:08:21.257524Z","iopub.status.idle":"2021-08-10T14:08:21.530032Z","shell.execute_reply.started":"2021-08-10T14:08:21.257492Z","shell.execute_reply":"2021-08-10T14:08:21.528798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# draws barchart for the given df\ndef plot_barchart(df,x,y,x_title,title,colors=None,text=None):\n    fig = px.bar(x=x,\n                 y=y,\n                 text=text,\n                 labels={x: x_title.title()},   \n                 data_frame=df,\n                 color=colors,\n                 barmode='group',\n                 template=\"simple_white\")\n    \n    texts = [df[col].values for col in y]\n    for i, t in enumerate(texts):\n        fig.data[i].text = t\n        fig.data[i].textposition = 'inside'\n        \n    fig['layout'].title=title\n\n    fig.update_layout(title_font_size=19)\n    fig.update_layout(title_font_family='Droid Serif')\n    fig.update_layout(width=400,height=400)\n        \n\n\n    for trace in fig.data:\n        trace.name = trace.name.replace('_',' ').title()\n\n    fig.update_yaxes(tickprefix=\"\", showgrid=True)\n\n    fig.show()\n\n# draws piechart for the given df\ndef plot_piechart(df, y, c, title):\n    \n    feature = df[y]\n    counts = df[c]\n\n    plt.figure(figsize = (10,5))\n    plt.pie(counts, labels=feature, autopct=\"%1.1f%%\")\n    plt.title(title)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:21.533957Z","iopub.execute_input":"2021-08-10T14:08:21.534287Z","iopub.status.idle":"2021-08-10T14:08:21.545616Z","shell.execute_reply.started":"2021-08-10T14:08:21.534255Z","shell.execute_reply":"2021-08-10T14:08:21.544711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get count of each class\n\ntrain_study_df1=train_study_df.groupby(['y_study']).size().reset_index(name='counts')\ntrain_study_df1","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:21.546783Z","iopub.execute_input":"2021-08-10T14:08:21.547089Z","iopub.status.idle":"2021-08-10T14:08:21.571206Z","shell.execute_reply.started":"2021-08-10T14:08:21.547058Z","shell.execute_reply":"2021-08-10T14:08:21.569979Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_study vs count barchart\nplot_barchart(train_study_df1, 'y_study', ['counts'], 'Classes', title='Count of Classes')","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:21.572964Z","iopub.execute_input":"2021-08-10T14:08:21.573425Z","iopub.status.idle":"2021-08-10T14:08:22.919505Z","shell.execute_reply.started":"2021-08-10T14:08:21.573380Z","shell.execute_reply":"2021-08-10T14:08:22.918307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_study share piechart\nplot_piechart(train_study_df1, 'y_study', 'counts', 'Class Frequency %')","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:22.921018Z","iopub.execute_input":"2021-08-10T14:08:22.921316Z","iopub.status.idle":"2021-08-10T14:08:23.073279Z","shell.execute_reply.started":"2021-08-10T14:08:22.921286Z","shell.execute_reply":"2021-08-10T14:08:23.072190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image level dataset is object detection problem. An Xray may have any of below two classes based on whether it has bounding boxes or not. There maybe 0 or more bounding box information for each image, given in x1,y1,W,H format for each box.\n- none: For images having no bounding box info (usually which are also Negative at study level), they have \"none\" at image level. No bounding box is drawn so a dummy one-pixel format is used as \"none 1 0 0 1 1\"\n- opacity: For images having bounding boxes, they have \"opacity\" at image level. Each bounding box info is given in a dict.\n\nlabel is given as class, confidence, bbinfo format for example when an image has 2 bounding boxes (one at each lungs):\nopacity 1 789.28836 582.43035 1815.94498 2499.73327 opacity 1 2245.91208 591.20528 3340.5737 2352.75472\n\nLoading image level dataset ...","metadata":{}},{"cell_type":"code","source":"# reading from image level dataset\ntrain_image_df = pd.read_csv('../input/siim-covid19-detection/train_image_level.csv')\ntrain_image_df.head(3)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.076390Z","iopub.execute_input":"2021-08-10T14:08:23.076814Z","iopub.status.idle":"2021-08-10T14:08:23.138762Z","shell.execute_reply.started":"2021-08-10T14:08:23.076782Z","shell.execute_reply":"2021-08-10T14:08:23.137723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get count of each class\ntrain_image_df['y_image'] = train_image_df.label.apply(lambda x: x.split()[0])\ntrain_image_df1=train_image_df.groupby(['y_image']).size().reset_index(name='counts')\ntrain_image_df1","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.141254Z","iopub.execute_input":"2021-08-10T14:08:23.142012Z","iopub.status.idle":"2021-08-10T14:08:23.166231Z","shell.execute_reply.started":"2021-08-10T14:08:23.141964Z","shell.execute_reply":"2021-08-10T14:08:23.164798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_image vs count barchart\nplot_barchart(train_image_df1, 'y_image', ['counts'], 'Classes', title='Count of Classes')","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.168001Z","iopub.execute_input":"2021-08-10T14:08:23.168338Z","iopub.status.idle":"2021-08-10T14:08:23.250950Z","shell.execute_reply.started":"2021-08-10T14:08:23.168308Z","shell.execute_reply":"2021-08-10T14:08:23.249845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_image share piechart\nplot_piechart(train_image_df1, 'y_image', 'counts', 'Class Frequency %')","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.252308Z","iopub.execute_input":"2021-08-10T14:08:23.252626Z","iopub.status.idle":"2021-08-10T14:08:23.338400Z","shell.execute_reply.started":"2021-08-10T14:08:23.252597Z","shell.execute_reply":"2021-08-10T14:08:23.337701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Merge both datasets\n\ntrain_study_df['StudyInstanceUID'] = train_study_df['id'].apply(lambda x: x.replace('_study', ''))\n#del train_study_df['id']\ntrain_image_df = train_image_df.merge(train_study_df, on='StudyInstanceUID')\ntrain_image_df.sample(3)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.339391Z","iopub.execute_input":"2021-08-10T14:08:23.339818Z","iopub.status.idle":"2021-08-10T14:08:23.379677Z","shell.execute_reply.started":"2021-08-10T14:08:23.339789Z","shell.execute_reply":"2021-08-10T14:08:23.378528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_distribution(df, clss, col, title, a):\n    sns.kdeplot(df[clss], shade=True,ax=a,color=col)\n    a.set_title(title,font=\"Serif\", fontsize=12)\n    a.set(xlabel=None)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.381080Z","iopub.execute_input":"2021-08-10T14:08:23.381395Z","iopub.status.idle":"2021-08-10T14:08:23.389429Z","shell.execute_reply.started":"2021-08-10T14:08:23.381365Z","shell.execute_reply":"2021-08-10T14:08:23.388030Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(2,2,figsize=(12,8))\nplot_distribution(train_image_df, \"Negative\", \"#00ff00\", \"Negative Distribution\", ax[0,0])\nplot_distribution(train_image_df, \"Typical\", \"#4209ff\", \"Typical Distribution\", ax[0,1])\nplot_distribution(train_image_df, \"Indeterminate\", \"#f72545\", \"Indeterminate Distribution\", ax[1,0])\nplot_distribution(train_image_df, \"Atypical\", \"#ffba08\", \"Atypical Distribution\", ax[1,1])","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:23.391283Z","iopub.execute_input":"2021-08-10T14:08:23.391636Z","iopub.status.idle":"2021-08-10T14:08:24.222858Z","shell.execute_reply.started":"2021-08-10T14:08:23.391604Z","shell.execute_reply":"2021-08-10T14:08:24.221439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# view Xrays\ndef dicom2array(path, voi_lut=True, fix_monochrome=True):\n    dicom = pydicom.read_file(path)\n    # VOI LUT (if available by DICOM device) is used to\n    # 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    # 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    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return data\n        \n    \ndef plot_img(img, size=(5, 5), 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=5, is_rgb=True, title=\"\", cmap='gray', img_size=(300,300)):\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-08-10T14:08:24.225932Z","iopub.execute_input":"2021-08-10T14:08:24.226300Z","iopub.status.idle":"2021-08-10T14:08:24.238732Z","shell.execute_reply.started":"2021-08-10T14:08:24.226261Z","shell.execute_reply":"2021-08-10T14:08:24.237260Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_paths = get_dicom_files('../input/siim-covid19-detection/train')\nimgs = [dicom2array(path) for path in dicom_paths[-4:]]\nplot_imgs(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:24.240767Z","iopub.execute_input":"2021-08-10T14:08:24.241256Z","iopub.status.idle":"2021-08-10T14:08:33.041627Z","shell.execute_reply.started":"2021-08-10T14:08:24.241203Z","shell.execute_reply":"2021-08-10T14:08:33.040266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imgs = [dicom2array(path) for path in dicom_paths[0:4]]\nplot_imgs(imgs)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:33.043091Z","iopub.execute_input":"2021-08-10T14:08:33.043398Z","iopub.status.idle":"2021-08-10T14:08:35.647045Z","shell.execute_reply.started":"2021-08-10T14:08:33.043369Z","shell.execute_reply":"2021-08-10T14:08:35.645961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#train_image_df = train_image_df[~train_image_df.boxes.isnull()] \nclass_names = ['Negative', 'Typical', 'Indeterminate', 'Atypical'] # we have 1 negative & 3 positive classes\nunique_classes = np.unique(train_image_df[class_names].values, axis=0)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:35.648535Z","iopub.execute_input":"2021-08-10T14:08:35.648885Z","iopub.status.idle":"2021-08-10T14:08:35.667951Z","shell.execute_reply.started":"2021-08-10T14:08:35.648850Z","shell.execute_reply":"2021-08-10T14:08:35.667044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique_classes","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:35.669092Z","iopub.execute_input":"2021-08-10T14:08:35.669496Z","iopub.status.idle":"2021-08-10T14:08:35.683933Z","shell.execute_reply.started":"2021-08-10T14:08:35.669466Z","shell.execute_reply":"2021-08-10T14:08:35.683098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"[[0, 0, 0, 1],[0, 0, 1, 0],[0, 1, 0, 0]])\n       \n[[0, 0, 1], [0, 1, 0],[1, 0, 0]]      ","metadata":{}},{"cell_type":"code","source":"from glob import glob\nimgs = []\nlabel2color = {\n    '[1, 0, 0, 0]': [255,255,255], # Negative Appearance - white\n    '[0, 1, 0, 0]': [66,9,255], # Typical Appearance - blue\n    '[0, 0, 1, 0]': [247,37,69], # Indeterminate Appearance - red\n    '[0, 0, 0, 1]': [255,186,8], # Atypical Appearance - yellow\n}\nthickness = 3\nscale = 6\n\n#for _, row in train_image_df[train_image_df['Negative']==0].iloc[12:20].iterrows():\nfor _, row in train_image_df.iloc[10:26].iterrows():\n    study_id = row['StudyInstanceUID']\n    img_path = glob(f'../input/siim-covid19-detection/train/{study_id}/*/*')[0]\n    img = dicom2array(path=img_path)\n    img = cv2.resize(img, None, fx=1/scale, fy=1/scale)\n    img = np.stack([img, img, img], axis=-1)\n    \n    claz = row[class_names].values\n    \n    #print(claz)\n    #continue\n    \n    color = label2color[str(claz.tolist())]\n\n    bboxes = []\n    bbox = []\n    for i, l in enumerate(row['label'].split(' ')):\n        if (i % 6 == 0) | (i % 6 == 1):\n            continue\n        bbox.append(float(l)/scale)\n        if i % 6 == 5:\n            bboxes.append(bbox)\n            bbox = []    \n    \n    for box in bboxes:\n        img = cv2.rectangle(\n            img,\n            (int(box[0]), int(box[1])),\n            (int(box[2]), int(box[3])),\n            color, thickness\n    )\n    img = cv2.resize(img, (600,600))\n    imgs.append(img)\n    \nplot_imgs(imgs, cmap=None)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:35.685095Z","iopub.execute_input":"2021-08-10T14:08:35.685497Z","iopub.status.idle":"2021-08-10T14:08:43.313185Z","shell.execute_reply.started":"2021-08-10T14:08:35.685467Z","shell.execute_reply":"2021-08-10T14:08:43.311783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Reading DICOM files","metadata":{}},{"cell_type":"code","source":"from tqdm import tqdm\nimport glob\nimport pydicom as dicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport pprint\n\nvoi_lut=True\nfix_monochrome=True\n\ndef dicom_dataset_to_dict(filename,func):\n    \"\"\"Credit: https://github.com/pydicom/pydicom/issues/319\n               https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    \"\"\"\n    \n    dicom_header = dicom.dcmread(filename) \n    \n    #====== DICOM FILE DATA ======\n    dicom_dict = {}\n    repr(dicom_header)\n    for dicom_value in dicom_header.values():\n        if dicom_value.tag == (0x7fe0, 0x0010):\n            #discard pixel data\n            continue\n        if type(dicom_value.value) == dicom.dataset.Dataset:\n            dicom_dict[dicom_value.name] = dicom_dataset_to_dict(dicom_value.value)\n        else:\n            v = _convert_value(dicom_value.value)\n            dicom_dict[dicom_value.name] = v\n      \n    del dicom_dict['Pixel Representation']\n    \n    if func!='metadata_df':\n        #====== DICOM IMAGE DATA ======\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_header.pixel_array, dicom_header)\n        else:\n            data = dicom_header.pixel_array\n        # depending on this value, X-ray may look inverted - fix that:\n        if fix_monochrome and dicom_header.PhotometricInterpretation == \"MONOCHROME1\":\n            data = np.amax(data) - data\n        data = data - np.min(data)\n        data = data / np.max(data)\n        modified_image_data = (data * 255).astype(np.uint8)\n    \n        return dicom_dict, modified_image_data\n    \n    else:\n        return dicom_dict\n\ndef _sanitise_unicode(s):\n    return s.replace(u\"\\u0000\", \"\").strip()\n\ndef _convert_value(v):\n    t = type(v)\n    if t in (list, int, float):\n        cv = v\n    elif t == str:\n        cv = _sanitise_unicode(v)\n    elif t == bytes:\n        s = v.decode('ascii', 'replace')\n        cv = _sanitise_unicode(s)\n    elif t == dicom.valuerep.DSfloat:\n        cv = float(v)\n    elif t == dicom.valuerep.IS:\n        cv = int(v)\n    else:\n        cv = repr(v)\n    return cv\n\n","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:43.315389Z","iopub.execute_input":"2021-08-10T14:08:43.315902Z","iopub.status.idle":"2021-08-10T14:08:43.334452Z","shell.execute_reply.started":"2021-08-10T14:08:43.315836Z","shell.execute_reply":"2021-08-10T14:08:43.332992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_directory = \"../input/siim-covid19-detection/train/\"\ntraining_paths = []\n\nfor sid in tqdm(train_image_df['StudyInstanceUID']):\n    training_paths.append(glob.glob(os.path.join(train_directory, sid +\"/*/*\"))[0])\n\ntrain_image_df['path'] = training_paths\n\nfor filename in train_image_df.path[0:1]:\n    dic, img_array = dicom_dataset_to_dict(filename, 'fetch_both_values')\n    fig, ax = plt.subplots(1, 2, figsize=[15, 8])\n    ax[0].imshow(img_array, cmap=plt.cm.gray)\n    ax[1].imshow(img_array, cmap=plt.cm.plasma)    \n    plt.show()\n    pprint.pprint(dic)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T14:08:43.336204Z","iopub.execute_input":"2021-08-10T14:08:43.336561Z","iopub.status.idle":"2021-08-10T14:08:54.553363Z","shell.execute_reply.started":"2021-08-10T14:08:43.336530Z","shell.execute_reply":"2021-08-10T14:08:54.549299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}