目录
2)Python代码(代码参考yolov3模型util.py文件)
3)Python代码实现(yolov3中util.py文件,增加了注释)
一、相关概念
1、iou
1)理论计算
参考:https://blog.csdn.net/zouxiaolv/article/details/107400193
物体检测需要定位出物体的bounding box,就像上面的图片一样,我们不仅要定位出车辆的bounding box 我们还要识别出bounding box 里面的物体就是车辆。
对于bounding box的定位精度,有一个很重要的概念,那就是定位精度评价公式:IOU。
IOU表示了bounding box 与 ground truth 的重叠度,如下图所示:
这里写图片描述
矩形框A、B的一个重合度IOU计算公式为:
IOU=Area(A∩B)/Area(A∪B)
就是矩形框A、B的重叠面积占A、B并集的面积比例:
IOU=SI/(SA+SB-SI)
如何计算IOU(交并比)
首先求出重合面积:
选取两个矩形框左顶角的横,纵坐标的最大值,x21,y21;选取两个矩形框右下边角的横纵坐标的最小值,x12,y12;
重合面积计算:
inter= | x12-x21 *| y12-y21 |
并集的面积计算:
b = | x12-x21 |*| y12-y21 |+ | x21-x22 || y21-y22 | - inter
计算IOU:
IOU=inter/b
2)Python代码(代码参考yolov3模型util.py文件)
def bboxes_iou(boxes1, boxes2):
'''
两个都是数组,注意两个box的维度必须一致,一般是二维
box:[[xmin,ymin,xmax,ymax],
[xmin,ymin,xmax,ymax],
...]
'''
boxes1 = np.array(boxes1)# (1,4)
boxes2 = np.array(boxes2) # (n,4)
# 计算两个box的面积
boxes1_area = (boxes1[..., 2] - boxes1[..., 0]) * (boxes1[..., 3] - boxes1[..., 1]) # (1,1)
boxes2_area = (boxes2[..., 2] - boxes2[..., 0]) * (boxes2[..., 3] - boxes2[..., 1]) # (n,1)
left_up = np.maximum(boxes1[..., :2], boxes2[..., :2])
right_down = np.minimum(boxes1[..., 2:], boxes2[..., 2:])
inter_section = np.maximum(right_down - left_up, 0.0)
inter_area = inter_section[..., 0] * inter_section[..., 1] # 重叠区域
union_area = boxes1_area + boxes2_area - inter_area # 全部面积
ious = np.maximum(1.0 * inter_area / union_area, np.finfo(np.float32).eps) # ious
return ious
2、nms
非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,用于目标检测中,就是提取置信度高的目标检测框,而抑制置信度低的误检框。一般来说,用在当解析模型输出到目标框时,目标框会非常多,具体数量由anchor数量决定,其中有很多重复的框定位到同一个目标,nms用来去除这些重复的框,获得真正的目标框。如下图所示,人、马、车上有很多框,通过nms,得到唯一的检测框。
1)基本思路
所谓非极大值抑制:先假设有6个矩形框,根据分类器类别分类概率做排序,从小到大分别属于车辆的概率分别为A<B<C<D<E<F。
(1) 从最大概率矩形框F开始,分别判断A、B、C、D、E与F的重叠度IOU是否大于某个设定的阈值;
(2) 假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。
(3) 从剩下的矩形框A、C、E中,选择概率最大的E,然后判断A、C与E的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
(4) 重复这个过程,找到所有被保留下来的矩形框。
2)标准nms和soft-nms
参考:https://zhuanlan.zhihu.com/p/89426063
bi为待处理BBox框,B为待处理BBox框集合,si是bi框更新得分,Nt是NMS的阈值,D集合用来放最终的BBox,f是置信度得分的重置函数。 bi和M的IOU越大,bi的得分si就下降的越厉害。
经典的NMS算法将IOU大于阈值的窗口的得分全部置为0,可表述如下:
这种是加权线性的
具体其他的可以参考:https://zhuanlan.zhihu.com/p/89426063
3)Python代码实现(yolov3中util.py文件,增加了注释)
def nms(bboxes, iou_threshold, sigma=0.3, method='nms'):
"""
:param bboxes: (xmin, ymin, xmax, ymax, score, class),是一个数组,关于(n,6),n是检测出的box个数
Note: soft-nms, https://arxiv.org/pdf/1704.04503.pdf
https://github.com/bharatsingh430/soft-nms
"""
classes_in_img = list(set(bboxes[:, 5])) # 得到不同类别标签的列表,如[0,1,2],标签号
best_bboxes = [] # 存放最好的box
for cls in classes_in_img: # 遍历类别号列表
cls_mask = (bboxes[:, 5] == cls) # 这里返回的是一个True or False的掩模列表,True则表示bboxes中对应索引号的box是属于这个类
cls_bboxes = bboxes[cls_mask] # 根据掩模获得属于那一类的box,依旧是二维数组
while len(cls_bboxes) > 0: # 这里一直在循环,直到box列表为空
max_ind = np.argmax(cls_bboxes[:, 4]) # 获得这类得分最高的box索引
best_bbox = cls_bboxes[max_ind] # 将分数最高的box作为最佳box
best_bboxes.append(best_bbox) # 添加
# 从box列表中去除最佳box
cls_bboxes = np.concatenate([cls_bboxes[: max_ind], cls_bboxes[max_ind + 1:]])
# 计算得到两个box之间的iou(重叠部分除以一起共同的面积),下面这里其实进行了批量操作,将最佳box和剩下的box进行了计算
# np.newaxies,表示增加一个维度
iou = bboxes_iou(best_bbox[np.newaxis, :4], cls_bboxes[:, :4])
weight = np.ones((len(iou),), dtype=np.float32) # 置信度,当iou大于0.7,说明重叠的部分很大,可以视为一个框,故丢弃
assert method in ['nms', 'soft-nms']
if method == 'nms': # 标准nms
iou_mask = (iou > iou_threshold) # 得到一个掩模,iou大于设定阈值的设置为TRUE
weight[iou_mask] = 0.0 #大于阈值的box的置信度置为0
if method == 'soft-nms': # soft-nms,不直接丢弃大于阈值的box
weight = np.exp(-(1.0 * iou ** 2 / sigma)) # sigma为惩罚因子,这个越小,置信度越小,越容易被抛弃
cls_bboxes[:, 4] = cls_bboxes[:, 4] * weight # 这里是将剩下的box的得分乘以了iou置信度,与bestbox重叠的丢弃了
score_mask = cls_bboxes[:, 4] > 0. # 取大于0的也就是丢弃了重叠的,这也是掩模
cls_bboxes = cls_bboxes[score_mask] # 根据掩模跟新剩下的box,进行下一轮的nms
return best_bboxes
4)标准nms的缺点
1、NMS算法中的最大问题就是它将相邻检测框的分数均强制归零(即将重叠部分大于重叠阈值Nt的检测框移除)。在这种情况下,如果一个真实物体在重叠区域出现,则将导致对该物体的检测失败并降低了算法的平均检测率。
2、NMS的阈值也不太容易确定,设置过小会出现误删,设置过高又容易增大误检。
3、NMS一般只能使用CPU计算,无法使用GPU计算。