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()


目录
相关文章
|
算法 计算机视觉
YOLOv8改进 | 损失函数篇 | 最新ShapeIoU、InnerShapeIoU损失助力细节涨点
YOLOv8改进 | 损失函数篇 | 最新ShapeIoU、InnerShapeIoU损失助力细节涨点
837 2
|
机器学习/深度学习 算法 PyTorch
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
这篇文章详细介绍了多种用于目标检测任务中的边界框回归损失函数,包括IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU和WIOU,并提供了它们的Pytorch实现代码。
2694 1
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
|
9月前
|
存储 Java 关系型数据库
java调用mysql存储过程
在 Java 中调用 MySQL 存储过程主要借助 JDBC(Java Database Connectivity)。其核心原理是通过 JDBC 与 MySQL 建立连接,调用存储过程并处理结果。具体步骤包括:加载 JDBC 驱动、建立数据库连接、创建 CallableStatement 对象、设置存储过程参数并执行调用。此过程实现了 Java 程序与 MySQL 数据库的高效交互。
|
机器学习/深度学习 数据可视化 测试技术
YOLO11实战:新颖的多尺度卷积注意力(MSCA)加在网络不同位置的涨点情况 | 创新点如何在自己数据集上高效涨点,解决不涨点掉点等问题
本文探讨了创新点在自定义数据集上表现不稳定的问题,分析了不同数据集和网络位置对创新效果的影响。通过在YOLO11的不同位置引入MSCAAttention模块,展示了三种不同的改进方案及其效果。实验结果显示,改进方案在mAP50指标上分别提升了至0.788、0.792和0.775。建议多尝试不同配置,找到最适合特定数据集的解决方案。
3063 0
|
机器学习/深度学习 PyTorch 算法框架/工具
空间金字塔池化(Spatial Pyramid Pooling, SPP)原理和代码实现(Pytorch)
想直接看公式的可跳至第三节 3.公式修正 一、为什么需要SPP 首先需要知道为什么会需要SPP。 我们都知道卷积神经网络(CNN)由卷积层和全连接层组成,其中卷积层对于输入数据的大小并没有要求,唯一对数据大小有要求的则是第一个全连接层,因此基本上所有的CNN都要求输入数据固定大小,例如著名的VGG模型则要求输入数据大小是 (224*224) 。
2484 0
|
9月前
YOLOv11改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)
YOLOv11改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)
991 4
YOLOv11改进策略【损失函数篇】| 通过辅助边界框计算IoU提升检测效果(Inner_GIoU、Inner_DIoU、Inner_CIoU、Inner_EIoU、Inner_SIoU)
|
机器学习/深度学习 编解码 监控
目标检测实战(六): 使用YOLOv8完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)
这篇文章详细介绍了如何使用YOLOv8进行目标检测任务,包括环境搭建、数据准备、模型训练、验证测试以及模型转换等完整流程。
20622 59
目标检测实战(六): 使用YOLOv8完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)
|
12月前
|
监控 持续交付 调度
Nacos支持哪些应用场景
Nacos支持哪些应用场景
|
10月前
|
存储 弹性计算 数据管理
阿里云对象存储OSS收费标准,存储、流量和请求等多个计费项
阿里云对象存储OSS收费标准涵盖存储、流量及请求费用,提供按量付费和包年包月两种模式。标准型OSS按量付费为0.09元/GB/月,包年包月40GB仅9元/年,500GB优惠价118元/年。流量费仅收取公网流出方向,内网流入流出免费。
739 13
|
机器学习/深度学习 存储 测试技术
【YOLOv8改进】iRMB: 倒置残差移动块 (论文笔记+引入代码)
该专栏聚焦YOLO目标检测的创新改进与实战案例,提出了一种融合CNN和Transformer优点的轻量级模型——倒置残差移动块(iRMB)。iRMB旨在平衡参数、运算效率与性能,适用于资源有限的移动端。通过集成多头自注意力和卷积,iRMB在ImageNet-1K等基准上超越SOTA,同时在iPhone14上展现出比EdgeNeXt快2.8-4.0倍的速度。此外,iRMB设计简洁,适用于各种计算机视觉任务,展示出良好的泛化能力。代码示例展示了iRMB模块的实现细节。更多详细信息和配置可在相关链接中找到。