【基础回顾&DIYIoU】尝试撸一份属于自己的IoU函数

简介: 【基础回顾&DIYIoU】尝试撸一份属于自己的IoU函数

前言

   在目标检测网络中,IoU(Intersection over Union)函数的作用是衡量检测框和真实框之间的重叠程度。具体来说,IoU函数计算检测框和真实框的交集面积和并集面积之间的比例,通常表示为IoU分数。

  通过比较检测框和真实框之间的IoU分数,可以判断检测框是否正确地定位了目标,并进一步优化目标检测网络的预测结果。在训练过程中,通常会使用IoU函数作为损失函数的一部分,来衡量预测框和真实框之间的差距,从而引导网络学习更准确的目标检测。

IoU家族函数简介

GIoU

  GIoU可以更准确地衡量检测结果的准确度,并且在训练中可以帮助模型更好地收敛。GIoU函数的计算方法在IoU的基础上增加了对检测框和真实标注框之间距离的考虑,以减小框的重叠部分面积的计算误差。具体来说,GIoU使用了检测框和真实标注框的最小闭合矩形(minimum enclosing rectangle)来计算距离,并将距离的贡献加入到计算中,从而得到更准确的评估结果。

image.png

CIoU

  相较于GIoU函数,可以更全面地考虑框的形状和尺度信息,以获得更准确的检测结果。CIoU函数的计算方法在GIoU的基础上增加了对框的长宽比例和中心点距离的惩罚项,以减小框的形状和尺度差异对检测结果的影响。具体来说,CIoU首先计算出GIoU值,然后在计算过程中加入长宽比例和中心点距离的惩罚项,最终得到一个更全面的评估结果。

image.png

DIoU

  DIoU 可以更好地考虑框的位置信息和不同尺寸之间的关系,以获得更准确的检测结果。 DIoU函数的计算方法在CIoU的基础上增加了对框之间距离的惩罚项,以减小框的位置差异对检测结果的影响。具体来说,DIoU计算出CIoU值,然后在计算过程中加入距离的惩罚项,将框之间的距离纳入到评估中,从而得到更准确的结果。

image.png

EIoU

  EIoU可以更好地考虑框之间的关系,EIoU函数的计算方法在DIoU的基础上增加了对框之间嵌入embedding)关系的考虑,以减小框之间的重叠部分的计算误差。具体来说,EIoU将框之间的嵌入关系表示为一个向量,然后通过向量的相似度来衡量框之间的重叠程度,从而得到更准确的评估结果。

image.png

SIOU

  SIoU可以更准确地衡量检在目标的边界模糊或重叠的情况下的准确度。SIoU函数的计算方法类似于IoU,但不同之处在于它使用了一种“软化”的方法来计算交集和并集面积。具体来说,SIoU使用了高斯函数对交集和并集区域进行平滑,以减小边界误差的影响,从而得到更精确的评估结果。

image.png

WIOU

  WIoU可以更好地考虑不同类别之间的重要性差异,以获得更准确的检测结果。WIoU函数的计算方法在传统的IoU的基础上引入了类别权重的概念,以减小不同类别之间的重要性差异对检测结果的影响。具体来说,WIoU为每个类别分配一个权重,然后在计算IoU时对不同类别之间的重叠部分使用不同的权重进行加权,从而得到更准确的评估结果。

image.png

代码示例

ini

复制代码

import math
import torch
def my_ious(pred_x1, pred_y1, pred_x2, pred_y2, target_x1, target_y1, target_x2, target_y2, eps=1e-7, MyIoU=0):
    """
    MyIoU 的值代表不同的IoU
    0 ----> IoU
    1 ----> CIoU
    2 ----> DIoU
    3 ----> EIoU
    4 ----> GIoU
    5 ----> SIoU
    6 ----> WIoU
    """
    # The overlapping region 重叠区域 求交集
    inter_x = (torch.min(pred_x2, target_x2) - torch.max(pred_x1, target_x1)).clamp(0)
    inter_y = (torch.min(pred_y2, target_y2) - torch.max(pred_y1, target_y1)).clamp(0)
    s_inter = inter_x * inter_y
    # The area covered  求并集
    w1, h1 = pred_x2 - pred_x1, pred_y2 - pred_y1 + eps
    w2, h2 = target_x2 - target_x1, target_y2 - target_y1 + eps
    s_union = w1 * h1 + w2 * h2 - s_inter + eps
    # IoU
    iou = s_inter / s_union
    ptx_min = torch.min(pred_x1, target_x1)
    pty_min = torch.min(pred_y1, target_y1)
    ptx_max = torch.max(pred_x2, target_x2)
    pty_max = torch.max(pred_y2, target_y2)
    cw = ptx_max - ptx_min
    ch = pty_max - pty_min
    cds = cw ** 2 + ch ** 2 + eps
    tpx2 = (target_x1 + target_x2 - pred_x1 - pred_x2) ** 2
    tpy2 = (target_y1 + target_y2 - pred_y1 - pred_y2) ** 2
    rho2 = (tpx2 + tpy2) / 4
    focaleiou = False
    if MyIoU != 0:
        # CIoU
        if MyIoU == 1:
            anat = torch.atan(w2 / h2) - torch.atan(w1 / h1)
            v = (4 / math.pi ** 2) * torch.pow(anat + eps, 2)
            alpha = v / (iou - v + (1 + eps))
            ciou = iou - (rho2 / cds + alpha * v)
            return ciou
        # DIoU
        if MyIoU == 2:
            diou = (iou - rho2 + eps) / cds
            return diou
        # EIoU
        if MyIoU == 3:
            diou_term = (rho2 + eps) / (cds + eps)
            c2_w = cw ** 2 + eps
            c2_h = ch ** 2 + eps
            rho2_w = (w1 - w2) ** 2
            rho2_h = (h1 - h2) ** 2
            eiou_term = (rho2_w / c2_w) + (rho2_h / c2_h)
            eiou = (1 - iou + diou_term + eiou_term) * 1.
            if focaleiou:
                eiou = iou ** 0.5 * eiou
            return eiou
        # GIoU
        if MyIoU == 4:
            s_box = cw * ch + eps
            giou = iou - (s_box - s_union) / s_box
            return giou
        # SIoU
        if MyIoU == 5:
            s_cw = (target_x1 + target_x2 - pred_x1 - pred_x2) * 0.5
            s_ch = (target_y1 + target_y2 - pred_y1 - pred_y2) * 0.5
            sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
            sin_alpha_1 = torch.abs(s_cw) / sigma
            sin_alpha_2 = torch.abs(s_ch) / sigma
            threshold = pow(2, 0.5) / 2
            sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
            # angle_cost = 1 - 2 * torch.pow( torch.sin(torch.arcsin(sin_alpha) - np.pi/4), 2)
            angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)
            rho_x = (s_cw / cw) ** 2
            rho_y = (s_ch / ch) ** 2
            gamma = angle_cost - 2
            distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)
            omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)
            omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)
            shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
            siou = iou - 0.5 * (distance_cost + shape_cost)
            return siou
        # WIoU
        if MyIoU == 6:
            exp_num = rho2 / cds
            dist = torch.exp(exp_num)
            wiou = dist * iou
            return wiou
    else:
        return iou

优化

  IoU函数的计算方法是,首先将检测框(bounding box)与真实标注框(ground truth box)进行交集(intersection)运算,然后将交集的面积除以两个框的并集(union)面积,得到一个介于0和1之间的值。当IoU值越大,表示检测结果越接近真实标注。

  一般来说,IoU值大于阈值(通常为0.5或0.75)时才认为检测结果是正确的。然而在实际应用中IoU函数往往满足不了需求,因此进行优化。

IoU函数的优化思路

  在目标检测网络中,IoU函数的优化思路通常可以从以下两个方面入手:

  1. 提高检测框的准确度:由于IoU函数的计算依赖于检测框和真实框的交集和并集面积,因此提高检测框的准确度是提高IoU函数表现的重要手段。这可以通过调整网络结构、加入更多的特征信息、优化目标函数等方式实现。
  2. 提高IoU函数本身的表现:除了通过提高检测框的准确度来提高IoU函数的表现之外,也可以直接优化IoU函数本身。一种常见的做法是使用一些基于IoU函数的损失函数,例如SmoothL1Loss、GIoULoss、DIoULoss等,来替代传统的L2Loss或交叉熵损失函数。这些损失函数可以更好地衡量预测框和真实框之间的相似度,从而提高目标检测网络的性能。 综上所述,提高检测框的准确度和优化IoU函数本身是提高目标检测网络性能的两个重要方面。

实现优化

  例如我们对检测框和真实标注框之间的包含关系:红色为真实标注框,绿色为检测框,这个时候可以发现有“外包”和“内包”这两种情况,在内外包之间有一层面积,我们需要对这一层面积单独设定,当面积越大的时候iou值就越小,反之iou值越大。

  判定“内外包” 的条件可以将检测框和真实标注框之间的并集面积等于检测框或真实标注框。

  1. 当并集的面积等于【检测框】的面积时说明【检测框】将【真实标注框】“包含”在里面了;
  2. 当并集的面积等于【真实标注框】的面积时说明【真实标注框】将【检测框】“包含”在里面了;

我们可以根据上述的特殊情况对IoU系列的函数进行更改。

ini

复制代码

def Punish(x, t=True):
    """ 惩罚函数:通过面积计算LOSS值。 这个阶段至少是包含的状态,因从y的值域为[0-1]"""
    if t:
        y = 1 - (x ** 6) / 1e+12
    else:
        y = 1 - (x ** 3) / 1e+6
    return y
def yiou(w1, h1, w2, h2, s_union, s_inter, eps):
    S_pred = w1 * h1 + eps
    S_target = w2 * h2 + eps
    # 相离状态
    if s_inter == 0:
        print("无交集")
        return eps  #
    
# --------------存在包含状态----------------------------
    if torch.equal(s_union, S_pred):
        S = S_pred - S_target
        return Punish(S, t=False)
    if torch.equal(s_union, S_target):
        S = S_target - S_pred


相关文章
|
10天前
|
资源调度 前端开发 数据可视化
R语言参数自抽样法Bootstrap:估计MSE、经验功效、杰克刀Jackknife、非参数自抽样法可视化自测题
R语言参数自抽样法Bootstrap:估计MSE、经验功效、杰克刀Jackknife、非参数自抽样法可视化自测题
14 0
|
9月前
|
Java BI 数据库
特别诺贝尔奖论文《天赋与运气:随机性在成功与失败中的作用》代码实现简版(JAVA)
特别诺贝尔奖论文《天赋与运气:随机性在成功与失败中的作用》代码实现简版(JAVA)
|
11月前
|
程序员
什么是好代码/坏代码?给普通人的图解示例
什么是好代码/坏代码?给普通人的图解示例
79 0
|
机器学习/深度学习 算法 BI
|
机器学习/深度学习 测试技术 异构计算
手撕Desenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Desenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Desenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
|
机器学习/深度学习 数据挖掘 测试技术
手撕Googlenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Googlenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Googlenet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
|
机器学习/深度学习 算法 测试技术
手撕Resnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Resnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕Resnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
|
机器学习/深度学习 数据挖掘 PyTorch
手撕VGG卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕VGG卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
手撕VGG卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
|
机器学习/深度学习 数据可视化 算法
2022数模国赛C题思路解析(可供训练用 源码可供参考)
2022数模国赛C题思路解析(可供训练用 源码可供参考)
355 2
2022数模国赛C题思路解析(可供训练用 源码可供参考)
|
IDE 算法 开发工具
面向 CV 编程:COPY 了别人文章中的代码,想让代码能像作者一样跑通,应该注意什么呢?怎样才能让代码愉快地跑起来呢
一千个读者,一千个哈姆雷特,写代码也是如此,不同人,理解不同,逻辑不同,同一道题,代码各有千秋,所以出现的问题也是千奇百怪;预期结果就一个,解法却千千万。正如列夫托尔斯泰的安娜卡列尼娜中所说:幸福的家庭千遍一律,不幸的家庭各有各的不幸,但是主要不离题万里,错误基本能在一个常见的范围。 有人写代码是为了生计,有的人写代码仅仅是自己的兴趣使然。应该大多人都是第一种情况,我也一样,到了大学才拥有自己的第一台电脑,那时候智能手机刚出现,再往前倒推几年,手机都是个稀罕物,没有条件所以跟兴趣不沾边,唯一沾点边的是以前我喜欢刷竞赛题,锻炼逻辑,对学习算法有点帮助,但是作用有限。甚至有些人对自己所选的专业一无
854 2
面向 CV 编程:COPY 了别人文章中的代码,想让代码能像作者一样跑通,应该注意什么呢?怎样才能让代码愉快地跑起来呢