ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一文读懂IoU,GIoU, DIoU, CIoU, Alpha-IoU (代码非常优雅)

2022-04-27 16:02:17  阅读:193  来源: 互联网

标签:GIoU box ac area IoU boxa boxb DIoU inter


IoU就是就是我们说的交并比 Intersection over Union ,具体就是两个box的交集除以并集。

当我们计算我们的anchors 或者 proposals 与 ground truth bounding boxes 的损失的时候,就需要用到IoU。不同的IoU有不同的特性。

IoU:

IoU计算了最简单的情况:
IoU

GIoU:

当两个anchor与gt box都不相交的时候,IoU的loss是一样大的,我们理论认为anchor距离gt box越近,loss应该越小,不应该一样大。这样GIoU就提出来了。GIoU通过计算两个box的最小闭包区域ac来计算loss。底色为红色的范围是Anchor2与Gt box的最小闭包区域,底色为黄色的范围是Anchor1与Gt box的最小闭包区域。明显Anchor2的最小闭包区域小,u代表并集,ac代表最小闭包区域,ac越大, \(L_{GIoU}\) 值越大。Anchor1的ac大,所以Anchor1的损失更高

\[L_{GIoU}=1- IoU+\frac{ac-u}{ac} \]

GIoU

DIoU:

黄色为proposal,蓝色为gt box。当propsals与gt box重叠时,我们认为下面的两种情况左边的效果好,因为它位于gt box中心。GIoU并不能解决这个问题,所以DIoU被提出来了,DIoU用,两个box中心点距离平方 除以 最小闭包区域对角线距离平方,来衡量预测的proposal是否位于gt box中心。

\[L_{DIoU}=IoU+\frac{\rho^2 (b,b^{gt})}{c^2} \]

DIoU

CIoU:

当两个proposals都位于gt box中心时,我们还是认为坐标的效果比较好,因为左边的宽高比跟我们的gt box一致,所以CIoU在DIoU的基础上改进了宽高比。

\[L_{CIoU}=1-IoU+\frac{\rho^2 (b,b^{gt})}{c^2}+\alpha v \]

v用来度量长宽比的相似性。

\[v=\frac{4}{\pi^2}(arctan\frac{w^{gt}}{h^{gt}}-arctan\frac{w}{h}) ^2 \]

alpha是权重值,衡量ciou公式中第三项和第四项的权重,当IoU越大,alpha就越大,alpha大就优先考虑v; IoU越小时,alpha越小,alpha小优先考虑第三项,距离比。

\[\alpha=\frac{v}{(1-IoU)+v} \]

CIoU

Alpha-IoU:

Alpha-IoU主要是考虑IoU大于0.5的时候的梯度,因为在普通 \(L_{IoU}=1-IoU\) 中,IoU的梯度一直是-1。但是在Alpha-IoU中,当iou大于0.5的时候,loss的梯度是大于-1的,收敛的更快,在map0.7/map0.9有提升效果。

\[L_{\alpha-IoU}=1-IoU^{\alpha} \]

\(\alpha>0\),当 \(\alpha=2,iou=0.6\) 时,\(L_{\alpha-IoU}\) 的梯度已经是-1.2了。训练时经验所得alpha取3比较好。

Alpha-IoU

下面是每个IoU的代码实现。

import math

def IoU(boxa, boxb):
    """
    boxa/boxb:[x1,y1,x2,y2],    x2,y2保证大于x1,y1
    loss = 1 - iou
    交集的左上角坐标正好是两个box的左上角角坐标的较大值,交集的右下角坐标正好是两个box的右下角坐标的较小值。
    画出所有的情况判断一下就可以了,求并集面积的时候别忘了减去交集面积
    """
    inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
    inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
    inter_h = max(0, inter_y2 - inter_y1)
    inter_w = max(0, inter_x2 - inter_x1)
    inter_area = inter_w * inter_h
    union_area = ((boxa[3] - boxa[1]) * (boxa[2] - boxa[0])) + \
                 ((boxb[3] - boxb[1]) * (boxb[2] - boxb[0])) - inter_area + 1e-8  # + 1e-8 防止除零
    iou = inter_area / union_area
    return iou


# 为了解决当两个bbox不相交时,距离远的和距离近的损失值一样大。我们认为距离近的损失应该小一点。
# 注意:划分anchor是否是正样本的时候,anchor与label不一定相交,这样giou能够起到积极的作用
# 当用正样本计算与label的iou损失时,这时候正样本与label都是相交的情况,这时候GIoU不一定起到积极的作用。
def GIoU(boxa, boxb):
    """
    giou = iou-(|ac-u|)/|ac|   ac最小闭包区域,u并集
    loss = 1 - giou
    """
    # 求交集
    inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
    inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
    inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
    inter_area = inter_w * inter_h

    # 求并集
    union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
                 ((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8  # + 1e-8 防止除零

    # 求最小闭包区域的x1,y1,x2,y2,h,w,area
    ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
    ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])
    ac_w = ac_x2 - ac_x1
    ac_h = ac_y2 - ac_y1
    ac_area = ac_w * ac_h

    giou = (inter_area / union_area) - (abs(ac_area - union_area) / ac_area)
    return giou


# 当boxes与真实box重合时,一个在中间重合,一个在边缘重合,我们认为在中间重合的是比较好的,
# 所以提出计算两个box中心点的距离,因为预测小目标的中心点box与真实值box本来距离就很小,
# 所以再除以一个最小闭包区域对角线长度,来平衡小目标和大目标的diou。都用平方不开根号减少计算量和精度损失。
def DIoU(boxa, boxb):
    """
    diou=iou-两个box中心点距离平方/最小闭包区域对角线距离平方
    loss=1-diou
    """
    # 求交集
    inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
    inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
    inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
    inter_area = inter_w * inter_h

    # 求并集
    union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
                 ((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8  # + 1e-8 防止除零

    # 求最小闭包区域的x1,y1,x2,y2
    ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
    ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])

    # 把两个bbox的x1,y1,x2,y2转换成ctr_x,ctr_y
    boxa_ctrx, boxa_ctry = boxa[0] + (boxa[2] - boxa[0]) / 2, boxa[1] + (boxa[3] - boxa[1]) / 2
    boxb_ctrx, boxb_ctry = boxb[0] + (boxb[2] - boxb[0]) / 2, boxb[1] + (boxb[3] - boxb[1]) / 2

    # 求两个box中心点距离平方length_box_ctr,最小闭包区域对角线距离平方length_ac,以及diou
    length_box_ctr = (boxb_ctrx - boxa_ctrx) * (boxb_ctrx - boxa_ctrx) + \
                     (boxb_ctry - boxa_ctry) * (boxb_ctry - boxa_ctry)
    length_ac = (ac_x2 - ac_x1) * (ac_x2 - ac_x1) + (ac_y2 - ac_y1) * (ac_y2 - ac_y1)
    # 求平方,相乘是最快的

    diou = inter_area / union_area - length_box_ctr / length_ac
    return diou


# 当boxes与真实box重合时,且都在在中心点重合时,一个长宽比接近真实box,一个差异很大
# 我们认为长宽比接近的是比较好的,损失应该是比较小的。所以ciou增加了对box长宽比的考虑
def CIoU(boxa, boxb):
    """
    ciou=iou+两个box中心点距离平方/最小闭包区域对角线距离平方+alpha*v
    loss=1-iou+两个box中心点距离平方/最小闭包区域对角线距离平方+alpha*v
    注意loss跟上边不一样,这里不是1-ciou
    v用来度量长宽比的相似性,4/(pi *pi)*(arctan(boxa_w/boxa_h)-arctan(boxb_w/boxb_h))^2
    alpha是权重值,衡量ciou公式中第二项和第三项的权重,
    alpha大优先考虑v,alpha小优先考虑第二项距离比,alpha = v / ((1 - iou) + v)。
    """
    # 求交集
    inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
    inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
    inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
    inter_area = inter_w * inter_h

    # 求并集
    union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
                 ((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8  # + 1e-8 防止除零

    # 求最小闭包区域的x1,y1,x2,y2
    ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
    ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])

    # 把两个bbox的x1,y1,x2,y2转换成ctr_x,ctr_y,w,h
    boxa_ctrx, boxa_ctry = boxa[0] + (boxa[2] - boxa[0]) / 2, boxa[1] + (boxa[3] - boxa[1]) / 2
    boxb_ctrx, boxb_ctry = boxb[0] + (boxb[2] - boxb[0]) / 2, boxb[1] + (boxb[3] - boxb[1]) / 2
    boxa_w, boxa_h = boxa[2] - boxa[0], boxa[3] - boxa[1]
    boxb_w, boxb_h = boxb[2] - boxb[0], boxb[3] - boxb[1]

    # 求两个box中心点距离平方length_box_ctr,最小闭包区域对角线距离平方length_ac
    length_box_ctr = (boxb_ctrx - boxa_ctrx) * (boxb_ctrx - boxa_ctrx) + \
                     (boxb_ctry - boxa_ctry) * (boxb_ctry - boxa_ctry)
    length_ac = (ac_x2 - ac_x1) * (ac_x2 - ac_x1) + (ac_y2 - ac_y1) * (ac_y2 - ac_y1)

    v = (4 / (math.pi * math.pi)) * (math.atan(boxa_w / boxa_h) - math.atan(boxb_w / boxb_h)) \
        * (math.atan(boxa_w / boxa_h) - math.atan(boxb_w / boxb_h))
    iou = inter_area / (union_area + 1e-8)
    alpha = v / ((1 - iou) + v)
    ciou = iou + length_box_ctr / length_ac + alpha * v
    return ciou

# 除了alpha-iou,还有alpha-giou, alpha-diou, alpha-ciou,这里就不写了。
# alpha-iou的优点是,例如alpha取2,当iou大于0.5的时候,loss的梯度是大于1的,
# 相比iou的loss一直等于-1,收敛的更快,map0.7/map0.9有提升效果。
def AlphaIoU(boxa, boxb, alpha):
    """
    loss = (1 - iou^alpha)   alpha>0,取3效果比较好
    """
    inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
    inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
    inter_h = max(0, inter_y2 - inter_y1)
    inter_w = max(0, inter_x2 - inter_x1)
    inter_area = inter_w * inter_h
    union_area = ((boxa[3] - boxa[1]) * (boxa[2] - boxa[0])) + \
                 ((boxb[3] - boxb[1]) * (boxb[2] - boxb[0])) - inter_area + 1e-8  # + 1e-8 防止除零
    iou = inter_area / union_area
    alpha_iou = (1 - math.pow(iou, alpha))
    return alpha_iou


if __name__ == '__main__':
    # box1 area=4, box2 area=4,inter area=1, union area=7, ac area=9, iou=1/7
    box1, box2 = [0, 0, 2, 2], [1, 1, 3, 3]
    # box1, box2 = [1, 0, 3, 2], [0, 1, 2, 3]

    # 下面我把并集中1e-8省略了,所以会有略微差距。
    print(IoU(box1, box2))
    print(1 / 7)
    # 0.14285714265306124
    # 0.14285714285714285

    print(GIoU(box1, box2))
    print(1 / (7) - (9 - 7) / 9)
    # -0.07936507845804988
    # -0.07936507936507936

    print(DIoU(box1, box2))
    print(1 / 7 - (1 * 1) / (3 * 3))
    # 0.03174603154195013
    # 0.031746031746031744

    print(CIoU(box1, box2))
    v = 4 / (math.pi * math.pi) * ((math.atan(2 / 2) - math.atan(2 / 2)) * (math.atan(2 / 2) - math.atan(2 / 2)))
    print(1 / 7 + (1 * 1) / (3 * 3) + v / ((1 - 1 / 7) + v) * v)
    # 0.2539682535600907
    # 0.25396825396825395

    print(AlphaIoU(box1, box2, 1))  # 求的是loss,所以跟第一个iou()值不一样
    print(1 - 1 / 7)
    # 0.8571428573469387
    # 0.8571428571428572
    print(AlphaIoU(box1, box2, 3))  # 求的是loss,所以跟第一个iou()值不一样
    print(1 - math.pow((1 / 7), 3))
    # 0.9970845481174511
    # 0.9970845481049563

标签:GIoU,box,ac,area,IoU,boxa,boxb,DIoU,inter
来源: https://www.cnblogs.com/gy77/p/16199305.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有