Knowhow/Vision

이미지 회전에 맞추어 intrinsic/extrinsic calibration 값 회전시키기

침닦는수건 2023. 8. 17. 13:49
반응형

실제 카메라를 설치할 때 공간적 제약 혹은 화각의 문제로 카메라를 돌려서 설치하는 경우가 있다. 시계 방향으로 90도 혹은 반시계 방향으로 90도가 가장 흔한 경우인데 이 때 이미지는 또 똑바로 보고 싶을 때도 많다. 

 

이미지만 사용할 경우에야 간단히 cv2.rotate를 쓰든 numpy를 쓰든 돌리면 끝이지만 만약 카메라 캘리브레이션 값도 같이 써야할 경우는 귀찮은 일이 많이 생긴다.

 

90도 돌린 경우에 intrinsic parameter는 focal length, fx, fy를 서로 스위칭해주어야 하고 principal point는 회전 방향에 따라 값을 재계산해주어야 한다. 또한 extrinsinc parameter 역시 yaw rotation을 가해주어야 한다. 

 

계산하는 것이 어렵지는 않다만 귀찮고 제대로 됐는지 확인하는 과정에 시간 소모가 매번 적지 않게 들기 때문에 함수로 구현하여 정리했다. 

 

def rotate_intrinsic(img_w, img_h, K, dist, direction="none"):
    if direction == "none":
        return K, dist

    K_new = np.eye(3, dtype=np.float32)
    fx, fy = K[0, 0], K[1, 1]
    cx, cy = K[0, 2], K[1, 2]

    if direction == "cw": # clockwise
        fx_new = fy
        fy_new = fx
        cx_new = img_h - cy
        cy_new = cx
        img_w_new = img_h
        img_h_new = img_w
    elif direction == "ccw": # couter-clockwise
        fx_new = fy
        fy_new = fx
        cx_new = cy
        cy_new = img_w - cx
        img_w_new = img_h
        img_h_new = img_w
    K_new[0, 0] = fx_new
    K_new[1, 1] = fy_new
    K_new[0, 2] = cx_new
    K_new[1, 2] = cy_new
    dist_new = dist
    return img_w_new, img_h_new, K_new, dist_new

def rotate_extrinsic(SE3, direction="none"):
    if direction == "none":
        SE3_new = SE3
    elif direction == "cw": #clockwise
        Rz = np.eye(4, dtype=np.float32)
        radian = -np.pi/2
        Rz[0, 0] = np.cos(radian)
        Rz[0, 1] = -np.sin(radian)
        Rz[1, 0] = np.sin(radian)
        Rz[1, 1] = np.cos(radian)
        SE3_new = np.matmul(SE3, Rz)
    elif direction == "ccw": #couter-clockwise
        Rz = np.eye(4, dtype=np.float32)
        radian = np.pi / 2
        Rz[0, 0] = np.cos(radian)
        Rz[0, 1] = -np.sin(radian)
        Rz[1, 0] = np.sin(radian)
        Rz[1, 1] = np.cos(radian)
        SE3_new = np.matmul(SE3, Rz)
    return SE3_new

def rotate_camera(img_w, img_h, K, dist, SE3, direction="none"):
    img_w_new, img_h_new, K_new, dist_new = rotate_intrinsic(img_w, img_h, K, dist, direction)
    SE3_new = rotate_extrinsic(SE3, direction)
    return img_w_new, img_h_new, K_new, dist_new, SE3_new
    
    
'''
img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
img_w_new, img_h_new, K_new, dist_new, T_new = rotate_camera(img_w, img_h, K, dist, T, direction="ccw")

T denotes SE3 matrix that converts camera coordinates to global coordinates (a.k.a T_gc)
'''

 

위 코드를 이용해 이미지를 시계 방향으로 돌렸다면 기존 카메라 캘리브레이션 값을 시계 방향으로 돌린 버전을 얻을 수 있고 반시계도 마찬가지다. 하지만 추천하지는 않는다!

 

가독성과 유지 보수를 쉽게 하지 위해선 이미지도 그대로 카메라 자세도 캘리브레이션 상태 그대로 사용하는 것이 가장 좋다. 이미지를 돌려보는 행위는 사람이 보기 쉽게 하려는 행위 밖에 없는데 그냥 어렵게 보는게 차라리 낫다.

반응형