一、边界框的置信度(confidence score)
置信度是每个bounding box输出的其中一个重要参数, 所谓置信度其实包含两个方面:一是这个边界框含有目标的可能性大小,也就是当前box是否有对象的概率,(注意,是对象,不是某个类别的对象,它用来说明当前box内只是个背景(backgroud)还是有某个物体(对象));二是这个边界框的准确度。
前者记为Pr(object),当该边界框是背景时(即不包含目标),此时Pr(object)=0;而当该边界框包含目标时,Pr(object)=1。边界框的准确度可以用预测框与实际框(ground truth)的IOU(intersection over union,交并比)来表征,记为。因此置信度可以定义为。其中,表示第i个grid cell的第j个bounding box的置信度。对于如何训练的方法,在损失函数小节中说明。
很多人可能将Yolo的置信度看成边界框是否含有目标的概率,但是其实它是两个因子的乘积,预测框的准确度也反映在里面。边界框的大小与位置可以用4个值来表征:(x,y,w,h),其中(x,y)是边界框的中心坐标,而w和h是边界框的宽与高。还有一点要注意,中心坐标的预测值(x,y)是相对于每个单元格左上角坐标点的偏移值,并且单位是相对于单元格大小的,单元格的坐标定义如图6所示。而边界框的ww和hh预测值是相对于整个图片的宽与高的比例,这样理论上4个元素的大小应该在[0,1]范围。这样,每个边界框的预测值实际上包含5个元素:(x,y,w,h,c),其中前4个表征边界框的大小与位置,而最后一个值是置信度。
二、NMS
在物体检测中,离不了的一个应用就是非极大值抑制(NMS),它主要目的是为了解决一个目标被多次检测的问题,也就是为了消除多余交叉重复的检测框,找到最佳的物体检测的位置。示意图如下:我们需要将左边图中找到右图中最理想的一个框来标注人脸的位置。本文从原理和代码两个方面讲解非极大值抑制。
就像上面的图片一样,定位一个人脸,最后算法就找出了一堆的方框,也就是一个人脸被多次检测,我们需要判别哪些矩形框是没用的,其实我们希望最后仅仅输出其中一个最好的预测框,比如对于美女,只想要红色那个检测结果。做法就是首先选择置信度最高的那个0.98的红框与其他两个绿框做IOU,若IOU大于设定的阈值,就剔除绿框,最后只剩下红色的框框。
举个栗子来理解原理:
非极大值抑制:先假设有6个候选框,根据分类器类别分类概率做排序,从小到大分别属于车辆的概率分别为A、B、C、D、E、F。
1、从最大概率矩形框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
2、假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。
3、从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
4、一直重复这个过程,找到所有曾经被保留下来的矩形框。
非极大值抑制(NMS)非极大值抑制顾名思义就是抑制不是极大值的元素,搜索局部的极大值。例如在对象检测中,滑动窗口经提取特征,经分类器分类识别后,每个窗口都会得到一个分类及分数。但是滑动窗口会导致很多窗口与其他窗口存在包含或者大部分交叉的情况。这时就需要用到NMS来选取那些邻域里分数最高(是某类对象的概率最大),并且抑制那些分数低的窗口。
代码部分:
#coding=utf-8
# athor: AnnSun
# date: 2020.04.28
import numpy as np
def nms(dets, thresh):
# 所有box的坐标信息。注意这里是数组而不是列表
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
# 计算出所有box的面积;图片评分(置信度)按降序排序
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1] # 注意这里orders是边界框的索引
# 保留最后需要保留的边框的索引
keep = []
while order.size > 0:
# order[0]是目前置信度最大的,肯定保留; i是还未处理的图片中的最大评分索引
i = order[0]
# 保留改图片的值
keep.append(i)
# 计算窗口i与其他窗口的交叠的面积
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
# 计算相交框的面积,不相交时用0代替
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
# 计算IOU:相交的面积/相并的面积
ovr = inter / (areas[i] + areas[order[1:]] - inter)
# 只保留比例小于阙值的box,然后继续处理,因为这可能是另外一个目标
inds = np.where(ovr < thresh)[0]
order = order[inds + 1]
return keep
# test
def test():
dets = np.array([[30, 20, 230, 200, 0.6],
[50, 50, 260, 220, 0.9],
[210, 30, 420, 5, 0.8],
[430, 280, 460, 360, 0.7]])
thresh = 0.35
keep_dets = nms(dets, thresh)
print(keep_dets)
print(dets[keep_dets])
if __name__ == "__main__":
test()
那么NMS存在什么问题呢,其中最主要的问题有这么几个:
- 稠密场景下漏检多:最大问题就是它将相邻检测框的分数均强制归零(即将重叠部分大于重叠阈值Nt的检测框移除)。在这种情况下,如果一个真实物体在重叠区域出现,则将导致对该物体的检测失败并降低了算法的平均检测率。(由于和最高置信度的框overlap过大),也就是出现了漏检。
- NMS的阈值也不太容易确定,设置过小会出现误删,设置过高又容易增大误检。
-
存在一些,所有的bbox都预测不准,对于下面第左图我们看到,不是所有的框都那么精准,有时甚至会出现某个物体周围的所有框都标出来了,但是都不准的情况
- 传统的NMS方法默认置信度分数较高的框,定位更精确,是基于分类分数的,只有最高分数的预测框能留下来,但是大多数情况下IoU和分类分数不是强相关,很多分类标签置信度高的框都位置都不是很准,置信度分数高的边界框并不总是比置信度低的框更可靠。(即分类和回归任务没有直接相关性,因此这个条件并不总是成立)。
- Ground Truth(标注框)的标注可能并不可靠。很多人为因素。
- NMS一般只能使用CPU计算,无法使用GPU计算。
假设这是一个函数,那么这个函数输入输出是什么,中间操作又是怎么做的
未完待续