首先说明计算mAP有多个版本,每个数据集和比赛用的版本和方式也不同,下以VOC与COCO举例说明。精确度(precision),召回率(recall)分别为:
常规的mAP计算为(这是一个N类检测任务):
1、计算单张图片中class1的精度P(VOC默认IOU大于0.5即为TP,COCO稍复杂些,下文再说)
2、循环所有测试集图片,重复1过程求所有图片P的均值即为class1的AP
3、对剩余N-1类重复1和2过程,对每个类的AP求均值,即为mAP
注:为什么说是常规呢,因为很多框架例如caffe中上述过程1可能不是单张,也可以为一个mini-batch,其实不差事了。
-----------------------------------------分割线------------------------------------------
以上分析了mAP值的原始初衷,仔细回想一下影响mAP值(即precision与recall)的有哪些因素呢:
1、IOU(例如IOU取接近0,recall会趋近于1,precision会趋紧于0)
2、bbox的score阈值
为了统一标准,VOC 2007年提出采用IOU阈值确定为0.5,采用11采样点来计算mAP,选择11个不同的recall([0, 0.1, …, 0.9, 1.0]),可以认为是选择了11个rank,由于按照置信度排序,所以实际上等于选择了11个不同的置信度阈值。2010年以后VOC提出11点改为全部点。
以上步骤其实就是在常规计算mAP值外加了一层循环,即选取一系列的recall与precision采用点,相当于上述影响mAP值中的2因素进行全面的计算和求均值。
以下为代码:
1、precision与recall计算
# 按照置信度降序排序
sorted_ind = np.argsort(-confidence)
BB = BB[sorted_ind, :] # 预测框坐标
image_ids = [image_ids[x] for x in sorted_ind] # 各个预测框的对应图片id
# 便利预测框,并统计TPs和FPs
nd = len(image_ids)
tp = np.zeros(nd)
fp = np.zeros(nd)
for d in range(nd):
R = class_recs[image_ids[d]]
bb = BB[d, :].astype(float)
ovmax = -np.inf
BBGT = R['bbox'].astype(float) # ground truth
if BBGT.size > 0:
# 计算IoU
# intersection
ixmin = np.maximum(BBGT[:, 0], bb[0])
iymin = np.maximum(BBGT[:, 1], bb[1])
ixmax = np.minimum(BBGT[:, 2], bb[2])
iymax = np.minimum(BBGT[:, 3], bb[3])
iw = np.maximum(ixmax - ixmin + 1., 0.)
ih = np.maximum(iymax - iymin + 1., 0.)
inters = iw * ih
# union
uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +
(BBGT[:, 2] - BBGT[:, 0] + 1.) *
(BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)
overlaps = inters / uni
ovmax = np.max(overlaps)
jmax = np.argmax(overlaps)
# 取最大的IoU
if ovmax > ovthresh: # 是否大于阈值
if not R['difficult'][jmax]: # 非difficult物体
if not R['det'][jmax]: # 未被检测
tp[d] = 1.
R['det'][jmax] = 1 # 标记已被检测
else:
fp[d] = 1.
else:
fp[d] = 1.
# 计算precision recall
fp = np.cumsum(fp)
tp = np.cumsum(tp)
rec = tp / float(npos)
# avoid divide by zero in case the first detection matches a difficult
# ground truth
prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)
2、这里最终得到一系列的precision和recall值,并且这些值是按照置信度降低排列统计的,可以认为是取不同的置信度阈值(或者rank值)得到的。然后据此可以计算AP:
def voc_ap(rec, prec, use_07_metric=False):
"""Compute VOC AP given precision and recall. If use_07_metric is true, uses
the VOC 07 11-point method (default:False).
"""
if use_07_metric: # 使用07年方法
# 11 个点
ap = 0.
for t in np.arange(0., 1.1, 0.1):
if np.sum(rec >= t) == 0:
p = 0
else:
p = np.max(prec[rec >= t]) # 插值
ap = ap + p / 11.
else: # 新方式,计算所有点
# correct AP calculation
# first append sentinel values at the end
mrec = np.concatenate(([0.], rec, [1.]))
mpre = np.concatenate(([0.], prec, [0.]))
# compute the precision 曲线值(也用了插值)
for i in range(mpre.size - 1, 0, -1):
mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
# to calculate area under PR curve, look for points
# where X axis (recall) changes value
i = np.where(mrec[1:] != mrec[:-1])[0]
# and sum (\Delta recall) * prec
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
return ap
而COCO数据集在计算mAP时针对因素1,即IOU选取也做了调整,即对IOU选取也做了一次循环求均值的过程,IOU选取为 0.50:0.05:0.95
参考:
https://zhuanlan.zhihu.com/p/37910324
https://blog.csdn.net/katherine_hsr/article/details/79266880
https://blog.csdn.net/lixiang_whu/article/details/64495093
https://www.jianshu.com/p/d7a06a720a2b
https://blog.csdn.net/qq_41994006/article/details/81051150
https://oldpan.me/archives/iu-iou-intersection-over-union-python
https://oldpan.me/archives/understand-coco-metric
https://blog.csdn.net/hsqyc/article/details/81702437