COLMAP images.txt

「instantNeRFで遊ぶ Advent Calendar 2022」の13日目です。

今日は「RealityCaptureのregistrationデータを流用する検討」の課題1として、colmap2nerf.pyに入力されるCOLMAPのimages.txtについて調べました。

:::note
業務外の個人的な検討活動です。
間違っていても責任はとれません。
間違えていたらできましたらご指摘ください
:::

#### images.txt
各画像のカメラパラメータと特徴点が1行ごとに書かれています。
![13-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/3d8e7a7e-c61c-821e-4665-3b35b15a1e00.png)

公式ページが教えているカメラパラメータにテストデータを入れながら確認していきます。

#### 座標
OpenCVと同じのようです。

https://colmap.github.io/format.html#images-txt

>投影/カメラ中心の座標は、-R^t * Tで与えられ、ここでR^tは四元数から構成される3x3回転行列の逆行列/転置、Tは並進ベクトルである。画像のローカルカメラ座標系は、画像から見て、X軸が右、Y軸が下、Z軸が前を指すように定義される。 
（DeepLで翻訳しました　https://www.deepl.com/translator）

こちらがimages.txtのカメラ座標に[0,0,0]、向きに[1.0,0,0]を入力したときのCOLMAPの画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/2cd0a923-e99d-2ac3-edc8-8832c1589643.png)


#### 並進
images.txtのTx, Ty, Tzへ[0,0,0], [-1,0,0], [0,-1,0], [0,0,-1]を入れてCOLMAPへ取り込んだ時の画面はこのようになりました。ただし、QW, QX, QY, QZは[1, 0, 0, 0]としました。
import modelでモデルデータのテキストファイル3点セットが入ったフォルダを指定しました。
![13-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/0199c2ab-e822-cfcc-76db-2c9020389c04.png)

カメラ中心の座標は-R^t * Tとあるので、カメラの中心座標は[0,0,0], [1,0,0], [0,1,0], [0,0,1]になっています。

表で検算した結果
![13-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/fb0c04d5-f9c2-d2cb-79fc-5e7f1cd19204.png)


#### 回転
images.txtのQW, QX, QY, QZにオイラー角X, Y, Zが[10deg, 0deg, 0deg], [10deg, 30deg, 0deg], [10deg, 30deg, 50deg]の回転の角度をいれてCOLMAPへ取り込んだ時の画面はこのようになりました。
それぞれ回転後の座標軸に対して右ねじの方向に回転していましたので、内因性のオイラー角、右手系になっているようです。

テストしたimges.txt
![13-3a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/468c7ca2-f077-a3ed-4830-879b25c9f869.png)

COLMAPの画面
![13-3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/d7664912-52fa-c358-5bd7-db4fc0f5d38b.png)

段階ごとに分解した図
図中の数字はカメラが回転した角度です。右ねじの逆方向に回転したのでマイナスをつけています。
![13-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/08a53980-f862-eccb-ce42-94667c74edd1.png)
![13-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/0f3b9572-1303-ceb2-42ef-3ccc5bbfe047.png)
![13-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/30dd784a-35a5-3330-6057-9217deec0759.png)

ここでそれぞれのオイラー角はscipy.spatial.transformのRotationで求めました。
>$python
\>>> from scipy.spatial.transform import Rotation as R
\>>> rot1=R.from_euler("ZYX", [0, 0, 10], degrees=True)
\>>> print(rot1.as_quat()[3], rot1.as_quat()[0], rot1.as_quat()[1], rot1.as_quat()[2])
0.9961946980917455 0.08715574274765817 0.0 0.0
\>>> rot2=R.from_euler("ZYX", [0, 30, 10], degrees=True)
\>>> print(rot2.as_quat()[3], rot2.as_quat()[0], rot2.as_quat()[1], rot2.as_quat()[2])
0.9622501868990583 0.08418598282936919 0.25783416049629954 -0.022557566113149834
\>>> rot3=R.from_euler("ZYX", [50, 30, 10], degrees=True)
\>>> print(rot3.as_quat()[3], rot3.as_quat()[0], rot3.as_quat()[1], rot3.as_quat()[2])
0.8816280768439285 -0.03266701292872763 0.26925564114813405 0.3862204035220014

#### 並進と回転
https://github.com/colmap/colmap/issues/434
>colmapはまず平行移動してから回転するようです。
（DeepLで翻訳しました　https://www.deepl.com/translator）

images.txtからカメラの向きと位置を出す計算について、具体的な計算がこちらに紹介されていました。

https://github.com/colmap/colmap/issues/1376

#### カメラ位置と向きの値をimages.txtの形式に変換
RealityCaptureからexportしたカメラの位置や向きの値をimages.txtに書き込むことが目的でした。
documentationにある式を変換しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/9a8ba7fe-2889-4b79-054d-bc48542b034b.png)

テストデータとして、位置[0.1, 0, 0]、向きがオイラー角 [10, 20, 30]degのカメラのimages.txtの内容を計算しました。
![13-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/ab91d7c8-a038-5d04-3edf-5aebdd00da7f.png)
COLMAPの画面でもX軸上にカメラが配置されました。
![13-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/734b1492-63a6-5e70-aa6b-c132ba1e799e.png)

#### まとめ
- COLMAPの座標系はOpenCVと同じ
- カメラの座標と向きはimages.txtの内容そのものではなく変換する必要がある
    - カメラの向きはクォータニオンを変換した回転行列Rの転置　R^t
    - カメラの位置はimages.txtにある位置ベクトルTに対して-R^t T

#### 参考
https://kamino.hatenablog.com/entry/rotation_expressions

https://www.andre-gaschler.com/rotationconverter/