草庐IT

【目标检测】IoU、GIoU、DIoU、CIoU、EIoU 5大评价指标

小殊小殊 2023-04-19 原文

目录

一、简介

二、IoU(Intersection over Union)

三、GIoU(Generalized IoU)

四、DIoU(Distance-IoU)

五、CIoU(Complete-IoU)

六、EIoU(Efficient-IoU)

七、pytorch代码实现

八、总结


一、简介

        在目标检测任务中,常用到一个指标IoU,即交并比,IoU可以很好的描述一个目标检测模型的好坏。在训练阶段IoU可以作为anchor-based方法中,划分正负样本的依据;同时也可用作损失函数;在推理阶段,NMS中会用到IoU。同时IoU有着比较严重的缺陷,于是出现了GIoU、DIoU、CIoU、EIoU,下面我们一起看一下这几种IoU。

二、IoU(Intersection over Union)

        IoU的计算是用预测框(A)和真实框(B)的交集除以二者的并集,其公式为:

        IoU的值越高也说明A框与B框重合程度越高,代表模型预测越准确。反之,IoU越低模型性能越差。

IoU优点:

        (1)IoU具有尺度不变性

        (2)结果非负,且范围是(0, 1)

IoU缺点:

        (1)如果两个目标没有重叠,IoU将会为0,并且不会反应两个目标之间的距离,在这种无重叠目标的情况下,如果IoU用作于损失函数,梯度为0,无法优化。

        (2)IoU无法精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。

三、GIoU(Generalized IoU)

        论文地址:https://arxiv.org/pdf/1902.09630.pdf

        为了解决IoU作为损失函数时的两个缺点,有大神提出了GIoU,在IoU后面增加了一项,计算两个框的最小外接矩形,用于表征两个框的距离,从而解决了两个目标没有交集时梯度为零的问题,公式为:

        其中C是两个框的最小外接矩形的面积。

        当IOU=0时:

        当IOU为0时,意味着A与B没有交集,这个时候两个框离得越远,GIOU越接近-1;两框重合,GIOU=1,所以GIOU的取值为(-1, 1]。

         GIOU作为loss函数时:

        当A、B两框不相交时不变,最大化GIoU就是最小化C,这样就会促使两个框不断靠近。

优点:

        (1)当IoU=0时,仍然可以很好的表示两个框的距离。

        (2)GIoU不仅关注重叠区域,还关注其他的非重合区域,能更好的反映两者的重合度。

缺点:

        (1)当两个框属于包含关系时,GIoU会退化成IoU,无法区分其相对位置关系,如下图:

         (2)由于GIoU仍然严重依赖IoU,因此在两个垂直方向,误差很大,很难收敛。两个框在相同距离的情况下,水平垂直方向时,此部分面积最小,对loss的贡献也就越小,从而导致在垂直水平方向上回归效果较差。

        如下图,三种情况下GIoU的值一样,GIoU将很难区分这种情况。

四、DIoU(Distance-IoU)

        论文地址:https://arxiv.org/pdf/1911.08287.pdf

        针对上述GIoU的两个问题,有大神将GIoU中最小外接框来最大化重叠面积的惩罚项修改成最小化两个BBox中心点的标准化距离从而加速损失的收敛过程,这就诞生了DIoU。

        DIoU要比GIou更加符合目标框回归的机制,将目标与预测之间的距离,重叠率以及尺度都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题。

        公式如下:

        其中分别代表了预测框和真实框的中心点,且 ρ 代表的是计算两个中心点间的欧式距离。 c 代表的是能够同时包含预测框和真实框的最小外接矩形的对角线长度。

优点:

        (1)DIoU loss可以直接最小化两个目标框的距离,因此比GIoU loss收敛快得多。

        (2)对于包含两个框在水平方向和垂直方向上这种情况,DIoU损失可以使回归非常快。

        (3)DIoU还可以替换普通的IoU评价策略,应用于NMS中,使得NMS得到的结果更加合理和有效。

缺点:

        虽然DIOU能够直接最小化预测框和真实框的中心点距离加速收敛,但是Bounding box的回归还有一个重要的因素纵横比暂未考虑。如下图,三个红框的面积相同,但是长宽比不一样,红框与绿框中心点重合,这时三种情况的DIoU相同,证明DIoU不能很好的区分这种情况。

五、CIoU(Complete-IoU)

        CIoU与DIoU出自同一篇论文,CIoU大多数用于训练。DIoU的作者考虑到,在两个框中心点重合时,c与d的值都不变。所以此时需要引入框的宽高比:

        其中α是权重函数,v用来度量宽高比的一致性:

        最终CIoU Loss定义为:

优点:

        考虑了框的纵横比,可以解决DIoU的问题。

缺点:

        通过CIoU公式中的v反映的纵横比的差异,而不是宽高分别与其置信度的真实差异,所以有时会阻碍模型有效的优化相似性。

六、EIoU(Efficient-IoU)

        论文地址:https://arxiv.org/pdf/2101.08158.pdf

        为了解决CIoU的问题,有学者在CIOU的基础上将纵横比拆开,提出了EIOU Loss,并且加入Focal聚焦优质的预测框,与CIoU相似的,EIoU是损失函数的解决方案,只用于训练。

        EIOU的惩罚项是在CIOU的惩罚项基础上将纵横比的影响因子拆开分别计算目标框和预测框的长和宽,该损失函数包含三个部分:重叠损失,中心距离损失,宽高损失,前两部分延续CIoU中的方法,但是宽高损失直接使目标框与预测框的宽度和高度之差最小,使得收敛速度更快。惩罚项公式如下:

        其中 是覆盖两个Box的最小外接框的宽度和高度。

        通过整合EIoU Loss和FocalL1 loss,最终得到了最终的Focal-EIoU loss,其中 γ是一个用于控制曲线弧度的超参。

优点:

1)将纵横比的损失项拆分成预测的宽高分别与最小外接框宽高的差值,加速了收敛提高了回归精度。

2)引入了Focal Loss优化了边界框回归任务中的样本不平衡问题,即减少与目标框重叠较少的大量锚框对BBox 回归的优化贡献,使回归过程专注于高质量锚框。

七、pytorch代码实现

        代码中使用pytorch是方便在作为损失函数的时候,方便反向传播,下面的代码计算各种IoU,用作损失函数的时候需要用1减去返回结果。

import torch
import math
import numpy as np


def bbox_iou(box1, box2, xywh=False, giou=False, diou=False, ciou=False, eiou=False, eps=1e-7):
    """
    实现各种IoU
    Parameters
    ----------
    box1        shape(b, c, h, w,4)
    box2        shape(b, c, h, w,4)
    xywh        是否使用中心点和wh,如果是False,输入就是左上右下四个坐标
    GIoU        是否GIoU
    DIoU        是否DIoU
    CIoU        是否CIoU
    EIoU        是否EIoU
    eps         防止除零的小量

    Returns
    -------

    """
    # 获取边界框的坐标
    if xywh:
        # 将 xywh 转换成 xyxy
        b1_x1, b1_x2 = box1[..., 0] - box1[..., 2] / 2, box1[..., 0] + box1[..., 2] / 2
        b1_y1, b1_y2 = box1[..., 1] - box1[..., 3] / 2, box1[..., 1] + box1[..., 3] / 2
        b2_x1, b2_x2 = box2[..., 0] - box2[..., 2] / 2, box2[..., 0] + box2[..., 2] / 2
        b2_y1, b2_y2 = box2[..., 1] - box2[..., 3] / 2, box2[..., 1] + box2[..., 3] / 2

    else:
        # x1, y1, x2, y2 = box1
        b1_x1, b1_y1, b1_x2, b1_y2 = box1[..., 0], box1[..., 1], box1[..., 2], box1[..., 3]
        b2_x1, b2_y1, b2_x2, b2_y2 = box2[..., 0], box2[..., 1], box2[..., 2], box2[..., 3]

    # 区域交集
    inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
            (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)

    # 区域并集
    w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
    w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
    union = w1 * h1 + w2 * h2 - inter + eps
    # 计算iou
    iou = inter / union

    if giou or diou or ciou or eiou:
        # 计算最小外接矩形的wh
        cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)
        ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)

        if ciou or diou or eiou:
            # 计算最小外接矩形角线的平方
            c2 = cw ** 2 + ch ** 2 + eps
            # 计算最小外接矩形中点距离的平方
            rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +
                    (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4
            if diou:
                # 输出DIoU
                return iou - rho2 / c2
            elif ciou:
                v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
                with torch.no_grad():
                    alpha = v / (v - iou + (1 + eps))
                # 输出CIoU
                return iou - (rho2 / c2 + v * alpha)
            elif eiou:
                rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
                rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
                cw2 = cw ** 2 + eps
                ch2 = ch ** 2 + eps
                # 输出EIoU
                return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
        else:
            c_area = cw * ch + eps  # convex area
            # 输出GIoU
            return iou - (c_area - union) / c_area
    else:
        # 输出IoU
        return iou


if __name__ == '__main__':
    box1 = torch.from_numpy(np.asarray([170, 110, 310, 370]))
    box1 = box1.expand(1, 1, 1, 1, 4)
    # 有交集
    box2 = torch.from_numpy(np.asarray([250, 60, 375, 300]))
    box2 = box2.expand(1, 1, 1, 1, 4)
    # 无交集
    box3 = torch.from_numpy(np.asarray([730, 420, 1000, 700]))
    box3 = box3.expand(1, 1, 1, 1, 4)
    print('iou有交集:', bbox_iou(box1, box2))
    print('giou有交集:', bbox_iou(box1, box2, giou=True))
    print('diou有交集:', bbox_iou(box1, box2, diou=True))
    print('ciou有交集:', bbox_iou(box1, box2, ciou=True))
    print('eiou有交集:', bbox_iou(box1, box2, eiou=True))
    print("=" * 20)
    print('iou无交集:', bbox_iou(box1, box3))
    print('giou无交集:', bbox_iou(box1, box3, giou=True))
    print('diou无交集:', bbox_iou(box1, box3, diou=True))
    print('ciou无交集:', bbox_iou(box1, box3, ciou=True))
    print('eiou无交集:', bbox_iou(box1, box3, eiou=True))

八、总结

        边界框回归的三大几何因素:重叠面积、中心点距离、纵横比。

重叠中心点纵横比优点缺点
IoU

××尺度不变性,非负性;同一性;对称性;三角不等性。1.如果两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小
GIoU××解决检测框和真实框没有重叠时loss等于0问题

1.当检测框和真实框出现包含现象的时候GIOU退化成IOU

2.两个框相交时,在水平和垂直方向上收敛慢

DIoU×直接回归两个框中心点的欧式距离,加速收敛。回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间
CIoU增加了检测框尺度的loss,增加了长和宽的loss,这样预测框就会更加的符合真实框。

1. 纵横比描述的是相对值,存在一定的模糊

2. 未考虑难易样本的平衡问题

EIoU分别计算宽高的差异值取代了纵横比,同时引入Focal Loss解决难易样本不平衡的问题。

有关【目标检测】IoU、GIoU、DIoU、CIoU、EIoU 5大评价指标的更多相关文章

  1. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  2. ruby - 检测由 RSpec、Ruby 运行的代码 - 2

    我想知道我的代码是否在rspec下运行。这可能吗?原因是我正在加载一些错误记录器,这些记录器在测试期间会被故意错误(expect{x}.toraise_error)弄得乱七八糟。我查看了我的ENV变量,没有(明显的)测试环境变量的迹象。 最佳答案 在spec_helper.rb的开头添加:ENV['RACK_ENV']='test'现在您可以在代码中检查RACK_ENV是否经过测试。 关于ruby-检测由RSpec、Ruby运行的代码,我们在StackOverflow上找到一个类似的问题

  3. ruby - 使用 Ruby Daemons gem 检测停止 - 2

    我正在使用rubydaemongem。想知道如何向停止操作添加一些额外的步骤?希望我能检测到停止被调用,并向其添加一些额外的代码。任何人都知道我如何才能做到这一点? 最佳答案 查看守护程序gem代码,它似乎没有用于此目的的明显扩展点。但是,我想知道(在守护进程中)您是否可以捕获守护进程在发生“停止”时发送的KILL/TERM信号...?trap("TERM")do#executeyourextracodehereend或者你可以安装一个at_exit钩子(Hook):-at_exitdo#executeyourextracodehe

  4. ruby - Ruby 脚本如何检测到它正在 irb 中运行? - 2

    我有一个定义类的Ruby脚本。我希望脚本执行语句BoolParser.generate:file_base=>'bool_parser'仅当脚本作为可执行文件被调用时,而不是当它被irbrequire(或通过-r在命令行上传递)时。我可以用什么来包装上面的语句,以防止它在我的Ruby文件加载时执行? 最佳答案 条件$0==__FILE__...!/usr/bin/ruby1.8classBoolParserdefself.generate(args)p['BoolParser.generate',args]endendif$0==_

  5. Ruby 无法检测字符串中的换行符 - 2

    我有以下字符串,我想检测那里的换行符。但是Ruby的字符串方法include?检测不到它。我正在运行Ruby1.9.2p290。我哪里出错了?"/'ædres/\nYour".include?('\n')=>false 最佳答案 \n需要在双引号内,否则无法转义。>>"\n".include?'\n'=>false>>"\n".include?"\n"=>true 关于Ruby无法检测字符串中的换行符,我们在StackOverflow上找到一个类似的问题: h

  6. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  7. ruby - 重新连接 tcpsocket(或如何检测已关闭的套接字) - 2

    我有一个连接到服务器的ruby​​tcpsocket客户端。在发送数据之前如何检查套接字是否已连接?我是否尝试“拯救”断开连接的tcpsocket,重新连接然后重新发送?如果是这样,有没有人有一个简单的代码示例,因为我不知道从哪里开始:(我很自豪我设法在rails中获得了一个持久连接的客户端tcpsocket。然后服务器决定杀死客户端,一切都崩溃了;)编辑我已经使用此代码解决了一些问题-如果未连接,它将尝试重新连接,但如果服务器已关闭则不会处理这种情况(它将继续重试)。这是正确方法的开始吗?谢谢defself.write(data)begin@@my_connection.write(

  8. css - 检测到 Sass 更改但 style.css 仅在我保存时每 5 到 7 次被覆盖 - 2

    我在一台Windows764位机器上使用Sass和Ruby(最新版本),我正在我的家庭服务器上处理一个共享文件夹。(但是,我不得不承认问题本身也出现在服务器上,因为我试图安装Ruby并直接-watch服务器上的文件)。问题如下:如果我第一次保存,检测到变化,我的style.css被直接覆盖。之后,我总是需要保存多达7次才能覆盖style.css。每次都会检测到更改,但不会编译任何内容。这是一个屏幕:>>>Sassiswatchingforchanges.PressCtrl-Ctostop.overwritestyle.css>>>Changedetectedto:E:/Websites

  9. ruby-on-rails - 检测 Rails 是否正在运行站点 - 2

    我所在的团队负责管理公司面向公众的云平台。我们拥有大量运行面向互联网的VM的用户群。我想对我们的地址空间进行自动扫描,看看是否有人在运行Rails应用程序,这样我就可以通知他们升级他们的Rails版本,以避免本周出现的严重安全漏洞。我注意到在某些Apache部署中,有一个有用的PassengerHeader:X-Powered-By:PhusionPassenger(mod_rails/mod_rack)2.0.3然而,这并不可靠。我想知道是否有一种可靠的方法来检测在Web服务器后面运行的Rails,无论是使用响应header还是某种可以确定的GET/POST。谢谢!

  10. ruby - 正则表达式 - 这个用于素数检测的正则表达式的复杂性是多少? - 2

    这行ruby​​代码检测素数(太棒了!)。("1"*n)!~/^1?$|^(11+?)\1+$/#wherenisapositiveinteger详细信息在这篇博文中解释http://www.noulakaz.net/weblog/2007/03/18/a-regular-expression-to-check-for-prime-numbers/我很好奇它在BIG-O表示法中的表现。有人帮忙吗? 最佳答案 根据经验数据,它似乎是O(n2)。我对前10000个质数中的每100个运行Ruby代码。以下是结果:蓝点是记录的时间,橙色线是

随机推荐