目标检测—IoU计算公式
在研究目标检测中,IOU的计算是肯定必不可少的。就比如说在R-CNN网络中,正负样本就是按照候选框与真实框之间的IOU值大小进行区分的,可见该细节还是值得单独拎出来写一篇blog的~~
下面的思路与代码是本人的理解结合百度飞浆的使用教程文档整理出来的(下面附上了飞浆的url,大家可以自主去研究~~~)
百度飞浆–IOU计算废话不多说,直接上干货~
思路分析
例如,在R-CNN网络中,我们通过SS(selective search)算法可以实现在每张图片上获得2k左右的候选框,那么如何区分这么多的候选框到底是属于正样本还是负样本呢?
那么IOU这个概念就诞生了~它其实是来源于数学中的集合的概念,用来反应两个集合之间的空间关系;下面是它的计算公式:
该公式是用来描述两个框之间的重合度的。公式分子是两个框的交集,公式分母是两个框的并集,所以他们的比值就代表交并比。
两个框可能存在的所有空间位置
1.第一种情况:两个框之间存在部分重叠
2.第二种情况:两个框之间不存在任何重叠
3.第三种情况:一个框在另外一个框内部
分析
假设,按照第一种情况进行分析~
a,b分别表示两个框,其中1,2表示每个框的左上角坐标与右下角坐标
先计算交集部分(即公式分子部分):
相交部分左上角坐标为:
相交部分右下角坐标为:
那么相交部分的面积计算公式就是为:
这里,都+1的目的,我个人认为是为了排除两个框之间重叠的像素对面积的影响,取max(*,0)的目的是为了避免出现负数的情况再来计算一下两个框的并集部分:
两个框的面积为:
计算相比部分面积:
所以,最终的iou计算公式为:
代码实现
在目标检测中,框一般都有两种表现手法,一种是(xyxy),一种是(xywh),其实二者之间实现方式都差不多,这里我就仅仅按照第一种方式进行代码实现了~
def cal_iou_xyxy(box1,box2):
x1min, y1min, x1max, y1max = box1[0], box1[1], box1[2], box1[3]
x2min, y2min, x2max, y2max = box2[0], box2[1], box2[2], box2[3]
#计算两个框的面积
s1 = (y1max - y1min + 1.) * (x1max - x1min + 1.)
s2 = (y2max - y2min + 1.) * (x2max - x2min + 1.)
#计算相交部分的坐标
xmin = max(x1min,x2min)
ymin = max(y1min,y2min)
xmax = min(x1max,x2max)
ymax = min(y1max,y2max)
inter_h = max(ymax - ymin + 1, 0)
inter_w = max(xmax - xmin + 1, 0)
intersection = inter_h * inter_w
union = s1 + s2 - intersection
#计算iou
iou = intersection / union
return iou
box1 = [100,100,200,200]
box2 = [120,120,220,220]
iou = cal_iou_xyxy(box1,box2)
print(iou)
下面的代码是指在实战中使用的iou计算代码:
def calc_iou(self, boxes1, boxes2, scope='iou'):
"""calculate ious
Args:
boxes1: 5-D tensor [BATCH_SIZE, CELL_SIZE, CELL_SIZE, BOXES_PER_CELL, 4] ====> 4:(x_center, y_center, w, h)
(2,7,7,2,4)
boxes2: 5-D tensor [BATCH_SIZE, CELL_SIZE, CELL_SIZE, BOXES_PER_CELL, 4] ===> 4:(x_center, y_center, w, h)
(2,7,7,2,4)
Return:
iou: 4-D tensor [BATCH_SIZE, CELL_SIZE, CELL_SIZE, BOXES_PER_CELL] --(2,7,7,2)
"""
with tf.variable_scope(scope):
# transform (x_center, y_center, w, h) to (x1, y1, x2, y2)
boxes1_t = tf.stack([boxes1[..., 0] - boxes1[..., 2] / 2.0,
boxes1[..., 1] - boxes1[..., 3] / 2.0,
boxes1[..., 0] + boxes1[..., 2] / 2.0,
boxes1[..., 1] + boxes1[..., 3] / 2.0],
axis=-1) #tf.stack:矩阵拼接
boxes2_t = tf.stack([boxes2[..., 0] - boxes2[..., 2] / 2.0,
boxes2[..., 1] - boxes2[..., 3] / 2.0,
boxes2[..., 0] + boxes2[..., 2] / 2.0,
boxes2[..., 1] + boxes2[..., 3] / 2.0],
axis=-1)
# calculate the left up point & right down point
lu = tf.maximum(boxes1_t[..., :2], boxes2_t[..., :2]) #左上角坐标最大值
rd = tf.minimum(boxes1_t[..., 2:], boxes2_t[..., 2:]) #右下角坐标最小值
# intersection
intersection = tf.maximum(0.0, rd - lu)
inter_square = intersection[..., 0] * intersection[..., 1]
# calculate the boxs1 square and boxs2 square
square1 = boxes1[..., 2] * boxes1[..., 3]
square2 = boxes2[..., 2] * boxes2[..., 3]
union_square = tf.maximum(square1 + square2 - inter_square, 1e-10)
return tf.clip_by_value(inter_square / union_square, 0.0, 1.0) #截断操作,即如果值不在指定的范围里,那么就会进行最大小值截断
效果展示