Knowhow/Vision

RealityCapture camera coordinate to opencv(vision) camera coordinate

침닦는수건 2023. 12. 6. 20:16
반응형

RealityCapture 라고 유명한 photogrammetry 툴이 있다. 연구하는 입장에서는 colmap보다 훨씬 좋은 성능과 속도로 SfM을 돌려주는 툴이라고도 볼 수 있는데 graphics쪽 좌표계와 각종 설정이 섞여있어 좌표계가 자주 쓰는 좌표계와 완전히 다르게 되어 있다.

 

결과적으로 RealityCapture에서 뱉어주는 카메라를 별도의 후처리 없이 그대로 사용하게 되면 카메라가 지멋대로 돌아가있다. 

 

온갖 검색을 해봐도 정확히 이해될 만한 좌표계 설명이 없고, 일반적으로 쓰는 roll, pitch, yaw 가 각각 x축, y축, z축 회전에 대응되는 것이 아니라 또 새로 정의한 순서이기 때문에 변환하는 일이 만만치 않았다.

 

결국 나도 온갖 자유도의 transformation과 roll, pitch, yaw 조합을 전부 다 뒤져서 무식하게 찾아냈다. 그래서 나도 RealityCapture camera coordinate가 어떻게 되어있는지, 어떤 축이 roll이고 yaw이고 pitch인지 모른다.

 

그러나 결과적으로 변환은 어찌됐든 할 수 있는 함수를 하나 찾아내긴 해서 여기에 기록해두려고 한다.

 

(누군가의 삽질을 줄여주는 글이 되길...)

 

def open_csv(csv_path):
    names = []
    Ts = []
    Ks = []
    dists = []

    with open(csv_path, 'r', newline='') as f:
        reader = csv.DictReader(f)
        for i, line in enumerate(reader):
            # decomposing
            name = line["#name"]
            x = float(line["x"])
            y = float(line["y"])
            z = float(line["alt"])
            yaw = float(line["heading"])
            pitch = float(line["pitch"])
            roll = float(line["roll"])
            f = float(line["f"])

            cx, cy = float(line["px"]), float(line["py"])
            k1, k2, k3 = float(line["k1"]), float(line["k2"]), float(line["k3"])

            # grouping
            R = Rot.from_euler("xyz", [yaw, pitch, roll], degrees=True).as_matrix()
            R = np.linalg.inv(R)

            t = np.array([x, y, z], dtype=np.float32)

            T = Rt2T(R,t) # cam to global, T_gk
            K = np.array([[f, 0, cx],
                          [0, f, cy],
                          [0, 0, 1]], dtype=np.float32)
            dist = np.array([k1, k2, 0, 0, k3], dtype=np.float32)

            names.append(name)

            Ts.append(T) # T_gk, cam k to global g
            Ks.append(K)
            dists.append(dist)
    return names, Ts, Ks, dists

 

RealityCapture 결과를 export하면 csv file을 얻을 수있다. 그 파일 경로를 위 함수에 던져 주면 최종적으로 이미지 이름, 카메라 포즈 (vision에서 사용하는), intrinsic parameter들을 얻을 수 있다. 

 

하나 주의할 점은 Ks 안에 담겨있는 focal length와 principal point는 mm scale로 담겨있을 수 있다. 우리가 흔히 쓰는 pixel scale로 변환하기 위해선 sensor size와 이미지 해상도를 알고 있어야 하는데 이건 RealityCapture를 사용할 당시에 설정해주는 값이어서 매번 바뀌므로 쓸 때마다 알아서 변환해서 써야한다. 여기선 그냥 냅뒀다. mm scale이니까 주의!

 

Update

def euler2mat(roll, pitch, yaw):
    yaw = -yaw # reverse sign of yaw
    seq = "yxz"
    so3 = Rot.from_euler(seq, [roll, pitch, yaw], degrees=True).as_matrix()

    Rx = np.eye(3)
    Rx[1, 1] = -1
    Rx[1, 2] = 0
    Rx[2, 1] = 0
    Rx[2, 2] = -1
    so3 = np.matmul(so3, Rx)

    return so3

 

흠 위 코드가 어떤 데이터에서 맞았는데, 또 RC 1.4 버전에서 해보니 안 맞는다. 근본 없이 계산해서 그런가 뭐가 틀린지 몰라서 새로 찾았는데 위 코드로는 좌표계가 맞았다.

반응형