这里写的是python实现,主要是为了demonstrate思想,后期有空会加上cython和cuda实现。
Intersection over Union (IoU) Overlap:
我们需要一些衡量两个框接近程度的方法。
一个比较常用的方法是IoU,如下图
IOU实现
简单版 从one to one 到 one to many 到 many to many
def iou_one2one(box1,box2):
left=np.max(box1[:,0], box2[:,0])
right=np.min(box1[:,2],box2[:,2])
top=np.max(box1[:,1], box2[:,1])
bottom=np.min(box1[:,3],box2[:,3])
inter=max(0, right-left)*max(0,bottom-top)
union=(box1[:,2]-box1[:,0])*(box1[:,3]-box1[:,1])+(box2[:,2]-box2[:,0])*(box2[:,3]-box2[:,1])-inter
return inter/union
def iou_one2many(box1, boxes,):
box1=np.reshape(box1,[-1,5])
boxes=np.reshape(boxes,[-1,5])
left=np.maximum(box1[:,0], boxes[:,0])
right=np.minimum(box1[:,2],boxes[:,2])
top=np.maximum(box1[:,1], boxes[:,1])
bottom=np.minimum(box1[:,3],boxes[:,3])
inter=np.maximum(0, right-left)*np.maximum(0,bottom-top)
union=(box1[:,2]-box1[:,0])*(box1[:,3]-box1[:,1])+(boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:,1])-inter
return inter/union
def iou_many2many(boxes1, boxes2):
res=[]
for box in boxes:
res.append(iou_one2many(box,boxes2))
return np,array(res)
复杂版直接many to many
def compute_overlap(a, b):
#a [N,4]
#b [M,4]
iw = np.minimum(np.expand_dims(a[:, 2], axis=-1), b[:, 2]) - np.maximum(np.expand_dims(a[:, 0], axis=-1), b[:, 0])
ih = np.minimum(np.expand_dims(a[:, 3], axis=-1), b[:, 3]) - np.maximum(np.expand_dims(a[:, 1], axis=-1), b[:, 1])
# 假设a的数目是N,b的数目是M
# (N,)和(M,)不能broadcast,需要将其中一个转化成矩阵
# np.expand_dims((N,),axis=-1)将(N,)变成(N,1) 这里也可以写axis=1
#注意两个矩阵(N,1)和(M,1)也是不能进行broadcast的,只能是矩阵和向量
# np.minimum((N,1),(M,)) 得到 (N M) 的矩阵 代表a和b逐一比较的结果
# 取x和y中较小的值 来计算intersection
# iw和ih分别是intersection的宽和高 iw和ih的shape都是(N,M), 代表每个anchor和groundTruth之间的intersection
iw = np.maximum(iw, 0)
ih = np.maximum(ih, 0) #不允许iw或者ih小于0
#union = np.expand_dims((a[:, 2] - a[:, 0] + 1) *(a[:, 3] - a[:, 1] + 1), axis=1) + area - iw * ih
area_b = (b[:, 2] - b[:, 0] ) * (b[:, 3] - b[:, 1] )
area_a = (a[:, 2] - a[:, 0] ) *(a[:, 3] - a[:, 1] )
union=np.expand_dims(area_a,axis=-1)+area_b- iw * ih
#矩阵和向量之间才能进行broadcast,所以先expand
# 并集的计算 S_a+S_b-interection_ab
union = np.maximum(union, np.finfo(float).eps)
intersection = iw * ih
print(intersection,union)
return intersection / union # (N,M)
Non-Maximum Suppression (NMS)
NMS方法的逻辑是先根据预测出的前景分数从高到低排序box,从分数最高的box开始遍历,与这个box的overlap大于某个threshold的box就会被去掉,例如下图我们以score=0.8的box为基准,overlap_threshold=0.2,那么与score=0.8的box的overlap大于0.2的框,都会被丢弃。如果框X与score=0.8的框的overlap小于0.2,那么我们会认为框X属于另一个物体,不用抑制。
NMS实现
简单版
def nms(boxes, nms_thres=0.3):
idx=np.argsort(boxes[:,-1])[::-1] # 这个函数没有reverse,只能手动
keep = []
while idx.size>0:
i = idx[0]
keep.append(i)
iou=iou_one2many(boxes[i], boxes[idx[1:]])
idx = idx[iou < nms_thres]
return keep
复杂版
def cpu_nms(boxes, iou_thres=0.5):
x1,y1,x2,y2= boxes[:, 0],boxes[:,1],boxes[:,2],boxes[:,3]
scores = boxes[:, 4]
areas = (x2 - x1) * (y2 - y1)
keep = []
order = scores.argsort()[::-1] # high score to low score index
while order.size > 0:
i = order[0]
keep.append(i)
# x1[i]是向量,x1[order[1:]]是矩阵
w=np.minimum(x2[i],x2[order[1:]])-np.maximum(x1[i],x1[order[1:]])
w=np.maximum(0.0,w)
h=np.minimum(y2[i],y2[order[1:]])-np.maximum(y1[i],y1[order[1:]])
h=np.maximum(0.0,h)
inter = w * h
union=areas[i] + areas[order[1:]] - inter
ovr = inter / union
inds = np.where(ovr <= iou_thres)[0]
#直接跳到满足iou小于threshold的index
order = order[inds + 1]
return keep
boxes=np.array([[0,0,1,1,0.5],[0,0,2,2,0.6],[0,0,2,2,0.7],[0,0,3,3,0.8],[0,0,4,4,0.85]])
print(cpu_nms(boxes,0.2))
下面是执行的结果
python tmp.py
4 [9. 4. 4. 1.] [16. 16. 16. 16.] [0.5625 0.25 0.25 0.0625]
0 [] [] []
[4, 0]