{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Fastai Tutorial for VinBigDate Chest Xray Abnormalities Detection\n\n## With Weighted Bbox fusion"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"!pip uninstall fastai -y","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import os,sys\nsys.path.append('../input/fastaiv1')\n!pip install -q object-detection-fastai","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"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 collections import defaultdict\nimport os\nfrom tqdm.notebook import tqdm\nfrom fastai import *\nfrom fastai.vision import *\nfrom fastai.callbacks import *\nimport seaborn as sns \nimport matplotlib.pyplot as plt\nimport matplotlib.image as immg\nfrom torchvision.models.detection.rpn import AnchorGenerator\nfrom sklearn.model_selection import StratifiedKFold,KFold\n\nfrom object_detection_fastai.helper.object_detection_helper import *\nfrom object_detection_fastai.loss.RetinaNetFocalLoss import RetinaNetFocalLoss\nfrom object_detection_fastai.models.RetinaNet import RetinaNet\nfrom object_detection_fastai.callbacks.callbacks import BBLossMetrics, BBMetrics, PascalVOCMetric","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.set_style('darkgrid')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/vinbigdata-weighted-bbox-fusion/weighted_box_fused_train_vinBigData.csv')\nimg_dim = pd.read_csv('../input/vinbigdata-resized-image-512/train_meta.csv')\ntr_img_dir = Path('../input/vinbigdata-resized-image-512/train')\nts_img_dir = Path('../input/vinbigdata-resized-image-512/test')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df = df.merge(img_dim,on='image_id',how='left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df1 = tr_df[(tr_df['class_id']!=14)&(tr_df['class_id']!=4)&(tr_df['class_id']!=2)&(tr_df['class_id']!=9)].copy()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Rescaling bounding boxes according to resized images"},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df1['x_min'] = tr_df1['x_min']*512/tr_df['dim1']\ntr_df1['x_max'] = tr_df1['x_max']*512/tr_df['dim1']\ntr_df1['y_min'] = tr_df1['y_min']*512/tr_df['dim0']\ntr_df1['y_max'] = tr_df1['y_max']*512/tr_df['dim0']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df1.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df1.class_id.value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unq_id = tr_df1.image_id.unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_grp = tr_df1.groupby(['image_id'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"one_hot = {}\nfor i in tqdm(unq_id):\n    l = np.zeros(14)\n    temp = df_grp.get_group(i)\n    for j in temp.class_id.unique():\n        l[j] = 1\n    one_hot[i] = l","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_grp.get_group('9a5094b2563a1ef3ff50dc5c7ff71345')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fold_df = pd.DataFrame.from_dict(one_hot,orient='index');\nfold_df.reset_index(inplace=True)\nfold_df.columns = ['image_id']+fold_df.columns.tolist()[1:]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sys.path.append('../input/multistartifiedkfold')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## MultiStratified KFOLD"},{"metadata":{"trusted":true},"cell_type":"code","source":"from iterstrat.ml_stratifiers import MultilabelStratifiedKFold","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fold_df['kfold'] = -1\nfold_df = fold_df.sample(frac=1.0).reset_index(drop=True)\ny = fold_df[fold_df.columns.tolist()[1:]].values\nkf = MultilabelStratifiedKFold(n_splits=5)\nfor fld, (trn_,val_) in enumerate(kf.split(X=fold_df,y=y)):\n    fold_df.loc[val_,'kfold'] = fld","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fold_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"b_fea = ['x_min', 'y_min', 'x_max', 'y_max']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Sample Check"},{"metadata":{"trusted":true},"cell_type":"code","source":"name = '0c7a38f293d5f5e4846aa4ca6db4daf1'\nloc = '../input/vinbigdata-resized-image-512/train/'+name+'.png'\naaa = df_grp.get_group(name)\nbbx = aaa.loc[:,b_fea]\nimg = immg.imread(loc)\nfig,ax = plt.subplots(figsize=(18,10))\nax.imshow(img,cmap='binary')\nfor i in range(len(bbx)):\n    box = bbx.iloc[i].values\n    x,y,w,h = box[0], box[1], box[2]-box[0], box[3]-box[1]\n    rect = patches.Rectangle((x,y),w,h,linewidth=1,edgecolor='r',facecolor='none',)\n    ax.text(*box[:2], aaa['class_id'].iloc[i], verticalalignment='top', color='white', fontsize=12, weight='bold')\n    ax.add_patch(rect)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get lblbox"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_lbl_img(train):\n    chest2bbox = {}\n    grp = train.image_id.unique()\n    tr_gr = train.groupby(['image_id'])\n    from tqdm.notebook import tqdm\n    for i in tqdm(range(len(grp))):\n        name = str(grp[i])+'.png'\n        bbox = []\n        lbls = []\n        temp_b = []\n        temp = tr_gr.get_group(grp[i])\n        tt = temp.loc[:, (['class_id','x_min', 'y_min', 'x_max', 'y_max'])].values\n        for j in range(len(temp)):\n            lbls.append(tt[j][0].astype(int))\n            b = list(np.round(tt[j][1:]))   # x,y, width, height\n            # Currently our coordinates are x,w,l,h and we want x1,y1,x2,y2\n            # To convert it, we need to add our width and height to the respective x and y.\n            t1 = [b[1],b[0],b[3],b[2]]\n\n            temp_b.append(t1)\n        bbox.append(temp_b)\n        bbox.append(lbls)\n        chest2bbox[name] = bbox\n    return chest2bbox","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"chest2bbox = get_lbl_img(tr_df1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"chest2bbox['0c7a38f293d5f5e4846aa4ca6db4daf1.png']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"get_y_func = lambda o: chest2bbox[Path(o).name] ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr = tr_df1.image_id.value_counts()\ntr = pd.DataFrame({'image_id':tr.index,'image_count':tr.values})\ntr = tr.sample(frac=1.,random_state=2020).reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fold_df.to_csv('kfold_train.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"FOLD = 2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_idx,val_idx = fold_df[fold_df['kfold']!=FOLD].index,fold_df[fold_df['kfold']==FOLD].index","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(trn_idx),len(val_idx)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data = (ObjectItemList.from_df(fold_df,path='../input/vinbigdata-resized-image-512', folder = 'train' ,cols='image_id',suffix='.png')\n        #Where are the images? ->\n        .split_by_idxs(train_idx=trn_idx, valid_idx=val_idx)                         \n        #How to split in train/valid? -> randomly with the default 20% in valid\n        .label_from_func(get_y_func)\n        #How to find the labels? -> use get_y_func on the file name of the data\n        .transform(size=512,resize_method=ResizeMethod.SQUISH)\n        #.add_test(ts)\n        #Data augmentation? -> Standard transforms; also transform the label images\n        .databunch(bs=16, collate_fn=bb_pad_collate))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data.show_batch( 1, figsize = (15,10))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(data.train_ds),len(data.valid_ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data.classes,data.c","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"size = 512","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"anchors = create_anchors(sizes=[(32,32),(16,16),(8,8),(4,4)], ratios=[0.5, 1.0, 2.0], scales=[0.25, 0.65, 1.25, 2.5, 3.5, 4.5])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(figsize=(10,10))\nax.imshow(image2np(data.valid_ds[0][0].data))\n\nfor i, bbox in enumerate(anchors[:18]):\n    bb = bbox.numpy()\n    x = (bb[0] + 1) * size / 2 \n    y = (bb[1] + 1) * size / 2 \n    w = bb[2] * size / 2\n    h = bb[3] * size / 2\n    \n    rect = [x,y,w,h]\n    draw_rect(ax,rect)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(anchors)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"n_classes = data.train_ds.c\n\ncrit = RetinaNetFocalLoss(anchors)\n\nencoder = create_body(models.resnet34, True, -2)\n\nmodel = RetinaNet(encoder, n_classes=data.train_ds.c, n_anchors=18, sizes=[32,16,8,4], chs=32, final_bias = -4., n_conv = 2)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"voc = PascalVOCMetric(anchors, size, [i for i in data.train_ds.y.classes[1:]])\nlearn = Learner(data,\n                model, \n                loss_func=crit,\n                callback_fns=[BBMetrics],\n                metrics=[voc],\n                model_dir = '/kaggle/working/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.split([model.encoder[6], model.c5top5]);\nlearn.freeze_to(-2)\n#learn = learn.to_fp16()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.fit_one_cycle(3, 1e-3 ,callbacks = [SaveModelCallback(learn, every ='improvement', monitor ='valid_loss', name ='best_model1',mode='min')])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.unfreeze()\nlearn.fit_one_cycle(10,1e-3/2,callbacks = [SaveModelCallback(learn, every ='improvement', monitor ='valid_loss', name ='best_model2',mode='min')])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.recorder.plot_losses()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.load('best_model2');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"show_results_side_by_side(learn, anchors, detect_thresh=0.4, nms_thresh=0.1, image_count=10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}