{"cells":[{"metadata":{},"cell_type":"markdown","source":"","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import torch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# %load /kaggle/input/weighted-boxes-fusion-submission/Weighted-Boxes-Fusion-master/ensemble_boxes/ensemble_boxes_wbf.py\n__author__ = 'ZFTurbo: https://kaggle.com/zfturbo'\n\n\nimport warnings\nimport numpy as np\nfrom numba import jit\n\n\n@jit(nopython=True)\ndef bb_intersection_over_union(A, B) -> float:\n    xA = max(A[0], B[0])\n    yA = max(A[1], B[1])\n    xB = min(A[2], B[2])\n    yB = min(A[3], B[3])\n\n    # compute the area of intersection rectangle\n    interArea = max(0, xB - xA) * max(0, yB - yA)\n\n    if interArea == 0:\n        return 0.0\n\n    # compute the area of both the prediction and ground-truth rectangles\n    boxAArea = (A[2] - A[0]) * (A[3] - A[1])\n    boxBArea = (B[2] - B[0]) * (B[3] - B[1])\n\n    iou = interArea / float(boxAArea + boxBArea - interArea)\n    return iou\n\n\ndef prefilter_boxes(boxes, scores, labels, weights, thr):\n    # Create dict with boxes stored by its label\n    new_boxes = dict()\n\n    for t in range(len(boxes)):\n\n        if len(boxes[t]) != len(scores[t]):\n            print('Error. Length of boxes arrays not equal to length of scores array: {} != {}'.format(len(boxes[t]), len(scores[t])))\n            exit()\n\n        if len(boxes[t]) != len(labels[t]):\n            print('Error. Length of boxes arrays not equal to length of labels array: {} != {}'.format(len(boxes[t]), len(labels[t])))\n            exit()\n\n        for j in range(len(boxes[t])):\n            score = scores[t][j]\n            if score < thr:\n                continue\n            label = int(labels[t][j])\n            box_part = boxes[t][j]\n            x1 = float(box_part[0])\n            y1 = float(box_part[1])\n            x2 = float(box_part[2])\n            y2 = float(box_part[3])\n\n            # Box data checks\n            if x2 < x1:\n                warnings.warn('X2 < X1 value in box. Swap them.')\n                x1, x2 = x2, x1\n            if y2 < y1:\n                warnings.warn('Y2 < Y1 value in box. Swap them.')\n                y1, y2 = y2, y1\n            if x1 < 0:\n                warnings.warn('X1 < 0 in box. Set it to 0.')\n                x1 = 0\n            if x1 > 1:\n                warnings.warn('X1 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                x1 = 1\n            if x2 < 0:\n                warnings.warn('X2 < 0 in box. Set it to 0.')\n                x2 = 0\n            if x2 > 1:\n                warnings.warn('X2 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                x2 = 1\n            if y1 < 0:\n                warnings.warn('Y1 < 0 in box. Set it to 0.')\n                y1 = 0\n            if y1 > 1:\n                warnings.warn('Y1 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                y1 = 1\n            if y2 < 0:\n                warnings.warn('Y2 < 0 in box. Set it to 0.')\n                y2 = 0\n            if y2 > 1:\n                warnings.warn('Y2 > 1 in box. Set it to 1. Check that you normalize boxes in [0, 1] range.')\n                y2 = 1\n            if (x2 - x1) * (y2 - y1) == 0.0:\n                warnings.warn(\"Zero area box skipped: {}.\".format(box_part))\n                continue\n\n            b = [int(label), float(score) * weights[t], x1, y1, x2, y2]\n            if label not in new_boxes:\n                new_boxes[label] = []\n            new_boxes[label].append(b)\n\n    # Sort each list in dict by score and transform it to numpy array\n    for k in new_boxes:\n        current_boxes = np.array(new_boxes[k])\n        new_boxes[k] = current_boxes[current_boxes[:, 1].argsort()[::-1]]\n\n    return new_boxes\n\n\ndef get_weighted_box(boxes, conf_type='avg'):\n    \"\"\"\n    Create weighted box for set of boxes\n    :param boxes: set of boxes to fuse\n    :param conf_type: type of confidence one of 'avg' or 'max'\n    :return: weighted box\n    \"\"\"\n\n    box = np.zeros(6, dtype=np.float32)\n    conf = 0\n    conf_list = []\n    for b in boxes:\n        box[2:] += (b[1] * b[2:])\n        conf += b[1]\n        conf_list.append(b[1])\n    box[0] = boxes[0][0]\n    if conf_type == 'avg':\n        box[1] = conf / len(boxes)\n    elif conf_type == 'max':\n        box[1] = np.array(conf_list).max()\n    box[2:] /= conf\n    return box\n\n\ndef find_matching_box(boxes_list, new_box, match_iou):\n    best_iou = match_iou\n    best_index = -1\n    for i in range(len(boxes_list)):\n        box = boxes_list[i]\n        if box[0] != new_box[0]:\n            continue\n        iou = bb_intersection_over_union(box[2:], new_box[2:])\n        if iou > best_iou:\n            best_index = i\n            best_iou = iou\n\n    return best_index, best_iou\n\n\ndef weighted_boxes_fusion(boxes_list, scores_list, labels_list, weights=None, iou_thr=0.55, skip_box_thr=0.0, conf_type='avg', allows_overflow=False):\n    '''\n    :param boxes_list: list of boxes predictions from each model, each box is 4 numbers.\n    It has 3 dimensions (models_number, model_preds, 4)\n    Order of boxes: x1, y1, x2, y2. We expect float normalized coordinates [0; 1]\n    :param scores_list: list of scores for each model\n    :param labels_list: list of labels for each model\n    :param weights: list of weights for each model. Default: None, which means weight == 1 for each model\n    :param iou_thr: IoU value for boxes to be a match\n    :param skip_box_thr: exclude boxes with score lower than this variable\n    :param conf_type: how to calculate confidence in weighted boxes. 'avg': average value, 'max': maximum value\n    :param allows_overflow: false if we want confidence score not exceed 1.0\n\n    :return: boxes: boxes coordinates (Order of boxes: x1, y1, x2, y2).\n    :return: scores: confidence scores\n    :return: labels: boxes labels\n    '''\n\n    if weights is None:\n        weights = np.ones(len(boxes_list))\n    if len(weights) != len(boxes_list):\n        print('Warning: incorrect number of weights {}. Must be: {}. Set weights equal to 1.'.format(len(weights), len(boxes_list)))\n        weights = np.ones(len(boxes_list))\n    weights = np.array(weights)\n\n    if conf_type not in ['avg', 'max']:\n        print('Unknown conf_type: {}. Must be \"avg\" or \"max\"'.format(conf_type))\n        exit()\n\n    filtered_boxes = prefilter_boxes(boxes_list, scores_list, labels_list, weights, skip_box_thr)\n    if len(filtered_boxes) == 0:\n        return np.zeros((0, 4)), np.zeros((0,)), np.zeros((0,))\n\n    overall_boxes = []\n    for label in filtered_boxes:\n        boxes = filtered_boxes[label]\n        new_boxes = []\n        weighted_boxes = []\n\n        # Clusterize boxes\n        for j in range(0, len(boxes)):\n            index, best_iou = find_matching_box(weighted_boxes, boxes[j], iou_thr)\n            if index != -1:\n                new_boxes[index].append(boxes[j])\n                weighted_boxes[index] = get_weighted_box(new_boxes[index], conf_type)\n            else:\n                new_boxes.append([boxes[j].copy()])\n                weighted_boxes.append(boxes[j].copy())\n\n        # Rescale confidence based on number of models and boxes\n        for i in range(len(new_boxes)):\n            if not allows_overflow:\n                weighted_boxes[i][1] = weighted_boxes[i][1] * min(weights.sum(), len(new_boxes[i])) / weights.sum()\n            else:\n                weighted_boxes[i][1] = weighted_boxes[i][1] * len(new_boxes[i]) / weights.sum()\n        overall_boxes.append(np.array(weighted_boxes))\n\n    overall_boxes = np.concatenate(overall_boxes, axis=0)\n    overall_boxes = overall_boxes[overall_boxes[:, 1].argsort()[::-1]]\n    boxes = overall_boxes[:, 2:]\n    scores = overall_boxes[:, 1]\n    labels = overall_boxes[:, 0]\n    return boxes, scores, labels\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"model = torch.load('/kaggle/input/faster-rcnn-version-2/Faster_RCNN_submission_10_2.pth')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def normalize_boxes(boxes, w, h):\n    boxes[:, 0] /= w\n    boxes[:, 1] /= h\n    boxes[:, 2] /= w\n    boxes[:, 3] /= h\n    return boxes\n  \ndef denormalize_boxes(boxes, w, h):\n    boxes[:, 0] *= w\n    boxes[:, 1] *= h\n    boxes[:, 2] *= w\n    boxes[:, 3] *= h\n    return boxes\n\ndef wbf(boxes, scores, labels, w, h, thresh, iou_thresh):\n    boxes = normalize_boxes(boxes, w, h)\n    \n#     print(\"bboxes before:\", len(boxes))\n#     for box, score in zip(boxes, scores):\n#         print(f\"{box}  -  {score}\")\n        \n    bboxes, sscores, _ = weighted_boxes_fusion([boxes], [scores], [labels],\n                          weights=None, iou_thr=iou_thresh, skip_box_thr=thresh)\n    \n#     print(\"bboxes after:\", len(bboxes))\n#     for box, score in zip(bboxes, sscores):\n#         print(f\"{box}  -  {score}\")\n        \n    bboxes = denormalize_boxes(bboxes, w, h)\n    return bboxes, sscores","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Assumption: model is already in cuda\n# was taken from: https://www.kaggle.com/bendvd/global-wheat-detection-first-submission\ndef model_prediction(image_path, cuda_model, threshold=0.5):\n    cuda_model.eval()\n    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n    image /= 255.0\n    # unsqueeze 0 for making it an array of image which contains 1 image\n    images = torch.from_numpy(image).float().permute(2,0,1).unsqueeze(0).cuda()\n    outputs = cuda_model(images)\n\n\n    outputs = [{k: v.detach().cpu().numpy() for k, v in t.items()} for t in outputs]\n\n    # take bboxes regarding to the scores, where: scores > threshold\n    bboxes = outputs[0][\"boxes\"]\n    sscores = outputs[0][\"scores\"]\n    llabels = [1] * len(sscores)\n    \n    boxes, scores = wbf(boxes=bboxes, scores=sscores, labels=llabels, w=1024, h=1024, thresh=0.3, iou_thresh=0.7)\n    \n#     if True:\n#         print(\"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\")\n#         boxes, scores = wbf(boxes=bboxes, scores=sscores, labels=llabels, w=1024, h=1024, thresh=0.3, iou_thresh=0.7)\n#     else:\n#         print(\"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\")\n#         boxes = bboxes[sscores > threshold]\n#         scores = sscores[sscores > threshold]\n\n    return boxes, scores","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from glob import glob\nfrom tqdm import tqdm\nimport os\nimport cv2\nimport numpy as np\nimport pandas as pd","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images_paths = glob(os.path.join('/kaggle/input/global-wheat-detection/test/*'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = []\n\nfor image in tqdm(test_images_paths):\n    boxes, scores = model_prediction(str(image), model)\n    prediction_string = []\n    for (x_min,y_min,x_max,y_max),s in zip(boxes,scores):\n        x = round(x_min)\n        y = round(y_min)\n        h = round(x_max-x_min)\n        w = round(y_max-y_min)\n        prediction_string.append(f\"{s} {x} {y} {h} {w}\")\n    prediction_string = \" \".join(prediction_string)\n    \n    image_name = image.split(\"/\")[-1].split(\".\")[0]\n    submission.append([image_name, prediction_string])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# !rm /kaggle/working/submission.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SUBMISSION_PATH = '/kaggle/working'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_id = 'submission'\ncur_submission_path = os.path.join(SUBMISSION_PATH, '{}.csv'.format(submission_id))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission = pd.DataFrame(submission, columns=[\"image_id\",\"PredictionString\"])\nsample_submission.to_csv(cur_submission_path, index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.read_csv(cur_submission_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df","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}