目标检测的评价指标

目标检测的评价指标定义:

目标检测任务是给你一张图像,给出图中含待检测类别的位置大小和类别。评价模型的好坏主要有三个方面:类别,位置和大小,检测速度。

分类的精度如何位置和大小的精度如何运行的精度如何
精度,召回率,PR曲线,AP, mAPIoUfps

分类的精度

评估指标准确度(accuracy)精度 (Precision)召回率 (Recall)
定义正确预测的数量与总预测数量的比例 T P + T N T P + F P + T N + F N \frac{TP + TN}{TP + FP + TN + FN} TP+FP+TN+FNTP+TN所有检测为正例的样本中,真正为正例的比例 T P T P + F P \frac{TP}{TP + FP} TP+FPTP正确检出的正例样本数占总正例样本数的比例 T P T P + F N \frac{TP}{TP + FN} TP+FNTP
运用在检测任务中不使用,因为背景类过多,无法通过该评估指标反映性能如果精度不高意味着有过多的fp, 即产生redundant如果召回率不高,意味着有很多FN, 即有很多前景类被误认为是背景类,即产生miss
预测值\真实值正例负例
正例TP (True Positive, 预测为positive同时预测对了true)FP (False Positive, 预测为positive同时预测错了false),产生redundant
负例FN (False Negative, 预测为negative同时预测错了false) , 产生missingTN (True Negative,预测为negative同时预测对了)

位置和大小的精度

位置和大小的精度主要是通过预测框和真实框IoU (Intersection of Union) 交并比来判断的。预测框和真实框交并比越大,说明位置和大小越精准。
在这里插入图片描述

计算两个bbox之间的IoU和两组bboxes之间的IoUs代码如下:

def two_bbox_iou(bbox1, bbox2):
    """Compute the iou of two bbox
    Args:
        bbox1: [x1_min, y1_min, x1_max, y1_max]
        bbox2: [x2_min, y2_min, y2_max, y2_max]
    Returns:
        the iou of two bbox: float
    """
    x1_min, y1_min, x1_max, y1_max = bbox1
    x2_min, y2_min, x2_max, y2_max = bbox2
    area1 = (y1_max - y1_min) * (x1_max - x1_min)
    area2 = (y2_max - y2_min) * (x2_max - x2_min)
    intersection_x_min = max(x1_min, x2_min)
    intersection_y_min = max(y1_min, y2_min)
    intersection_x_max = min(x1_max, x2_max)
    intersection_y_max = min(y1_max, y2_max)
    # process the situation when having no intersection
    intersection_width = max(0, intersection_x_max - intersection_x_min)
    intersection_height = max(0, intersection_y_max - intersection_y_min)
    intersection_area = intersection_width * intersection_height

    iou = intersection_area / (area1 + area2 - intersection_area)
   	return iou
   	
def bboxes_iou(bboxes1, bboxes2):
    """Compute the iou of two group bboxes
    Args:
        bboxes1: np.array with shape (n, 4), represent n bbox (x_min, y_min, x_max, y_max)
        bboxes2: np.array with shape (m, 4), represent m bbox (x_min, y_min, x_max, y_max)
    Returns:
        np.array: ious with the shape (n, m)
    """
    rows = bboxes1.shape[0]
    cols = bboxes2.shape[0]
    ious = np.zeros((rows, cols), dtype=np.float32)
    if rows * cols == 0:
        return ious
    for i in range(rows):
        for j in range(cols):
            ious[i, j] = two_bbox_iou(bboxes1[i], bboxes2[j])
    return ious

if __name__ == "__main__":
    bboxes1 = np.array([[1, 1, 4, 4], [5, 5, 8, 8]])
    bboxes2 = np.array([[2, 2, 5, 5], [1, 1, 3, 3], [6, 6, 9, 9]])
    ious = bboxes_iou(bboxes1, bboxes2)
    print(ious)

利用numpy中的广播机制可以把两两计算iou的for循环给去除掉

def bboxes_iou_np(bboxes1, bboxes2):
    """Compute the iou of two group bboxes
    Args:
        bboxes1: np.array with shape (n, 4), represent n bbox (x_min, y_min, x_max, y_max)
        bboxes2: np.array with shape (m, 4), represent m bbox (x_min, y_min, x_max, y_max)
    Returns:
        np.array: ious with the shape (n, m)
    """
    x1_min = bboxes1[..., 0].reshape(-1, 1)
    y1_min = bboxes1[..., 1].reshape(-1, 1)
    x1_max = bboxes1[..., 2].reshape(-1, 1)
    y1_max = bboxes1[..., 3].reshape(-1, 1)
    x2_min = bboxes2[..., 0].reshape(1, -1)
    y2_min = bboxes2[..., 1].reshape(1, -1)
    x2_max = bboxes2[..., 2].reshape(1, -1)
    y2_max = bboxes2[..., 3].reshape(1, -1)
    intersection_x_min = np.maximum(x1_min, x2_min)
    intersection_y_min = np.maximum(y1_min, y2_min)
    intersection_x_max = np.minimum(x1_max, x2_max)
    intersection_y_max = np.minimum(y1_max, y2_max)
    intersection_width = np.maximum(intersection_x_max - intersection_x_min, 0)
    intersection_height = np.maximum(intersection_y_max - intersection_y_min, 0)
    intersection_area = intersection_width * intersection_height
    area1 = (y1_max - y1_min) * (x1_max - x1_min)
    area2 = (y2_max - y2_min) * (x2_max - x2_min)
    return intersection_area / (area1 + area2 - intersection_area)

if __name__ == "__main__":
    bboxes1 = np.array([[1, 1, 4, 4], [5, 5, 8, 8]])
    bboxes2 = np.array([[2, 2, 5, 5], [1, 1, 3, 3], [6, 6, 9, 9]])
    ious_np = bboxes_iou_np(bboxes1, bboxes2)
    print(ious_np)

利用ious计算tp, fp的代码

import numpy as np

def tpfp_computation(dets, gts, threshold):
    """Compute the tp and fp
    Args:
        dets: np.array with the shape (n, 5), represent n predicted bboxes with score
            (x_min, y_min, x_max, y_max, score).
        gts: np.array with the shape (m, 4), represent m gt bboxes
            (x_min, y_min, x_max, y_max).
    Returns:
        tp: np.array with the shape (n, 1) whose item is 0 or 1.
        fp: np.array with the shape (n, 1) whose item is 0 or 1.
    """
    ious = bboxes_iou_np(dets[..., :4], gts[..., :4])
    ious_max = ious.max(axis=1)
    ious_argmax = ious.argmax(axis=1)
    sort_inds = np.argsort(-dets[..., -1])
    gt_covered = np.zeros(gts.shape[0], np.bool)
    tp = np.zeros(dets.shape[0], np.int32)
    fp = np.zeros(dets.shape[0], np.int32)
    for i in sort_inds:
        if ious_max[i] > threshold and not gt_covered[ious_argmax[i]]:
            tp[i] = 1
            gt_covered[ious_argmax[i]] = True
        else:
            fp[i] = 1
    return tp, fp

if __name__ == "__main__":
    bboxes1 = np.array([[1, 1, 4, 4, 1.0], [5, 5, 8, 8, 0.5]])
    bboxes2 = np.array([[2, 2, 5, 5], [1, 1, 3, 3], [6, 6, 9, 9]])
    tp, fp = tpfp_computation(bboxes1, bboxes2, 0.3)

通过tp和fp计算precisions和recalls

def precisions_recalls_computation(dets, gts, threshold):
    """Compute precisions and recalls
    Args:
        dets: np.array with the shape (n, 5), represent n predicted bboxes with score
            (x_min, y_min, x_max, y_max, score).
        gts: np.array with the shape (m, 4), represent m gt bboxes
            (x_min, y_min, x_max, y_max).
        threshold: used for judging whether matching.
    Returns:
        precisions: np.array with shape (n,)
        recalls: np.array with shape (n,)
    """
    tp, fp = tpfp_computation(dets, gts, threshold)
    sort_inds = np.argsort(-dets[..., -1])
    tp = tp[sort_inds]
    fp = fp[sort_inds]
    tp = np.cumsum(tp, axis=0)
    fp = np.cumsum(fp, axis=0)
    eps = np.finfo(np.float32).eps
    precisions = tp / np.maximum((tp + fp), eps)
    recalls = tp / np.maximum(gts.shape[0], eps)
    return precisions, recalls

通过precisions和recalls计算ap, 有两种方式计算ap, 一种是精细的求面积,一种是将recall划分成11个区域,求11个区域内最大的precision和。

def average_precision(recalls: np.array, precisions: np.array, mode: str = "area"):
    """Calculate average precision (for single or multiple scales).
    Args:
        recalls (ndarray): shape (num_scales, num_dets) or (num_dets, )
        precisions (ndarray): shape (num_scales, num_dets) or (num_dets, )
        mode (str): 'area' or '11points', 'area' means calculating the area
            under precision-recall curve, '11points' means calculating
            the average precision of recalls at [0, 0.1, ..., 1]
    Returns:
        float or ndarray: calculated average precision
    """
    ap = 0
    if mode == "area":
        zeros = np.zeros((1,), dtype=recalls.dtype)
        ones = np.ones((1,), dtype=recalls.dtype)
        mrec = np.concatenate([zeros, recalls, ones])
        mpre = np.concatenate([zeros, precisions, zeros])
        # compute the maximum of the precision
        for i in range(mpre.size - 1, 0, -1):
            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
        # find the point that recall changed
        indxs = np.where(mrec[1:] != mrec[:-1])[0]
        ap = np.sum((mrec[indxs + 1] - mrec[indxs]) * mpre[indxs + 1])
    elif mode == "11points":
        for thr in np.arange(0, 1 + 1e-3, 0.1):
            precs = precisions[recalls > thr]
            prec = precs.max() if precs.size > 0 else 0
            ap += prec
        ap /= 11
    else:
        raise ValueError('Unrecognized mode, only "area" and "11points" are supported')
    return ap

def plot_pr_curve(recalls, precisions, n_rows, n_cols, figsize=(15, 10)):
    fig, axs = plt.subplots(n_rows, n_cols, figsize=figsize)
    ax = axs[0, 0]
    ax.plot(recalls, precisions, label="pr_curve")
    ax.set_xlabel("Recall")
    ax.set_ylabel("Precision")
    ax.set_title("Precision-Recall Curve")
    ax.legend()
    plt.show()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值