360度動画や写真からフォトグラメトリ(他 3D Gaussian SplattingやNeRF活用)する方法

追記) 記事中盤のFFmpegに関して「3Dスキャン何でもLT会」でVRChat登壇しました。その時に使用しましたスライドはこちら。併せてお読みいただけると嬉し。


普段はガチめの広域フォトグラメトリ&3D Gaussian Splattingやってる人ですが、旅先で広い範囲を撮影する場合は時間も限られているので360度カメラで撮影した動画や写真を使っちゃう場合もあります。

(観光がてらの撮影なのに作業着と反射ベストも持っていった人。安全第一)

一眼カメラの撮影やレーザースキャンでのスキャンに比べて圧倒的に効率的!
その分品質は劣りますが。

この記事ではそうして撮影した360度素材を元にSfMする(からのフォトグラメトリや3Dガウシアンスプラッティングする)手段と手順についてまとめてみました。

前半でいくつかの手法を紹介、後半で具体的な手順を紹介します。

価格 インターフェース
無料利用可能 有料のみ コマンドラインのみ GUI
Metashape  ◯
3DF Zephyr
FFmpeg
Meshroom
360-to-planer-images
Unity Recorder
DaVinci Resolve
Premiere Pro
PTGui

A) 360度写真に対応したソフトを使う場合

対応しているソフトを使っちゃうのが最も簡単。

 Metashape

全天周画像(エクイレクタングラー)に対応しているのでそのまま処理できます。

特徴点が少ない上に超狭小空間といったケース(エレベーター内とかトイレ内とか)だと普通のカメラで撮影した画像からフォトグラメトリするのは困難な場合も多いで、360°撮影+Metashape処理という組み合わせは業務でも予備として採用しているワークフローです。

公式の解説ページはこちら

 3DF Zehpyr

こちらも全天周画像(エクイレクタングラー)を読み込むことができますが、Metashapeとは異なりそのまま処理するのではなく展開した画像が切り出されその画像が使われます。
切り出す際は方向(上下左右前後)と画角が指定できます。

参考記事: 3DF Zephyrで360度ビデオを直接取り込んでフォトグラメトリを行う

画像として切り出されるのでその画像を他のソフトで利用することも可能です。

B) 360度動画/写真から各方向に画像を切り出して利用する場合

全天周画像の状態から各方向に歪みを補正した状態の画像に切り出す方法です。
切り出した後はお好みのフォトグラメトリ/3D Gaussian Splatting/NeRFのソフト等で利用できます。

手法編

数が多いので前半では各種手法と参考記事を紹介。
言わばリンク集。
みなさまの記事に感謝!

「CUI」とつけたものはコマンドラインツールです。
コマンドラインツールの利用が難しい場合は「GUI」の方を利用してみてください。

後半では参考記事の内容を参考にしつつ一部をより具体的に手順編としてまとめました。

 FFmpeg (CUI)

参考記事: 360°動画からフォトグラメトリで現場を3Dモデル化する話
詳しい手順は後述します。

 Meshroom の aliceVision_utils_split360Images (CUI)

参考動画: 360 Photogrammetry [Reality Capture]
詳しい手順は後述します。

 360-to-planer-images (CUI)

360-to-planer-images

 Unity Recorder (GUI)

参考記事: パーソナルユース版・Insta360OneXの動画を使ったフォトグラメトリ

 DaVinci Resolve (GUI)

参考記事: DaVinci Resolveで360度映像を16:9にリフレームする
参考記事: DaVinci Resolveでの360度動画の手ブレ補正とリフレームをする方法

 Premiere Pro + GoPro FX (GUI 有料ソフト)

参考動画: 360カメラ映像からフォトグラメトリで3Dモデルを生成してVRChatまで持っていく配信
詳しい手順は後述します。

 PTGuiを使う (GUI 有料ソフト)

参考記事: Insta360OneXの動画を使ったフォトグラメトリ

手順編

上記で紹介した手法のうちいくつか具体的な使い方を以下にまとめました。
ちなみ私は使い勝手の良さから普段はFFmpegを利用しています。

 FFmpeg を使う場合

FFmpegのv360を使うと切り出す方向や画角等を細かく指定できるので、どのように切り出すとフォトグラメトリ(SfM)がうまくいくのかトライアンドエラーするのにも便利です。

v360のドキュメントはこちら

使い方は以下のとおりです。

1) FFmpegをインストールしパスを通す

参考記事: FFMPEGをWindowsパソコンにインストールしよう

2) Python環境構築

私はAnacondaを使用しているので以下のように環境を作成しました。

まずAnaconda Promptを起動。

v360という名前の仮想環境を作成。名前はわかりやすければ何でも良いです。

conda create -n v360 python=3.10

作成した仮想環境をアクティブにする。

conda activate v360

3) FFmpeg-pythonをインストールします。

参考記事: PythonとFFmpegで長時間の動画を短く分割しよう

condaの場合はインストールコマンドはこちら。
https://anaconda.org/conda-forge/ffmpeg-python

conda install -c conda-forge ffmpeg-python

4) Pythonファイルを用意する

こちらのサイトを参考にさせていただきつつ(感謝!)、私の用途にも使いやすいよう少し内容を変更しました。
パスやファイル名に具体的な名前が入っていますが適宜変更してください。

import subprocess
import os

# 動画を保存しているフォルダ 作業フォルダ
path = r'I:\Scans\20240609_OsakaCastle\_Export' + '\\'
# ファイル名 (拡張子は含まず)
filiname ='VID_20240609_173041_00_001'
# 拡張子名
extension ='mp4'

# 出力フォルダ (なければ作る)
output_path = fr'{path}Export' + '\\'
os.makedirs(output_path, exist_ok=True)

input_file_path = f'{path}{filiname}.{extension}'

# 視点リスト(yaw,pitch,roll)


#''' 
# 8方向
transforms = (
 ( 0, 0,0),
 ( 45, 0,0),
 ( 90, 0,0),
 ( 135, 0,0),
 ( 180, 0,0),
 (-135, 0,0),
 ( -90, 0,0),
 ( -45, 0,0),
 )
#'''

'''
# 8方向+見上げ4方向
transforms = (
 ( 0, 0,0),
 ( 0, 20,0),
 ( 45, 0,0),
 ( 90, 0,0),
 ( 90, 20,0),
 ( 135, 0,0),
 ( 180, 0,0),
 ( 180, 20,0),
 (-135, 0,0),
 ( -90, 0,0),
 ( -90, 20,0),
 ( -45, 0,0),
 )
'''

''' 
# 12方向
transforms = (
 ( 0, 0,0),
 ( 30, 0,0),
 ( 60, 0,0),
 ( 90, 0,0),
 ( 120, 0,0),
 ( 150, 0,0),
 ( 180, 0,0),
 (-150, 0,0),
 (-120, 0,0),
 ( -90, 0,0),
 ( -60, 0,0),
 ( -30, 0,0),
 )
'''

# 画像切り出しのフレームレート 撮影時の移動スピードや対象物からの距離をみて調節
fps = 2

# 明るさ調整 (ただしコントラストが落ち階調も失われるので次のガンマ調整を使った方が良さそう)
brightness = 0

# ガンマ調整
gamma = 1.0


# 各視点で書き出す
for index, transform in enumerate(transforms):

 # 出力ファイル名をつくる
 # %04dで4桁連番
 #output_file_path=fr'{output_path}Output_{index}_%04d.jpg'
 output_file_path=fr'{output_path}{filiname}_%04d_{index}.jpg'

 # v360ライブラリのオプション
 v360_options = ':'.join([
 'input=e', # Equirectangular projection.
 'output=rectilinear', # Regular video.
 'h_fov=90',
 'v_fov=90',
 'w=1472',
 'h=1472',
 # 'interp=gauss',
 f'yaw={transform[0]}',
 f'pitch={transform[1]}',
 f'roll={transform[2]}'
 ])

 # コマンドをつくる
 
 # 順再生
 command = f'ffmpeg -i "{input_file_path}" -vf "v360={v360_options}, eq=gamma={gamma}" -qmin 1 -q 1 -r {fps} "{output_file_path}"'

 # 逆再生
 # command = f'ffmpeg -i "{input_file_path}" -vf "v360={v360_options}, eq=gamma={gamma}, reverse" -qmin 1 -q 1 -r {fps} "{output_file_path}"'

 # ffmpegを実行
 subprocess.call(command, shell=True)

ファイル名は「v360.py」等わかり易い名前で保存します。.pyというのはPythonファイルの事。

切り出す視点の方向は「#視点リスト(yaw,pitch,roll)」のところで指定しています。
切り出し方によってSfMの結果がどう変わるか検証したかったので、コード内に切り出し方を事前にいくつか用意しておいて適宜コメントアウト部分を変更して検証しました。

通常は動画が再生されている順番そのまま切り出すので問題ないかと思いますが、同じ道を往復して撮影していた場合、復路は逆順で切り出したかったのでコードの最後の部分で「#逆再生にして切り出す場合」も作りました。

その他オリジナルのコードから変更した部分で、ガンマ調整を入れたり、書き出す画像の品質を最高品質になるようにしています。

Pythonファイルが出来たら、Anaconda Promptにてそのファイルを保存した場所に移動します。
私の環境の場合はこんな感じ。

cd D:\OneDrive\Garage\FFmpeg\Python_v360

移動したら以下のコマンドでPythonスクリプトを実行します。

python v360.py

以上!
(書き出した画像をお好みのフォトグラメトリ/3DGS/NeRF等のソフトに放り込んで処理しよう)

 Meshroom の aliceVision_utils_split360Images を使う場合

Meshroomはフォトグラメトリのソフトですが、全天周画像を展開できるユーティリティツールも配布されています。

まず Meshroomのサイト からMeshroomのソフト本体をダウンロードします。

ダウンロードしたzipを展開すると Meshroom > aliceVision > bin 内に aliceVision_utils_split360Images.exe があるのでこれを利用します。

使えるオプションは以下の通りです。

This program is used to extract multiple images from equirectangular or dualfisheye images or image folder
AliceVision split360Images:

Required parameters:
 -i [ --input ] arg Input image file or image folder.
 -o [ --output ] arg Output keyframes folder for .jpg

Optional parameters:
 -m [ --splitMode ] arg (=equirectangular)
 Split mode (exif, equirectangular,
 dualfisheye)
 --dualFisheyeSplitPreset arg (=center)
 Dual-Fisheye split type preset (center,
 top, bottom)
 --equirectangularNbSplits arg (=2) Equirectangular number of splits
 --equirectangularSplitResolution arg (=1200)
 Equirectangular split resolution
 --equirectangularDemoMode arg (=0) Export a SVG file that simulate the
 split

Log parameters:
 -v [ --verboseLevel ] arg (=info) verbosity level (fatal, error,
 warning, info, debug, trace).

具体的には以下のようなコマンドで実行できます。

aliceVision_utils_split360Images.exe -i <全天周画像を入れたフォルダのパス> --equirectangularNbSplits <切り出し枚数> -o <処理後の画像を保存するフォルダのパス>

equirectangularNbSplitsに8を指定すると水平方向で8分割で画像が書き出されます。

以上!
(書き出した画像をお好みのフォトグラメトリ/3DGS/NeRF等のソフトに放り込んで処理する)

前述のFFmpegほど細かい設定はできないようですが、使うまでの工程はこちらの方が簡単。

 Premiere ProとGoPro FXを使う場合

上記まではコマンドラインですが、GUIで作業したい方はこちらをどうぞ。

Premiere ProをインストールしたらPremiere用のプラグインもそれぞれインストールします。

・GoPro FX Reframe
https://community.gopro.com/s/article/GoPro-FX-Reframe

・Insta360 STUDIO
https://www.insta360.com/jp/download/hot-download
Insta360 Studioのインストール時にPremiere Pro用のプラグインもインストールできます。

360度静止画撮影していた場合は読み込ませるファイル名を連番にしておきます。
連番にしておくことで一つの動画としてPremierに読み込むことができるようになります。

新規プロジェクトを作成する。

プロジェクトにアセットを読み込む。

画像シーケンスにチェックを入れて読み込むと連番静止画を動画として読み込める。

タイムラインへ配置する。

GoPro ReFrameを適応する。

GoPro ReFrameのProjectionを「4K (4096×2160)」に設定する。

シーケンス設定を開く。

GoPro ReFrameのProjectionで選んだものと同じ解像度に設定する。

GoPro Reframeの「Lens Curve」を調整し歪みを補正する。
この例では65にした。
画面を見ながら歪みのなくなる数値に調整する。

「Zoom」の値はデフォルトの100で問題ないと思われるが、SfM処理時にオーバーラップ不足が心配な場合は少し広角にする。
オーバーラップが足りるかどうかは画像を何方向に書き出すかにもよる。
8方向であれば画角90度相当でも問題ないが、4方向のみに書き出す場合はもう少し広角にしてオーバーラップを確保すると安心。

ただし広角にしすぎると画面脇の解像が下がるので注意。↓広角にしすぎた例

設定を終えたら書き出しに進む。

・形式: JPEG
・画質: 100
・フレームレート: 静止画連番を読み込ませていた場合はプロジェクトのフレームレートのままにする(静止画のフレームが全て書き出されるようにする)。動画を読み込ませていた場合は撮影時の移動速度や対象までの距離よって調整する。通常は1または2程度。

OKを押して書き出す。

これで1方向分の書き出しが完了。

続いて残りの方向についても書き出すためGoPro Reframeの設定を変更する。
4方向に書き出したい場合は 0° の他、90° / 180° / 270° に設定したものでそれぞれ書き出す。
上方向の画角が足りない場合はTiltの値を調整したものも加える。

以上!
(書き出した画像をお好みのフォトグラメトリ/3DGS/NeRF等のソフトに放り込んで処理する)

8方向に書き出す場合はPan等の設定を変えて8回書き出さないとならないのが難点。
(そこも一括処理するうまくやり方あるかもしれない?)

作例

最後に作例も紹介。

Insta360 ONE R 1-inchで撮影した360°動画から京都 清水寺の参道をフォトグラメトリ!
メタバースプラットフォーム、VRChatやclusterでワールドを公開しました。

「京都VR」の起動リンクやメイキングについてはこちら
どのように360°カメラで撮影したのか もメイキングで触れています。

同じデータから大規模、広域なガウシアンスプラッティングも作ってみました。