Jaccard Index(IoU,重叠程度)
Jaccard Index(Jaccard 系数) 或称作 Jaccard Overlap(Jaccard重叠) 或称作 Intersection-over-Union(IoU,交并比),测量了两个框的重叠程度,这个指标可以反映预测框与真实框的接近程度,在计算loss和进行NMS(非极大抑制)时会用到。如下图所示。
显然,Jaccard系数的取值在
[
0
,
1
]
[0, 1]
[0,1] 之间,取值为0时,说明两个框的交集为0,也就是两个框没有重叠;取值为1时,说明两个框的交集与并集相同,也就是两个框完全重叠。
Jaccard系数的代码实现可能不能很好的理解,我们需要求两个量,一个是交集面积,一个是并集面积,而且
并
集
面
积
=
框
A
的
面
积
+
框
B
的
面
积
−
交
集
面
积
并集面积=框A的面积+框B的面积-交集面积
并集面积=框A的面积+框B的面积−交集面积 ,框A和框B的面积都非常容易计算,因此我们将目光放在交集面积的计算上。考虑一些常见的交集方式,如下图,绿色点表示交集区域左上角,蓝色点表示交集区域右下角。
从上图中,我们可以看出,交集的区域都可以用两个点表示,这正是交集区域的边界坐标,整个交集区域的边界坐标可以表示为:
(
I
x
min
,
I
y
min
,
I
x
max
,
I
y
max
)
(I_{x_{\min}}, I_{y_{\min}}, I_{x_{\max}}, I_{y_{\max}})
(Ixmin,Iymin,Ixmax,Iymax)
其
中
:
I
x
min
=
max
{
x
min
,
x
min
′
}
,
I
y
min
=
max
{
y
min
,
y
min
′
}
,
I
x
max
=
min
{
x
max
,
x
max
′
}
,
I
y
max
=
min
{
y
max
,
y
max
′
}
其中: I_{x_{\min}} =\max\{x_{\min}, x^{\prime}_{\min}\}, \quad I_{y_{\min}} =\max\{y_{\min}, y^{\prime}_{\min}\}, \\ I_{x_{\max}} =\min\{x_{\max}, x^{\prime}_{\max}\}, \quad I_{y_{\max}} =\min\{y_{\max}, y^{\prime}_{\max}\}
其中:Ixmin=max{xmin,xmin′},Iymin=max{ymin,ymin′},Ixmax=min{xmax,xmax′},Iymax=min{ymax,ymax′}
因此可以得到交集面积为
S
=
(
I
y
max
−
I
y
min
)
(
I
x
max
−
I
x
min
)
S = (I_{y_{\max}} - I_{y_{\min}})(I_{x_{\max}} - I_{x_{\min}})
S=(Iymax−Iymin)(Ixmax−Ixmin)。
然而当两个框不重合时,这个式子会计算出什么?考虑不重合情形,如下图所示,绿色点表示上述式子计算的“左上角”,蓝色点表示上述式子计算的“右下角”:
此时,我们考虑上述面积式子:
S
=
(
I
y
max
−
I
y
min
)
(
I
x
max
−
I
x
min
)
S = (I_{y_{\max}} - I_{y_{\min}})(I_{x_{\max}} - I_{x_{\min}})
S=(Iymax−Iymin)(Ixmax−Ixmin),可以发现要么
I
y
max
−
I
y
min
<
0
I_{y_{\max}} - I_{y_{\min}} < 0
Iymax−Iymin<0 ,要么
I
x
max
−
I
x
min
<
0
I_{x_{\max}} - I_{x_{\min}} < 0
Ixmax−Ixmin<0 ,要么二者均小于0,因此我们可以这样计算交集的面积:一旦这两个式子小于0,我们就将其设置为0,这样0乘任何数均是0,因此交集面积计算结果为0,否则就用上述式子得到交集面积。
综上我们可以得到如下求交集的代码:
def find_intersection(set_1, set_2):
"""
计算第一个集合中每个框与第二个集合中每个框的交集面积
:param set_1: 一个shape为[m,4]的tensor,代表m个边界坐标
:param set_2: 一个shape为[n,4]的tensor,代表n个边界坐标
:return: 一个shape为[m,n]的tensor,例如:[0,:]表示set_1中第1个框与set_2中每个框的交集面积
"""
# max函数中的两个tensor的shape分别为[m,1,2], [1,n,2],可以应用广播机制,最后得到的tensor的shape为[m,n,2]
# 例如:[0, :, 2]表示set_1中第一个框与set_2中所有框交集的左上角坐标
lower_bounds = torch.max(set_1[:, :2].unsqueeze(1), set_2[:, :2].unsqueeze(0)) # [m, n, 2]
# 计算右下角的坐标
upper_bounds = torch.min(set_1[:, 2:].unsqueeze(1), set_2[:, 2:].unsqueeze(0)) # [m, n, 2]
# 将两个减式小于0的设置为0
intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0) # [m, n, 2]
# 相乘得到交集面积
return intersection_dims[:, :, 0] * intersection_dims[:, :, 1] # [m, n]
利用 并 集 面 积 = 框 A 的 面 积 + 框 B 的 面 积 − 交 集 面 积 并集面积=框A的面积+框B的面积-交集面积 并集面积=框A的面积+框B的面积−交集面积 ,进而得到计算 Jaccard系数的代码:
def find_jaccard_overlap(set_1, set_2):
"""
计算第一个集合中每个框与第二个集合中每个框的Jaccard系数
:param set_1: 一个shape为[m,4]的tensor,代表m个边界坐标
:param set_2: 一个shape为[n,4]的tensor,代表n个边界坐标
:return: 一个shape为[m,n]的tensor,例如:[0,:]表示set_1中第1个框与set_2中每个框的Jaccard系数
"""
# 每个框与其他框的交集
intersection = find_intersection(set_1, set_2) # [m, n]
# 计算每个集合中每个框的面积
areas_set_1 = (set_1[:, 2] - set_1[:, 0]) * (set_1[:, 3] - set_1[:, 1]) # [m]
areas_set_2 = (set_2[:, 2] - set_2[:, 0]) * (set_2[:, 3] - set_2[:, 1]) # [n]
# 总面积减去交集就是并集
# unsqueeze的作用同样是为了满足广播机制的条件
union = areas_set_1.unsqueeze(1) + areas_set_2.unsqueeze(0) - intersection # [m, n]
# Jaccard系数 = 交集面积 / 并集面积
return intersection / union # [m, n]