IoU、GIoU、DIoU、CIoU详解及python代码实现

一、IoU、GIoU、DIoU、CIoU详解:

(1)IoU

IoU 的全称为交并比(Intersection over Union),其计算是 “预测的边框” 和 “真实的边框” 的交集和并集的比值。计算过程可以由下图表示:
IoU、GIoU、DIoU、CIoU详解及python代码实现_第1张图片
由图可知IoU的值域为[0, 1]。
IoU的优点:
1、IOU可以作为损失函数,IoU loss=1-IOU。但是当两个物体不相交时无回传梯度。
2、 IOU对尺度变化具有不变性,即不受两个物体尺度大小的影响。
IoU的缺点:
1、当预测框和真实框不相交时,IoU(A,B)=0时,不能反映A,B距离的远近,此时损失函数不可导,IoU Loss 无法优化两个框不相交的情况。
2、假设预测框和真实框的大小都确定,只要两个框的相交值是确定的,其IoU值是相同时,IoU值不能反映两个框是如何相交的。

(2)GIoU

GIoU是为克服IoU的缺点同时充分利用优点而提出的,GIoU计算如下:
IoU、GIoU、DIoU、CIoU详解及python代码实现_第2张图片
上图C是包含A与B的最小框。
GIoU的值域为(-1, 1]。
GIoU的优点:
1、GIoU和IoU一样,可以作为损失函数,GIoU Loss = 1 - GIoU。
2、GIoU能够更好地反应相交情况。如下图,虽然两种情况下IOU一致,但是(a)中相交的更为整齐,因此GIOU要比(b)中大。
IoU、GIoU、DIoU、CIoU详解及python代码实现_第3张图片
GIoU缺点
当目标框完全包裹预测框的时候,IoU和GIoU的值都一样,此时GIoU退化为IoU, 无法区分其相对位置关系。
IoU、GIoU、DIoU、CIoU详解及python代码实现_第4张图片

(3)DIoU

基于GIoU的缺点,DIoU被提出。其论文为:Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression
论文给出DIoU Loss为:
在这里插入图片描述
IoU、GIoU、DIoU、CIoU详解及python代码实现_第5张图片
上图中绿色框为目标框,黑色框为预测框,灰色框为两者的最小外界矩形框,d表示目标框和真实框的中心点距离,c表示最小外界矩形框的距离。

DIoU的性质:
1、尺度不变性。
2、DIoU Loss可以直接优化2个框直接的距离,比GIoU Loss收敛速度更快。
3、对于目标框包裹预测框的这种情况,DIoU Loss可以收敛的很快,而GIoU Loss此时退化为IoU Loss收敛速度较慢

(4)CIoU

一个好的目标框回归损失应该考虑三个重要的几何因素:重叠面积、中心点距离、长宽比。
GIoU:为了归一化坐标尺度,利用IoU,并初步解决IoU为零的情况。
DIoU:DIoU损失同时考虑了边界框的重叠面积和中心点距离。
然而,anchor框和目标框之间的长宽比的一致性也是极其重要的。基于此,论文作者提出了Complete IoU Loss。
CIoU Loss又引入一个box长宽比的惩罚项,该Loss考虑了box的长宽比,定义如下:
IoU、GIoU、DIoU、CIoU详解及python代码实现_第6张图片
上述损失函数中,CIoU比DIoU多出了α和v这两个参数。其中α是用于平衡比例的参数。v用来衡量anchor框和目标框之间的比例一致性。
从α参数的定义可以看出,损失函数会更加倾向于往重叠区域增多方向优化,尤其是IoU为零的时候。

二、Python代码实现:

这里是实现IoU、GIoU、DIoU、CIoU并非实现其Loss。代码如下:
定义框BBox类:

import math
def euclidean_distance(p1, p2):
    '''
    计算两个点的欧式距离
    '''
    x1, y1 = p1
    x2, y2 = p2
    return math.sqrt((x2-x1)**2 + (y2-y1)**2)

class BBox:
    def __init__(self, x, y, r, b):
        '''
        定义框,左上角及右下角坐标
        '''
        self.x, self.y, self.r, self.b = x, y, r, b
    
    def __xor__(self, other):
        '''
        计算box和other的IoU
        '''
        cross = self & other
        union = self | other
        return cross / (union + 1e-6)
    
    def __or__(self, other):
        '''
        计算box和other的并集
        '''
        cross = self & other
        union = self.area + other.area - cross
        return union
    
    def __and__(self, other):
        '''
        计算box和other的交集
        '''
        xmax = min(self.r, other.r)
        ymax = min(self.b, other.b)
        xmin = max(self.x, other.x)
        ymin = max(self.y, other.y)
        cross_box = BBox(xmin, ymin, xmax, ymax)
        if cross_box.width <= 0 or cross_box.height <= 0:
            return 0
        return cross_box.area
    
    def boundof(self, other):
        '''
        计算box和other的边缘外包框,使得2个box都在框内的最小矩形
        '''
        xmin = min(self.x, other.x)
        ymin = min(self.y, other.y)
        xmax = max(self.r, other.r)
        ymax = max(self.b, other.b)
        return BBox(xmin, ymin, xmax, ymax)
    
    def center_distance(self, other):
        '''
        计算两个box的中心点距离
        '''
        return euclidean_distance(self.center, other.center)
    
    def bound_diagonal_distance(self, other):
        '''
        计算两个box的bound的对角线距离
        '''
        bound = self.boundof(other)
        return euclidean_distance((bound.x, bound.y), (bound.r, bound.b))
    
    @property
    def center(self):
        return (self.x + self.r) / 2, (self.y + self.b) / 2
    
    @property
    def area(self):
        return self.width * self.height
    
    @property
    def width(self):
        return self.r - self.x #+ 1
    
    @property
    def height(self):
        return self.b - self.y #+ 1

定义IoU

def IoU(a, b):
    return a ^ b

def GIoU(a, b):
    bound_area = a.boundof(b).area
    union_area = a | b
    return IoU(a, b) - (bound_area - union_area) / bound_area

def DIoU(a, b):
    d = a.center_distance(b)
    c = a.bound_diagonal_distance(b)
    return IoU(a, b) - (d ** 2) / (c ** 2)

def CIoU(a, b):
    v = 4 / (math.pi ** 2) * (math.atan(a.width / a.height) - math.atan(b.width / b.height)) ** 2
    iou = IoU(a, b)
    alpha = v / (1 - iou + v)
    return DIoU(a, b) - alpha * v

代码测试结果:

a = BBox(5, 5, 10, 10)
b = BBox(100, 100, 105, 105)
print("IoU:", IoU(a, b), "\nGIoU:", GIoU(a, b), "\nDIoU:", DIoU(a, b), "\nCIoU:", CIoU(a, b))

最终结果:
IoU: 0.0
GIoU: -0.995
DIoU: -0.9025
CIoU: -0.9025

你可能感兴趣的:(深度学习,机器学习,计算机视觉,python)