Esempio n. 1
0
def np_box_3d_to_box_8co(box_3d):
    """Computes the 3D bounding box corner positions from Box3D format.

    The order of corners are preserved during this conversion.

    Args:
        box_3d: 1 x 7 ndarray of box_3d in the format
            [x, y, z, l, w, h, ry]
    Returns:
        corners_3d: An ndarray or a tensor of shape (3 x 8) representing
            the box as corners in the following format ->
            [[x1,...,x8], [y1...,y8], [z1,...,z8]].
    """

    format_checker.check_box_3d_format(box_3d)

    ry = box_3d[6]
    # Compute transform matrix
    # This includes rotation and translation
    rot = np.array([[np.cos(ry), 0, np.sin(ry), box_3d[0]],
                    [0, 1, 0, box_3d[1]],
                    [-np.sin(ry), 0, np.cos(ry), box_3d[2]]])

    length = box_3d[3]
    width = box_3d[4]
    height = box_3d[5]

    # 3D BB corners
    x_corners = np.array([length / 2, length / 2,
                          -length / 2, -length / 2,
                          length / 2, length / 2,
                          -length / 2, -length / 2])

    y_corners = np.array([0.0, 0.0, 0.0, 0.0,
                          -height, -height, -height, -height])

    z_corners = np.array([width / 2, -width / 2,
                          -width / 2, width / 2,
                          width / 2, -width / 2,
                          -width / 2, width / 2])

    # Create a ones column
    ones_col = np.ones(x_corners.shape)

    # Append the column of ones to be able to multiply
    box_8c = np.dot(rot, np.array([x_corners,
                                   y_corners,
                                   z_corners,
                                   ones_col]))
    # Ignore the fourth column
    box_8c = box_8c[0:3]

    return box_8c
Esempio n. 2
0
    def test_check_box_3d_format(self):

        # Case 1, invalid type
        test_var = [0, 0, 0, 0, 0, 0, 0]
        np.testing.assert_raises(TypeError, fc.check_box_3d_format, test_var)

        # Case 2, invalid shape
        test_var = np.ones([1, 5])
        np.testing.assert_raises(TypeError, fc.check_box_3d_format, test_var)

        test_var = np.ones([5, 6])
        np.testing.assert_raises(TypeError, fc.check_box_3d_format, test_var)

        test_var = np.ones([1, 7])
        fc.check_box_3d_format(test_var)

        test_var = np.ones([10, 7])
        fc.check_box_3d_format(test_var)

        test_var = tf.ones([5, 7])
        fc.check_box_3d_format(test_var)

        test_var = tf.ones([5, 3])
        np.testing.assert_raises(TypeError, fc.check_box_3d_format, test_var)
Esempio n. 3
0
def tf_box_3d_to_box_4c(boxes_3d, ground_plane):
    """Vectorized conversion of box_3d to box_4c tensors

    Args:
        boxes_3d: Tensor of boxes_3d (N, 7)
        ground_plane: Tensor ground plane coefficients (4,)

    Returns:
        Tensor of boxes_4c (N, 10)
    """
    format_checker.check_box_3d_format(boxes_3d)

    anchors = box_3d_encoder.tf_box_3d_to_anchor(boxes_3d)

    centroid_x = anchors[:, 0]
    centroid_y = anchors[:, 1]
    centroid_z = anchors[:, 2]
    dim_x = anchors[:, 3]
    dim_y = anchors[:, 4]
    dim_z = anchors[:, 5]

    # Create temporary box at (0, 0) for rotation
    half_dim_x = dim_x / 2
    half_dim_z = dim_z / 2

    # Box corners
    x_corners = tf.stack([half_dim_x, half_dim_x,
                          -half_dim_x, -half_dim_x], axis=1)

    z_corners = tf.stack([half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z], axis=1)

    # Rotations from boxes_3d
    all_rys = boxes_3d[:, 6]

    # Find nearest 90 degree
    half_pi = np.pi / 2
    ortho_rys = tf.round(all_rys / half_pi) * half_pi

    # Get rys and 0/1 padding
    ry_diffs = all_rys - ortho_rys
    zeros = tf.zeros_like(ry_diffs, dtype=tf.float32)
    ones = tf.ones_like(ry_diffs, dtype=tf.float32)

    # Create transformation matrix, including rotation and translation
    tr_mat = tf.stack(
        [tf.stack([tf.cos(ry_diffs), tf.sin(ry_diffs), centroid_x], axis=1),
         tf.stack([-tf.sin(ry_diffs), tf.cos(ry_diffs), centroid_z], axis=1),
         tf.stack([zeros, zeros, ones], axis=1)],
        axis=2)

    # Create a ones row
    ones_row = tf.ones_like(x_corners)

    # Append the column of ones to be able to multiply
    points_stacked = tf.stack([x_corners, z_corners, ones_row], axis=1)
    corners = tf.matmul(tr_mat, points_stacked,
                        transpose_a=True,
                        transpose_b=False)

    # Discard the last row (ones)
    corners = corners[:, 0:2]
    flat_corners = tf.reshape(corners, [-1, 8])

    # Get ground plane coefficients
    a = ground_plane[0]
    b = ground_plane[1]
    c = ground_plane[2]
    d = ground_plane[3]

    # Calculate heights off ground plane
    ground_y = -(a * centroid_x + c * centroid_z + d) / b
    h1 = ground_y - centroid_y
    h2 = h1 + dim_y

    batched_h1 = tf.reshape(h1, [-1, 1])
    batched_h2 = tf.reshape(h2, [-1, 1])

    # Stack into (?, 10)
    box_4c = tf.concat([flat_corners, batched_h1, batched_h2], axis=1)
    return box_4c
Esempio n. 4
0
def np_box_3d_to_box_4c(box_3d, ground_plane):
    """Converts a single box_3d to box_4c

    Args:
        box_3d: box_3d (6,)
        ground_plane: ground plane coefficients (4,)

    Returns:
        box_4c (10,)
    """
    format_checker.check_box_3d_format(box_3d)

    anchor = box_3d_encoder.box_3d_to_anchor(box_3d, ortho_rotate=True)[0]

    centroid_x = anchor[0]
    centroid_y = anchor[1]
    centroid_z = anchor[2]
    dim_x = anchor[3]
    dim_y = anchor[4]
    dim_z = anchor[5]

    # Create temporary box at (0, 0) for rotation
    half_dim_x = dim_x / 2
    half_dim_z = dim_z / 2

    # Box corners
    x_corners = np.asarray([half_dim_x, half_dim_x,
                            -half_dim_x, -half_dim_x])

    z_corners = np.array([half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z])

    ry = box_3d[6]

    # Find nearest 90 degree
    half_pi = np.pi / 2
    ortho_ry = np.round(ry / half_pi) * half_pi

    # Find rotation to make the box ortho aligned
    ry_diff = ry - ortho_ry

    # Create transformation matrix, including rotation and translation
    tr_mat = np.array([[np.cos(ry_diff), np.sin(ry_diff), centroid_x],
                       [-np.sin(ry_diff), np.cos(ry_diff), centroid_z],
                       [0, 0, 1]])

    # Create a ones row
    ones_row = np.ones(x_corners.shape)

    # Append the column of ones to be able to multiply
    points_stacked = np.vstack([x_corners, z_corners, ones_row])
    corners = np.matmul(tr_mat, points_stacked)

    # Discard the last row (ones)
    corners = corners[0:2]

    # Calculate height off ground plane
    ground_y = geometry_utils.calculate_plane_point(
        ground_plane, [centroid_x, None, centroid_z])[1]
    h1 = ground_y - centroid_y
    h2 = h1 + dim_y

    # Stack into (10,) ndarray
    box_4c = np.hstack([corners.flatten(), h1, h2])
    return box_4c
Esempio n. 5
0
def tf_box_3d_to_box_8co(boxes_3d):
    """Computes the 3D bounding box corner positions from Box3D format.

    The order of corners are preserved during this conversion.

    Args:
        boxes_3d: N x 7 tensor of box_3d in the format
            [x, y, z, l, w, h, ry]
    Returns:
        corners_3d: An ndarray or a tensor of shape (N x 3 x 8) representing
            the box as corners in following format -> [[[x1,...,x8],[y1...,y8],
            [z1,...,z8]]].
    """

    format_checker.check_box_3d_format(boxes_3d)

    all_rys = boxes_3d[:, 6]
    ry_sin = tf.sin(all_rys)
    ry_cos = tf.cos(all_rys)

    zeros = tf.zeros_like(all_rys, dtype=tf.float32)
    ones = tf.ones_like(all_rys, dtype=tf.float32)

    # Rotation matrix
    rot_mats = tf.stack([tf.stack([ry_cos, zeros, ry_sin], axis=1),
                         tf.stack([zeros, ones, zeros], axis=1),
                         tf.stack([-ry_sin, zeros, ry_cos], axis=1)],
                        axis=2)

    length = boxes_3d[:, 3]
    width = boxes_3d[:, 4]
    height = boxes_3d[:, 5]

    half_length = length / 2
    half_width = width / 2

    x_corners = tf.stack([half_length, half_length,
                          -half_length, -half_length,
                          half_length, half_length,
                          -half_length, -half_length], axis=1)

    y_corners = tf.stack([zeros, zeros, zeros, zeros,
                          -height, -height, -height, -height], axis=1)

    z_corners = tf.stack([half_width, -half_width,
                          -half_width, half_width,
                          half_width, -half_width,
                          -half_width, half_width], axis=1)

    corners = tf.stack([x_corners,
                        y_corners,
                        z_corners], axis=1)

    boxes_8c = tf.matmul(rot_mats, corners,
                         transpose_a=True,
                         transpose_b=False)

    # Translate the corners
    corners_3d_x = boxes_8c[:, 0] + tf.reshape(boxes_3d[:, 0], (-1, 1))
    corners_3d_y = boxes_8c[:, 1] + tf.reshape(boxes_3d[:, 1], (-1, 1))
    corners_3d_z = boxes_8c[:, 2] + tf.reshape(boxes_3d[:, 2], (-1, 1))

    boxes_8c = tf.stack([corners_3d_x,
                         corners_3d_y,
                         corners_3d_z], axis=1)

    return boxes_8c
Esempio n. 6
0
def tf_box_3d_to_box_8c(boxes_3d):
    """Computes the 3D bounding box corner positions from box_3d format.

    This function does not preserve corners order during conversion from
    box_3d -> box_8c. Instead of using the box_3d's orientation, 'ry',
    nearest 90 degree angle is selected to create an axis-aligned box.
    This helps in calculating the closest corner to corner when comparing
    the corners to the ground-truth boxes.

    Args:
        boxes_3d: N x 7 tensor of box_3d in the format
            [x, y, z, l, w, h, ry]
    Returns:
        corners_3d: A tensor of shape (N x 3 x 8) representing
            the box as corners in following format -> [[[x1,...,x8],[y1...,y8],
            [z1,...,z8]]].
    """

    format_checker.check_box_3d_format(boxes_3d)
    anchors = box_3d_encoder.tf_box_3d_to_anchor(boxes_3d)

    centroid_x = anchors[:, 0]
    centroid_y = anchors[:, 1]
    centroid_z = anchors[:, 2]
    dim_x = anchors[:, 3]
    dim_y = anchors[:, 4]
    dim_z = anchors[:, 5]

    all_rys = boxes_3d[:, 6]

    # Find nearest 90 degree
    half_pi = np.pi / 2
    ortho_rys = tf.round(all_rys / half_pi) * half_pi

    ry_diff = all_rys - ortho_rys

    ry_sin = tf.sin(ry_diff)
    ry_cos = tf.cos(ry_diff)

    zeros = tf.zeros_like(ry_diff, dtype=tf.float32)
    ones = tf.ones_like(ry_diff, dtype=tf.float32)

    # Rotation matrix
    rot_mats = tf.stack([tf.stack([ry_cos, zeros, ry_sin], axis=1),
                         tf.stack([zeros, ones, zeros], axis=1),
                         tf.stack([-ry_sin, zeros, ry_cos], axis=1)],
                        axis=2)

    half_dim_x = dim_x / 2
    half_dim_z = dim_z / 2

    x_corners = tf.stack([half_dim_x, half_dim_x,
                          -half_dim_x, -half_dim_x,
                          half_dim_x, half_dim_x,
                          -half_dim_x, -half_dim_x], axis=1)

    y_corners = tf.stack([zeros, zeros, zeros, zeros,
                          -dim_y, -dim_y, -dim_y, -dim_y], axis=1)

    z_corners = tf.stack([half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z,
                          half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z], axis=1)

    corners = tf.stack([x_corners,
                        y_corners,
                        z_corners], axis=1)

    boxes_8c = tf.matmul(rot_mats, corners,
                         transpose_a=True,
                         transpose_b=False)

    # Translate the corners
    corners_3d_x = boxes_8c[:, 0] + tf.reshape(centroid_x, (-1, 1))
    corners_3d_y = boxes_8c[:, 1] + tf.reshape(centroid_y, (-1, 1))
    corners_3d_z = boxes_8c[:, 2] + tf.reshape(centroid_z, (-1, 1))

    boxes_8c = tf.stack([corners_3d_x,
                         corners_3d_y,
                         corners_3d_z], axis=1)

    return boxes_8c
Esempio n. 7
0
def np_box_3d_to_box_8c(box_3d):
    """Computes the 3D bounding box corner positions from box_3d format.

    This function does not preserve corners order but rather the corners
    are rotated to the nearest 90 degree angle. This helps in calculating
    the closest corner to corner when comparing the corners to the ground-
    truth boxes.

    Args:
        box_3d: ndarray of size (7,) representing box_3d in the format
            [x, y, z, l, w, h, ry]
    Returns:
        corners_3d: An ndarray or a tensor of shape (3 x 8) representing
            the box as corners in following format -> [[x1,...,x8],[y1...,y8],
            [z1,...,z8]].
    """

    format_checker.check_box_3d_format(box_3d)

    # This function is vectorized and returns an ndarray
    anchor = box_3d_encoder.box_3d_to_anchor(box_3d, ortho_rotate=True)[0]

    centroid_x = anchor[0]
    centroid_y = anchor[1]
    centroid_z = anchor[2]
    dim_x = anchor[3]
    dim_y = anchor[4]
    dim_z = anchor[5]

    half_dim_x = dim_x / 2
    half_dim_z = dim_z / 2

    # 3D BB corners
    x_corners = np.array([half_dim_x, half_dim_x,
                          -half_dim_x, -half_dim_x,
                          half_dim_x, half_dim_x,
                          -half_dim_x, -half_dim_x])

    y_corners = np.array([0.0, 0.0, 0.0, 0.0,
                          -dim_y, -dim_y, -dim_y, -dim_y])

    z_corners = np.array([half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z,
                          half_dim_z, -half_dim_z,
                          -half_dim_z, half_dim_z])

    ry = box_3d[6]

    # Find nearest 90 degree
    half_pi = np.pi / 2
    ortho_ry = np.round(ry / half_pi) * half_pi

    # Find rotation to make the box ortho aligned
    ry_diff = ry - ortho_ry

    # Compute transform matrix
    # This includes rotation and translation
    rot = np.array([[np.cos(ry_diff), 0, np.sin(ry_diff), centroid_x],
                    [0, 1, 0, centroid_y],
                    [-np.sin(ry_diff), 0, np.cos(ry_diff), centroid_z]])

    # Create a ones column
    ones_col = np.ones(x_corners.shape)

    # Append the column of ones to be able to multiply
    box_8c = np.dot(rot, np.array([x_corners,
                                   y_corners,
                                   z_corners,
                                   ones_col]))
    # Ignore the fourth column
    box_8c = box_8c[0:3]

    return box_8c
Esempio n. 8
0
def project_to_image_space(box_3d, calib_p2,
                           truncate=False, image_size=None,
                           discard_before_truncation=True, distortion=None):
    """ Projects a box_3d into image space

    Args:
        box_3d: single box_3d to project
        calib_p2: stereo calibration p2 matrix
        truncate: if True, 2D projections are truncated to be inside the image
        image_size: [w, h] must be provided if truncate is True,
            used for truncation
        discard_before_truncation: If True, discard boxes that are larger than
            80% of the image in width OR height BEFORE truncation. If False,
            discard boxes that are larger than 80% of the width AND
            height AFTER truncation.

    Returns:
        Projected box in image space [x1, y1, x2, y2]
            Returns None if box is not inside the image
    """

    format_checker.check_box_3d_format(box_3d)

    obj_label = box_3d_encoder.box_3d_to_object_label(box_3d)
    corners_3d = obj_panoptic_utils.compute_box_corners_3d(obj_label)

    projected = calib_panoptic_utils.project_to_image(corners_3d, calib_p2, dist=distortion)

    x1 = np.amin(projected[0])
    y1 = np.amin(projected[1])
    x2 = np.amax(projected[0])
    y2 = np.amax(projected[1])

    img_box = np.array([x1, y1, x2, y2])
    # print('img_box = ', img_box)

    if truncate:
        if not image_size:
            raise ValueError('Image size must be provided')

        image_w = image_size[0]
        image_h = image_size[1]

        # Discard invalid boxes (outside image space)
        # Watch this code carefully. It is NOT deleting the boxes larger than the image.
        # It is just deleting invalid boxes!
        if img_box[0] > image_w or \
                img_box[1] > image_h or \
                img_box[2] < 0 or \
                img_box[3] < 0:
            print('Discard invalid boxes (outside image space)')
            return None

        # Discard boxes that are larger than 80% of the image width OR height
        # (This part does not work well with the Panoptic dataset! Apparenly,chances are very high that one box height is larger than 80% of the image height)
        # if discard_before_truncation:
        #     img_box_w = img_box[2] - img_box[0]
        #     img_box_h = img_box[3] - img_box[1]
        #     if img_box_w > (image_w * 0.8) or img_box_h > (image_h * 0.8):
        #         print('Discard boxes that are larger than 80% of the image width OR height')
        #         return None

        # Truncate remaining boxes into image space
        if img_box[0] < 0:
            img_box[0] = 0
        if img_box[1] < 0:
            img_box[1] = 0
        if img_box[2] > image_w:
            img_box[2] = image_w
        if img_box[3] > image_h:
            img_box[3] = image_h

        # Discard boxes that are covering the the whole image after truncation
        # (This part does not work well with the Panoptic dataset! Apparenly,chances are very high that one box height is larger than 80% of the image height)
        # if not discard_before_truncation:
        #     img_box_w = img_box[2] - img_box[0]
        #     img_box_h = img_box[3] - img_box[1]
        #     if img_box_w > (image_w * 0.8) and img_box_h > (image_h * 0.8):
        #         print('Discard boxes that are covering the the whole image after truncation')
        #         return None

    return img_box
Esempio n. 9
0
def project_to_bev(boxes_3d, bev_extents):
    """
    Projects an array of 3D boxes into bird's eye view

    Args:
        boxes_3d: list of 3d boxes in the format:
            N x [x, y, z, l, w, h, ry]
        bev_extents: xz extents of the 3d area
            [[min_x, max_x], [min_z, max_z]]

    Returns:
        box_points: counter-clockwise order box points in bev map space
            N x [[x0, y0], ... [x3, y3]] - (N x 4 x 2)
        box_points_norm: points normalized as a percentage of the map size
            N x [[x0, y0], ... [x3, y3]] - (N x 4 x 2)
    """

    format_checker.check_box_3d_format(boxes_3d)

    boxes_3d = np.array(boxes_3d, dtype=np.float32)
    x = boxes_3d[:, 0]
    z = boxes_3d[:, 2]
    l = boxes_3d[:, 3]
    w = boxes_3d[:, 4]
    ry = boxes_3d[:, 6]

    # 1|0 2D corners
    # 2|3
    l_2 = l / 2.0
    w_2 = w / 2.0

    p0 = np.array([l_2, w_2])
    p1 = np.array([-l_2, w_2])
    p2 = np.array([-l_2, -w_2])
    p3 = np.array([l_2, -w_2])

    box_points = np.empty((len(boxes_3d), 4, 2))

    for box_idx in range(len(boxes_3d)):
        rot = ry[box_idx]

        rot_mat = np.reshape([[np.cos(rot), np.sin(rot)],
                              [-np.sin(rot), np.cos(rot)]],
                             (2, 2))

        box_x = x[box_idx]
        box_z = z[box_idx]

        box_xz = [box_x, box_z]

        box_p0 = np.dot(rot_mat, p0[:, box_idx]) + box_xz
        box_p1 = np.dot(rot_mat, p1[:, box_idx]) + box_xz
        box_p2 = np.dot(rot_mat, p2[:, box_idx]) + box_xz
        box_p3 = np.dot(rot_mat, p3[:, box_idx]) + box_xz

        box_points[box_idx] = np.array([box_p0, box_p1, box_p2, box_p3])

    # Calculate normalized box corners for ROI pooling
    x_extents_min = bev_extents[0][0]
    z_extents_min = bev_extents[1][1]  # z axis is reversed
    points_shifted = box_points - [x_extents_min, z_extents_min]

    x_extents_range = bev_extents[0][1] - bev_extents[0][0]
    z_extents_range = bev_extents[1][0] - bev_extents[1][1]
    box_points_norm = points_shifted / [x_extents_range, z_extents_range]

    box_points = np.asarray(box_points, dtype=np.float32)
    box_points_norm = np.asarray(box_points_norm, dtype=np.float32)

    return box_points, box_points_norm
Esempio n. 10
0
def project_to_image_space(box_3d,
                           calib_p2,
                           truncate=False,
                           image_size=None,
                           discard_before_truncation=True):
    """ Projects a box_3d into image space

    Args:
        box_3d: single box_3d to project
        calib_p2: stereo calibration p2 matrix
        truncate: if True, 2D projections are truncated to be inside the image
        image_size: [w, h] must be provided if truncate is True,
            used for truncation
        discard_before_truncation: If True, discard boxes that are larger than
            80% of the image in width OR height BEFORE truncation. If False,
            discard boxes that are larger than 80% of the width AND
            height AFTER truncation.

    Returns:
        Projected box in image space [x1, y1, x2, y2]
            Returns None if box is not inside the image
    """

    format_checker.check_box_3d_format(box_3d)

    obj_label = box_3d_encoder.box_3d_to_object_label(box_3d)
    corners_3d = obj_utils.compute_box_corners_3d(obj_label)

    projected = calib_utils.project_to_image(corners_3d, calib_p2)

    x1 = np.amin(projected[0])
    y1 = np.amin(projected[1])
    x2 = np.amax(projected[0])
    y2 = np.amax(projected[1])

    img_box = np.array([x1, y1, x2, y2])

    if truncate:
        if not image_size:
            raise ValueError('Image size must be provided')

        image_w = image_size[0]
        image_h = image_size[1]

        # Discard invalid boxes (outside image space)
        if img_box[0] > image_w or \
                img_box[1] > image_h or \
                img_box[2] < 0 or \
                img_box[3] < 0:
            return None

        # Discard boxes that are larger than 80% of the image width OR height
        if discard_before_truncation:
            img_box_w = img_box[2] - img_box[0]
            img_box_h = img_box[3] - img_box[1]
            if img_box_w > (image_w * 0.8) or img_box_h > (image_h * 0.8):
                return None

        # Truncate remaining boxes into image space
        if img_box[0] < 0:
            img_box[0] = 0
        if img_box[1] < 0:
            img_box[1] = 0
        if img_box[2] > image_w:
            img_box[2] = image_w
        if img_box[3] > image_h:
            img_box[3] = image_h

        # Discard boxes that are covering the the whole image after truncation
        if not discard_before_truncation:
            img_box_w = img_box[2] - img_box[0]
            img_box_h = img_box[3] - img_box[1]
            if img_box_w > (image_w * 0.8) and img_box_h > (image_h * 0.8):
                return None

    return img_box