其他分享
首页 > 其他分享> > 一文读懂IoU,GIoU, DIoU, CIoU, Alpha-IoU (代码非常优雅)

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

作者:互联网

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