def voc_ap(precision, recall):
    """
    参考：http://host.robots.ox.ac.uk/pascal/VOC/voc2012/htmldoc/devkit_doc.html#3.4
    根据 precision 和 recall 数组计算 AP：
        1、计算随着 precision 递减的 Precision-recall 曲线
        2、AP 就是 Precision-recall 曲线下面的面积
    代码参考 VOC development kit code 中 VOCap.m
    :param precision: 精确率 TP / (TP + FP) = TP / n
    :param recall: 召回率 TP / (TP + FN) = TP / P
    :return: ap: average-precision 平均精度
    :return: precision: 横轴
    :return: recall: 纵轴
    """
    # 在 precision 和 recall 数组前后插值
    precision.insert(0, 0.0)
    precision.append(0.0)
    recall.insert(0, 0.0)
    recall.append(1.0)
    # 让 precision 单调递减（从后往前）
    for i in range(len(precision) - 2, -1, -1):
        precision[i] = max(precision[i], precision[i + 1])
    # 记录 recall 改变的位置
    change_list = []
    for i in range(1, len(recall)):
        if recall[i] != recall[i - 1]:
            change_list.append(i)
    # 使用数值积分计算 Precision-recall 曲线下面的面积
    ap = 0.0
    for i in change_list:
        ap += ((recall[i] - recall[i - 1]) * precision[i])
    return ap, precision, recall