개발일지

8/7 - [25/10K] - bev_corners = box3d_to_corners(bboxes_3d) 분석

wandering developer 2024. 8. 8. 00:42

박스를 그리는 함수가 있는데 chat gpt에게 물어봐서 코드를 분석 했다.

        bev_corners = box3d_to_corners(bboxes_3d)[:, [0, 3, 4, 7]][
            ..., [0, 1]
        ]
        xs = bev_corners[..., 0] / bev_resolution + bev_w / 2
        ys = -bev_corners[..., 1] / bev_resolution + bev_h / 2
        for obj_idx, (x, y) in enumerate(zip(xs, ys)):
            for p1, p2 in ((0, 1), (0, 2), (1, 3), (2, 3)):
                if isinstance(color[0], (list, tuple)):
                    tmp = color[obj_idx]
                else:
                    tmp = color
                cv2.line(
                    bev,
                    (int(x[p1]), int(y[p1])),
                    (int(x[p2]), int(y[p2])),
                    tmp,
                    thickness=thickness,
                )

 

box3d_to_corners(bboxes_3d) 의 출력 크기는 (11, 8, 3)

bev_corners (11, 4, 2) 의 출력 크기를 가지게 됨. 그래서 박스를 그릴 수 있게 됨.

bev_corners = box3d_to_corners(bboxes_3d)[:, [0, 3, 4, 7]][..., [0, 1]]
  • box3d_to_corners(bboxes_3d):
    • 이 함수는 3D 박스를 입력받아, 각 박스의 8개 모서리의 좌표를 반환합니다.
    • 출력되는 결과는 [num_boxes, 8, 3] 형태의 배열입니다.
      • num_boxes는 박스의 수를 의미합니다.
      • 8은 각 박스의 8개의 코너 포인트를 나타냅니다.
      • 3은 각 코너 포인트의 3D 좌표 (x, y, z)를 나타냅니다.
  • [:, [0, 3, 4, 7]]:
    • 이 부분은 각 박스에서 특정 4개의 코너를 선택합니다.
    • 0, 3, 4, 7은 각 박스의 8개 코너 중에서 이 4개를 선택하는 것을 의미합니다.
    • 선택한 코너들은 보통 3D 박스의 바닥면을 구성하는 코너들입니다. (상하좌우 네 모서리)
  • [..., [0, 1]]:
    • 여기서 ...는 생략 연산자입니다. 이는 기존의 모든 차원을 유지하면서 특정 차원만 선택한다는 의미입니다.
    • [0, 1]은 3D 좌표에서 x와 y 축만 선택하여 2D 좌표로 변환합니다.
    • 결과적으로, 3D 공간에서 x, y 값만 남기고 z 좌표를 제외하여 2D 평면에서의 좌표로 변환하는 역할을 합니다.

여기서 입력으로 사용되는 "bboxes_3d" 는

  1. X (위치): 박스 중심의 X 좌표.
  2. Y (위치): 박스 중심의 Y 좌표.
  3. Z (위치): 박스 중심의 Z 좌표.
  4. W (너비): 박스의 너비 (width).
  5. L (길이): 박스의 길이 (length).
  6. H (높이): 박스의 높이 (height).
  7. YAW (회전 각도): 박스의 회전 각도. 보통 Y 축을 기준으로 한 회전 각도.
  8. SIN_YAW (옵션): 회전 각도의 사인 값 (선택적으로 포함될 수 있음).
  9. COS_YAW (옵션): 회전 각도의 코사인 값 (선택적으로 포함될 수 있음).
  10. VX (옵션): X 방향 속도 (선택적으로 포함될 수 있음).
  11. VY (옵션): Y 방향 속도 (선택적으로 포함될 수 있음).
  12. VZ (옵션): Z 방향 속도 (선택적으로 포함될 수 있음).

상수 정의

YAW = 6  # decoded
X, Y, Z, W, L, H, SIN_YAW, COS_YAW, VX, VY, VZ = list(range(11))  # undecoded
CNS, YNS = 0, 1  # centerness and yawness indices in qulity

박스 tensor 변환

if isinstance(box3d, torch.Tensor):
    box3d = box3d.detach().cpu().numpy()

모서리 좌표 변환

corners_norm = np.stack(np.unravel_index(np.arange(8), [2] * 3), axis=1)
corners_norm = corners_norm[[0, 1, 3, 2, 4, 5, 7, 6]]
# 원점 0.0으로 이동.
corners_norm = corners_norm - np.array([0.5, 0.5, 0.5])
# 11(박스개수)x1x3 * 1x8x3 = 11 x 8 x 3
corners = box3d[:, None, [W, L, H]] * corners_norm.reshape([1, 8, 3])
    # rotate around z axis
    rot_cos = np.cos(box3d[:, YAW])
    rot_sin = np.sin(box3d[:, YAW])
    rot_mat = np.tile(np.eye(3)[None], (box3d.shape[0], 1, 1))
    rot_mat[:, 0, 0] = rot_cos
    rot_mat[:, 0, 1] = -rot_sin
    rot_mat[:, 1, 0] = rot_sin
    rot_mat[:, 1, 1] = rot_cos
    corners = (rot_mat[:, None] @ corners[..., None]).squeeze(axis=-1)
    # 위치 이동
    corners += box3d[:, None, :3]

회전 행렬과 코너 좌표의 곱

np.eye(3)[None].shape
(1, 3, 3)
(box3d.shape[0], 1, 1)
(11, 1, 1)
rot_mat = np.tile(np.eye(3)[None], (box3d.shape[0], 1, 1))
rot_mat.shape
(11, 3, 3)

rot_mat[:, None].shape
(11, 1, 3, 3)
corners[..., None].shape
(11, 8, 3, 1)
(rot_mat[:, None] @ corners[..., None]).shape
(11, 8, 3, 1)
(rot_mat[:, None] * corners[..., None]).shape
(11, 8, 3, 3)

box3d[:, None, :3].shape
(11, 1, 3)

corners.shape
(11, 8, 3)

rot_mat[:, None]:

rot_mat의 형태는 (num_boxes, 3, 3)입니다. 이 형태에서 None을 추가하면 (num_boxes, 1, 3, 3)이 됩니다.
None을 추가함으로써 새로운 차원을 추가하고, 이를 통해 브로드캐스팅을 통해 다차원 곱셈이 가능합니다.
corners[..., None]:

corners의 형태는 (num_boxes, 8, 3)입니다.
None을 추가하면 (num_boxes, 8, 3, 1)이 됩니다.
...은 모든 차원을 포함하는 것을 의미합니다.
rot_mat[:, None] @ corners[..., None]:

이 연산은 rot_mat과 corners의 행렬 곱을 수행합니다.
rot_mat[:, None]의 형태가 (num_boxes, 1, 3, 3)이고, corners[..., None]의 형태가 (num_boxes, 8, 3, 1)이므로, 결과는 (num_boxes, 8, 3, 1)입니다.
결과적으로 각 박스에 대해 (8, 3) 형태의 코너 좌표가 회전 행렬에 의해 변환됩니다.

위치 이동

box3d[:, None, :3]:

box3d의 형태는 (num_boxes, 10)입니다.
box3d[:, None, :3]의 형태는 (num_boxes, 1, 3)입니다.
:3은 박스의 중심 좌표 (X, Y, Z)를 의미합니다.
corners += box3d[:, None, :3]:

이 연산은 각 박스의 중심 좌표를 회전된 코너 좌표에 더합니다.
브로드캐스팅에 의해 box3d[:, None, :3]는 (num_boxes, 8, 3) 형태로 확장됩니다.
결과적으로 각 박스의 회전된 코너 좌표가 원래 박스의 중심 좌표로 이동합니다.

@

다차원 배열의 경우:
다차원 배열에서 @ 연산자는 기본적으로 마지막 두 차원의 행렬 곱셈을 수행합니다.
나머지 차원은 브로드캐스팅에 따라 확장됩니다.

np.tile

np.tile(A, repeat_shape)
형태이며, A 배열이 repeat_shape 형태로 반복되어 쌓인 형태가 반환됩니다.

모델의 입력

아래와 같다. 어떻게 계산이 되었는지 파악해보자!!!

'img_metas':

  • T_global
  • T_global_inv
  • timestamp
  • data['img_metas'][0]['T_global'].shape
    (4, 4)
    data['img_metas'][0]['T_global_inv'].shape
    (4, 4)
    data['img_metas'][0]['timestamp']
    1533151603.54759

data['img'].shape
torch.Size([1, 6, 3, 256, 704])

 

data['timestamp']
tensor([1.5332e+09], device='cuda:0', dtype=torch.float64)

 

data['projection_mat'].shape
torch.Size([1, 6, 4, 4])

 

data['image_wh'].shape
torch.Size([1, 6, 2])

 

data['image_wh']
tensor([[[704., 256.],
[704., 256.],
[704., 256.],
[704., 256.],
[704., 256.],
[704., 256.]]], device='cuda:0')

 

 

data['projection_mat']
tensor([[
    [[ 5.4692e+02, 3.6989e+02, 1.4416e+01, -1.5591e+02],
      [-6.3702e+00, 9.6405e+01, -5.4680e+02, -2.2414e+02],
      [-1.1703e-02, 9.9847e-01, 5.4022e-02, -4.2520e-01],
      [ 0.0000e+00, 0.0000e+00, 0.0000e+00, 1.0000e+00]],

    [[ 6.0058e+02, -2.7248e+02, -1.7749e+01, -2.0312e+02],
      [ 4.8886e+01,  6.5852e+01, -5.5001e+02, -2.1927e+02],
      [ 8.4341e-01,  5.3631e-01,  3.2160e-02, -6.1037e-01],
      [ 0.0000e+00,  0.0000e+00,  0.0000e+00,  1.0000e+00]],

     [[ 1.4243e+01,  6.6139e+02,  3.4154e+01, -1.3307e+02],
      [-5.6023e+01,  6.1763e+01, -5.5025e+02, -2.2479e+02],
      [-8.2342e-01,  5.6594e-01,  4.1220e-02, -5.2968e-01],
      [ 0.0000e+00,  0.0000e+00,  0.0000e+00,  1.0000e+00]],

     [[-3.5375e+02, -3.7432e+02, -1.1633e+01, -3.8315e+02],
      [-3.5956e+00, -5.6038e+01, -3.5308e+02, -1.6952e+02],
      [-8.3335e-03, -9.9920e-01, -3.9103e-02, -1.0165e+00],
      [ 0.0000e+00,  0.0000e+00,  0.0000e+00,  1.0000e+00]],

     [[-5.2209e+02,  4.0624e+02,  2.3436e+01, -2.7515e+02],
      [-7.0893e+01, -3.9026e-01, -5.5153e+02, -1.8665e+02],
      [-9.4759e-01, -3.1948e-01,  3.1695e-03, -4.3253e-01],
      [ 0.0000e+00,  0.0000e+00,  0.0000e+00,  1.0000e+00]],

     [[ 1.2548e+02, -6.4648e+02, -2.6208e+01, -1.1994e+02],
      [ 6.6316e+01, -5.2879e-01, -5.4965e+02, -1.9378e+02],
      [ 9.2405e-01, -3.8225e-01, -3.7099e-03, -4.6465e-01],
      [ 0.0000e+00,  0.0000e+00,  0.0000e+00,  1.0000e+00]]]],
   device='cuda:0')
반응형