CIOU实现及可视化过程【附代码】

简介: 笔记

先附上一张大家熟悉的CIOU图

1.png

我们知道,CIOU是在IOU的基础上考虑了两个真实框和预测框的中心点距离(图中的d)还有两个框的最小包裹框的对角线距离(图中的c,最小包裹矩形框是图中虚线部分)。比如当两个框不重叠的时候,那么此时的IOU是等于0的,这样会导致无法进行反向传播,而CIOU可以很好的解决这一问题。

2.png

下面我们看看从代码如何实现,我们网络的输出特征层一般是这种shape 【batch_size,feature_W,featuer_H,num_anchors,4】。比如yolo,那么会得到一个[batch_size,13,13,507,4]这样的张量。13是特征层大小,然后有3种锚框,因此该特征层有13*13*3=507个框,每个框又有4个值(center_x,center_y,w,h)表示框的中心点坐标和框的宽和高。


为了方便起见,那么我们现在假设一下,我现在只有两个框,一个真实框(自己标注的),一个预测框,不去管特征层尺寸,只是处理框,那么可以有以下两个张量。其中box1是预测框,box2是真实框(这里的数值没有做归一化处理,不影响理解)。这两个矩形框如下图(绿色真实框):

box1 = torch.tensor([[5.4555e+01, 1.7518e+01, 4.0713e+01, 3.3931e+01]])  # 预测框
box2 = torch.tensor([[7.8304e+01, 1.6306e+01, 4.9968e+01, 3.6646e+01]])  # 真实框
fig = plt.figure()
ax = fig.add_subplot(111)
rec1 = plt.Rectangle((box1[:, 0].numpy() - box1[:, 2].numpy() / 2, box1[:, 1].numpy() - box1[:, 3].numpy() / 2),
                     box1[:, 2].numpy(), box1[:, 3].numpy(), fill=False)  # 预测框
rec2 = plt.Rectangle((box2[:, 0].numpy() - box2[:, 2].numpy() / 2, box2[:, 1].numpy() - box2[:, 3].numpy() / 2),
                     box2[:, 2].numpy(), box2[:, 3].numpy(), fill=False, color='g')  # 真实框
ax.add_patch(rec1)
ax.add_patch(rec2)
plt.xlim(-10, 150)
plt.ylim(-20, 110)
plt.gca().invert_yaxis()
plt.show()

3.png

然后我们可以连接一下这两个框的对角线

plt.plot((box1[:, 0].numpy(), box2[:, 0].numpy()), (box1[:, 1], box2[:, 1]), color='blue')  # 连接中心点

4.png

因为张量给的box信息是xywh接下来是计算这两个框左上角和右下角坐标


那么我们如何计算呢?


我们可以观察box发现:


左上角x坐标=中心点x坐标-w/2;


左上角y坐标=中心点y坐标-h/2;


右下角x坐标=中心点x坐标+w/2;


右下角y坐标=中心点y坐标+h/2;


因此代码可以这样写:

b1_xy = box1[:, :2]  # 取预测框的中心点
b1_wh = box1[:, 2:]  # 取预测框的wh
b1_wh_half = b1_wh / 2.
b1_mins = b1_xy - b1_wh_half  # 求得左上角坐标
b1_maxes = b1_xy + b1_wh_half  # 求得右下角坐标
b2_xy = box2[:, :2]  # 取真实框的中心点
b2_wh = box2[:, 2:]  # 取真实框的wh
b2_wh_half = b2_wh / 2.
b2_mins = b2_xy - b2_wh_half  # 求得左上角坐标
b2_maxes = b2_xy + b2_wh_half  # 求得右下角坐标

然后接下来我们需要计算两个框相交的坐标,为计算IOU做准备。然后分析一下如何计算相交部分的左上角坐标和右下坐标,通过观察两个框的相交,可以发现:


相交左上角=max(box1左上坐标, box2左上坐标)


相交右下角=min(box1右下角坐标,box2右下角坐标)


因此代码可以这样写:

intersect_mins = torch.max(b1_mins, b2_mins)  # 相交的左上角
intersect_max = torch.min(b1_maxes, b2_maxes)  # 相交的右下角
intersect_wh = torch.max(intersect_max - intersect_mins, torch.zeros_like(intersect_max)) # 求得相交的w和h

其中上面代码中的  torch.zeros_like(intersect_max)  是防止两个框不相交,可令wh为0【相交面积也自然为0了】


那么我们就可以得到相交的面积了:


intersect_area = intersect_wh[:, 0] * intersect_wh[:, 1]  # 相交面积

然后是计算两个框的并集面积,并集面积可以这样算:


并集面积=box1面积+box2面积-相交面积


因此代码如下:


b1_area = b1_wh[:, 0] * b1_wh[:, 1]  # 预测框面积
b2_area = b2_wh[:, 0] * b2_wh[:, 1]  # 真实框面积
union_area = b1_area + b2_area - intersect_area  # 并集面积

然后我们就计算普通的IOU了(IOU就是交集面积和并集面积之比)


iou = intersect_area / union_area  # 计算IOU

此时的iou为【大家可以在纸上计算一下】:


iou =  tensor([0.2954])


然后是计算两个框的中心点的欧式距离,即公式中的。


# 计算中心差距d
center_distance = torch.sum(torch.pow(b1_xy - b2_xy, 2), axis=-1)  # 先求平方再相加,得到欧氏距离axis=-1是最后一个维度操作 d(b,bgt)

然后是计算包含两个框最小矩形的左上角和右下角(为计算对角线距离做准备),然后可以分析一下这个最小矩形怎么找,坐标怎么算。通过观察,可以发现:


最小矩形的左上角=min(box1_左上角,box2_左上角)


最小矩形的右下角=max(box1_右下角,box2_右下角)


# 计算包含两个框的最小框的左上角和右下角
closebox_min = torch.min(b1_mins, b2_mins)  # 左上角
closebox_max = torch.max(b1_maxes, b2_maxes)  # 右下角
closebox_wh = torch.max(closebox_max - closebox_min, torch.zeros_like(intersect_max))

然后我们就可以将其绘制一下,最小矩形对角线是图中浅蓝色部分 ,图中虚线是所求最小矩矩形。(绿色是真实框)


5.png

然后可以计算一个这个对角线距离:


# 计算对角线的距离
closebox_distance = torch.sum(torch.pow(closebox_max - closebox_min, 2), axis=-1)

我们现在已经得到了公式中所需要的所有数据了,接下来就是把上面所求组合成CIOU的公式。


因为我们上面已经计算过IOU了,现在计算一下公式中的【代码较长,大家对着公式看】,其中1e+6是为了防止分母出现0而已。


v = math.pow(math.atan(b2_wh[:, 0] / (b2_wh[:, 1] + 1e-6)) 
- math.atan(b1_wh[:, 0] / (b1_wh[:, 1] + 1e-6)), 2) * 4/math.pow(math.pi, 2)

那么也可以得到了:


alpha = v / (1 - iou + v)

然后我们就可以得到CIOU的整体公式了(代码中的clamp是将输入限定在一个区域,限制最小为1e-8也是为了防止分母为0):


ciou = iou - center_distance / torch.clamp(closebox_distance, min=1e-8) - alpha * v

此时的CIOU损失函数为loss_ciou = 1-ciou,所以:


loss_ciou = 1 - ciou

我们可以打印一下此刻的loss为多少:


loss_ciou =  tensor([0.7970])


其实到这就已经可以了,不过我们可以在做一个小小的实验,如果我们把预测框(最开始图中的黑色框)往右边平移5个单位,即让预测框与真实框重合度增大(说明预测的又准了一点点),然后我们看下loss会怎么变。


6.png

iou =  tensor([0.3905])
ciou =  tensor([0.3258])
loss_ciou =  tensor([0.6742])


我们可以看到,loss确实降低了,iou比之前上升了,说明我们的预测框确实离真实框距离又进了一步,预测也更准了那么一点点。


希望大家可以更好的理解CIOU的代码实现和分析过程。


完整代码:

import math
import torch
import matplotlib.pyplot as plt
box1 = torch.tensor([[5.4555e+01, 1.7518e+01, 4.0713e+01, 3.3931e+01]])  # 预测框
box2 = torch.tensor([[7.8304e+01, 1.6306e+01, 4.9968e+01, 3.6646e+01]])  # 真实框
fig = plt.figure()
ax = fig.add_subplot(111)
rec1 = plt.Rectangle((box1[:, 0].numpy() - box1[:, 2].numpy() / 2, box1[:, 1].numpy() - box1[:, 3].numpy() / 2),
                     box1[:, 2].numpy(), box1[:, 3].numpy(), fill=False)  # 预测框
rec2 = plt.Rectangle((box2[:, 0].numpy() - box2[:, 2].numpy() / 2, box2[:, 1].numpy() - box2[:, 3].numpy() / 2),
                     box2[:, 2].numpy(), box2[:, 3].numpy(), fill=False, color='g')  # 真实框
plt.plot((box1[:, 0].numpy(), box2[:, 0].numpy()), (box1[:, 1], box2[:, 1]), color='blue')  # 连接中心点
ax.add_patch(rec1)
ax.add_patch(rec2)
# plt.xlim(-10, 150)
# plt.ylim(-20, 110)
# plt.gca().invert_yaxis()
# plt.show()
b1_xy = box1[:, :2]  # 取预测框的中心点
b1_wh = box1[:, 2:]  # 取预测框的wh
b1_wh_half = b1_wh / 2.
b1_mins = b1_xy - b1_wh_half  # 求得左上角坐标
b1_maxes = b1_xy + b1_wh_half  # 求得右下角坐标
b2_xy = box2[:, :2]  # 取真实框的中心点
b2_wh = box2[:, 2:]  # 取真实框的wh
b2_wh_half = b2_wh / 2.
b2_mins = b2_xy - b2_wh_half  # 求得左上角坐标
b2_maxes = b2_xy + b2_wh_half  # 求得右下角坐标
intersect_mins = torch.max(b1_mins, b2_mins)  # 相交的左上角
intersect_max = torch.min(b1_maxes, b2_maxes)  # 相交的右下角
intersect_wh = torch.max(intersect_max - intersect_mins, torch.zeros_like(intersect_max)) # 求得相交的w和h
intersect_area = intersect_wh[:, 0] * intersect_wh[:, 1]  # 相交面积
b1_area = b1_wh[:, 0] * b1_wh[:, 1]  # 预测框面积
b2_area = b2_wh[:, 0] * b2_wh[:, 1]  # 真实框面积
union_area = b1_area + b2_area - intersect_area  # 并集面积
iou = intersect_area / union_area  # 计算IOU
print("iou = ", iou)
# CIOU = IOU - d(b,bgt)/c^2 - αv
# 计算中心差距d
center_distance = torch.sum(torch.pow(b1_xy - b2_xy, 2), axis=-1)  # 先求平方再相加,得到欧氏距离axis=-1是最后一个维度操作 d(b,bgt)
# 计算包含两个框的最小框的左上角和右下角
closebox_min = torch.min(b1_mins, b2_mins)  # 左上角
closebox_max = torch.max(b1_maxes, b2_maxes)  # 右下角
closebox_wh = torch.max(closebox_max - closebox_min, torch.zeros_like(intersect_max))
plt.plot((closebox_min[:, 0], closebox_max[:, 0]), (closebox_min[:, 1], closebox_max[:, 1]))  # 绘制对角线
# 计算对角线的距离
closebox_distance = torch.sum(torch.pow(closebox_max - closebox_min, 2), axis=-1)
# 计算ciou
v = math.pow(math.atan(b2_wh[:, 0] / (b2_wh[:, 1] + 1e-6)) - math.atan(b1_wh[:, 0] / (b1_wh[:, 1] + 1e-6)), 2) * 4/math.pow(math.pi, 2)
alpha = v / (1 - iou + v)
ciou = iou - center_distance / torch.clamp(closebox_distance, min=1e-8) - alpha * v
print("ciou = ",ciou)
loss_ciou = 1 - ciou
print("loss_ciou = ",loss_ciou)
rec3 = plt.Rectangle((closebox_min[:, 0].numpy(), closebox_min[:, 1].numpy()),  # xy
                     closebox_max[:, 0].numpy() - closebox_min[:, 0].numpy(),  # w
                     closebox_max[:, 1].numpy() - closebox_min[:, 1].numpy(), fill=False, linestyle='dotted')  # h
ax.add_patch(rec3)
plt.xlim(20, 120)
plt.ylim(-5, 40)
plt.gca().invert_yaxis()
plt.show()


目录
相关文章
|
6月前
|
数据可视化 数据挖掘
【数据分析与可视化】对图像进行SVD分解并重构图像实战(附源码)
【数据分析与可视化】对图像进行SVD分解并重构图像实战(附源码)
164 0
|
机器学习/深度学习 数据挖掘
这图怎么画| 一个用于展示多种机器学习模型结果的热图
这图怎么画| 一个用于展示多种机器学习模型结果的热图
140 0
|
6月前
|
机器学习/深度学习 算法 数据挖掘
【Python机器学习】回归任务、线性回归评价指标讲解及温度与花朵数线性回归实战(图文解释 附源码)
【Python机器学习】回归任务、线性回归评价指标讲解及温度与花朵数线性回归实战(图文解释 附源码)
148 0
|
1月前
|
数据采集 移动开发 数据可视化
模型预测笔记(一):数据清洗分析及可视化、模型搭建、模型训练和预测代码一体化和对应结果展示(可作为baseline)
这篇文章介绍了数据清洗、分析、可视化、模型搭建、训练和预测的全过程,包括缺失值处理、异常值处理、特征选择、数据归一化等关键步骤,并展示了模型融合技术。
48 1
模型预测笔记(一):数据清洗分析及可视化、模型搭建、模型训练和预测代码一体化和对应结果展示(可作为baseline)
|
4月前
|
机器学习/深度学习 数据采集 存储
在机器学习和数据科学中,数据预处理是一个至关重要的步骤。数据规范化(或称为特征缩放)是预处理的一种常见技术,它可以帮助我们改进模型的性能。`sklearn.preprocessing`模块提供了多种数据规范化的方法,其中`StandardScaler`和`MinMaxScaler`是最常用的两种。
在机器学习和数据科学中,数据预处理是一个至关重要的步骤。数据规范化(或称为特征缩放)是预处理的一种常见技术,它可以帮助我们改进模型的性能。`sklearn.preprocessing`模块提供了多种数据规范化的方法,其中`StandardScaler`和`MinMaxScaler`是最常用的两种。
|
6月前
时间序列分析实战(十一):ECM误差修正模型
时间序列分析实战(十一):ECM误差修正模型
|
6月前
|
存储 资源调度 数据可视化
R语言STAN贝叶斯线性回归模型分析气候变化影响北半球海冰范围和可视化检查模型收敛性
R语言STAN贝叶斯线性回归模型分析气候变化影响北半球海冰范围和可视化检查模型收敛性
|
6月前
|
移动开发 数据可视化
广义线性模型beta二项分布的淋巴结疾病风险预测可视化R语言2实例合集|附数据代码
广义线性模型beta二项分布的淋巴结疾病风险预测可视化R语言2实例合集|附数据代码
|
6月前
|
资源调度 数据可视化 算法
R语言贝叶斯MCMC:用rstan建立线性回归模型分析汽车数据和可视化诊断
R语言贝叶斯MCMC:用rstan建立线性回归模型分析汽车数据和可视化诊断
|
机器学习/深度学习 数据可视化 PyTorch
深度学习之如何使用Grad-CAM绘制自己的特征提取图-(Pytorch代码,详细注释)神经网络可视化-绘制自己的热力图
深度学习之如何使用Grad-CAM绘制自己的特征提取图-(Pytorch代码,详细注释)神经网络可视化-绘制自己的热力图
深度学习之如何使用Grad-CAM绘制自己的特征提取图-(Pytorch代码,详细注释)神经网络可视化-绘制自己的热力图