티스토리 뷰

포스트는 뭔가 좀 잘못됐다. 내가 경식이형의 visualization 코드를 잘 이해를 못했다. 또한 원래 python matplotlib는 right hand rule이다. left hand rule이 아니라.

def vis_3d_pose(kps_3d, kps_line, joint_set_name='', prefix='vis3dpose', gt=False, ax_in=None):
    if joint_set_name == 'human36':
        r_joints = [1, 2, 3, 14, 15, 16]
    elif joint_set_name == 'coco':
        r_joints = [2, 4, 6, 8, 10, 12, 14, 16]
    elif joint_set_name == 'smpl':
        r_joints = [2, 5, 8, 11, 14, 17, 19, 21, 23]
    else:
        r_joints = []
    # r_joints = [2, 4, 6, 10, 12, 14]

    kps_3d_vis = np.ones((len(kps_3d), 1))
    if not ax_in:
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
    else:
        ax = ax_in

    for l in range(len(kps_line)):
        i1 = kps_line[l][0]
        i2 = kps_line[l][1]
        x = np.array([kps_3d[i1, 0], kps_3d[i2, 0]])
        y = np.array([kps_3d[i1, 1], kps_3d[i2, 1]])
        z = np.array([kps_3d[i1, 2], kps_3d[i2, 2]])

        if kps_3d_vis[i1, 0] > 0 and kps_3d_vis[i2, 0] > 0:
            ax.plot(x, z, -y, c='r', linewidth=1) # 여기
        if kps_3d_vis[i1, 0] > 0:
            c = 'g' if i1 in r_joints else 'b'
            ax.scatter(kps_3d[i1, 0], kps_3d[i1, 2], -kps_3d[i1, 1], c=c, marker='o') # 여기
        if kps_3d_vis[i2, 0] > 0:
            c = 'g' if i2 in r_joints else 'b'
            ax.scatter(kps_3d[i2, 0], kps_3d[i2, 2], -kps_3d[i2, 1], c=c, marker='o') # 여기

    ax.set_xlabel('X axis')
    ax.set_ylabel('Z axis') # 여기
    ax.set_zlabel('Y axis') # 여기

    title = f'3D Ground Truth' if gt else f' 3D Prediction'
    ax.set_title(title)
    ax.legend()
    axisEqual3D(ax)
    if not ax_in:
        plt.show()
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        cv2.waitKey(1)

        now = datetime.now()
        file_name = f'{prefix}_{now.isoformat()[:-7]}_{"3d_gt" if gt else "3d_pred"}.jpg'
        # fig.savefig(osp.join(cfg.vis_dir, file_name))
        plt.close(fig=fig)
    else:
        return ax

이 코드는 3D joint skeleton을 matplotlib로 그리는 코드인데, for loop부분을 보면 알 수 있듯이 z와 y값을 자리를 바꾸고 y값을 invert한다. 대체 이러는 이유를 잘 이해하지 못했고, 무언가 심오한 게 있는 줄 알았다. 데이터의 카메라좌표계와 matplotlib의 좌표계가 달라서 그걸 맞췄나 보다 등 이 전 포스트에 개소리를 써놨었다...

일단, 저런 처리(axis change)를 안하고, x,y,z있는 그대로 visualize해도 이상할 건 없다. 아래는 input 이미지 위에 2d pose overlay한 것, 그리고 axis change없이 그대로 visualize했을 때 figure다.

input image, 2d pose overlayed
gt 3d joint skeleton, no axis changed

처음에 왜 y축의 방향이 이상하지? 라고 생각했다. 직관적으로 생각하면 y축이 위로 올라가야 하니까. 아 핀홀카메라 모델이라 그런가? 그럼 왜 x축은 직관적인 방향이지? 등등 계속 헷갈렸는데, 사실 코드의 관점에서 보면 헷갈리지 않다.

def cam2pixel(cam_coord, f, c):
    x = cam_coord[:, 0] / (cam_coord[:, 2] + 1e-8) * f[0] + c[0]
    y = cam_coord[:, 1] / (cam_coord[:, 2] + 1e-8) * f[1] + c[1]
    z = cam_coord[:, 2]
    img_coord = np.concatenate((x[:,None], y[:,None], z[:,None]),1)
    return img_coord

3d joint skeleton이 cam2pixel을 거쳐 결국 그 위의 2d pose가 되는 것인데, 이미지의 x,y좌표값의 방향을 잘 생각해보자.

이미지의 x,y값은 오른쪽 아래로 갈수록 커진다

이렇게 보면, gt 3d joint skeleton의 값과, y축의 방향이 이미지의 좌표축과 align되있음을 알 수 있다. 그리고 gt 3d joint skeleton이 cam2pixel을 거치면 자연스럽게 이미지의 2d pose처럼 되리라는 것도 유추할 수 있다. 

위의 cam2pixel과정이 결국 이 스크린샷이고, 다만 Y축만 아래로 향하게끔 그려주면 완전히 동일한 과정이다.

그렇다면 다시 첫 번 째 코드, vis_3d_pose로 돌아와서. 왜 저렇게 귀찮은 짓(axis change, 여기 라고 주석달린 부분)을 하는 걸까? 그건 우리가 3d joint skeleton을 볼 때 가장 자연스럽게 보여주고자 함이다. 우리는 보통 Y축은 위를 향한다고 생각하니까.

gt 3d joint skeleton, axis changed

Y축이 위로 향하니 마음이 편안하다. 다음 포스트는 rotation과 visualization에 대해 쓰겠다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함