GTSAMのSphericalCameraをPythonで使って360度SfMを動かす
背景
GTSAMには SphericalCamera(方位ベクトル Unit3 を測定値とするカメラモデル)がC++で存在しますが、Pythonバインディングと周辺ファクターの対応が不足していました。PR #2446 でこれらを追加し、developブランチにマージされています。
SphericalCameraが360度SfMに向いている理由
通常のSfMで使われる PinholeCamera は、3D点をピクセル座標 (u, v) に投影します。この投影には焦点距離・主点などの内部パラメータが必要で、カメラキャリブレーションが前提となります。
360度カメラが出力するequirectangular画像は、各ピクセルが球面上の一方向に対応しています。横軸が経度 [-π, π]、縦軸が緯度 [-π/2, π/2] に線形対応しているため、ピクセル座標から一意に方位ベクトル(Unit3)に変換できます。
longitude = (u / width - 0.5) × 2π
latitude = (v / height - 0.5) × π
SphericalCamera はこの方位ベクトル Unit3 を測定値として扱うカメラモデルです。焦点距離などの内部パラメータを持たず、カメラ座標系における3D点の向きだけを観測量とします。
一般的なSfMツールであるCOLMAPは透視投影モデルを前提としており、equirectangular画像をそのまま入力することができません。360度画像を使う場合は画像をクロップして複数の透視投影画像に分割するか、魚眼モデルで近似するしかなく、視野全体を一枚の画像として扱えないという制約があります。SphericalCamera を使うことでこの制約をなくし、equirectangular画像をそのままSfMに入力できます。
インストール
git clone https://github.com/borglab/gtsam.git
cd gtsam && git checkout develop
mkdir build && cd build
cmake .. -DGTSAM_BUILD_PYTHON=ON -DCMAKE_BUILD_TYPE=Release
make -j$(nproc) && make python-install
gtsfmで360度SfMパイプラインを動かす
サンプルとして使用した360度画像(equirectangular形式):
gtsfm(筆者フォーク) の feature/spherical_sfm ブランチに、このバインディングを使った360度SfMを簡易的に実装しています。環境構築には uv を使います。
git clone https://github.com/inuex35/gtsfm.git
cd gtsfm
git submodule update --init --recursive
uv sync
uv run python -m gtsfm.spherical_sfm --dataset_dir ./sample --output_dir ./output
特徴点検出に AliKeD、マッチングに LightGlue を使用しています。AliKeDは360度画像のような広角・歪みの大きい画像でも安定して多くの特徴点を取得できる点が360度SfMに向いています。
結果は ./output に出力されます。以下のコマンドでブラウザ上から点群・カメラポーズを確認できます。
uv run python -m gtsfm.visualization.view_scene_browser --output_dir ./output
一周しているので正しく再構成されています。
おわりに
GTSAMのfactor graphベースの設計により、SphericalCamera を使った360度SfMはIMUやGNSS、オドメトリなど他のファクターと自然に組み合わせることができます。360度カメラの広い視野とGTSAMの柔軟な最適化フレームワークの組み合わせは、屋外環境でのロバストなSLAMや自律走行への応用でも有効です。

