Loading

『代码』手写IOU计算

计算2d iou

来自OpenPCDet的eval.py。关键就在于:重叠部分的宽度 = 两个图像右边缘的较小值 - 两个图像左边缘的较大值,重叠部分的高度 = 两个图像下边缘的较小值 - 两个图像上边缘的较大值U。右小减左大,下小减上大。然后IoU等于交除以并减交

点击查看代码
@numba.jit(nopython=True)
def image_box_overlap(boxes, query_boxes, criterion=-1):
    """
    计算图像box的iou
    Args:
        boxes:一个part中的全部gt,以第一个part为例(642,4)
        query_boxes:一个part中的全部dt,以第一个part为例(233,4)
    """
    N = boxes.shape[0] # gt_box的总数
    K = query_boxes.shape[0] # det_box的总数
    # 初始化overlap矩阵
    overlaps = np.zeros((N, K), dtype=boxes.dtype)
    # 两层for循环逐个box计算iou,因为有jit加速,所以成for循环的形式
    for k in range(K):
        # 计算第k个dt box的面积(box是左上角和右下角的形式[x1,y1,x2,y2])
        qbox_area = ((query_boxes[k, 2] - query_boxes[k, 0]) *
                     (query_boxes[k, 3] - query_boxes[k, 1]))
        for n in range(N): # 遍历gt boxes
            # 重叠部分的宽度 = 两个图像右边缘的较小值 - 两个图像左边缘的较大值
            iw = (min(boxes[n, 2], query_boxes[k, 2]) -
                  max(boxes[n, 0], query_boxes[k, 0]))
            if iw > 0: # 如果宽度方向有重叠,再计算高度
                # 重叠部分的高度 = 两个图像下边缘的较小值 - 两个图像上边缘的较大值
                ih = (min(boxes[n, 3], query_boxes[k, 3]) -
                      max(boxes[n, 1], query_boxes[k, 1]))
                if ih > 0:
                    if criterion == -1: # 默认执行criterion = -1
                        # 求两个box的并
                        ua = (
                            (boxes[n, 2] - boxes[n, 0]) *
                            (boxes[n, 3] - boxes[n, 1]) + qbox_area - iw * ih)
                    elif criterion == 0:
                        ua = ((boxes[n, 2] - boxes[n, 0]) *
                              (boxes[n, 3] - boxes[n, 1]))
                    elif criterion == 1:
                        ua = qbox_area
                    else:
                        ua = 1.0
                    
                    overlaps[n, k] = iw * ih / ua
    return overlaps

计算3d iou

首先,结果可以拆分成2d bev下带rotation的IoU与height交叉高度相乘。于是,问题的关键其实在于2d rotated iou这件事。计算2d rotated iou:
1. 根据box信息计算corners(基于box center,先得到local的角点坐标,并将corners编码为顺时针顺序,然后旋转加平移得到绝对坐标)
2. 求两box范围的所有交点,于是得到polygon。交点虽然不规则,但总不会超过八个点,于是可以预先分配buffer。分为a. 在对方box内的点 b. 边与边的交叉点
3. 根据polygon中心点对角点进行排序,使其满足逆时针方向
4. 计算多边形面积。采用累计计算三角形面积(利用叉乘)的方式(由于夹角逐渐增大,与第一个点构成的三角形面积一定是在同侧的符号相同)

计算box的3d iou

d3_box_overlap()开始,其包括: rotate_iou_gpu_eval() 计算bev iou + d3_box_overlap_kernel() 结合bev iou计算3d iou

点击查看代码
def d3_box_overlap(boxes, qboxes, criterion=-1):
    """
    Args:
        boxes:一个part中的全部gt,以第一个part为例(642,7) [x,y,z,dx,dy,dz,alpha]
        query_boxes:一个part中的全部dt,以第一个part为例(233,7)
        rinc:在鸟瞰图视角下的iou(642,233)
    Returns:
        返回3D iou-->rinc
    """
    rinc = rotate_iou_gpu_eval(boxes[:, [0, 2, 3, 5, 6]],
                               qboxes[:, [0, 2, 3, 5, 6]], 2)
    d3_box_overlap_kernel(boxes, qboxes, rinc, criterion)
    return rinc
点击查看代码
@numba.jit(nopython=True, parallel=True)
def d3_box_overlap_kernel(boxes, qboxes, rinc, criterion=-1):
    """
    计算box的3D iou
    Args:
        boxes:一个part中的全部gt,以第一个part为例(642,7) [x,y,z,dx,dy,dz,alpha]
        query_boxes:一个part中的全部dt,以第一个part为例(233,7)
        rinc:在鸟瞰图视角下的iou(642,233)
    Returns:
        返回3D iou-->rinc
    """
    # ONLY support overlap in CAMERA, not lidar.
    # 在相机坐标系下进行计算,z轴朝前,y轴朝下,x轴朝右,因此,俯视图取x和z
    N, K = boxes.shape[0], qboxes.shape[0]
    # 遍历gt
    for i in range(N):
        # 遍历dt
        for j in range(K):
            # 如果鸟瞰视角存在重叠
            if rinc[i, j] > 0:
                # iw = (min(boxes[i, 1] + boxes[i, 4], qboxes[j, 1] +
                #         qboxes[j, 4]) - max(boxes[i, 1], qboxes[j, 1]))
                # 这里的1是y轴,在相机坐标系下就是高度方向,重叠高度=上边缘的最小值-下边缘的最大值
                iw = (min(boxes[i, 1], qboxes[j, 1]) - max(
                    boxes[i, 1] - boxes[i, 4], qboxes[j, 1] - qboxes[j, 4]))
                # 如果重叠高度 > 0
                if iw > 0:
                    # 1.求两个box的体积
                    area1 = boxes[i, 3] * boxes[i, 4] * boxes[i, 5]
                    area2 = qboxes[j, 3] * qboxes[j, 4] * qboxes[j, 5]
                    # 2.求交集体积
                    inc = iw * rinc[i, j]
                    # 3.根据criterion,计算并集体积
                    if criterion == -1:
                        ua = (area1 + area2 - inc)
                    elif criterion == 0:
                        ua = area1
                    elif criterion == 1:
                        ua = area2
                    else:
                        ua = inc
                    # 4.计算交并比
                    rinc[i, j] = inc / ua
                else:
                    rinc[i, j] = 0.0

计算2d bev iou

rotate_iou_gpu_eval()rotate_iou_kernel_eval()devRotateIoUEval()

@cuda.jit('(float32[:], float32[:], int32)', device=True, inline=True)
def devRotateIoUEval(rbox1, rbox2, criterion=-1):
    # 1.计算两个box的面积
    area1 = rbox1[2] * rbox1[3]
    area2 = rbox2[2] * rbox2[3]
    # 2.求两个box的交集
    area_inter = inter(rbox1, rbox2)
    # 3.求交并比
    if criterion == -1:
        return area_inter / (area1 + area2 - area_inter)
    elif criterion == 0:
        return area_inter / area1
    elif criterion == 1:
        return area_inter / area2
    else:
        return area_inter

开始计算交集

点击查看代码
@cuda.jit('(float32[:], float32[:])', device=True, inline=True)
def inter(rbbox1, rbbox2):
    # 0.初始化
    # 在cuda上开辟局部内存,存储角点信息
    corners1 = cuda.local.array((8, ), dtype=numba.float32)
    corners2 = cuda.local.array((8, ), dtype=numba.float32)
    # 由于交集部分不规则,但是最多8个点,共16个坐标
    intersection_corners = cuda.local.array((16, ), dtype=numba.float32)
    # 1.求两个box的四个角点坐标,先平移,然后旋转
    rbbox_to_corners(corners1, rbbox1)
    rbbox_to_corners(corners2, rbbox2)
    # 2.求box的所有交点
    num_intersection = quadrilateral_intersection(corners1, corners2,
                                                  intersection_corners)
    # 3.根据对交集多边形中心点对其角点进行排序,使其满足逆时针方向
    sort_vertex_in_convex_polygon(intersection_corners, num_intersection)
    # print(intersection_corners.reshape([-1, 2])[:num_intersection])
    # 4.根据叉乘几何意义,求取交集多边形面积(由于夹角逐渐增大,与第一个点构成的三角形面积一定是在同侧的符号相同)
    return area(intersection_corners, num_intersection)

1. 计算角点坐标

@cuda.jit('(float32[:], float32[:])', device=True, inline=True)
def rbbox_to_corners(corners, rbbox):
    # generate clockwise corners and rotate it clockwise
    # 将中心点加宽高的形式和角度的形式转换为角点形式(x1,y1,x2,y2,x3,y3,x4,y4)
    # 1.初始化
    angle = rbbox[4]
    a_cos = math.cos(angle)
    a_sin = math.sin(angle)
    center_x = rbbox[0]
    center_y = rbbox[1]
    x_d = rbbox[2]
    y_d = rbbox[3]
    corners_x = cuda.local.array((4, ), dtype=numba.float32)
    corners_y = cuda.local.array((4, ), dtype=numba.float32)
    # 2.计算在坐标原点的box模板
    corners_x[0] = -x_d / 2
    corners_x[1] = -x_d / 2
    corners_x[2] = x_d / 2
    corners_x[3] = x_d / 2
    corners_y[0] = -y_d / 2
    corners_y[1] = y_d / 2
    corners_y[2] = y_d / 2
    corners_y[3] = -y_d / 2
    # 3.然后旋转和平移,计算4个角点
    for i in range(4):
        corners[2 *
                i] = a_cos * corners_x[i] + a_sin * corners_y[i] + center_x
        corners[2 * i
                + 1] = -a_sin * corners_x[i] + a_cos * corners_y[i] + center_y

2. 计算交集多边形:在框内的点 + 边与边的交点

@cuda.jit('(float32[:], float32[:], float32[:])', device=True, inline=True)
def quadrilateral_intersection(pts1, pts2, int_pts):
    """
    计算两个box交集的角点坐标
    Args:
        pts1:(8,)
        pts2:(8,)
    Returns:
        int_pts:(16,)
    """
    num_of_inter = 0
    # 计算交集的角点
    for i in range(4):
        # 分别检查四个角点在对方box内
        if point_in_quadrilateral(pts1[2 * i], pts1[2 * i + 1], pts2):
            int_pts[num_of_inter * 2] = pts1[2 * i]
            int_pts[num_of_inter * 2 + 1] = pts1[2 * i + 1]
            num_of_inter += 1
        if point_in_quadrilateral(pts2[2 * i], pts2[2 * i + 1], pts1):
            int_pts[num_of_inter * 2] = pts2[2 * i]
            int_pts[num_of_inter * 2 + 1] = pts2[2 * i + 1]
            num_of_inter += 1
    temp_pts = cuda.local.array((2, ), dtype=numba.float32)
    
    for i in range(4):
        for j in range(4):
            # 求交点坐标
            has_pts = line_segment_intersection(pts1, pts2, i, j, temp_pts)
            if has_pts:
                int_pts[num_of_inter * 2] = temp_pts[0]
                int_pts[num_of_inter * 2 + 1] = temp_pts[1]
                num_of_inter += 1

    return num_of_inter

3. 排序多边形角点

@cuda.jit('(float32[:], int32)', device=True, inline=True)
def sort_vertex_in_convex_polygon(int_pts, num_of_inter):
    if num_of_inter > 0:
        center = cuda.local.array((2, ), dtype=numba.float32)
        center[:] = 0.0
        # 1.计算多边形的平均中心点
        for i in range(num_of_inter):
            center[0] += int_pts[2 * i]
            center[1] += int_pts[2 * i + 1]
        center[0] /= num_of_inter
        center[1] /= num_of_inter
        # 2.对多边形的点排序
        v = cuda.local.array((2, ), dtype=numba.float32)
        vs = cuda.local.array((16, ), dtype=numba.float32)
        for i in range(num_of_inter):
            v[0] = int_pts[2 * i] - center[0]
            v[1] = int_pts[2 * i + 1] - center[1]
            d = math.sqrt(v[0] * v[0] + v[1] * v[1])
            v[0] = v[0] / d
            v[1] = v[1] / d
            if v[1] < 0:
                v[0] = -2 - v[0]
            vs[i] = v[0]
        j = 0
        temp = 0
        for i in range(1, num_of_inter):
            if vs[i - 1] > vs[i]:
                temp = vs[i]
                tx = int_pts[2 * i]
                ty = int_pts[2 * i + 1]
                j = i
                while j > 0 and vs[j - 1] > temp:
                    vs[j] = vs[j - 1]
                    int_pts[j * 2] = int_pts[j * 2 - 2]
                    int_pts[j * 2 + 1] = int_pts[j * 2 - 1]
                    j -= 1

                vs[j] = temp
                int_pts[j * 2] = tx
                int_pts[j * 2 + 1] = ty

4. 计算多边形面积

@cuda.jit('(float32[:], int32)', device=True, inline=True)
def area(int_pts, num_of_inter):
    area_val = 0.0
    # 逐多边形点遍历,求解面积
    for i in range(num_of_inter - 2):
        area_val += abs(
            trangle_area(int_pts[:2], int_pts[2 * i + 2:2 * i + 4],
                         int_pts[2 * i + 4:2 * i + 6]))
    return area_val
@cuda.jit('(float32[:], float32[:], float32[:])', device=True, inline=True)
def trangle_area(a, b, c):
    """
    计算三角形面积(叉乘法)
       /a
     c/___b    
    """
    return ((a[0] - c[0]) * (b[1] - c[1]) - (a[1] - c[1]) *(b[0] - c[0])) / 2.0
posted @ 2022-06-25 22:10  traviscui  阅读(158)  评论(0)    收藏  举报