{"cells":[{"metadata":{},"cell_type":"markdown","source":"# VinBigData Chest X-ray Abnormalities Detection\nAutomatically localize and classify thoracic abnormalities from chest radiographs\n\n### The aim of this notebook is to: \n1. Read DICOM x-ray images efficiently.\n2. Explore and visualize images and annotation metadata.\n3. Apply suitable Image Enhancement techniques to images.\n4. Preprocess annotation metadata.\n5. Perform Stratified K-Fold Sharding to create training and validation sets.\n6. Encode images as JPEG and store them along with annotations as TFRecords."},{"metadata":{},"cell_type":"markdown","source":"## Install TF 2 Object Detection API\n1. TF Model Garden\n2. Protobuf\n3. COCO API\n4. Object Detection API "},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!# Download models\n!git clone --depth 1 https://github.com/tensorflow/models\n\n!# Compile proto files \n! # sudo apt install -y protobuf-compiler # Already present\n%cd models/research\n!protoc object_detection/protos/*.proto --python_out=.\n%cd ..\n%cd ..\n\n!# Install cocoapi\n!pip install cython \n!git clone https://github.com/cocodataset/cocoapi.git\n%cd cocoapi/PythonAPI\n!make\n%cd ..\n%cd ..\n!cp -r cocoapi/PythonAPI/pycocotools models/research/\n\n!# Install object detection api\n%cd models/research\n!cp object_detection/packages/tf2/setup.py .\n!python -m pip install .\n%cd ..\n%cd ..","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Import libraries\n1. **NumPy:** Numerical computing\n2. **Pandas:** Data manipulation \n3. **Open-CV:** Computer Vision\n4. **Matplotlib:** Plotting\n5. **Scikit-learn:** Machine Learning\n6. **TensorFlow:** Deep Learning\n7. **Miscellaneous**"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install tensorflow_io\n!pip install ensemble-boxes","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":false,"_kg_hide-output":false,"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom sklearn.model_selection import StratifiedKFold\nfrom ensemble_boxes import *\nfrom tqdm.notebook import tqdm\n\nimport pydicom\nfrom pydicom.tag import Tag\n\nimport tensorflow as tf\nimport tensorflow_io as tfio\n\nfrom object_detection.protos.string_int_label_map_pb2 import StringIntLabelMap, StringIntLabelMapItem\nfrom object_detection.dataset_tools import tf_record_creation_util\nfrom object_detection.utils import dataset_util\nimport contextlib2\n\nfrom google.protobuf import text_format","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Read data\n1. Chest X-Ray annotations by radiologists (metadata)\n2. Sample Chest X-Ray (DICOM image)"},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Reading dataset of annotations\npath = \"../input/vinbigdata-chest-xray-abnormalities-detection\"\ndf = pd.read_csv(os.path.join(path, \"train.csv\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Reading DICOM images\ndef read_dicom(path, max_dim):\n    image_bytes = tf.io.read_file(path)\n    image = tfio.image.decode_dicom_image(\n        image_bytes, \n        dtype = tf.uint16\n    )\n    \n    image = tf.squeeze(image, axis = 0)\n    \n    h, w, _ = image.shape\n    \n    image = tf.image.resize(\n        image, \n        (max_dim, max_dim), \n        preserve_aspect_ratio = True\n    )\n    \n    image = image - tf.reduce_min(image)\n    image = image / tf.reduce_max(image)\n    image = tf.cast(image * 255, tf.uint8)\n    \n    return image, h, w","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Visualize and preprocess data"},{"metadata":{},"cell_type":"markdown","source":"1. Exploring distribution of radiologists"},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"temp = df[[\"image_id\", \"rad_id\"]].drop_duplicates().reset_index(drop = True)\ntemp = temp.groupby([\"rad_id\"]).agg(\n    count = pd.NamedAgg(\"image_id\", \"count\")\n).reset_index()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"%matplotlib inline\n\nfig, ax = plt.subplots(1, 2, figsize = (15, 5))\n\nsns.countplot(\n    df[\"rad_id\"], \n    palette = \"tab10\", \n    order = list(temp[\"rad_id\"]), \n    ax = ax[0]\n)\nax[0].set_title(\"Number of annotations by radiologists\")\n\nsns.barplot(\n    x = \"rad_id\", \n    y = \"count\", \n    data = temp, \n    palette = \"tab10\", \n    ax = ax[1]\n)\nax[1].set_title(\"Number of x-rays seen by radiologists\")\n\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Radiologists 9, 10 and 8 saw most number of x-rays and made most annotations."},{"metadata":{},"cell_type":"markdown","source":"2. Exploring distribution of thoracic abnormalities"},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"temp = df[[\"image_id\", \"class_name\"]].drop_duplicates().reset_index(drop = True)\ntemp = temp.groupby([\"class_name\"]).agg(\n    count = pd.NamedAgg(\"image_id\", \"count\")\n).reset_index()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"%matplotlib inline\n\nsns.barplot(\n    x = \"class_name\", \n    y = \"count\", \n    data = temp, \n    palette = \"tab10\"\n)\nplt.xticks(rotation = 90)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Looks like most x-rays have no finding. Aortic enlargement is the most common abnormality. At least one occurrence was found in about 3000 x-rays. Cardiomegaly, Pleural thickening and Pulmonary fibrosis follow."},{"metadata":{},"cell_type":"markdown","source":"3. Exploring x-rays"},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"%matplotlib inline\n\nmax_dim = 500\ndemo_image = \"6d5acf3f8a973a26844d617fffe72998.dicom\"\nimage, h, w = read_dicom(os.path.join(path, \"train\", demo_image), max_dim)\n\nplt.figure(figsize = (5, 5))\nplt.imshow(tf.squeeze(image), 'gray')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's improve the contrast of this image using CLAHE (Contrast Limited Adaptive Histogram Equalization). Such image pre-processing redistributes the lightness values of the image making patterns more apparent."},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"def CLAHE(image):\n    clahe = cv2.createCLAHE(\n        clipLimit = 2., \n        tileGridSize = (10, 10)\n    )\n    \n    image = clahe.apply(image.numpy()) \n    image = tf.expand_dims(image, axis = 2)\n    \n    return image","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"%matplotlib inline\n\nfig = plt.figure(figsize = (8, 8))\n\naxes = fig.add_subplot(1, 2, 1)\nplt.imshow(tf.squeeze(image), cmap = \"gray\")\naxes.set_title(\"Original\")\n\naxes = fig.add_subplot(1, 2, 2)\nimage = CLAHE(image)\nplt.imshow(tf.squeeze(image), cmap = \"gray\")\naxes.set_title(\"Post CLAHE\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Before visualizing the abnormalities on the x-rays, let's perform some preprocessing.\n\n**IMPORTANT**\n\nThe API requires the classes to be from 1 to n and outputs 0 when no class is found. Since our labels start with 0, we make unit increment to the class_id and use the new label-map."},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"# Creating LabelMap\ndf[\"class_id\"] = df[\"class_id\"] + 1 # Incrementing by 1\nLabelMap = df.loc[df[\"class_name\"] != \"No finding\", [\"class_name\", \"class_id\"]] # Removing the examples with no finding\nLabelMap = LabelMap.drop_duplicates().reset_index(drop = True)\nLabelMap","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Using 14 unique colors to annotate the abnormalities.\nLABEL_COLORS = [\n    (230, 25, 75), (60, 180, 75), (255, 225, 25), (0, 130, 200), (245, 130, 48), (145, 30, 180), (70, 240, 240), \n    (240, 50, 230), (210, 245, 60), (250, 190, 212), (0, 128, 128), (220, 190, 255), (170, 110, 40), (255, 250, 200), \n]\nLabelMap[\"colors\"] = LABEL_COLORS","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's also save the label mapping as .pbtxt (required). With that we can now visualize the abnormalities on the x-rays."},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Save mappings as .pbtxt\ndef save_mapping(LabelMap):\n    msg = StringIntLabelMap()\n    \n    for i, row in LabelMap.iterrows():\n        msg.item.append(StringIntLabelMapItem(id = row[\"class_id\"], name = row[\"class_name\"]))\n    \n    text = str(text_format.MessageToBytes(msg, as_utf8 = True), 'utf-8')\n    \n    f = open(\"LabelMap.pbtxt\", \"w\")\n    f.write(text)\n    f.close()\n    \nsave_mapping(LabelMap)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Remove examples with no findings (won't be used for training)\ndf = df.dropna().reset_index(drop = True)\n\n# Change data types\ndf = df.astype({\n    \"x_min\": int, \n    \"y_min\": int, \n    \"x_max\": int, \n    \"y_max\": int,\n    \"class_id\": str\n})","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"def plot_boxes(image, data, title):    \n    img = cv2.cvtColor(image.numpy(), cv2.COLOR_GRAY2RGB)\n    \n    for i, row in data.iterrows():\n    \n        x1, y1 = row[\"x_min\"], row[\"y_min\"]\n        x2, y2 = row[\"x_max\"], row[\"y_max\"]\n    \n        cv2.rectangle(\n            img,\n            pt1 = (x1, y1),\n            pt2 = (x2, y2),\n            color = row[\"colors\"],\n            thickness = 2\n        )\n    \n        cv2.putText(\n            img, \n            row[\"class_name\"], \n            (x1, y1-5), \n            cv2.FONT_HERSHEY_SIMPLEX, \n            0.5, \n            row[\"colors\"], \n            1\n        )\n\n    plt.figure(figsize = (8, 8))\n    plt.imshow(img) \n    plt.title(title)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"# Selecting a particular radiologist\ndemo_rad = \"R9\"\n\n# Preprocessing metadata to suit needs\ndata = df.loc[\n    (df[\"image_id\"] == demo_image[:-6]) & (df[\"rad_id\"] == demo_rad),\n    [\"class_name\", \"x_min\", \"y_min\", \"x_max\", \"y_max\"]\n]\n\nH, W, _ = image.shape\ndata[[\"x_min\", \"x_max\"]] = (data[[\"x_min\", \"x_max\"]]* W/w).astype(int)\ndata[[\"y_min\", \"y_max\"]] = (data[[\"y_min\", \"y_max\"]]* H/h).astype(int)\n\ndata = pd.merge(data, LabelMap)\n\n# Plotting annotation by radiologist\nplot_boxes(image, data, \"Labels for \" + demo_image + \" by \" + demo_rad)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's now explore annotations by other radiologists for this x-ray."},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"# Preprocessing metadata to suit needs\ndata = df.loc[\n    (df[\"image_id\"] == demo_image[:-6]),\n    [\"class_name\", \"x_min\", \"y_min\", \"x_max\", \"y_max\"]\n]\n\nH, W, _ = image.shape\ndata[[\"x_min\", \"x_max\"]] = (data[[\"x_min\", \"x_max\"]]* W/w).astype(int)\ndata[[\"y_min\", \"y_max\"]] = (data[[\"y_min\", \"y_max\"]]* H/h).astype(int)\n\ndata = pd.merge(data, LabelMap)\n\n# Plotting annotation by all radiologists\nplot_boxes(image, data, \"Labels for \" + demo_image + \" by all radiologists\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"That's cluttered. We need not train our model on multiple annotations of the same abnormality. We shall use a technique called Weighted Boxes Fusion (WBF) to provide us with the best annotation. This will definitely reduce the metadata size by a lot."},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Preprocessing as needed for WBF\ndata = df.loc[\n    (df[\"image_id\"] == demo_image[:-6]),\n    [\"class_name\", \"x_min\", \"y_min\", \"x_max\", \"y_max\"]\n]\n\ndata[[\"x_min\", \"x_max\"]] = data[[\"x_min\", \"x_max\"]]/w\ndata[[\"y_min\", \"y_max\"]] = data[[\"y_min\", \"y_max\"]]/h\n\ndata = pd.merge(data, LabelMap)\n\nboxes_list = data[[\"x_min\", \"y_min\", \"x_max\", \"y_max\"]].values.tolist()\nscores_list = [1]*len(boxes_list)\nlabels_list = list(data[\"class_id\"])\n\n# Applying WBF\nboxes, _, labels = weighted_boxes_fusion(\n    boxes_list = [boxes_list],\n    scores_list = [scores_list],\n    labels_list = [labels_list],\n    weights = None, \n    iou_thr = 0.3, \n    skip_box_thr = 0.0001\n)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"# Postprocessing after applying WBF \ndata = pd.DataFrame(boxes, columns = [\"x_min\", \"y_min\", \"x_max\", \"y_max\"])\n\nH, W, _ = image.shape\ndata[[\"x_min\", \"x_max\"]] = (data[[\"x_min\", \"x_max\"]]* W).astype(int)\ndata[[\"y_min\", \"y_max\"]] = (data[[\"y_min\", \"y_max\"]]* H).astype(int)\n\ndata[\"class_id\"] = labels.astype(int)\n\ndata = pd.merge(data, LabelMap)\n\n# Plotting annotation by all radiologists\nplot_boxes(image, data, \"Labels for \" + demo_image + \" post WBF\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Awesome. We successfully eliminated multiple annotations for the same abnormality."},{"metadata":{},"cell_type":"markdown","source":"## TFRecord Creation"},{"metadata":{},"cell_type":"markdown","source":"The TFRecord format is a simple format for storing a sequence of binary records. This format is efficient in terms of storage and retrieval. It is the desired input format for the API. But before creating TFRecords, we must first apply WBF to the metadata. To apply WBF we must normalize the coordinates. Reading each image to extract dimensions can be time consuming. Using PyDICOM we can obtain x-ray metadata from which dimensions can be quickly extracted. "},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Dropping rad_id as it is not required for training\ndf = df.drop(columns = [\"rad_id\"])\n\n# Obtaining set of x-rays with at least one finding\nxrays = set(df[\"image_id\"]) # Only 4394 x-rays, not 15000. Roughly 30% of the x-rays remain.","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"dimensions = []\nfor i, xray in tqdm(enumerate(xrays)):\n    ds = pydicom.dcmread(\n        os.path.join(path, \"train\", xray + \".dicom\"), \n        specific_tags = [\n            Tag(\"0028\", \"0010\"), # Tag for Rows (Height)\n            Tag(\"0028\", \"0011\")  # Tag for Columns (Width)\n        ]\n    )\n    \n    dimensions.append([xray, ds.Rows, ds.Columns])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"dimensions = pd.DataFrame(dimensions, columns = [\"image_id\", \"height\", \"width\"])\ndf = pd.merge(dimensions, df)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Normalize coordinates\ndf[\"x_min\"], df[\"x_max\"] = df[\"x_min\"]/df[\"width\"], df[\"x_max\"]/df[\"width\"]\ndf[\"y_min\"], df[\"y_max\"] = df[\"y_min\"]/df[\"height\"], df[\"y_max\"]/df[\"height\"]","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Before applying WBF we had 36096 rows\ndf_list = []\nfor i, xray in tqdm(enumerate(xrays)):\n    data = df[df[\"image_id\"] == xray]\n\n    boxes_list = data[[\"x_min\", \"y_min\", \"x_max\", \"y_max\"]].values.tolist()\n    scores_list = [1]*len(boxes_list)\n    labels_list = list(data[\"class_id\"])\n\n    # Applying WBF\n    boxes, _, labels = weighted_boxes_fusion(\n        boxes_list = [boxes_list],\n        scores_list = [scores_list],\n        labels_list = [labels_list],\n        weights = None, \n        iou_thr = 0.3, \n        skip_box_thr = 0.0001\n    )\n    \n    data = pd.DataFrame(boxes, columns = [\"x_min\", \"y_min\", \"x_max\", \"y_max\"]) \n    # Leaving the coordinates normalized since the API expects them to be so. \n    \n    data[\"class_id\"] = labels.astype(int)\n    \n    data[\"image_id\"] = xray \n    \n    df_list.append(data)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"df = pd.concat(df_list) # After applying WBF we have 21836 rows\ndf = pd.merge(df, LabelMap)\ndf = df.drop(columns = [\"colors\"])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Since we have more than a few thousand examples, it is beneficial to shard the dataset into multiple files:\n* Parallel reading improves throughput.\n* Easy shuffling improves performance.\n\nSharding is cool but you know what's cooler? Stratified K-Fold Sharding. Basically we break down our dataset into multiple (\"K\") TFRecords (each is a shard) in such a way that: \n* The distribution of abnormalities remains the same in each shard.\n* Each x-ray is part of exactly one shard (to avoid information leak). \n\nWe can conveniently use these shards for training, validation and testing."},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Stratified K-Fold Sharding\n\nnum_shards = 25\n\nskf = StratifiedKFold(\n    n_splits = num_shards, \n    shuffle = True, \n    random_state = 0\n)\n\ndf_folds = df[['image_id']].copy()\n\ndf_folds.loc[:, 'bbox_count'] = 1\ndf_folds = df_folds.groupby('image_id').count()   # Number of bounding boxes in the image\ndf_folds.loc[:, 'object_count'] = df.groupby('image_id')['class_id'].nunique() # Number of classes in the image\n\n# Preparing stratify groups\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)\n\n# Determining which fold the x-ray will fall in\ndf_folds.loc[:, 'fold'] = 0\nskf_split = skf.split(\n    X = df_folds.index, \n    y = df_folds['stratify_group']\n)\n\nfor fold_number, (train_index, val_index) in enumerate(skf_split):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number\n    \ndf_folds.reset_index(inplace = True)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"df = pd.merge(df, df_folds)\n\ntemp = df.groupby([\"fold\", \"class_name\"]).agg(\n    count = pd.NamedAgg(\"class_name\", \"count\")\n).reset_index()\n\ntemp = temp.pivot_table(\n    index = \"class_name\",\n    columns = \"fold\",\n    values = \"count\"\n)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (20, 10))\nsns.heatmap(\n    temp,\n    annot = True,\n    cmap = \"YlGnBu\",\n    fmt = \"g\"\n)\nplt.title(\"Heatmap of class distribution\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Notice how color is similar along a row. The color distribution indicates the similar class disturbution across all folds (shards).\n\nOnce sharding is done, it is important to create TFRecords after applying CLAHE to each x-ray. We must remember to apply the same transformations to the x-rays we intend to make predictions for."},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"def create_tf_record(img_path, max_dim, img_df):\n    \n    filename = img_path.split(\"/\")[-1].encode()\n    source_id = img_path.encode()\n    \n    # Preprocess image \n    img, _, _ = read_dicom(img_path, max_dim)\n    height, width, _ = img.shape\n    img = CLAHE(img)\n    \n    # Encode as JPEG (Lossy compression)\n    img = tf.io.encode_jpeg(\n        img, \n        quality = 100, \n        format = 'grayscale'\n    )\n    \n    img_bytes = img.numpy()\n    \n    img_format = b'jpeg'\n\n    xmin_list = list(img_df[\"x_min\"])\n    xmax_list = list(img_df[\"x_max\"])\n    ymin_list = list(img_df[\"y_min\"])\n    ymax_list = list(img_df[\"y_max\"])\n    \n    class_name_list = list(img_df[\"class_name\"])\n    class_name_list = [c.encode() for c in class_name_list]\n    \n    class_id_list = list(img_df[\"class_id\"])\n    \n    # Creating TFRecord\n    tf_record = tf.train.Example(\n        features = tf.train.Features(\n            feature = {\n                'image/height': dataset_util.int64_feature(height),\n                'image/width': dataset_util.int64_feature(width),\n                'image/filename': dataset_util.bytes_feature(filename),\n                'image/source_id': dataset_util.bytes_feature(source_id),\n                'image/encoded': dataset_util.bytes_feature(img_bytes),\n                'image/format': dataset_util.bytes_feature(img_format),\n                'image/object/bbox/xmin': dataset_util.float_list_feature(xmin_list),\n                'image/object/bbox/xmax': dataset_util.float_list_feature(xmax_list),\n                'image/object/bbox/ymin': dataset_util.float_list_feature(ymin_list),\n                'image/object/bbox/ymax': dataset_util.float_list_feature(ymax_list),\n                'image/object/class/text': dataset_util.bytes_list_feature(class_name_list),\n                'image/object/class/label': dataset_util.int64_list_feature(class_id_list),\n            }\n        )\n    )\n    \n    return tf_record","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"annot_path = \"workspace/annotations\" \nos.makedirs(annot_path, exist_ok = True) ","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"img_cnt = np.zeros(num_shards, dtype = int)\n\nwith contextlib2.ExitStack() as tf_record_close_stack:\n    output_tfrecords = tf_record_creation_util.open_sharded_output_tfrecords(\n        tf_record_close_stack, \n        annot_path, \n        num_shards\n    )\n    \n    for i in tqdm(range(num_shards)):\n        df_shard = df[df[\"fold\"] == i]\n        xrays = set(df_shard[\"image_id\"])\n        \n        for xray in xrays:\n            df_image = df_shard[df_shard[\"image_id\"] == xray]\n            \n            img_path = os.path.join(path, \"train\", xray + \".dicom\")\n            tf_record = create_tf_record(img_path, max_dim, df_image)\n            output_tfrecords[i].write(tf_record.SerializeToString())\n            \n            img_cnt[i] += 1\n\nprint(\"Converted {} images\".format(np.sum(img_cnt)))\nprint(\"Images per shard: {}\".format(img_cnt))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"# Save dataframe\ndf.to_csv(\"data.csv\", index = False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"TFRecords created! We are now ready to use these for training, validation and testing. Jump to my [second notebook](https://www.kaggle.com/bhallaakshit/training-evaluation-with-tf2-object-detection-api). "},{"metadata":{},"cell_type":"markdown","source":"## CREDITS\n### I'm a novice TensorFlow developer. This notebook would not have been possible without the following:\n1. https://www.kaggle.com/mistag/data-create-tfrecords-of-vinbigdata-chest-x-rays\n2. https://www.kaggle.com/backtracking/smart-data-split-train-eval-for-object-detection/comments\n\n\nPlease consider upvoting these notebooks as well. :D"}],"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}