目标检测中ROC的实现【1】

该文详细介绍了如何在目标检测任务中计算和绘制ROC曲线,通过数据预处理、IoU计算以及匹配检测结果与标注数据,实现对检测算法性能的评估。代码示例展示了针对一个类别的人脸检测ROC曲线的绘制过程,但可能存在在多类别场景下的应用问题。
摘要由CSDN通过智能技术生成

评价目标检测中的各种标准,如map,ROC通常用于分类算法中,按照混淆矩阵的计算方式:

ROC计算方式

混淆矩阵
ROC 为FPR(假阳率)为横坐标,TPR(真阳率)为纵坐标,围成的面积即为ROC值:
在这里插入图片描述
而在目标检测中,因为负样本(TN)的值很大,所以在目标检测中会直接考虑使用groundtruth个数,作为(TP + FN)个数;

ROC的实现

这里参考python 实现目标检测中roc
该文中因为只有人脸一个类别,实现完在自己的检测结果的ROC曲线有点问题
在这里插入图片描述
这里基于ROC的计算方式,对代码做了局部修改。

2.1 数据预处理

需要从算法端的检测结果保存成以下格式,将检测到的不同label的结果保存到一个txt中:
在这里插入图片描述

同一label检测结果保存成
[ 文件名
目标数
label xmin/ymin/xmax/ymax、conf] ,
同时检测的时候需要将conf的阈值设成很小的值,比如0.0001,这样ROC曲线的recall才能保证接近100%,保存成绝对长度就不需要再去读取图片获取长宽了;

2.2 代码实现

对于每个label,实现ROC :

import numpy as np
import cv2
import matplotlib.pyplot as plt
def load(txtfile):
    '''
    读取检测结果或 groundtruth 的文档, 若为椭圆坐标, 转换为矩形坐标
    :param txtfile: 读入的.txt文件, 格式要求与FDDB相同
    :return imagelist: list, 每张图片的信息单独为一行, 第一列是图片名称, 第二列是个数, 后面的列均为列表, 包含4个矩形坐标和1个分数
    :return num_allboxes: int, 矩形框的总个数
    '''
    imagelist = []  # 包含所有图片的信息的列表

    txtfile = open(txtfile, 'r')
    lines = txtfile.readlines()  # 一次性全部读取, 得到一个list
    num_gt = 0
    i = 0
    while i < len(lines):  # 在lines中循环一遍
        image = []  # 包含一张图片信息的列表
        image.append(lines[i].strip())  # 去掉首尾的空格和换行符, 向image中写入图片名称
        num_ = int(lines[i + 1])
        if num_ != 0:
            num_gt = num_gt + 1
        image.append(num_)  # 向image中写入个数

        if num_ > 0:
            for num in range(num_):
                boundingbox = lines[i + 2 + num].strip()  # 去掉首尾的空格和换行符
                boundingbox = boundingbox.split()[1: ]  # 按中间的空格分割成多个元素
                boundingbox = list(map(float, boundingbox))  # 转换成浮点数列表

                image.append(boundingbox)  # 向image中写入包含矩形坐标和分数的浮点数列表

        imagelist.append(image)  # 向imagelist中写入一张图片的信息

        i = i + num_ + 2  # 增加index至下张图片开始的行数

    txtfile.close()

    return imagelist



def cal_IoU(detectedbox, groundtruthbox):
    """
    计算两个水平竖直的矩形的交并比
    :param detectedbox: list, [leftx_det, topy_det, width_det, height_det, confidence]
    :param groundtruthbox: list, [leftx_gt, topy_gt, width_gt, height_gt, 1]
    :return iou: 交并比
    """
    leftx_det, topy_det, width_det, height_det, _ = detectedbox
    leftx_gt, topy_gt, width_gt, height_gt = groundtruthbox

    centerx_det = leftx_det + width_det / 2
    centerx_gt = leftx_gt + width_gt / 2
    centery_det = topy_det + height_det / 2
    centery_gt = topy_gt + height_gt / 2

    distancex = abs(centerx_det - centerx_gt) - (width_det + width_gt) / 2
    distancey = abs(centery_det - centery_gt) - (height_det + height_gt) / 2

    if distancex <= 0 and distancey <= 0:
        intersection = distancex * distancey
        union = width_det * height_det + width_gt * height_gt - intersection
        iou = intersection / union
        return iou
    else:
        return 0




def thres(maxiou_confidence, threshold = 0.5):
    """
    将大于阈值的最大交并比记为1, 反正记为0
    :param maxiou_confidence: np.array, 存放所有检测框对应的最大交并比和置信度
    :param threshold: 阈值
    :return tf_confidence: np.array, 存放所有检测框对应的tp或fp和置信度
    """
    tf_confidence = maxiou_confidence[maxiou_confidence[:, 0] > threshold, :]
    # tf_confidence = np.array([true_or_flase, confidences])
    #tf_confidence = tf_confidence.T
    if tf_confidence.size == 0:
        tf_confidence = np.array([0])
    else:
        tf_confidence = tf_confidence[np.argsort(-tf_confidence[:, 1]), 1]
    return tf_confidence


def match(resultsfile, groundtruthfile):
    """
    匹配检测框和标注框, 为每一个检测框得到一个最大交并比   
    :param resultsfile: 包含检测结果的.txt文件
    :param groundtruthfile: 包含标准答案的.txt文件
    :param show_images: 是否显示图片
    :return maxiou_confidence: np.array, 存放所有检测框对应的最大交并比和置信度
    :return num_detectedbox: int, 检测框的总数
    :return num_groundtruthbox: int, 标注框的总数
    """
    results = load(resultsfile)
    groundtruth = load(groundtruthfile)
    print(results)
    print(groundtruth)
    assert len(results) == len(groundtruth), "数量不匹配: 标准答案中图片数量为%d, 而检测结果中图片数量为%d" % (
        len(groundtruth), len(results))
    results_predlist = []
    results_name = []
    for i in range(len(results)):
        results_name.append(results[i][0])

    for i in range(len(groundtruth)):# 对每个图片
        results_pred = []
        maxiou_confidence = np.array([])
        if groundtruth[i][0] in results_name:
            m = results_name.index(results[i][0])
        else:
            print("Not found ,Error")
        results_pred.append(groundtruth[i][0])
        if len(groundtruth[i]) == 2:
            results_pred.append(0)
            if len(results[m]) == 2:
                maxiou_confidence = 0
            else:
                for k in range(2, len(results[m])):  # 匹配这张图片中的每一个检测框
                    detectedbox = results[m][k]
                    confidence = detectedbox[-1]
                    maxiou_confidence = np.append(maxiou_confidence, confidence)
                    maxiou_confidence = max(maxiou_confidence)

            results_pred.append(maxiou_confidence)

        else:
            results_pred.append(1)

            for j in range(2, len(groundtruth[i])):#图片中每个真实框

                iou_array = np.array([])
                groundtruthbox = groundtruth[i][j]
                if len(results[m]) == 2:
                    maxiou_confidence = np.append(maxiou_confidence, [0, 0])
                else:
                    for k in range(2, len(results[m])): #匹配这张图片中的每一个检测框
                        detectedbox = results[m][k]
                        confidence = detectedbox[-1]
                        iou = cal_IoU(detectedbox, groundtruthbox)
                        iou_array = np.r_[iou_array, [iou, confidence]]
                        # iou_array = np.append(iou_array, [iou, confidence], axis=0) # 得到一个交并比的数组


                    # maxiou_index = np.argmax(iou_array[0], axis=0)  # 最大交并比
                   # maxiou_confidence = np.append(maxiou_confidence, [maxiou, confidence])
                    maxiou_confidence = np.append(maxiou_confidence, iou_array)

            maxiou_confidence = maxiou_confidence.reshape(-1, 2)
            maxiou_confidence = maxiou_confidence[np.argsort(-maxiou_confidence[:, 1])] #置信度从大到小排列
            tf_confidence = thres(maxiou_confidence, threshold=0.5)
            tf_confidence = max(tf_confidence)
            results_pred.append(tf_confidence)

        results_predlist.append(results_pred)

        # maxiou_confidence = maxiou_confidence[np.argsort(-maxiou_confidence[:, 1])]  # 按置信度从大到小排序

    return results_predlist


def draw_curves(resultsfile, groundtruthfile, show_images = False, threshold = 0.1):
    """
    读取包含检测结果和标准答案的两个.txt文件, 画出ROC曲线和PR曲线
    :param resultsfile: 包含检测结果的.txt文件
    :param groundtruthfile: 包含标准答案的.txt文件
    :param show_images: 是否显示图片, 若需可视化, 需修改Calculate.match中的代码, 找到存放图片的路径
    :param threshold: IoU阈值
    """
    print("---")
    results_predlist = match(resultsfile, groundtruthfile)
    # tf_confidence = thres(results_predlist, threshold)
    return results_predlist

def plot(results_predlist, lvyaoyao_predlist):
    """
    从上到下截取tf_confidence, 计算并画图
    :param tf_confidence: np.array, 存放所有检测框对应的tp或fp和置信度
    :param num_groundtruthbox: int, 标注框的总数
    """
    fp_list = []
    recall_list = []
    precision_list = []
    auc = 0
    mAP = 0
    gt_num = 0
    ng_num = 0
    for num in range(len(results_predlist)):
        results_pred = results_predlist[num]
        gt = results_pred[1]  # gt
        if gt == 0:
            ng_num = ng_num + 1
        else:
            gt_num = gt_num + 1

    print("tf_confidence", results_predlist)
    for num in range(len(results_predlist)):
        results_pred = results_predlist[num]
        conf = results_pred[2]  # confidence
        print("conf", conf)
        fp = 0
        tp = 0
        for j in range(len(results_predlist)):
            results_pred = results_predlist[j]
            gt = results_pred[1] # gt
            if gt == 0:
                if results_pred[2] >= conf:
                    fp = fp + 1
            else:
                if results_pred[2] >= conf:
                    tp = tp + 1
        recall_list.append(tp)
        fp_list.append(fp)
        if num > 0:
            auc = auc + (fp_list[-1] - fp_list[-2])/gt_num * (recall_list[-1] + recall_list[-2])/ng_num / 2
            print("auc=", auc)

    recall_list_1 = [x/gt_num for x in recall_list ]
    fp_list_1 = [x / ng_num for x in fp_list]
    print(recall_list_1)
    print(fp_list_1)
    #-------------------------------------------------------------------------------------
    fp_list = []
    recall_list = []
    precision_list = []
    auc2 = 0
    mAP = 0
    gt_num = 0
    ng_num = 0
    for num in range(len(lvyaoyao_predlist)):
        results_pred = lvyaoyao_predlist[num]
        gt = results_pred[1]  # gt
        if gt == 0:
            ng_num = ng_num + 1
        else:
            gt_num = gt_num + 1

    print("tf_confidence", lvyaoyao_predlist)
    for num in range(len(lvyaoyao_predlist)):
        results_pred = lvyaoyao_predlist[num]
        conf = results_pred[2]  # confidence
        print("conf", conf)
        fp = 0
        tp = 0
        for j in range(len(lvyaoyao_predlist)):
            results_pred = lvyaoyao_predlist[j]
            gt = results_pred[1]  # gt
            if gt == 0:
                if results_pred[2] >= conf:
                    fp = fp + 1
            else:
                if results_pred[2] >= conf:
                    tp = tp + 1
        recall_list.append(tp)
        fp_list.append(fp)
        if num > 0:
            auc2 = auc2 + (fp_list[-1] - fp_list[-2]) / gt_num * (recall_list[-1] + recall_list[-2]) / ng_num / 2
            print("auc=", auc2)

    recall_list_2 = [x / gt_num for x in recall_list]
    fp_list_2 = [x / ng_num for x in fp_list]
    print(recall_list_2)
    print(fp_list_2)


    "draw picture"
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    plt.title('ROC')
    plt.xlabel('False Positives')
    plt.xscale('log')
    plt.ylabel('True Positive rate')
    plt.ylim(0, 1)
    ax1.plot(fp_list_1, recall_list_1, label='AUC: ' + str(auc))
    ax1.plot(fp_list_2, recall_list_2, label='AUC: ' + str(auc2))
    plt.legend()

    # plt.figure()
    # plt.title('Precision-Recall')
    # plt.xlabel('Recall')
    # plt.ylabel('Precision')
    # plt.axis([0, 1, 0, 1])
    # plt.plot(recall_list, precision_list, label='mAP: ' + str(mAP))
    # plt.legend()

    plt.show()


if __name__ == '__main__':
    gt_txt = r"G:\all_gt.txt"
    pred_txt =r"G:\all_1012_pred_rcnn.txt"
    lvy_pred = r"G:\lvyy.txt"

    results_predlist = draw_curves(pred_txt, gt_txt)
    lvyy_predlist = draw_curves(lvy_pred, gt_txt)
    results_predlist.sort(key=lambda x: x[2], reverse=True)
    lvyy_predlist.sort(key=lambda x: x[2], reverse=True)
    # results_predlist.sort(lambda x, y:cmp(x[2], y[2]),reverse=True)
    # results_predlist = results_predlist[np.argsort(-results_predlist[:, 2])]
    plot(results_predlist, lvyy_predlist)

在自己的检测结果上,实现:
在这里插入图片描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值