1 IOU计算: 交并比
1.1 先上代码
Python版
class Iou:
def compute_iou(self, x1, y1, x2, y2, x3, y3, x4, y4):
#两矩阵面积之和
area = (x2 - x1) * (y2 - y1) + (x4 - x3) * (y4 - y3)
# 两矩阵相交部分的左上角和右下角坐标确定
middle_x1 = max(x1, x3)
middle_x2 = min(x2, x4)
middle_y1 = max(y1, y3)
middle_y2 = min(y2, y4)
middle_w = max(0.0, middle_x2 - middle_x1)
middle_h = max(0.0, middle_y2 - middle_y1)
#两矩阵之交
middle_area = middle_w * middle_h
# return 交 / 并
return middle_area / (area - middle_area)
if __name__=="__main__":
rec1 = [27, 661, 47, 679]
rec2 = [27, 662, 47, 682]
a = Iou().compute_iou(*rec1, *rec2)
print(f"a: {a}")
C++ 版
#include<iostream>
#include<vector>
using namespace std;
double compute_iou(const vector<vector<int>>& a, const vector<vector<int>>& b){
int middle_x1 = max(a[0][0], b[0][0]);
int middle_y1 = max(a[0][1], b[0][1]);
int middle_x2 = min(a[1][0], b[1][0]);
int middle_y2 = min(a[1][1], b[1][1]);
int middle_w = max(0, middle_x2 - middle_x1);
int middle_h = max(0, middle_y2 - middle_y1);
int middle_area = middle_w * middle_h;
int area = (a[1][0] - a[0][0]) * (a[1][1] - a[0][1]) + (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
return double(middle_area) / (area - middle_area);
}
int main(){
vector<vector<int>> a{{100, 100}, {120, 120}};
vector<vector<int>> b{{110, 110}, {130, 130}};
cout << compute_iou(a, b) << endl;
return 0;
}
1.2 推导过程
IOU公式如下:
I O U = 两矩阵交 / 两矩阵并 IOU = 两矩阵交 /两矩阵并 IOU=两矩阵交/两矩阵并
以左上角为远点。已知两个矩阵的左上角和右下角坐标,计算IOU。具体地,
第一个矩形rec1,左上:(x1, y1) ,右下:(x2, y2) ;
第二个矩形rec2,左上:(x3, y3) ,右下:(x4, y4) ;
很容易计算两个矩阵的和,公式如下:
a
r
e
a
=
r
e
c
1
+
r
e
c
2
=
(
x
2
−
x
1
)
∗
(
y
2
−
y
1
)
+
(
x
4
−
x
3
)
∗
(
y
4
−
y
3
)
area = rec1 + rec2 = (x2 - x1)*(y2 - y1) + (x4 - x3)*(y4 - y3)
area=rec1+rec2=(x2−x1)∗(y2−y1)+(x4−x3)∗(y4−y3)
要计算两矩阵的交,则要确定相交的面积的左上和右下坐标,则有:
m
i
d
d
l
e
_
l
e
f
t
=
m
a
x
(
x
1
,
x
3
)
middle\_left = max(x1, x3)
middle_left=max(x1,x3)
m
i
d
d
l
e
_
r
i
g
h
t
=
m
i
n
(
x
2
,
x
4
)
middle\_right = min(x2, x4)
middle_right=min(x2,x4)
m
i
d
d
l
e
_
t
o
p
=
m
a
x
(
y
1
,
y
3
)
middle\_top = max(y1, y3)
middle_top=max(y1,y3)
m
i
d
d
l
e
_
b
u
t
t
o
m
=
m
i
n
(
y
2
,
y
4
)
middle\_buttom = min(y2, y4)
middle_buttom=min(y2,y4)
因此矩阵的交为:
m
i
d
d
l
e
_
a
r
e
a
=
m
a
x
(
0
,
m
i
d
d
l
e
_
r
i
g
h
t
−
m
i
d
d
l
e
_
l
e
f
t
)
∗
m
a
x
(
0
,
m
i
d
d
l
e
_
b
u
t
t
o
m
−
m
i
d
d
l
e
_
t
o
p
)
middle\_area = max(0, middle\_right - middle\_left) * max(0, middle\_buttom - middle\_top)
middle_area=max(0,middle_right−middle_left)∗max(0,middle_buttom−middle_top)
矩阵的并为:
矩阵的并
=
矩阵的和
−
矩阵的交
矩阵的并 = 矩阵的和 - 矩阵的交
矩阵的并=矩阵的和−矩阵的交
因此最终公式为
I
O
U
=
m
i
d
d
l
e
_
a
r
e
a
a
r
e
a
−
m
i
d
d
l
e
_
a
r
e
a
IOU = \frac{middle\_area}{area - middle\_area}
IOU=area−middle_areamiddle_area
2 NMS:非极大值抑制(Non-Maximum Suppression, NMS)
2.1 先上代码
import numpy as np
import matplotlib.pyplot as plt
class Solution:
def nms(self, boxes, threshold):
# 计算所有候选框面积,利用numpy一次计算所有的候选框
x1, y1, x2, y2, score = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3], boxes[:, 4]
#计算每个框的面积
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
# 对每个框的置信度进行从小到大排序,order存储的是对应score中从小到大的索引值
order = np.argsort(score)
# keep是返回值,经过 NMS 后挑选出的框
keep = []
while order.size > 0:
# 将当前置信度最大的框加入返回值,并用其抑制iou>给定阈值的框,小于阈值的框继续做NMS,直到所有框被挑选完
index = order[-1]
keep.append(index)
# 计算置信度最大的框和其余框的iou,这里计算的数量为当前order.size - 1.
middle_x1 = np.maximum(x1[index], x1[order[:-1]])
middle_x2 = np.minimum(x2[index], x2[order[:-1]])
middle_y1 = np.maximum(y1[index], y1[order[:-1]])
middle_y2 = np.minimum(y2[index], y2[order[:-1]])
# 特殊情况处理,对左上角和右下角框的坐标进行判断:右下角坐标 - 左上角坐标 > 0 返回对应值,否则返回 0
middle_w = np.maximum(0.0, middle_x2 - middle_x1 + 1)
middle_h = np.maximum(0.0, middle_y2 - middle_y1 + 1)
middle_area = middle_h * middle_w
# 计算当前置信度最大的框与其余框的iou,将iou大于阈值的框删除
iou_ratio = middle_area / (areas[index] + areas[order[:-1]] - middle_area)
# 挑选小于给定阈值的iou继续进行 NMS
left = np.where(iou_ratio < threshold)
# 将所有< threshold的索引取出来
order = order[left]
return keep
def plot_bbox(self, dets, c='k'):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
plt.plot([x1, x2], [y1, y1], c)
plt.plot([x1, x1], [y1, y2], c)
plt.plot([x1, x2], [y2, y2], c)
plt.plot([x2, x2], [y1, y2], c)
plt.title(" nms")
if __name__=="__main__":
boxes = np.array([[100, 100, 250, 250, 0.8],
[250, 250, 420, 420, 0.95],
[220, 220, 320, 330, 0.92],
[100, 100, 210, 210, 0.7],
[230, 240, 325, 330, 0.81],
[220, 230, 315, 340, 0.9]])
threshold = 0.7
s1 = Solution()
a = s1.nms(boxes, threshold)
for i in boxes[a]:
print(f"boxes_nms: {i}")
# 可视化 NMS 效果
plt.figure(1)
ax1 = plt.subplot(1, 2, 1)
ax2 = plt.subplot(1, 2, 2)
plt.sca(ax1)
s1.plot_bbox(boxes, 'k') # before nms
keep = a
plt.sca(ax2)
s1.plot_bbox(boxes[keep], 'r') # after nm
plt.show()
2.2 推导过程
- 我们先将所有候选框的置信度排序,因为我们最终是要最大的
- 将置信度最大的加入到最终的返回值中
- 将其他的候选框和当前置信度最大的框计算iou
- 如果iou大于一个阈值,则可删除(说明和置信度大的那个是重叠的)
- 将剩下的框重复以上过程