{"cells":[{"metadata":{},"cell_type":"markdown","source":"# VinBigData Abnormalities Detection [EDA+Infer]\n- < Reference Code > <br>\n    - [Visual In-Depth EDA – VinBigData Competition Data](https://www.kaggle.com/dschettler8845/visual-in-depth-eda-vinbigdata-competition-data/notebook)<br>\n    - [VinBigData-CXR-AD YOLOv5 14 Class [infer]](https://www.kaggle.com/awsaf49/vinbigdata-cxr-ad-yolov5-14-class-infer)<br>\n    - [Quick data analysis with YOLOv5 at a glance](https://www.kaggle.com/jamsilkaggle/quick-data-analysis-with-yolov5-at-a-glance)\n- if this helps, please do Upvote this code and the original 👍🏼\n- The goal of this code is EDA of the VinBigData and to analyze the validset with YOLOv5 "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import GroupKFold\n\nfrom tqdm.notebook import tqdm\nfrom glob import glob\nimport shutil, os\nimport random\nimport cv2\n\nimport matplotlib\nimport matplotlib.pyplot as plt\nfrom mpl_toolkits.axes_grid1 import ImageGrid\nimport plotly.express as px\nimport seaborn as sns\n\nimport torch\nfrom IPython.display import Image, clear_output","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Import Dataset"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"train_dir = f'/kaggle/input/vinbigdata-512-image-dataset/vinbigdata/train'\nweights_dir = '/kaggle/input/vinbigdata-cxr-ad-yolov5-14-class-train/yolov5/runs/train/exp/weights/best.pt'\n\ntrain_df = pd.read_csv('../input/vinbigdata-512-image-dataset/vinbigdata/train.csv')\ndisplay(train_df.head(3))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Check Train csv Length"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print('train data total length : ', len(train_df))\nprint('train data unique length : ', len(train_df.image_id.unique()))\nprint('The average number of disease per patient :', len(train_df)/len(train_df.image_id.unique()))\nprint('The average number of disease per patient (without healty people):', len(train_df[train_df.class_id!=14])/len(train_df[train_df.class_id!=14].image_id.unique()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Image Number / Unique Abnormalities per Patient"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"color_palette = [px.colors.label_rgb(px.colors.convert_to_RGB_255(x)) for x in sns.color_palette(\"viridis\", 15)]\nfig = px.histogram(train_df.image_id.value_counts(),\n                   title=\"<b>Distribution of Image Number per Patient\",\n                   color_discrete_sequence=color_palette,\n                   log_y = True\n                   )\nfig.update_layout(showlegend=False,\n                  xaxis_title=\"<b>Number of Images</b>\",\n                  yaxis_title=\"<b>Count of Unique Patient\",)\nfig.show()\n\nfig = px.histogram(train_df.groupby('image_id')[\"class_name\"].unique().apply(lambda x: len(x)),  \n                   color_discrete_sequence=color_palette,\n                   title=\"<b>Distribution of Unique Abnormalities per Patient\",\n                   log_y = True\n                   )\nfig.update_layout(showlegend=False,\n                  xaxis_title=\"<b>Number of Unique Abnormalities</b>\",\n                  yaxis_title=\"<b>Count of Unique Patients</b>\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot Label Distribution"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig = px.bar(train_df.class_name.value_counts().sort_index(), \n             color=train_df.class_name.value_counts().sort_index().index,\n             color_discrete_sequence=color_palette,\n             title=\"<b>Label Distribution</b>\")\nfig.update_layout(legend_title=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.show()\n\nfig = px.bar(train_df[train_df.class_id!=14].class_name.value_counts().sort_index(), \n             color=train_df[train_df.class_id!=14].class_name.value_counts().sort_index().index,\n             color_discrete_sequence=color_palette,\n             title=\"<b>Label Distribution except No Finding</b>\")\nfig.update_layout(legend_title=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Heatmap Showing Bounding Box Placement"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"bbox_df = train_df[train_df.class_id!=14].reset_index(drop=True)\nbbox_df['frac_x_min'] = bbox_df.apply(lambda x: (x.x_min)/x.width, axis =1)\nbbox_df['frac_y_min'] = bbox_df.apply(lambda x: (x.y_min)/x.height, axis =1)\nbbox_df['frac_x_max'] = bbox_df.apply(lambda x: (x.x_max)/x.width, axis =1)\nbbox_df['frac_y_max'] = bbox_df.apply(lambda x: (x.y_max)/x.height, axis =1)\nbbox_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"avg_width  = int(np.mean(bbox_df.width))\navg_height = int(np.mean(bbox_df.height))\n\nheatmap_size = (avg_width, avg_height, 14)\nheatmap = np.zeros((heatmap_size), dtype=np.int16)\n\nbbox_np = bbox_df[[\"class_id\", \"frac_x_min\", \"frac_x_max\", \"frac_y_min\", \"frac_y_max\"]].to_numpy()\nbbox_np[:, 1:3] *= avg_width; bbox_np[:, 3:5] *= avg_height\nbbox_np = np.floor(bbox_np).astype(np.int16)\n\nlabel_dic = {i:train_df[train_df[\"class_id\"]==i].iloc[0][\"class_name\"] for i in range(15)}\n\ncustom_cmaps = [matplotlib.colors.LinearSegmentedColormap.from_list(colors=[(0.,0.,0.), c, (0.95,0.95,0.95)], \n        name=f\"custom_{i}\") for i,c in enumerate(sns.color_palette(\"Spectral\", 15))]\ncustom_cmaps.pop(8) # Remove No-Finding\n\nfor row in tqdm(bbox_np, total=bbox_np.shape[0]):\n    heatmap[row[3]:row[4]+1, row[1]:row[2]+1, row[0]] += 1\n    \nfig = plt.figure(figsize=(20,25))\nplt.suptitle(\"Heatmaps Showing Bounding Box Placement\\n \", fontweight=\"bold\", fontsize=16)\nfor i in range(15):\n    plt.subplot(4, 4, i+1)\n    if i==0:\n        plt.imshow(heatmap.mean(axis=-1), cmap=\"bone\")\n        plt.title(f\"Average of All Classes\", fontweight=\"bold\")\n    else:\n        plt.imshow(heatmap[:, :, i-1], cmap=custom_cmaps[i-1])\n        plt.title(f\"{label_dic[i-1]} – id : {i}\", fontweight=\"bold\")\n        \n    plt.axis(False)\nfig.tight_layout(rect=[0, 0.03, 1, 0.97])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Bounding Box Area Percentage of Image"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"bbox_df[\"frac_bbox_area\"] = (bbox_df[\"frac_x_max\"]-bbox_df[\"frac_x_min\"])*(bbox_df[\"frac_y_max\"]-bbox_df[\"frac_y_min\"])\nfig = px.box(bbox_df.sort_values(by=\"class_name\"), x=\"class_name\", y=\"frac_bbox_area\", color=\"class_name\", \n             color_discrete_sequence=color_palette, notched=True,\n             labels={\"class_id_as_str\":\"Class Name\", \"frac_bbox_area\":\"BBox Area (%)\"},\n             title=\"<b>Bounding Box Area % of Image\")\n\nfig.update_layout(showlegend=True,\n                  yaxis_range=[-0.025,0.35],\n                  legend_title_text=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---"},{"metadata":{},"cell_type":"markdown","source":"# Make Valid Set with Group K Fold (except No Finding)"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/vinbigdata-512-image-dataset/vinbigdata/train.csv')\ntrain_df = train_df[train_df.class_id!=14].reset_index(drop = True)\n\ntrain_df['fold'] = -1; val_fold = 4\ngroup_kfold  = GroupKFold(n_splits = 5)\nsplit = group_kfold.split(train_df, groups = train_df.image_id.tolist())\nfor fold, (train_idx, val_idx) in enumerate(split):\n    train_df.loc[val_idx, 'fold'] = fold\nval_df = train_df[train_df['fold']== val_fold]\ndisplay(val_df.head(3))\n\ntrain_df['image_path'] = f'/kaggle/input/vinbigdata-512-image-dataset/vinbigdata/train/'+train_df.image_id+('.png')\ntrain_files = []; val_files = []\ntrain_files += list(train_df[train_df.fold!=fold].image_path.unique())\nval_files += list(train_df[train_df.fold==fold].image_path.unique())\nprint('train file length : {}, valid file length : {}'.format(len(train_files), len(val_files)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"os.makedirs('/kaggle/working/vinbigdata/labels/train', exist_ok = True)\nos.makedirs('/kaggle/working/vinbigdata/labels/val', exist_ok = True)\nos.makedirs('/kaggle/working/vinbigdata/images/train', exist_ok = True)\nos.makedirs('/kaggle/working/vinbigdata/images/val', exist_ok = True)\nlabel_dir = '/kaggle/input/vinbigdata-yolo-labels-dataset/labels'\n\nfor file in train_files:\n    shutil.copy(file, '/kaggle/working/vinbigdata/images/train')\n    filename = file.split('/')[-1].split('.')[0]\n    shutil.copy(os.path.join(label_dir, filename+'.txt'), '/kaggle/working/vinbigdata/labels/train')\n    \nfor file in val_files:\n    shutil.copy(file, '/kaggle/working/vinbigdata/images/val')\n    filename = file.split('/')[-1].split('.')[0]\n    shutil.copy(os.path.join(label_dir, filename+'.txt'), '/kaggle/working/vinbigdata/labels/val')\n    \nval_dir = f'/kaggle/working/vinbigdata/images/val'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# YOLO v5 Setup"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"shutil.copytree('/kaggle/input/yolov5-official-v31-dataset/yolov5', '/kaggle/working/yolo5')\nos.chdir('/kaggle/working/yolo5')\n\nclear_output()\nprint('Setup complete. Using torch %s' % (torch.__version__))\nprint(torch.cuda.get_device_properties(0) if torch.cuda.is_available() else 'CPU')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Yolo v5 Inference"},{"metadata":{"trusted":true,"collapsed":true,"_kg_hide-output":true},"cell_type":"code","source":"!python detect.py --weights $weights_dir\\\n--img 640\\\n--conf 0.15\\\n--iou 0.4\\\n--source $val_dir\\\n--save-txt --save-conf --exist-ok","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot Inference Results"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"files = glob('runs/detect/exp/*png')\nfor _ in range(1):\n    row = 6; col = 4\n    grid_files = random.sample(files, row*col)\n    images     = []\n    for image_path in tqdm(grid_files):\n        img          = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)\n        images.append(img)\n\n    fig = plt.figure(figsize=(col*5, row*5))\n    grid = ImageGrid(fig, 111,  # similar to subplot(111)\n                     nrows_ncols=(row, col),  # creates 2x2 grid of axes\n                     axes_pad=0.05,  # pad between axes in inch.\n                     )\n\n    for ax, im in zip(grid, images):\n        # Iterating over the grid returns the Axes.\n        ax.imshow(im)\n        ax.set_xticks([])\n        ax.set_yticks([])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def yolo2voc(image_height, image_width, bboxes):\n    bboxes = bboxes.copy().astype(float)\n    bboxes[..., [0, 2]] = bboxes[..., [0, 2]]* image_width\n    bboxes[..., [1, 3]] = bboxes[..., [1, 3]]* image_height\n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] - bboxes[..., [2, 3]]/2\n    bboxes[..., [2, 3]] = bboxes[..., [0, 1]] + bboxes[..., [2, 3]]\n    return bboxes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"image_ids = []; PredictionStrings = []; classes = []; scores = []\nx_min = []; y_min = []; x_max = []; y_max = []\n\nfor file_path in glob('runs/detect/exp/labels/*txt'):\n    image_id = file_path.split('/')[-1].split('.')[0]\n    w, h = val_df[val_df.image_id==image_id][['width', 'height']].values[0]\n    f = open(file_path, 'r')\n    data = np.array(f.read().replace('\\n', ' ').strip().split(' ')).astype(np.float32).reshape(-1, 6)\n    data = data[:, [0, 5, 1, 2, 3, 4]]\n    bboxes = list(np.concatenate((data[:, :2], np.round(yolo2voc(h, w, data[:, 2:]))), axis =1).reshape(-1))#.astype(str))\n    for i in range(len(bboxes)//6):\n        image_ids.append(image_id)\n        classes.append(int(bboxes[i*6]))\n        scores.append(int(bboxes[i*6+1]))\n        x_min.append(int(bboxes[i*6+2]))\n        y_min.append(int(bboxes[i*6+3]))\n        x_max.append(int(bboxes[i*6+4]))\n        y_max.append(int(bboxes[i*6+5]))\n        \npred_df = pd.DataFrame({'image_id':image_ids,'classes':classes,'scores':scores,'x_min':x_min,'y_min':y_min,'x_max':x_max,'y_max':y_max})\npred_df['class_name'] = pred_df.classes.apply(lambda x : label_dic[x])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot Actual Distribution vs Predicted Distribution"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig = px.bar(val_df.class_name.value_counts().sort_index(), \n             color=val_df.class_name.value_counts().sort_index().index,\n             color_discrete_sequence=color_palette,\n             title=\"<b>Actual Distribution of Validation Dataset</b>\")\nfig.update_layout(legend_title=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.show()\n\nfig = px.bar(pred_df.class_name.value_counts().sort_index(), \n             color=pred_df.class_name.value_counts().sort_index().index,\n             color_discrete_sequence=color_palette,\n             title=\"<b>Predicted Distribution of Validation Dataset</b>\")\nfig.update_layout(legend_title=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.show()\n\ndiff = val_df.class_name.value_counts().sort_index()-pred_df.class_name.value_counts().sort_index()\nfig = px.bar(diff, \n             color=diff.index,\n             color_discrete_sequence=color_palette,\n             title=\"<b>The Difference between Actual Distribution and Predicted Distribution</b>\")\nfig.update_layout(legend_title=None,\n                  xaxis_title=\"\",\n                  yaxis_title=\"\")\nfig.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}