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 버전에서 해보니 안 맞는다. 근본 없이 계산해서 그런가 뭐가 틀린지 몰라서 새로 찾았는데 위 코드로는 좌표계가 맞았다.
'Knowhow > Vision' 카테고리의 다른 글
Fisheye 카메라 모델도 solvePnP 이용해서 자세 초기화/추정하는 방법 (0) | 2024.02.28 |
---|---|
COLMAP[python] pycolmap 보다 편하게 colmap 사용하기 (0) | 2023.12.07 |
Open3d를 이용한 디버깅용 camera, bbox, origin visualization (0) | 2023.12.06 |
Sphere 상에서 normal vector uniform sampling (0) | 2023.11.07 |
이미지 회전에 맞추어 intrinsic/extrinsic calibration 값 회전시키기 (0) | 2023.08.17 |