在目标检测任务中,我们时常会让模型一次性生成大量的候选框(candidate bound),然后再根据每一个框的置信度对框进行排序,进而依次计算框与框之间的IoU,以非极大值抑制的方式,来判断到底哪一个是我们真正要找的物体,哪几个又该删除。例如在做人脸检测时,模型输出的可能是左图,而最终我们得到的是右图。
代码实现
import numpy as np
# box:[上, 左, 下, 右]
box1 = [0,0,8,6]
box2 = [2,3,10,9]
def IoU(box1, box2):
# 计算中间矩形的宽高
in_h = min(box1[2], box2[2]) - max(box1[0], box2[0])
in_w = min(box1[3], box2[3]) - max(box1[1], box2[1])
# 计算交集、并集面积
inter = 0 if in_h < 0 or in_w < 0 else in_h * in_w
union = (box1[2] - box1[0]) * (box1[3] - box1[1]) + \
(box2[2] - box2[0]) * (box2[3] - box2[1]) - inter
# 计算IoU
iou = inter / union
return iou
IoU(box1, box2)
其他的评价指标:分类的精度如何。一般可以用准确度(Accuracy),精度(Precision),召回率(Recall Rate), PR 曲线,AP,mAP等
定位的精度如何。比如 IoU
运行的速度如何。比如 fps,一秒处理几张图。
严格说某些场合也会很在意模型的大小,这也是一个研究方向,比如 squeeze net, mobile net, shuffle net 等。所以除了上面三个维度,模型的大小也可以是一个评价维度。
AP(Average Precision) mAP(mean AP全局所有种类加权平均的平均准确率) IoU(Intersection over Union)
在计算mAP之前,需要了解下面四个衡量指标:
(1)True Positive (TP):表示一个正确的检测结果。在标注边界框附近存在预测边界框,并且预测边界框与标注边界框之间的IoU大于交并比阈值。
(2)False Positive (FP):表示一个错误的检测结果。在标注边界框附近存在预测边界框,但是预测边界框与标注边界框之间的IoU小于交并比阈值。
(3)False Negative(FN):表示在一个标注边界框附近,检测网络并没有输出预测值。换句话说,就是漏检了。
(4)True Negative(TN):计算mAP的时候,这个指标并不需要,因此就不再详细介绍。
1.3 查准率(Precision)和查全率(Recall)
查准率表示模型成功预测正样本的能力。它的计算公式如下:
Precision=TP/(TP+FP)
查全率表示模型能够预测出正样本的能力。它的计算公式如下:
RECALL=TP/(TP+FN)
查准率和查全率是一对矛盾的度量,一般而言,查准率高时,查全率往往偏低;而查全率高时,查准率往往偏低。我们从直观理解确实如此:我们如果希望好瓜尽可能多地选出来,则可以通过增加选瓜的数量来实现,如果将所有瓜都选上了,那么所有好瓜也必然被选上,但是这样查准率就会越低;若希望选出的瓜中好瓜的比例尽可能高,则只选最有把握的瓜,但这样难免会漏掉不少好瓜,导致查全率较低。通常只有在一些简单任务中,才可能使查全率和查准率都很高。所以为了更全面的衡量模型的性能提出了 AP。平均精确率(Average Precision).
平均准确率(AP)是衡量目标检测器的一个性能指标,它可以根据P-R曲线与坐标轴所围成的面积计算得到。其中,P-R曲线表示在以查全率为横坐标,查准率为纵坐标的坐标系下,所绘制的曲线,具体绘制过程下面会详细介绍。其中计算AP值有两种方法,但是这边我只详细介绍其中一种,因为这个方法更为常用。该方法称为Interpolating all points(另一个方法称为11-point interpolation),该方法求的AP值就是在查准率-查全率坐标图中曲线与坐标轴的面积。
重要的来了:mAP的计算过程就是求每一类物体的AP值,然后求和取平均
classes <- 所有类别标签的集合
rests <- 每一类precision和recall以及AP的值的集合
predicts <- 模型预测的所有结果
ground_truth <- 标注的所有结果
# 对每一类物体求AP
for cls in classes do:
preds <- precits中所有类别为cls的边界框信息
gts <- ground_truth中所有类别为cls的边界框信息
TP <- 长度为len(preds)的数组,并初始化为0
FP <- 长度为len(preds)的数组,并初始化为0
occupied <- 记录每个图像中n个gt是否被pred负责预测的词典
# 确定每一个pred是TP还是FP
for i, pred in enumerate(preds) do:
# 找到该pred与gts哪个边界框的IOU最大,将让该pred负责预测该gt,并保存该gt的索引
for j, gt in enumerate(gts) do:
jou = IOU(pred, gt)
jmax = j
# 当iou不小于阈值并且该边界框预测的gt没有被预测,将其设置为TP
if iou >= threshold and (occupied[cls][imax] == 0):
TP[i] = 1
else
FP[i] = 1
# 根据TP和FP求acc_TP和acc_FP
acc_TP = np.cumsum(TP)
acc_FP = np.cumsum(FP)
rec = acc_TP / len(gts)
prec = acc_TP / (acc_TP + acc_FP)
# 根据rec和prec求ap
ap <- CalculateAveragePrecision(rec, prec)
# 将每一类的ap保存在rests中
rests.append(ap)
当采用VOC数据集格式时:
```bash
def GetPascalVOCMetrics(boundingboxes,
IOUThreshold=0.5,
method=MethodAveragePrecision.EveryPointInterpolation):
# 输出结果初始化为一个列表
ret = []
# List with all ground truths (Ex: [imageName,class,confidence=1, (bb coordinates XYX2Y2)])
# 存放标记的所有边界框(与下文的detections一起使用,用于求每个预测结果是TP还是FP)
groundTruths = []
# List with all detections (Ex: [imageName,class,confidence,(bb coordinates XYX2Y2)])
detections = []
# 存放该数据集中所有的类别(需要求每个类别的AP值)
classes = []
# 这段代码涉及到一些更加细节的问题,你可以将其视为从boundingboxes中获取gt和pred的边界框信息
# detections = [['00001', 'person', 0.88, (5.0, 67.0, 36.0, 115.0)],[],....]
# gt的值也类似,只不过第三列数据是0或者1,而不是小数
for bb in boundingboxes.getBoundingBoxes():
# [imageName, class, confidence, (bb coordinates XYX2Y2)]
if bb.getBBType() == BBType.GroundTruth:
groundTruths.append([
bb.getImageName(),
bb.getClassId(), 1,
bb.getAbsoluteBoundingBox(BBFormat.XYX2Y2)
])
else:
detections.append([
bb.getImageName(),
bb.getClassId(),
bb.getConfidence(),
bb.getAbsoluteBoundingBox(BBFormat.XYX2Y2)
])
# get class
if bb.getClassId() not in classes:
classes.append(bb.getClassId())
# 对类别进行排序
classes = sorted(classes)
...
return ret
###以下是补充对TP/FP的判断:正确的检测结果和错误的判断
# 精确到该类在哪个图像中
for d in range(len(dects)):
# print('dect %s => %s' % (dects[d][0], dects[d][3],))
# Find ground truth image
# 将所有属于文件detect[d][0]图像的gt都保存下来
gt = [gt for gt in gts if gt[0] == dects[d][0]]
iouMax = sys.float_info.min
# 选择gt与当前预测值的边界框最大的交并比
for j in range(len(gt)):
# print('Ground truth gt => %s' % (gt[j][3],))
iou = Evaluator.iou(dects[d][3], gt[j][3])
if iou > iouMax:
iouMax = iou
jmax = j
# Assign detection as true positive/don't care/false positive
# 如果阈值
if iouMax >= IOUThreshold:
# 表示当前jmax个物体
if det[dects[d][0]][jmax] == 0:
TP[d] = 1 # count as true positive
# 表示这个gt已经有相应的prediction进行预测了
det[dects[d][0]][jmax] = 1 # flag as already 'seen'
# print("TP")
else:
FP[d] = 1 # count as false positive
# print("FP")
# - A detected "cat" is overlaped with a GT "cat" with IOU >= IOUThreshold.
# 假阴性
else:
FP[d] = 1 # count as false positive
# print("FP")
版权声明:本文为CSDN博主「Life is a joke」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45756171/article/details/122744194
暂无评论