HoudiniとMeshroomでカメラトラッキングする方法
1. あらすじ
2. 素材の準備
3. Meshroomでフォトグラメトリ
4. Houdiniでカメラ情報の復元
あらすじ
カメラトラッキング(マッチムーブ)とは、主に映像合成で使用される技術のことで、映像からカメラの動きを検出して、3Dオブジェクトや画像などの、映像にぴったりと合わせる目的で使用されます。
古くからは、映像用途として各ソフトやプラグインなどでカメラトラッキングの機能がついているものが多く、VFXなどの実写合成などの技術の基本的な部分を担っている技術ですが、最近では、ARの技術の進歩によってリアルタイムにカメラの位置をトラッキングできるようになっています。
そして、今回使う技術はフォトグラメトリです。複数枚の写真から3Dモデルを復元する技術のことで、現実にある物体を3Dデータに変換する際によく使用される技術です。
今回の記事は、フォトグラメトリをしているときに、カメラ位置の復元もされていることに気づき、そこからカメラトラッキングをしてみようという試みです。果たして精度のほどは・・・!?
今回の流れ
1. 動画を静止画の連番に
2. Meshroomでフォトグラメトリ
3. Houdiniでカメラモーションに復元
素材の準備
今回は、個人的に鎌倉の海辺でiPhone11で撮影した映像を使用します。今回の試作ですがフォトグラメトリの技術を使用するので、フォトグラメトリに適した素材である必要があります。
フォトグラメトリに適した動画の条件
・ ボケが少ないように、ゆっくりカメラを動かす
・ 特徴点が多く取れるような対象物をフレームに収める
・ 複雑な動きをしない
などなど、気をつけるべき点は他にもたくさんありますが、トライアンドエラーで色々試してみないとわからないことがたくさんあるかと思います。
フォトグラメトリ用の素材として扱うために、動画データを画像の連番形式に変換します。連番にするだけなので、各動画ソフトなどでも大丈夫ですが、効率化のためにここではFFmpegを使用します。
// png連番に15fpsで変換 (windowsのバッチ)
ffmpeg -y -i IMG_0440.MOV -vcodec png -r 15 h:\IMG_0440\image_%%^3d.png
Meshroomでフォトグラメトリ
ここでは、オープンソースソフトウェアのMeshroomを使用します。AliceVisonというフォトグラメトリフレームワークをGUIで触れるソフトで、WindowsとLinuxでしか動きませんが、無料で試せる上にコードが公開されているので、勉強にもとてもいいソフトになっています。
ソフトの使い方は割愛しますが、左下のノード画面がキモで、ここで処理の流れがわかりますので途中で処理をとめたり追加したり分岐したりと、カスタマイズが容易になります。
ここでは、カメラデータのみを別フォルダに保存するようにPublishノードを追加しました。カメラデータのみが必要な場合は、StructureFromMotionノードで止めてしまってもいいかと思います。
生成されたデータは、キャッシュフォルダーに一時保存されます。ノードを右クリックして、Open Folderで該当のノードで生成されたデータの場所を参照することができます。今回は、StructureFromMotionノードで生成されたsfm.abc(Alembicファイル)を使用します。
Houdiniでカメラモーションに復元
手順
1. 生成された3Dモデルとカメラデータを読み込む
2. カメラデータをモーションに変換
3. 合成
Houdiniでモデルとカメラデータの読み込み
カメラ位置の情報は、トランスフォーム情報が並べておいてある状況で読み込まれます。これをタイムラインに並べることでカメラモーションへと復元することができます。
Alembicファイルの情報はIntrinsicパラメーターとして、隠されてしまっているので、まずは、Geometry Spreadsheetでカメラの位置情報を表示してみましょう。packedfulltransformというパラメーターにトランスフォーム情報が格納されています。
また、カメラデータは順不同で格納されてしまっているので、pathパラメーターの文字列から、フレーム数に並び替えないといけません。幸い、復元もとの画像ファイル名がpathに記載されていますので、その文字列を利用します。
位置情報はPointAttributeには入っていて、あとは順番(フレーム数)と回転情報を関連付けしないといけません。
ノードはこんな感じで、
//primitivewrangle1
string pts[] = split(s@path, "/"); // pathをSplitで配列に
if(pts[1] == "mvgCameras"){ // 不要なデータを削除
string cam[] = split(pts[2], "_");
if(cam[3] == "image"){
string id = cam[4]; // image_XXX の数字部分を抜き出す。
i@index = atoi(id); // 整数型に変換
if(@index == 0){
removeprim(0, @primnum, 1);
}
i[]@pt = primpoints(0, @primnum); // pointの読み出し
setpointattrib(0 , "index", @pt[0], @index); // PointAttributeにIndexをセット
}else{
removeprim(0, @primnum, 1);
}
}else{
removeprim(0, @primnum, 1);
}
文字列操作して、Indexを抜き出して、PointAttributeにセットしてあげています。
// pointwrangle1
matrix m = primintrinsic(0,'packedfulltransform',@ptnum);
//Pointに回転をセット
@orient = degrees(quaterniontoeuler(quaternion(matrix3(m)), 0));
あとは、回転情報をセットしてあげます。
sortノードは、indexにしてあげて、順番を並び替えてあげます。
最後に、カメラデータとの関連付けですが、半ば強引な手法ですがポイントのindexをフレーム数に基づいてAttributeを読みに行っています。カメラのFOVなどは撮影時のズーム状況などに寄りますので、適宜目視で調整で大丈夫かなと思います。
あとは、カメラモーションをベイクしてあげればモーションデータが作成できます。
実際に撮影した動画と、3Dオブジェクトを重ねてみた動画がこちらになります。
それなりにカメラ位置が復元されている様子がわかりますね。しかし、完璧にぴったりというわけには行っていません。
手軽で完璧な手法とは言い難いですが、動画だけでカメラトラッキングが実現できることがわかりました。
追記)
記事を書き終わったあとに、以下のノードを見つけたのは秘密です。
ExportAnimatedCameraノードでカメラモーションのみのAlembicファイルが出力されるので、Houdiniいらずでカメラモーションが取り出せるかと思います。