{"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":"# **SIIM-FISABIO-RSNA COVID-19 Detection**","metadata":{}},{"cell_type":"markdown","source":"**Task:** To identify and localize COVID-19 abnormalities on chest radiographs","metadata":{}},{"cell_type":"markdown","source":"# **Tensorflow Records**","metadata":{}},{"cell_type":"markdown","source":"A TFRecord file stores your data as a sequence of binary strings. Binary data takes up less space on disk, takes less time to copy and can be read much more efficiently from disk. However, pure performance isn’t the only advantage of the TFRecord file format. It is optimized for use with Tensorflow in multiple ways. One use being, it makes it easy to combine multiple datasets and integrates seamlessly with the data import and preprocessing functionality provided by the library. Especially for datasets that are too large to be stored fully in memory this is an advantage as only the data that is required at the time (e.g. a batch) is loaded from disk and then processed. ","metadata":{}},{"cell_type":"markdown","source":"# **Loading Packages**","metadata":{}},{"cell_type":"code","source":"!conda install gdcm -c conda-forge -y","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-06-23T19:47:22.875241Z","iopub.execute_input":"2021-06-23T19:47:22.875693Z","iopub.status.idle":"2021-06-23T19:48:30.435529Z","shell.execute_reply.started":"2021-06-23T19:47:22.875601Z","shell.execute_reply":"2021-06-23T19:48:30.434413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nimport pydicom\nfrom pydicom import dcmread\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport hashlib\nimport os\nfrom io import BytesIO\nfrom PIL import Image, ImageFont, ImageDraw\nimport cv2\nfrom tqdm.auto import tqdm\n#import warnings\n#warnings.filterwarnings('ignore')\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:30.439515Z","iopub.execute_input":"2021-06-23T19:48:30.439830Z","iopub.status.idle":"2021-06-23T19:48:36.221547Z","shell.execute_reply.started":"2021-06-23T19:48:30.439798Z","shell.execute_reply":"2021-06-23T19:48:36.220444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Reading csv file**","metadata":{}},{"cell_type":"markdown","source":"Here I'm using an updated csv file not the original file provided in the competition.","metadata":{}},{"cell_type":"code","source":"raw_df = pd.read_csv('../input/updated-csv/combined_train_data.csv')\nraw_df.class_id = raw_df.class_id - 1\nraw_df.head()\n\n","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:36.223549Z","iopub.execute_input":"2021-06-23T19:48:36.223877Z","iopub.status.idle":"2021-06-23T19:48:36.350167Z","shell.execute_reply.started":"2021-06-23T19:48:36.223845Z","shell.execute_reply":"2021-06-23T19:48:36.349226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Helpers Functions**","metadata":{}},{"cell_type":"code","source":"IMAGE_SIZE = 1024 # change this to desired value\nCLIP_LIMIT = 2.\nGRID_SIZE = (8,8)\n\nNUM_CLASSES = 3\n\n# https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/\nLABEL_COLORS = [(230, 25, 75), (60, 180, 75), (255, 225, 25), (0, 130, 200)]\n\ndef read_image(path, voi_lut = True, target_size=IMAGE_SIZE, use_clahe=True):\n    # Original from: https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    ds = dcmread(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \n    # \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(ds.pixel_array, ds)\n    else:\n        data = ds.pixel_array\n    im = data - np.min(data)\n    im = 255. * im / np.max(im)\n    if ds.PhotometricInterpretation == \"MONOCHROME1\": # check for inverted image\n        im = 255. - im\n    if use_clahe:\n        clahe = cv2.createCLAHE(clipLimit=CLIP_LIMIT, tileGridSize=GRID_SIZE)\n        climg = clahe.apply(im.astype('uint8'))\n        img = Image.fromarray(climg.astype('uint8'), 'L')\n    else:\n        img = Image.fromarray(im.astype('uint8'), 'L')\n    org_size = img.size\n    if max(img.size) > target_size:\n        img.thumbnail((target_size, target_size), Image.ANTIALIAS)\n    return img, org_size","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:36.352098Z","iopub.execute_input":"2021-06-23T19:48:36.352555Z","iopub.status.idle":"2021-06-23T19:48:36.363215Z","shell.execute_reply.started":"2021-06-23T19:48:36.352509Z","shell.execute_reply":"2021-06-23T19:48:36.362242Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"findings = raw_df[raw_df.class_id != -1]\nclass_names = []\nfor i in range(NUM_CLASSES):\n    class_names.append(findings[findings.class_id == i].class_name.iloc[0])\nclass_names","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:36.364388Z","iopub.execute_input":"2021-06-23T19:48:36.364882Z","iopub.status.idle":"2021-06-23T19:48:36.385730Z","shell.execute_reply.started":"2021-06-23T19:48:36.364834Z","shell.execute_reply":"2021-06-23T19:48:36.384522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wbf = []\n\nfor _, row in tqdm(findings.iterrows()):\n    image_name = row['id'].replace('_image','')        \n    label = row['label'].split()\n    length = row['No_of_findings']\n    class_id = row['class_id']\n    class_name = row['class_name']\n    path = row['img_path']\n    \n    j = 0\n    for i in range(length):\n        x_min = float(label[j+2])\n        y_min = float(label[j+3])            \n        x_max = float(label[j+4])           \n        y_max = float(label[j+5])\n        # Hard code below class_name and class_id to opacity and 0 resp. if you want to train on 2-class objects (disease or no disease)\n        #\n        wbf.append([image_name, class_name, class_id, x_min, y_min, x_max, y_max, path])\n        j += 6\n    \nwbf_df = pd.DataFrame (wbf, columns=['image_id', 'class_name', 'class_id', 'x_min', 'y_min', 'x_max', 'y_max', 'path'])\nwbf_df.to_csv('wbf_objects.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:36.386939Z","iopub.execute_input":"2021-06-23T19:48:36.387233Z","iopub.status.idle":"2021-06-23T19:48:37.148390Z","shell.execute_reply.started":"2021-06-23T19:48:36.387203Z","shell.execute_reply":"2021-06-23T19:48:37.147197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wbf_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:37.149982Z","iopub.execute_input":"2021-06-23T19:48:37.150395Z","iopub.status.idle":"2021-06-23T19:48:37.166239Z","shell.execute_reply.started":"2021-06-23T19:48:37.150350Z","shell.execute_reply":"2021-06-23T19:48:37.164995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wbf_df.class_id.value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:37.169556Z","iopub.execute_input":"2021-06-23T19:48:37.170224Z","iopub.status.idle":"2021-06-23T19:48:37.183717Z","shell.execute_reply.started":"2021-06-23T19:48:37.170172Z","shell.execute_reply":"2021-06-23T19:48:37.182603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\n\nNUM_SHARDS = 20\n\nskf = StratifiedKFold(n_splits=NUM_SHARDS, shuffle=True, random_state=42)\ndf_folds = wbf_df[['image_id']].copy()\n\ndf_folds.loc[:, 'bbox_count'] = 1\ndf_folds = df_folds.groupby('image_id').count()\ndf_folds.loc[:, 'object_count'] = wbf_df.groupby('image_id')['class_id'].nunique()\n\ndf_folds.loc[:, 'stratify_group'] = np.char.add(\n    df_folds['object_count'].values.astype(str),\n    df_folds['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str))\n\ndf_folds.loc[:, 'fold'] = 0\nfor fold_number, (train_index, val_index) in enumerate(skf.split(X=df_folds.index, y=df_folds['stratify_group'])):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number\ndf_folds.reset_index(inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:37.185643Z","iopub.execute_input":"2021-06-23T19:48:37.185971Z","iopub.status.idle":"2021-06-23T19:48:38.049960Z","shell.execute_reply.started":"2021-06-23T19:48:37.185943Z","shell.execute_reply":"2021-06-23T19:48:38.048996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_folds","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:38.051149Z","iopub.execute_input":"2021-06-23T19:48:38.051450Z","iopub.status.idle":"2021-06-23T19:48:38.070222Z","shell.execute_reply.started":"2021-06-23T19:48:38.051421Z","shell.execute_reply":"2021-06-23T19:48:38.069195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_shard = pd.merge(wbf_df, df_folds[df_folds['fold'] == 0], on='image_id')\ndfs = df_shard.class_name.value_counts().to_frame('S0').sort_index()\nfor i in range(1,20):\n    df_shard = pd.merge(wbf_df, df_folds[df_folds['fold'] == i], on='image_id')\n    dfs['S'+str(i)] = df_shard.class_name.value_counts().to_frame().sort_index()\ndfs","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:38.071645Z","iopub.execute_input":"2021-06-23T19:48:38.071959Z","iopub.status.idle":"2021-06-23T19:48:38.259724Z","shell.execute_reply.started":"2021-06-23T19:48:38.071919Z","shell.execute_reply":"2021-06-23T19:48:38.258664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create example for TensorFlow Object Detection API\ndef create_tf_example(imagedf, longest_edge=IMAGE_SIZE):  \n    fname = imagedf.path.iloc[0]\n    filename=fname.split('/')[-1] # exclude path    \n    img, org_size = read_image(fname, target_size=IMAGE_SIZE, use_clahe=True)\n    height = img.size[1] # Image height\n    width = img.size[0] # Image width\n    buf= BytesIO()\n    img.save(buf, format= 'JPEG') # encode to jpeg in memory\n    encoded_image_data= buf.getvalue()\n    image_format = b'jpeg'\n    source_id = imagedf.image_id.iloc[0]\n    # A hash of the image is used in some frameworks\n    key = hashlib.sha256(encoded_image_data).hexdigest()   \n    # object bounding boxes \n    xmins = imagedf.x_min.values/org_size[0] # List of normalized left x coordinates in bounding box \n    xmaxs = imagedf.x_max.values/org_size[0] # List of normalized right x coordinates in bounding box\n    ymins = imagedf.y_min.values/org_size[1] # List of normalized top y coordinates in bounding box \n    ymaxs = imagedf.y_max.values/org_size[1] # List of normalized bottom y coordinates in bounding box\n    # List of string class name & id of bounding box (1 per box)\n    object_cnt = len(imagedf)\n    classes_text = []\n    classes = []\n    for i in range(object_cnt):\n        classes_text.append(imagedf.class_name.iloc[i].encode())\n        classes.append(1+imagedf.class_id.iloc[i]) # 0 is not a valid class\n        \n    # unused features from Open Image \n    depiction = np.zeros(object_cnt, dtype=int)\n    group_of = np.zeros(object_cnt, dtype=int)\n    occluded = np.zeros(object_cnt, dtype=int) #also Pascal VOC\n    truncated = np.zeros(object_cnt, dtype=int) # also Pascal VOC\n    # Pascal VOC\n    view_text = []\n    for i in range(object_cnt):\n        view_text.append('frontal'.encode())\n    difficult = np.zeros(object_cnt, dtype=int)\n\n    tf_record = tf.train.Example(features=tf.train.Features(feature={\n        'image/height': tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),\n        'image/width': tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),\n        'image/filename': tf.train.Feature(bytes_list=tf.train.BytesList(value=[filename.encode()])),\n        'image/source_id': tf.train.Feature(bytes_list=tf.train.BytesList(value=[source_id.encode()])),\n        'image/encoded': tf.train.Feature(bytes_list=tf.train.BytesList(value=[encoded_image_data])),\n        'image/key/sha256': tf.train.Feature(bytes_list=tf.train.BytesList(value=[key.encode()])),\n        'image/format': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_format])),\n        'image/object/bbox/xmin': tf.train.Feature(float_list=tf.train.FloatList(value=xmins)),\n        'image/object/bbox/xmax': tf.train.Feature(float_list=tf.train.FloatList(value=xmaxs)),\n        'image/object/bbox/ymin': tf.train.Feature(float_list=tf.train.FloatList(value=ymins)),\n        'image/object/bbox/ymax': tf.train.Feature(float_list=tf.train.FloatList(value=ymaxs)),\n        'image/object/class/text': tf.train.Feature(bytes_list=tf.train.BytesList(value=classes_text)),\n        'image/object/class/label': tf.train.Feature(int64_list=tf.train.Int64List(value=classes)),\n        'image/object/depiction': tf.train.Feature(int64_list=tf.train.Int64List(value=depiction)),\n        'image/object/group_of': tf.train.Feature(int64_list=tf.train.Int64List(value=group_of)),\n        'image/object/occluded': tf.train.Feature(int64_list=tf.train.Int64List(value=occluded)),\n        'image/object/truncated': tf.train.Feature(int64_list=tf.train.Int64List(value=truncated)),\n        'image/object/difficult': tf.train.Feature(int64_list=tf.train.Int64List(value=difficult)),\n        'image/object/view': tf.train.Feature(bytes_list=tf.train.BytesList(value=view_text))\n    }))\n    return tf_record","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:38.261000Z","iopub.execute_input":"2021-06-23T19:48:38.261303Z","iopub.status.idle":"2021-06-23T19:48:38.283774Z","shell.execute_reply.started":"2021-06-23T19:48:38.261274Z","shell.execute_reply":"2021-06-23T19:48:38.282682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\nimport contextlib2\n\ndef open_sharded_tfrecords(exit_stack, base_path, num_shards):\n    tf_record_output_filenames = [\n        '{}-{:03d}-of-{:03}.tfrecord'.format(base_path, idx, num_shards)\n        for idx in range(num_shards)\n        ]\n    tfrecords = [\n        exit_stack.enter_context(tf.io.TFRecordWriter(file_name))\n        for file_name in tf_record_output_filenames\n    ]\n    return tfrecords\n\noutput_filebase='./SiimCovid19'\n\nimg_cnt = np.zeros(NUM_SHARDS, dtype=int)\nwith contextlib2.ExitStack() as tf_record_close_stack:\n    output_tfrecords = open_sharded_tfrecords(tf_record_close_stack, output_filebase, NUM_SHARDS)\n    for i in tqdm(range(NUM_SHARDS)):\n        df_shard = pd.merge(wbf_df, df_folds[df_folds['fold'] == i], on='image_id')\n        ids = df_shard.image_id.unique()\n        for j in range (len(ids)):\n            imagedf = df_shard[df_shard.image_id == ids[j]]\n            tf_record = create_tf_example(imagedf, longest_edge=IMAGE_SIZE)            \n            output_tfrecords[i].write(tf_record.SerializeToString())\n            img_cnt[i] += 1\nprint(\"Converted {} images\".format(np.sum(img_cnt)))\nprint(\"Images per shard: {}\".format(img_cnt))","metadata":{"execution":{"iopub.status.busy":"2021-06-23T19:48:38.285466Z","iopub.execute_input":"2021-06-23T19:48:38.285817Z","iopub.status.idle":"2021-06-23T20:18:52.873875Z","shell.execute_reply.started":"2021-06-23T19:48:38.285784Z","shell.execute_reply":"2021-06-23T20:18:52.871793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Exporting the TFRecords**","metadata":{}},{"cell_type":"code","source":"%%time\n!tar -zcf siim_data.tar.gz -C \"/kaggle/working/\" .","metadata":{"execution":{"iopub.status.busy":"2021-06-23T20:18:52.877876Z","iopub.execute_input":"2021-06-23T20:18:52.878239Z","iopub.status.idle":"2021-06-23T20:19:13.503630Z","shell.execute_reply.started":"2021-06-23T20:18:52.878157Z","shell.execute_reply":"2021-06-23T20:19:13.502482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Download it from the output folder","metadata":{}},{"cell_type":"code","source":"import json\n\ndparams = {\n    \"IMAGE_SIZE\": IMAGE_SIZE,\n    \"CLIP_LIMIT\": CLIP_LIMIT,\n    \"GRID_SIZE\": GRID_SIZE}\nwith open(\"dparams.json\", \"w\") as json_file:\n    json_file.write(json.dumps(dparams, indent = 4))","metadata":{"execution":{"iopub.status.busy":"2021-06-23T20:19:13.505531Z","iopub.execute_input":"2021-06-23T20:19:13.505813Z","iopub.status.idle":"2021-06-23T20:19:13.512626Z","shell.execute_reply.started":"2021-06-23T20:19:13.505784Z","shell.execute_reply":"2021-06-23T20:19:13.511860Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = ['Typical Appearance', 'Indeterminate Appearance', 'Atypical Appearance']\n\nwith open('./SiimCovid19.pbtxt', 'w') as f:\n    for i in range (len(labels)): \n        f.write('item {{\\n id: {}\\n name:\\'{}\\'\\n}}\\n\\n'.format(i+1, labels[i]))","metadata":{"execution":{"iopub.status.busy":"2021-06-23T20:19:13.513742Z","iopub.execute_input":"2021-06-23T20:19:13.514052Z","iopub.status.idle":"2021-06-23T20:19:13.526929Z","shell.execute_reply.started":"2021-06-23T20:19:13.513999Z","shell.execute_reply":"2021-06-23T20:19:13.526033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Download these files too (json.dumps and the pbtxt file) for training purpose","metadata":{}},{"cell_type":"markdown","source":"# Checking TFRecords","metadata":{}},{"cell_type":"code","source":"# Some helper functions to draw image with object boundary boxes\nfontname = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'\nfont = ImageFont.truetype(fontname, 40) if os.path.isfile(fontname) else ImageFont.load_default()\n\ndef bbox(img, xmin, ymin, xmax, ymax, color, width, label, score):\n    draw = ImageDraw.Draw(img)\n    xres, yres = img.size[0], img.size[1]\n    box = np.multiply([xmin, ymin, xmax, ymax], [xres, yres, xres, yres]).astype(int).tolist()\n    txt = \" {}: {}%\" if score >= 0. else \" {}\"\n    txt = txt.format(label, round(score, 1))\n    ts = draw.textsize(txt, font=font)\n    draw.rectangle(box, outline=color, width=width)\n    if len(label) > 0:\n        if box[1] >= ts[1]+3:\n            xsmin, ysmin = box[0], box[1]-ts[1]-3\n            xsmax, ysmax = box[0]+ts[0]+2, box[1]\n        else:\n            xsmin, ysmin = box[0], box[3]\n            xsmax, ysmax = box[0]+ts[0]+2, box[3]+ts[1]+1\n        draw.rectangle([xsmin, ysmin, xsmax, ysmax], fill=color)\n        draw.text((xsmin, ysmin), txt, font=font, fill='white')\n\ndef plot_img(img, axes, xmin, ymin, xmax, ymax, classes, class_label, by):\n    img = img.convert(\"RGB\")\n    for i in range(len(xmin)):\n        color = LABEL_COLORS[class_label[i]]\n        bbox(img, xmin[i], ymin[i], xmax[i], ymax[i], color, 5, classes[i].decode(), -1)\n    plt.setp(axes, xticks=[], yticks=[])\n    axes.set_title(by)\n    plt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2021-06-23T20:19:13.528187Z","iopub.execute_input":"2021-06-23T20:19:13.528502Z","iopub.status.idle":"2021-06-23T20:19:13.569292Z","shell.execute_reply.started":"2021-06-23T20:19:13.528473Z","shell.execute_reply":"2021-06-23T20:19:13.568347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fname='./SiimCovid19-018-of-020.tfrecord' \ndataset3 = tf.data.TFRecordDataset(fname)\nfig = plt.figure(figsize=(20,30))\nidx=1\nfor raw_record in dataset3.take(6):\n    axes = fig.add_subplot(3, 2, idx)\n    example = tf.train.Example()\n    example.ParseFromString(raw_record.numpy())\n    xmin=example.features.feature['image/object/bbox/xmin'].float_list.value[:]\n    xmax=example.features.feature['image/object/bbox/xmax'].float_list.value[:]\n    ymin=example.features.feature['image/object/bbox/ymin'].float_list.value[:]\n    ymax=example.features.feature['image/object/bbox/ymax'].float_list.value[:]\n    classes=example.features.feature['image/object/class/text'].bytes_list.value[:]\n    class_label=example.features.feature['image/object/class/label'].int64_list.value[:]\n    img_encoded=example.features.feature['image/encoded'].bytes_list.value[0]\n    img = Image.open(BytesIO(img_encoded))\n    plot_img(img, axes, xmin, ymin, xmax, ymax, classes, class_label, \"\")\n    idx=idx+1","metadata":{"execution":{"iopub.status.busy":"2021-06-23T20:19:13.571030Z","iopub.execute_input":"2021-06-23T20:19:13.571466Z","iopub.status.idle":"2021-06-23T20:19:15.512577Z","shell.execute_reply.started":"2021-06-23T20:19:13.571419Z","shell.execute_reply":"2021-06-23T20:19:15.505906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Credits","metadata":{}},{"cell_type":"markdown","source":"Copied from the below mentioned notebook and modified according to the given data for this competition","metadata":{}},{"cell_type":"markdown","source":"https://www.kaggle.com/mistag/data-create-tfrecords-of-vinbigdata-chest-x-rays\n\nThankyou so much sir.","metadata":{}}]}