フォトグラメトリやガウシアンスプラッティング等 画像を撮影して立体データを作成する手法では
360度カメラのように全周を動画撮影して
SfM(Structure from Motion)の解析をすると比較的少ない手間で広い範囲のデータを取得できます
Insta360やOsmo360などの360度カメラは、前後に配置された超広角の魚眼レンズで撮影し、
そのデータを円筒座標画像や通常のカメラに近い画像へ変換して利用するのが一般的です。
SfMの解析でも、こうした変換後の画像が使われることが多いですが、
変換時の誤差により精度が低下する場合があります。
そのため、魚眼レンズのデータをそのまま解析できれば、
誤差や画質劣化を抑えられると考えられます。
本記事では、Osmo360で撮影した魚眼画像を直接解析する手順を解説します。
前半はColmapのGUI版の操作で全体の流れを見て処理の流れを順に整理し
後半にその一連の処理に加えて 前後のカメラの相対位置をリグとして拘束して解析するバッチファイルを置いておきます
今回の作業はffmpeg-N-101352 Colmap 4.0.2で行っています
全体の流れ
360度カメラで撮影した動画を用意し、以下の手順で処理を行います。
- 動画を魚眼データのまま連番画像に変換
- 特徴点の抽出
- 特徴点のマッチング
- 再構成(Reconstruction)
- 解析結果と歪み補正画像の書き出し
連番画像の書き出しにはffmpegを使用し、それ以降はCOLMAPで処理します。
Colmapでは自動で一括して処理を行う設定もありますが
今回は標準的でない設定を含むため、自動処理ではなく各工程を個別に実行します。
作業用にプロジェクトフォルダを作成し、関連データをまとめておくことをお勧めします
撮影データから連番画像を書き出す
Osmo360の動画は拡張子こそ独自ですが、実体はMPEG-4コンテナです。
マルチストリーム構造により、前後カメラの映像が同時に記録されています。
ffmpegの-mapオプションを使用することで、各ストリームを個別に取り出し、
連番画像として書き出すことができます。
ドロップした動画ファイルを元に前後のカメラ個別のフォルダで静止画書き出しするバッチファイルの例です
rem 動画の場所にフォルダ作成
rem 画像を収める親フォルダ
md %~dp1img
rem 前側画像フォルダ
md %~dp1img\imgF
rem 後側画像のフォルダ
md %~dp1img\imgR
rem 作成したフォルダに動画から秒間2枚画像を書き出し
%~dp0ffmpeg-N-101352\bin\ffmpeg.exe -i %1 -r 2 -map 0:0 -q 1 %~dp1img\imgF\img%%06d.jpg -r 2 -map 0:1 -q 1 %~dp1img\imgR\img%%06d.jpg
pause
ちなみにinsta360のシリーズは前後2枚の動画を横並びにした動画形式で記録している様子ですので
以前 書いたXREAL Beam Pro用の書き出しバッチを改良すれば同様に書き出しできるでしょう
マスク画像の作成
魚眼レンズの外周(黒帯や強い歪み部分)は誤検出の原因になるため、解析対象から除外します。
撮影画像と同サイズのマスク画像を作成し、
使用する領域を白、除外する領域を黒で指定します

キャプチャは撮影画像にマスクを半透明表示した例です
360度カメラは実際に片側180度以上をカバーしていますが、 colmap解析は180度までのため
マスクする範囲は以外に広くて 手元の4K撮影(3840pixel)で直径3600Pixel程度の円になります

参考までに 実際に利用しているマスク画像も添付しておきます
Colmapによる解析
解析する素材とマスクが用意できたらcolmapを利用して解析をします
プロジェクトの作成と画像読み込み
COLMAP GUIを起動し、解析結果の保存先と入力画像を設定します。
File > New project を選択し、

Database のNewで解析の元になるデータを保存するファイル名
さっき作成した連番画像の入った場所の親フォルダ(バッチファイルの場合は「img」)を指定します
特徴点抽出(Feature Extraction)
画像同士を比較するための画像の変化のある部分(特徴点)を抽出する処理です
メニューのProcessing > Feature extraction 実行してカメラの設定を指定します

Camera model: OPENCV_FISHEYE を選択
Parameters from EXIF: にチェック(EXIFが無くても内部計算で推定されます)
Shered per folder: 前後レンズを別々のフォルダに分けている場合、それぞれのレンズ特性に合わせて個別に最適化されます。
CameraMask:先ほど作成したマスク画像を指定します
設定後、「Extract」で実行します。
特徴点マッチング(Feature Matching)
画像間で共通する特徴点を対応付けて。 同じ場所を映したものを探します
メニューの Processing > Feature matching 実行して
今回は動画由来の連番画像なので Sequentialのタブを選択した状態で「Sequential」タブを使用して「Run」を実行します。

Sequentialでは近いフレーム同士のみ比較するため、高速に処理できます。
個別に撮った写真を解析する場合には 「Exhaustive」タブを使用します
全ての画像同士を比較同士を比較することになるので計算時間がかかります
疎な再構成 (Sparse Reconstruction)
特徴点の対応関係から、カメラ位置と3D構造を推定します。
この過程でカメラパラメータの自己校正も行われます。
「Reconstruction > Start reconstruction」を実行します。

解析が終わると特徴点の座標を示す点群とカメラを示す四角錐が表示されます

今回は全部のデータが一連のものとして認識されましたが
繋がったデータとして認識されなかった場合には ツールバーのプルダウン中に複数のモデルが表示されることになります
データの書き出し
COLMAPでは、推定されたカメラモデルを元に歪み補正画像を出力できます。
Extras >Undistortion で

Output_path指定して「Undistort」実行すると
現在画面に表示している解析結果で 補正後の画像と解析データが同時に出力されます。
歪みのない画像だった場合には画像の変換出力がされない様子で
その場合には必要な画像をコピーする必要があるようです
バッチでの自動処理
ここまでの処理は主にGUIでの操作でしたが
Colmapもコマンドラインからの操作ができます
(というかメニューに項目のない機能も多いです)
ffmpegの操作からColmapの再構成までの手順をバッチファイルにしてみました
rem 実行スクリプトのディレクトリを親ディレクトリに
set root_dir=%~dp0
rem 動画書き出しの秒間枚数
set exprate=2
rem 画像フォルダ名
set imgdir_name=image
rem 書き出しフォルダの名前
set outputdir_name=sparse
rem ffmpegのパス
set ffmpegpath=%root_dir%ffmpeg\bin\ffmpeg.exe
rem colmapのパス
set colmap_path=%root_dir%colmap-x64-windows-cuda\bin\colmap.exe
rem colmapのcameraマスクのパス
set cameramask_path=%root_dir%osmo360Mask.png
rem リグ設定のパス
set rig_data_path=%root_dir%Osmo_rig_config.json
set workdir_name=%~n1
set workdir_path=%root_dir%%workdir_name%
set osv_path=%1
cd %~dp0
rem ffmpegで連番の切り出し
call :export_ffmpeg %osv_path% %workdir_path% %outputdir_name% %imgdir_name% %ffmpegpath%
rem colmap解析
call :colmap %colmap_path% %workdir_path% %outputdir_name% %imgdir_name% %cameramask_path% %rig_data_path%
pause
:export_ffmpeg
set osv_path=%1
set workdir_path=%2
set outputdir=%3
set imgdir_name=%4
set ffmpegpath=%5
rem 作業フォルダ作成
md %workdir_path%
rem 計算結果を収めるフォルダを作成
md %workdir_path%\%outputdir%
rem 画像フォルダ作成
set image_path=%workdir_path%\%imgdir_name%\
md %image_path%
rem 画像フォルダ下に前後の画像フォルダ作成
md %image_path%imgF
md %image_path%imgR
rem 前後画像を書き出し
%ffmpegpath% -i %osv_path% -r %exprate% ^
-map 0:0 -q 1 %image_path%imgR\%%06d.jpg -r %exprate% ^
-map 0:1 -q 1 %image_path%imgF\%%06d.jpg
rem -f 書き出しでは低画質 -q 書き出しでクオリティ指定
exit /b
:colmap
set colmap_path=%1
set workdir_path=%2
set outputdir_name=%3
set imgdir_name=%4
set cameramask_path=%5
set rig_data_path=%6
rem colmap操作
rem データベースファイル
set database_path=%workdir_path%\database.db
set image_path=%workdir_path%\%imgdir_name%
set output_path=%workdir_path%\%outputdir_name%
rem 各種設定
"%colmap_path%" feature_extractor ^
--database_path %database_path% ^
--image_path %image_path% ^
--ImageReader.single_camera_per_folder 1 ^
--ImageReader.camera_model "OPENCV_FISHEYE" ^
--ImageReader.camera_mask_path %cameramask_path% ^
rem リグの設定
%colmap_path% rig_configurator ^
--database_path %database_path% ^
--rig_config_path %rig_data_path%
rem シーケンスでマッチング
%colmap_path% sequential_matcher ^
--database_path %database_path% ^
--SequentialMatching.overlap 15
rem 座標の計算
%colmap_path% mapper ^
--database_path %database_path% ^
--image_path %image_path% --Mapper.ba_use_gpu 1 ^
--output_path %output_path%
exit /b
このバッチファイルに.OSVの動画ファイルをドロップすると
バッチのある場所に動画と同じ名前のフォルダを作成してSfM解析まで自動で進めるようになっています
Osmo360はUSB-C接続するとドライブとしてマウントされるので そこから直接ドロップするだけで楽です

静止画書き出し>特徴点抽出>マッチング>座標の計算という流れは一緒ですが
一点「リグの設定」項目が追加してあります
ここでのリグは前と後ろのカメラの相対位置がずっと同じだという設定を加えることで
より多くの画像の位置をマッチさせることができる設定です
フォルダで分けた画像の同じ名前の画像が同じ相対位置で同時に撮られたものとして処理されます
今回の設定はこうなっています
[
{
"cameras": [
{
"image_prefix": "imgR/",
"ref_sensor": true
},
{
"image_prefix": "imgF/",
"cam_from_rig_rotation": [
0.0,
0.0,
1.0,
-0.0
],
"cam_from_rig_translation": [
0,
0,
0
]
}
]
}
]
片方を基準にZ軸180度回転した設定となっています
バッチファイルとリグの設定を保存して
ffmpegとcolmapのバイナリのフォルダとカメラのマスク画像を同じば場所に置くことで機能するようになっています
解析結果は [動画ファイル名]/sparse フォルダ下に保存されています
動画全体を一連のものとして解析できなかった場合には複数のフォルダが作成されます

ColmapのGUIのFile>Import model でこの数字のフォルダを指定することで 解析結果を確認できます
結果が良好なものを Extras >Undistortion で書き出し処理するとよいでしょう
年明けから街並みや桜など20本ほどテスト処理してみましたが
このスクリプトでの処理は非常に良好でしたので何かの参考になればと思います
