{"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":"# Version note\n- Ver 4: Fold 4 hyper\n- Ver 5: Fold4 Finetune\n- Ver 7: Fold 3 hyper\n- Ver 9: Fold 3 Finetune\n- Ver 10: Fold 2 hyper\n- Ver 11: Fold 2 Finetune\n- Ver 12: Fold 1 hyper\n- Ver 13: Fold 1 Finetune\n- Ver 14: Fold 0 hyper\n- Ver 15: Fold 0 Finetune\n# NOTES\n\n## Step 1: Preprocessing\n### 1.1: Download Data and create your own Dataset by read dicom image and resize\n### 1.2: Try to merge Annotation by WBF\n### 1.3: Load Dataset to kaggle, create a DataFrame with include x_mid, y_mid, w and h that follow YOLOv5 annotation bbbox format\n### 1.4: Create a final Training DataFrame\n\n## Step 2: \n### 2.1: Create all file for training include:\n        - Annotation .txt file\n        - train.txt and valid.txt and test.txt\n        - custom.yaml\n### 2.2: Hyperparameter to findout best setting of YOLOv5\n        - hyp.scratch.yaml\n### 2.3: Training YOLOv5 with Abnormal Images\n        - hyp.finetune.yaml\n## Step 3: 2 filter classes\n### 2.1: Training Abnormal and Normal to have a classification model\n### 2.2: Combines 2-filter classification model and Yolov5 result -> Submission file","metadata":{}},{"cell_type":"markdown","source":"# SET UP FOR NOTEBOOK","metadata":{}},{"cell_type":"markdown","source":"## Import libaries","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom tqdm.notebook import tqdm\nimport seaborn as sns\nimport os\nfrom sklearn.model_selection import GroupKFold\nimport cv2\nimport PIL.Image as Image\n\n\nprint('Setup Completed')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Global value","metadata":{}},{"cell_type":"code","source":"# ===============================\n#Conf for WBF\niou_thr = 0.5\nskip_box_thr = 0.0001\nsigma = 0.1\n# ===============================\n#Fold value\ndim = 640 #512, 256, 'original'\nfold_num = 4","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Global function","metadata":{}},{"cell_type":"code","source":"def Preprocessing(input_path, output_path):\n    #Example image\n    #Read image\n    img = cv2.imread(input_path)\n    gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    \n    #Histogram Equlization\n    # create a CLAHE object\n    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))\n    cl1 = clahe.apply(gray_image)\n    img_f = cv2.cvtColor(cl1, cv2.COLOR_GRAY2BGR)\n    \n    #Normalization\n    norm_img = np.zeros((800,800))\n    n_img = cv2.normalize(img_f,  norm_img, 0, 255, cv2.NORM_MINMAX)\n    \n    final_image = Image.fromarray(n_img)\n    final_image.save(output_path)\n    \ndef create_annotation(image_id,output_path,data):\n    save_txt_path = os.path.join(output_path, image_id+\".txt\") \n    file = open(save_txt_path, \"w+\")\n    image_data = data.loc[data.image_id == image_id]\n    for i in image_data.index:\n        object_label = image_data[\"class_id\"][i]\n        x_centre = image_data[\"x_mid\"][i]\n        y_centre = image_data[\"y_mid\"][i]\n        w = image_data[\"w\"][i]\n        h = image_data[\"h\"][i]\n        file.write(f'{object_label} {x_centre} {y_centre} {w} {h}\\n')\n    file.close()\n    \n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# STEP1: IMPORT DATA + SPLIT DATASET + MERGE ANNOTATION","metadata":{}},{"cell_type":"markdown","source":"## Read data","metadata":{}},{"cell_type":"code","source":"#Define folder path (Custom)\nTRAIN_DIR = '/kaggle/input/vinbigdata-640pixel/train_vin.csv'\nTEST_DIR = '/kaggle/input/vinbigdata-640pixel/test_vin.csv'\n#Define folder path (Origin)\nOrigin_TRAIN_DIR = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train.csv'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Merge Annotation\nWe have annotations from many radiologists. They annotated the same issue, but different target box. -> we merge the boxes.","metadata":{}},{"cell_type":"code","source":"!pip install ensemble-boxes\nfrom ensemble_boxes import *\n\nprint('Setup WBF completed')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(Origin_TRAIN_DIR)\ndf.fillna(0, inplace=True)\ndf.loc[df[\"class_id\"] == 14, ['x_max', 'y_max']] = 1.0\n\nresults = []\nimage_ids = df[\"image_id\"].unique()\n\nfor image_id in tqdm(image_ids, total=len(image_ids)):\n\n    # All annotations for the current image.\n    data = df[df[\"image_id\"] == image_id]\n    data = data.reset_index(drop=True)\n\n    annotations = {}\n    weights = []\n\n    # WBF expects the coordinates in 0-1 range.\n    max_value = data.iloc[:, 4:].values.max()\n    data.loc[:, [\"x_min\", \"y_min\", \"x_max\", \"y_max\"]] = data.iloc[:, 4:] / max_value\n\n    # Loop through all of the annotations\n    for idx, row in data.iterrows():\n\n        rad_id = row[\"rad_id\"]\n\n        if rad_id not in annotations:\n            annotations[rad_id] = {\n                \"boxes_list\": [],\n                \"scores_list\": [],\n                \"labels_list\": [],\n            }\n\n            # We consider all of the radiologists as equal.\n            weights.append(1.0)\n\n        annotations[rad_id][\"boxes_list\"].append([row[\"x_min\"], row[\"y_min\"], row[\"x_max\"], row[\"y_max\"]])\n        annotations[rad_id][\"scores_list\"].append(1.0)\n        annotations[rad_id][\"labels_list\"].append(row[\"class_id\"])\n\n    boxes_list = []\n    scores_list = []\n    labels_list = []\n\n    for annotator in annotations.keys():\n        boxes_list.append(annotations[annotator][\"boxes_list\"])\n        scores_list.append(annotations[annotator][\"scores_list\"])\n        labels_list.append(annotations[annotator][\"labels_list\"])\n\n    # Calculate WBF\n    boxes, scores, labels = weighted_boxes_fusion(\n        boxes_list,\n        scores_list,\n        labels_list,\n        weights=weights,\n        iou_thr=iou_thr,\n        skip_box_thr=skip_box_thr\n    )\n\n    for idx, box in enumerate(boxes):\n        results.append({\n            \"image_id\": image_id,\n            \"class_id\": int(labels[idx]),\n            \"rad_id\": \"wbf\",\n            \"x_min\": box[0] * max_value,\n            \"y_min\": box[1] * max_value,\n            \"x_max\": box[2] * max_value,\n            \"y_max\": box[3] * max_value,\n        })\n\nFinal_df = pd.DataFrame(results)\ndisplay(df.head())\ndisplay(Final_df.head())\nprint(f'Size of origin Dataframe: {df.shape}')\nprint(f'Size of WBF Dataframe: {Final_df.shape}')\nprint(f'Number of images: {len(image_ids)}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Custom dataset\ntrain_df = pd.read_csv(TRAIN_DIR)\n#test_df = pd.read_csv(TEST_DIR)\ndisplay(train_df.head())\nprint(train_df.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"width = {}\nheight = {}\nfor indx in tqdm(image_ids, total=len(image_ids)):\n    width.update({indx:train_df[train_df.image_id == indx].width.unique()[0]})\n    height.update({indx:train_df[train_df.image_id == indx].height.unique()[0]})","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#ADD width and height of image from train_df to Final_df\nFinal_df['width'] = Final_df.apply(lambda row: width[row.image_id], axis =1)\nFinal_df['height'] = Final_df.apply(lambda row: height[row.image_id], axis =1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Caculate x_mid y_mid height_bbox width_bbox","metadata":{}},{"cell_type":"code","source":"Final_df['x_mid'] = Final_df.apply(lambda row: ((row.x_max)/row.width+(row.x_min)/row.width)/2, axis =1)\nFinal_df['y_mid'] = Final_df.apply(lambda row: ((row.y_max)/row.height+(row.y_min)/row.height)/2, axis =1)\nFinal_df['w'] = Final_df.apply(lambda row: ((row.x_max)/row.width-(row.x_min)/row.width), axis =1)\nFinal_df['h'] = Final_df.apply(lambda row: ((row.y_max)/row.height-(row.y_min)/row.height), axis =1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(Final_df.head())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Split data to Normal and Abnormal","metadata":{}},{"cell_type":"code","source":"# ===============================\n#Abnormal\nabnormal_train_df = Final_df[Final_df.class_id!=14].reset_index(drop = True)\nprint('Abnormal: ')\ndisplay(abnormal_train_df.head())\nprint(f'Number of Abnormal value: {abnormal_train_df.shape[0]}')\nprint(f'Number of Abnormal image: {len(abnormal_train_df.image_id.unique())}')\n# ===============================\n#Normal\nnormal_train_df = Final_df[Final_df.class_id==14].reset_index(drop = True)\nprint('\\nNormal: ')\ndisplay(normal_train_df.head())\nprint(f'Number of Normal value: {normal_train_df.shape[0]}')\nprint(f'Number of Normal image: {len(normal_train_df.image_id.unique())}')\n# ===============================","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## List of classes","metadata":{}},{"cell_type":"code","source":"# ===============================\n#List of Disease in Data\nclass_ids, class_names = list(zip(*set(zip(df.class_id, df.class_name))))\nclasses = list(np.array(class_names)[np.argsort(class_ids)])\nclasses = list(map(lambda x: str(x), classes))\nclasses.pop()\nclasses\n# ===============================","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 2:\n## 2.1: Create all file for training include\n","metadata":{}},{"cell_type":"markdown","source":"### Cross validation","metadata":{}},{"cell_type":"code","source":"gkf  = GroupKFold(n_splits = 5)\nabnormal_train_df['fold'] = -1\nfor fold, (train_idx, val_idx) in enumerate(gkf.split(abnormal_train_df, groups = abnormal_train_df.image_id.tolist())):\n    abnormal_train_df.loc[val_idx, 'fold'] = fold\ndisplay(abnormal_train_df.head())\n\ntrain_files = []\nval_files   = []\nval_files += list(abnormal_train_df[abnormal_train_df.fold==fold_num].image_id.unique())\ntrain_files += list(abnormal_train_df[abnormal_train_df.fold!=fold_num].image_id.unique())\nprint(len(train_files))\nprint(len(val_files))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Copy Files from input to working directory (Also apply Preprocessing)","metadata":{}},{"cell_type":"code","source":"Dir_origin_image = '/kaggle/input/vinbigdata-640pixel/Train'\n# ===============================    \ntrain_image_path = '/kaggle/working/custom_data/images/train'\ntrain_labels_path = '/kaggle/working/custom_data/labels/train'\nval_image_path = '/kaggle/working/custom_data/images/val'\nval_labels_path = '/kaggle/working/custom_data/labels/val'\n# ===============================    \nos.makedirs(train_image_path, exist_ok = True)\nos.makedirs(val_image_path, exist_ok = True)\nos.makedirs(train_labels_path, exist_ok = True)\nos.makedirs(val_labels_path, exist_ok = True)\n# ===============================    \n#Copy, processing image from input to Working and create annotation file\nfor image_index in tqdm(train_files, total=len(train_files)):\n    path_origin_images = os.path.join(Dir_origin_image, image_index +\".jpg\")\n    # ===============================\n    path_images = os.path.join(train_image_path, image_index +\".jpg\")\n    path_label = os.path.join(train_labels_path, image_index +\".txt\")\n    # ===============================\n    Preprocessing(path_origin_images, path_images)\n    create_annotation(image_index,train_labels_path,abnormal_train_df)\n# ===============================    \nfor image_index in tqdm(val_files, total=len(val_files)):\n    path_origin_images = os.path.join(Dir_origin_image, image_index +\".jpg\")\n    # ===============================\n    path_images = os.path.join(val_image_path, image_index +\".jpg\")\n    path_label = os.path.join(val_labels_path, image_index +\".txt\")\n    # ===============================\n    Preprocessing(path_origin_images, path_images)\n    create_annotation(image_index,val_labels_path,abnormal_train_df)\n# ===============================       \n#Create train.txt and test.txt\nDir_custom = '/kaggle/working/custom_data'\ntrain = 'train'\ntest = 'test'\n# ===============================    \ntrain_txt_path = os.path.join(Dir_custom,'train.txt')\ntest_txt_path = os.path.join(Dir_custom,'test.txt') \n# ===============================    \nfile_train = open(train_txt_path, \"w+\")\nfor image_id in tqdm(train_files,total=len(train_files)):\n    file_path = os.path.join(train_image_path, image_id +\".jpg\")\n    file_train.write(f'{file_path}\\n')\nfile_train.close()\n# ===============================    \nfile_test = open(test_txt_path, \"w+\")\nfor image_id in tqdm(val_files,total=len(val_files)):\n    file_path = os.path.join(val_image_path, image_id +\".jpg\")\n    file_test.write(f'{file_path}\\n')\nfile_test.close()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create custom.yaml files","metadata":{}},{"cell_type":"code","source":"#Create custom.yaml files\nfrom os.path import isfile, join\nimport yaml\ndata = dict(\n    train =  train_txt_path ,\n    val   =  test_txt_path,\n    nc    = 14,\n    names = classes\n)\nwith open(join( Dir_custom , f'custom.yaml'), 'w') as outfile:\n    yaml.dump(data, outfile, default_flow_style=False)\n\nf = open(join( Dir_custom , f'custom.yaml'), 'r')\nprint('\\nyaml:')\nprint(f.read())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# YOLOv5","metadata":{}},{"cell_type":"markdown","source":"## Install Enviroment","metadata":{}},{"cell_type":"code","source":"#cloning yolov5 model\n!git clone https://github.com/ultralytics/yolov5\n\n#cloning NVIDIA/apex to speed up the process\n!git clone https://github.com/NVIDIA/apex.git","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom IPython.display import Image, clear_output  # to display images\nprint('Setup complete. Using torch %s %s' % (torch.__version__, torch.cuda.get_device_properties(0) if torch.cuda.is_available() else 'CPU'))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mv yolov5/* ./","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -r requirements.txt","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python detect.py --weights yolov5s.pt --img 640 --conf 0.25 --source /kaggle/working/data/images/zidane.jpg","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nImage(filename='/kaggle/working/runs/detect/exp/zidane.jpg', width=600)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Hyper parameter YOLOv5","metadata":{}},{"cell_type":"markdown","source":"import shutil \ntrain_input = os.path.join('/kaggle/input/trainfile','train.py')\ntrain_working = os.path.join('/kaggle/working','train.py')\nshutil.copy(train_input,train_working)","metadata":{}},{"cell_type":"code","source":"!WANDB_MODE=\"dryrun\"  python train.py --img 640 --batch 16 --epochs 100 --data /kaggle/working/custom_data/custom.yaml --weights yolov5s.pt --hyp /kaggle/working/data/hyp.scratch.yaml\n\n#!WANDB_MODE=\"dryrun\"  python train.py --batch 16 --epochs 100 --data /kaggle/working/custom_data/custom.yaml --weights /kaggle/input/weight/best_fold0_stratch.pt --hyp /kaggle/working/data/hyp.finetune.yaml\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.display import FileLink\nFileLink(r'runs/train/exp/weights/best.pt')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nplt.figure(figsize=(30,15))\nplt.axis('off')\nplt.imshow(plt.imread('runs/train/exp/results.png'));","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}