{"cells":[{"metadata":{},"cell_type":"markdown","source":"I started working on this competition quite late but I thought I'd be okay. I easisy reached ~0.72 score and then decided to rewrite everything (I my solution was based on someone else's notebooks) and make it clean and easy to edit.\n\nTraining notebook (https://www.kaggle.com/poddiachyi/train-yolov5-cleanest-notebook) is working just fine but I got issues with inference notebook (this one).\n\nAll in all I made about 10 submissions all of which resulted in \"Submission CSV not found\" even though it perfectly worked on the test set.\n\nJust making it all public cause I don't care anymore.","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","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)\nimport os\nimport torch\nimport sys\nimport cv2\nfrom matplotlib import pyplot as plt\n\nimport glob\nfrom tqdm.auto import tqdm\nimport shutil as sh\n\nsys.path.insert(0, \"../input/weightedboxesfusion\")\nfrom ensemble_boxes.ensemble_boxes_wbf import weighted_boxes_fusion\n\nimport warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!cp -r ../input/yolov5trainstable/* ./","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from utils.datasets import LoadImages\nfrom utils.utils import scale_coords, non_max_suppression","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\ncsv_path = '../input/global-wheat-detection/train.csv'\ndataset_path = '../input/global-wheat-detection'\nmodel_path = '../input/train-yolov5-simple/trained_models/weights/best_yolov5x_fold0.pt'\n# model_path = '../input/train-yolov5-simple/trained_models/weights/last_yolov5x_fold0.pt'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def set_seed():\n    seed = 42\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    np.random.seed(seed)\n    random.seed(seed)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_df(path):\n    df = pd.read_csv(csv_path)\n\n    bboxs = np.stack(df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\n\n    for i, column in enumerate(['x', 'y', 'w', 'h']):\n        df[column] = bboxs[:,i]\n\n    df.drop(columns=['bbox'], inplace=True)\n\n    df['x_center'] = df['x'] + df['w'] / 2\n    df['y_center'] = df['y'] + df['h'] / 2\n    df['classes'] = 0\n\n    df = df[['image_id','x', 'y', 'w', 'h','x_center','y_center','classes']]\n    \n    return df\n\n\ndef create_folder(path):\n    if not os.path.exists(path):\n        os.makedirs(path)\n        \n        \ndef write_bboxes_to_ann(file, bboxes):\n    for j in range(len(bboxes)):\n        text = ' '.join(bboxes[j])\n        file.write(text)\n        file.write(\"\\n\")\n        \n        \ndef process_bboxes(ann_file, table):\n    with open(ann_file, 'w+') as f:\n        bboxes = table[['classes','x_center','y_center','w','h']].astype(float).values\n        bboxes = bboxes / 1024 # because images always 1024 at the beginning and we need to scale bboxes on original image\n        bboxes = bboxes.astype(str)\n        write_bboxes_to_ann(f, bboxes)\n        \n\ndef prepare_data_for_training(df):\n    index = list(set(df.image_id))\n    val_index = index[0 : len(index)//5]\n    source = 'train'\n    for name, table in tqdm(df.groupby('image_id')):\n\n        if name in val_index:\n            phase = 'val2017/'\n        else:\n            phase = 'train2017/'\n\n        full_labels_path = os.path.join('convertor', phase, 'labels')\n        create_folder(full_labels_path)\n\n        ann_file_path = os.path.join(full_labels_path, name + '.txt') # annotation file\n        process_bboxes(ann_file_path, table)\n\n        img_folder = os.path.join('convertor', phase, 'images')\n        create_folder(img_folder)\n\n        name_with_ext = name + '.jpg'\n        img_src = os.path.join(dataset_path, source, name_with_ext)\n        img_dst = os.path.join('convertor', phase, 'images', name_with_ext)\n        sh.copy(img_src, img_dst)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -r convertor/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train = load_df(csv_path)\nprepare_data_for_training(df_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"! cd convertor/ && ls","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_model(path):\n    model = torch.load(path, map_location=device)['model'].float()  # load to FP32\n    model.to(device)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = load_model(model_path)\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def pred_to_bboxes_and_scores(pred, img_shape, org_img_shape):\n    bboxes = []\n    scores = []\n    for i, det in enumerate(pred):  # detections per image\n        if det is not None and len(det):\n            # Rescale boxes from img_size to im0 size\n            det[:, :4] = scale_coords(img_shape, det[:, :4], org_img_shape).round()\n\n            # Write results\n            for *xyxy, conf, cls in det:\n                xywh = torch.tensor(xyxy).view(-1).numpy()  # normalized xywh\n                bboxes.append(xywh)\n                scores.append(conf)\n                \n    return np.array(bboxes), np.array(scores)  ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_bboxes(img, bboxes, scores):\n    font = cv2.FONT_HERSHEY_SIMPLEX \n    fontScale = 0.7\n    color = (255, 0, 0) \n    text_shift = 5\n    thickness = 1\n    for b,s in zip(bboxes, scores):\n        img_show = cv2.rectangle(img, (b[0], b[1]), (b[2], b[3]), color, 1) \n        img_show = cv2.putText(img_show, '{:.2}'.format(s), (int(b[0]) + text_shift, int(b[1]) + text_shift), font,  \n                       fontScale, color, thickness, cv2.LINE_AA)\n    \n    return img_show","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot(imgs):\n    if len(imgs) == 1:\n        plt.figure(figsize=[20, 20])\n        plt.imshow(imgs[0])\n    else:\n        fig = plt.figure(figsize=(30, 30))\n        columns = 5\n        rows = 2\n        for i in range(len(imgs)):\n            fig.add_subplot(rows, columns, i + 1)\n            plt.imshow(imgs[i])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def detect(model, img_path, img_size, conf_thres, iou_thres, show=False, augment=True):\n  \n    dataset = LoadImages(img_path, img_size=img_size)\n    \n    imgs_show =  []\n\n    for path, img, img0, _ in dataset:\n        img = torch.from_numpy(img).to(device)\n        img = img.float()\n        img /= 255.0  \n        if img.ndimension() == 3:\n            img = img.unsqueeze(0)\n        \n        pred = model(img, augment=augment)[0]\n    \n        pred = non_max_suppression(pred, conf_thres, iou_thres, merge=True, classes=None, agnostic=True)\n    \n        bboxes, scores = pred_to_bboxes_and_scores(pred, img.shape[2:], img0.shape)\n        \n        if show:\n            img_show = draw_bboxes(img0, bboxes, scores)\n            imgs_show.append(img_show[:,:,::-1])\n    \n    if show:\n        plot(imgs_show)\n        \n    return bboxes, scores","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def run_wbf(boxes, scores, img_size=512, iou_thr=0.5, skip_box_thr=0.5, weights=None):\n    labels = [np.zeros(score.shape[0]) for score in scores]\n    boxes = [box / img_size for box in boxes]\n    boxes, scores, labels = weighted_boxes_fusion(boxes, scores, labels, weights=None, iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    boxes = boxes * (img_size)\n    return boxes, scores, labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_pseudo_labels(model, source, img_size, conf_thres=0.5, iou_thres=0.9, is_tta=False):\n    \n    image_files = os.listdir(source)\n    \n    phase = 'train2017/'\n    \n    label_folder = os.path.join('convertor', phase, 'labels')\n    create_folder(label_folder)\n    img_folder = os.path.join('convertor', phase, 'images')\n    create_folder(img_folder)\n    \n    dataset = LoadImages(source, img_size=img_size)\n    \n    for path, img, img0, _ in dataset:\n        img_id = path.split('/')[-1].split('.')[0]\n        img = torch.from_numpy(img).to(device)\n        img = img.float()\n        img /= 255.0  \n        if img.ndimension() == 3:\n            img = img.unsqueeze(0)\n        \n        pred = model(img, augment=True)[0]\n    \n        pred = non_max_suppression(pred, conf_thres, iou_thres, merge=True, classes=None, agnostic=True)\n    \n        boxes, scores = pred_to_bboxes_and_scores(pred, img.shape[2:], img0.shape)\n        \n        boxes, scores, labels = run_wbf([boxes], [scores], img_size=img_size, iou_thr=0.6, skip_box_thr=0.5)\n\n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        boxes = boxes[scores >= 0.05].astype(np.int32).clip(min=0, max=1023)\n        scores = scores[scores >=float(0.05)]\n        \n        line = ''\n        for box in boxes:\n            x1, y1, w, h = box\n            xc, yc, w, h = (x1+w/2)/1024, (y1+h/2)/1024, w/1024, h/1024\n            line += '0 %f %f %f %f\\n'%(xc, yc, w, h)\n        \n        ann_file = os.path.join(label_folder, img_id + '.txt')\n        with open(ann_file, 'w+') as f:\n            f.write(line)\n        \n        name_with_ext = img_id + '.jpg'\n        img_dst = os.path.join(img_folder, name_with_ext)\n        sh.copy(path, img_dst)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# in order to train only on pseudo labels\n!rm -r convertor/train2017/","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"generate_pseudo_labels(model, '../input/global-wheat-detection/test/', img_size=512)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!cd convertor/train2017/labels/ && ls","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!python train.py --img 512 --batch 16 --epochs 50 --data ../input/yolostuff/wheat0.yaml --cfg ../input/yolostuff/yolov5x.yaml --name yolov5x_fold0 --weights ../input/train-yolov5-simple/trained_models/weights/best_yolov5x_fold0.pt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# i didn't figure how and when model is saved. it can be save either as path_1 or as path_2 that's why\nnew_model_path = ''\npath_1 = 'runs/exp1_yolov5x_fold0/weights/best_yolov5x_fold0.pt'\npath_2 = 'runs/exp0_yolov5x_fold0/weights/best_yolov5x_fold0.pt'\nif os.path.exists(path_1):\n    new_model_path = path_1\nelse:\n    new_model_path = path_2\nnew_model = load_model(new_model_path)\nnew_model.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def format_prediction_string(bboxes, scores):\n    pred_strings = []\n    for j in zip(scores, bboxes):\n        pred_strings.append(\"{0:.4f} {1} {2} {3} {4}\".format(j[0], j[1][0], j[1][1], j[1][2], j[1][3]))\n\n    return \" \".join(pred_strings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def detect_for_sub(model, source, img_size, conf_thres=0.7, iou_thres=0.9, is_tta=False, show=False):\n    \n    results = []\n    \n    if show:\n    \n        fig, ax = plt.subplots(5, 2, figsize=(30, 70))\n        count = 0\n    \n    dataset = LoadImages(source, img_size=img_size)\n    \n    for path, img, img0, _ in dataset:\n        img_id = path.split('/')[-1].split('.')[0]\n        img = torch.from_numpy(img).to(device)\n        img = img.float()\n        img /= 255.0  \n        if img.ndimension() == 3:\n            img = img.unsqueeze(0)\n        \n        pred = model(img, augment=True)[0]\n    \n        pred = non_max_suppression(pred, conf_thres, iou_thres, merge=True, classes=None, agnostic=True)\n    \n        boxes, scores = pred_to_bboxes_and_scores(pred, img.shape[2:], img0.shape)\n        \n        boxes, scores, labels = run_wbf([boxes], [scores], img_size=img_size, iou_thr=0.6, skip_box_thr=0.5)\n\n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        boxes = boxes[scores >= 0.05].astype(np.int32).clip(min=0, max=1023)\n        scores = scores[scores >=float(0.05)]\n        \n        if show:\n            for box, score in zip(boxes,scores):\n                cv2.rectangle(img0, (box[0], box[1]), (box[2]+box[0], box[3]+box[1]),(220, 0, 0), 2)\n                cv2.putText(img0, '%.2f'%(score), (box[0], box[1]),cv2.FONT_HERSHEY_SIMPLEX , 0.5, (255,255,255), 2, cv2.LINE_AA)\n            ax[count%5][count//5].imshow(img0)\n            count += 1\n        \n        result = {\n            'image_id': img_id,\n            'PredictionString': format_prediction_string(boxes, scores)\n        }\n        results.append(result)\n        \n        \n    test_df = pd.DataFrame(results, columns=['image_id', 'PredictionString'])\n    \n    return test_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_data = '../input/global-wheat-detection/test/'\nwith torch.no_grad():\n    test_df = detect_for_sub(new_model, test_data, 512, conf_thres=0.5, iou_thres=0.9, is_tta=False, show=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -r *","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.to_csv('submission.csv', index=False)\ntest_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df","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}