import numpy as np

from collections import defaultdict, Counter

from .rbbox_np import rbbox_iou


def get_ap(recall, precision):
    recall = [0] + list(recall) + [1]
    precision = [0] + list(precision) + [0]
    for i in range(len(precision) - 1, 0, -1):
        precision[i - 1] = max(precision[i - 1], precision[i])
    ap = sum((recall[i] - recall[i - 1]) * precision[i] for i in range(1, len(recall)) if recall[i] != recall[i - 1])
    return ap * 100


def get_ap_07(recall, precision):
    ap = 0.
    for t in np.linspace(0, 1, 11, endpoint=True):
        mask = recall >= t
        if np.any(mask):
            ap += np.max(precision[mask]) / 11
    return ap * 100


def get_det_aps(detect, target, num_classes, iou_thresh=0.5, use_07_metric=False):
    # [[index, bbox, score, label], ...]
    aps = []
    for c in range(num_classes):
        target_c = list(filter(lambda x: x[3] == c, target))
        detect_c = filter(lambda x: x[3] == c, detect)
        detect_c = sorted(detect_c, key=lambda x: x[2], reverse=True)
        tp = np.zeros(len(detect_c))
        fp = np.zeros(len(detect_c))
        target_count = Counter([x[0] for x in target_c])
        target_count = {index: np.zeros(count) for index, count in target_count.items()}
        target_lut = defaultdict(list)
        for index, bbox, conf, label in target_c:
            target_lut[index].append(bbox)
        detect_lut = defaultdict(list)
        for index, bbox, conf, label in detect_c:
            detect_lut[index].append(bbox)
        iou_lut = dict()
        for index, bboxes in detect_lut.items():
            if index in target_lut:
                iou_lut[index] = rbbox_iou(np.stack(bboxes), np.stack(target_lut[index]))
        counter = defaultdict(int)
        for i, (index, bbox, conf, label) in enumerate(detect_c):
            count = counter[index]
            counter[index] += 1
            iou_max = -np.inf
            hit_j = 0
            if index in iou_lut:
                for j, iou in enumerate(iou_lut[index][count]):
                    if iou > iou_max:
                        iou_max = iou
                        hit_j = j
            if iou_max > iou_thresh and target_count[index][hit_j] == 0:
                tp[i] = 1
                target_count[index][hit_j] = 1
            else:
                fp[i] = 1
        tp_sum = np.cumsum(tp)
        fp_sum = np.cumsum(fp)
        npos = len(target_c)
        recall = tp_sum / npos
        precision = tp_sum / (tp_sum + fp_sum)
        aps.append((get_ap_07 if use_07_metric else get_ap)(recall, precision))
    return aps

[((19, 13, 19, 49), 'numpy.linspace', 'np.linspace', (), '', True, 'import numpy as np\n'), ((21, 11, 21, 23), 'numpy.any', 'np.any', ({(21, 18, 21, 22): 'mask'}, {}), '(mask)', True, 'import numpy as np\n'), ((35, 23, 35, 56), 'collections.Counter', 'Counter', ({(35, 31, 35, 55): '[x[0] for x in target_c]'}, {}), '([x[0] for x in target_c])', False, 'from collections import defaultdict, Counter\n'), ((37, 21, 37, 38), 'collections.defaultdict', 'defaultdict', ({(37, 33, 37, 37): 'list'}, {}), '(list)', False, 'from collections import defaultdict, Counter\n'), ((40, 21, 40, 38), 'collections.defaultdict', 'defaultdict', ({(40, 33, 40, 37): 'list'}, {}), '(list)', False, 'from collections import defaultdict, Counter\n'), ((47, 18, 47, 34), 'collections.defaultdict', 'defaultdict', ({(47, 30, 47, 33): 'int'}, {}), '(int)', False, 'from collections import defaultdict, Counter\n'), ((63, 17, 63, 30), 'numpy.cumsum', 'np.cumsum', ({(63, 27, 63, 29): 'tp'}, {}), '(tp)', True, 'import numpy as np\n'), ((64, 17, 64, 30), 'numpy.cumsum', 'np.cumsum', ({(64, 27, 64, 29): 'fp'}, {}), '(fp)', True, 'import numpy as np\n'), ((36, 31, 36, 46), 'numpy.zeros', 'np.zeros', ({(36, 40, 36, 45): 'count'}, {}), '(count)', True, 'import numpy as np\n'), ((22, 18, 22, 41), 'numpy.max', 'np.max', ({(22, 25, 22, 40): 'precision[mask]'}, {}), '(precision[mask])', True, 'import numpy as np\n'), ((46, 43, 46, 59), 'numpy.stack', 'np.stack', ({(46, 52, 46, 58): 'bboxes'}, {}), '(bboxes)', True, 'import numpy as np\n'), ((46, 61, 46, 88), 'numpy.stack', 'np.stack', ({(46, 70, 46, 87): 'target_lut[index]'}, {}), '(target_lut[index])', True, 'import numpy as np\n')]