IoU是Intersection of Union的缩写,中文意思为:并集交点
IoU表示真实框和预测框的重合程度,取值区间为[0,1],IoU值越大,表明两个框重合越好
IoU的计算公式:
IoU代码(python):
def iou(boxA,boxB):
#计算重合部分的上、下、左、右4个边的值,注意最大最小函数的使用
left_max = max(boxA[0],boxB[0]) #boxA[0]:真实框的左边值 boxB[0]:预测框的左边值
top_max = max(boxA[1],boxB[1]) #boxA[1]:真实框的上边值 boxB[0]:预测框的上边值
right_min = min(boxA[2],boxB[2]) #boxA[2]:真实框的右边值 boxB[2]:预测框的右边值
bottom_min = min(boxA[3],boxB[3]) #boxA[3]:真实框的下边值 boxB[3]:预测框的下边值
#计算重合部分的面积
inter = max(0,(right_min - left_max) )* max(0,(bottom_min-top_max))
Sa = (boxA[2] - boxA[0])*(boxA[3] - boxA[1])
Sb = (boxB[2] - boxB[0])*(boxB[3] - boxB[1])
#计算所有区域的面积并计算iou
union = Sa + Sb - inter
iou = inter/union
return iou
对于IoU而言,我们通常选取一个阈值,如0.5,来确定检测框是正确的还是错误的。当两个框的IoU大于0.5时,我们认为时一个有效的检测,否则输入无效的检测。
由于图像中存在背景与物体两种标签,预测框也分为正确和错误,因此在评测时会产生以下四种样本:
- 正确检测框TP(True Positive):预测框正确地与标签框匹配,IoU大于0.5
- 误检框FP(False Positive):将背景预测成了物体,通常这种框与图中是所有标签的IoU都不会超过0.5
- 漏检框FN(False Negative):本来需要模型检测出的物体,模型没有检测出
- 正确背景(True Negative):本身是背景,模型也没有检测出来,通常在物体检测中不考虑
对于一个检测器,通常用mAP(mean Average Precision)这一指标来评价一个模型好坏(AP:个类别检测精度 mAP:多个类别的平均精度),评测需要每张图片的预测值与标签值,二者包含的内容如下:
- 预测值(Dets):物体类别、边框位置的四个预测值、该物体的得分
- 标签值(GTs):物体类别、边框位置的四个真实值
AP计算过程图:
我们首先将所有的预测框按照得分从高到低进行排序,然后从高到低遍历预测框
在遍历完所有预测框后,我们会得到每一个预测框是否正确,即TP或FP,在遍历过程中我们可以通过TP和FP的数量来计算模型的召回率和准确率
召回率(Recall,R):当前一共检测出的标签框与所有标签框的比值
准确率(Precision,P):当前遍历过的预测框中,属于正确预测边框的比值
计算公式:
遍历到每一个预测框时,都可以生成对应的P和R,这两个值可以组成一个点(R,P),将这些点绘制成曲线,就形成了P-R曲线,如下图
不过,即使有了P-R曲线,评价模型依旧不是非常直观,因为不能在曲线上直接选取到合适的点,召回率高的时候准确率会很低,准确率高的时候召回率往往很低,这个时候AP就派上用场了,计算公式如下:
从公式可以看出AP代表了曲线的面积,总和考虑了不同召回率下的准确率,不会对P和R有任何偏好
除了求面积的方式,还可以使用不同召回率对应的准确率求平均的方式求AP
每个类别的AP是相互独立的,将每个类别的AP进行平均,就得到了mAP
(严格意义上来讲,需要对曲线进行一定的修正再进行AP的计算)
AP过程代码(python):
det_boxes:包含全部图像中所有类别的预测框,其中一个表框包含了[left,top,right,bottom,score,NameofImage]
gt_boxes:包含了全部图像中所有类别的标签,其中一个标签的内容为[left,top,right,bottom,0],最后一位0代表该标签没有被匹配过,如果匹配过则会置为1,其它预测框再去匹配则为误检框
- left:左边值
- top:上边值
- right:右边值
- bottom:下边值
- score:得分
- Nameoflmage:图像名称(标签)
for c in classes:
#通过类别作为关键字,得到每个类别的预测、标签及总标签数
dects = det_boxes[c]
gt_class = gt_boxes[c]
npos = num_pos[c]
#利用得分作为关键字,对预测框按照得分从高到低排序
dect = sorted(dects,key = lambda conf: conf[4],reverse = True)
#设置两个与预测边框长度相同的列表,标记是True Positive还是False Positive
TP = np.zeros(len(dects))
FP = np.zeros(len(dects))
#对某一个类别的预测框进行遍历
for d in range(len(dects)):
#将IoU默认置为最低
iouMax = sys.float_info.min
#遍历与预测框同一图像中的同一类别的标签,计算IoU
if dects[d][-1] in gt_class:
for j in range(len(gt_class[dects[d][-1]])):
iou = Evaluator.iou(dects[d][:4],gt_class[dects[d][-1]][j][:4])
if iou > iouMax:
iouMax = iou
jmax = j #记录与预测有最大IoU的标签
#如果最大IoU大于阈值,并没有被匹配通过则赋予TF
if iouMax >= cfg['iouThreshold']:
if gt_class[dects[d][-1]][jmax][4] == 0:
TP[d] = 1
gt_class[dects[d][-1]][jmax][4] = 1
#如果被匹配过,赋予FP
else:
FP[d] = 1
#如果最大IoU没有超过阈值,赋予FP
else:
FP[d] = 1
#如果对应图像中没有该类别的标签,赋予FP
else:
FP[d] = 1
#利用Numpy的cumsum()函数,计算累计的FP与TP
acc_FP = np.cumsum(FP)
acc_TP = np.cumsum(TP)
rec = acc_TP/npos #得到每个点的Recall
prec = np.divide(acc_TP,(acc_FP + acc_TP)) #得到每个点的Precision
#利用Recall与Precision进一步计算得到AP
[ap,mpre,mrec,ii] = Evaluator.CalculateAveragePrecision(rec,prec)