1.NMS代码复现笔记
from __future__ import absolute_import
import numpy as np
import torch
dets = np.array([
[204, 102, 358, 250, 0.5],
[257, 118, 380, 250, 0.7],
[280, 135, 400, 250, 0.6],
[255, 118, 360, 235, 0.7]])
thresh = 0.8 #这里为了能在Debug过程中比较清楚的显示NMS的多轮少筛选过程,因此将thresh设置较高,实际情况thresh一般比较小,约0.3
def nms_cpu(dets, thresh):
dets = dets.numpy()
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
# 根据score由大到小将anchor进行排序,得到对应的排序后的anchor的索引。
# 作者使用索引来进行anchor的筛选的原因是作者希望所有的anchor的删除和
# 保留都要在最原始的anchor的索引上进行操作。
keep = [] # keep保存了最后经过NMS后的anchor的索引的集合。order中的索
# 引最终有两个去向,要么进入keep作为NMS后的anchor输出对应索
# 引,要么IOU高于设定thresh,被舍弃。直到最后order中不含有
# 任何anchor索引
while order.size > 0:
i = order.item(0) # order中的第一个索引所对应的anchor一直是比较的
# 对象。
keep.append(i) # 将该anchor索引加入keep
# 比较该anchor的左上角坐标和右下角坐标与其余所有anchor的大小,为了
# 为了计算重叠区域的w,h,以及面积,重叠区域的左上角坐标取最大值,
# 重叠区域右下角坐标取最小值。
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.maximum(x2[i], x2[order[1:]])
yy2 = np.maximum(y2[i], y2[order[1:]])
# 此处考虑到该anchor可能与某些anchor不重叠,导致计算的重叠区域的
# w,h为负数,保证w,h均为正数。
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
# areas[order[1:]是分别计算了该anchor与其余每个anchor之间的IOU
ovr = inter / (areas[i] + areas[order[1:]] - inter)
# 保留符合要求的anchor的索引,由于此时inds对应的索引上进行操作,
# ovr的索引相比较原始的anchor索引均少1,需要恢复,故+1。
inds = np.where(ovr <= thresh)[0]
# 去除掉了本轮的anchor以及IOU阈值高于thresh的anchor,剩下的anchor
# 进入下一轮继续筛选和比较。
order = order[inds + 1]
return torch.IntTensor(keep)
nms_cpu(det, thresh)
参考:
Faster R-CNN源代码(TF)–nms/py_cpu_nms.py.
最详细的Faster-RCNN代码解读Pytorch版 (二) RPN部分.