Unreal Engine 4で作ったバーチャル空間上で3Dモデルがぬるぬるアニメーションしているところを複数視点でcubemosの骨格検知にかけてみた
前回の記事では、UnrealCVプラグインが組み込まれたUE4デモアプリでUnrealCVを触ってみました。 今回は、アニメーションする人間の3Dモデルを配置したUnreal Engine 4(UE4)プロジェクトを自作して、UnrealCVでバーチャル空間のRGBイメージを連続的に取得しつつcubemosの骨格検知にかけてみます。
準備
配布されているコンパイル済みのUnrealCVプラグインが対応しているのはUE4.16までです。新しめのバージョンのUE4を使いたいので、githubからリポジトリをクローンしてプラグインをビルドしてみます。使用するUE4のバージョンは今回はUE4.20としました。本記事執筆時に使用した主なツール・動作確認環境は以下のとおりです。
- Windows 10
- Python 3.7.7
- UnrealCV Pythonパッケージ 0.4.0 (
pip install unrealcv
) - Unreal Engine 4.20.3
- VC2017 15.9.23 (UnrealCVプラグインのビルドに使用)
Unreal Engine 4とVisual Studioのインストール
Unreal Engine公式サイトでユーザー登録をすませ、ダウンロードとインストールを済ませておきます。プラグインのビルドにはVisual Studio(VS)が必要なので、こちらもインストールを済ませておきます。執筆時はVS2017のバージョン15.9.23を使用しました。
参考:
- Unreal Engine をインストールする | Unreal Engine Documentation
- Visual Studio の以前のダウンロード - 2017、2015、および以前のバージョン
UnrealCVプラグインのビルド
githubからリポジトリをクローンしてプラグインをビルドします。
git clone https://github.com/unrealcv/unrealcv.git cd unrealcv python build.py --UE4 "D:¥Epic Games¥UE4_20"
ビルドに成功したら、unrealcvのフォルダにPlugins
というフォルダが作成されます。
実験用UE4プロジェクトの作成
"Epic Games Launcher"で、インストールしたUE4.20を起動します。
空のブループリントを選択してプロジェクトを作成します。
エディタウィンドウが起動したらプロジェクトの作成完了です。
プロジェクトにUnrealCVプラグインを追加
プロジェクトが作成されたので、UnrealCVプラグインをプロジェクトに追加します。 UE4自体にプラグインを追加することもできますが、今回はプロジェクトに追加するかたちで利用することにします。
unrealcvフォルダでビルドした結果できたPlugins
フォルダを中身ごと、作成したUE4プロジェクトのフォルダにコピーします。
コピーしたら、プラグインを有効化するために、一度エディターウィンドウを閉じてプロジェクトを開き直します。
エディターウィンドウのメニューから[Edit]->[Plugins]と選択してプラグインブラウザーを開きます。
プラグインブラウザーウィンドウが開くと、UnrealCVプラグインがインストールされて有効化された状態になっていることが確認できるはずです。
UnrealCVプラグインの動作確認
エディターウィンドウ上部の[Play]ボタンをクリックします。UnrealCVはPlay状態でないと動作しません。
ウィンドウ中央のゲームレベルが表示されている領域をクリックしてから、バッククオートキーを2回押下して、UE4コンソールをコマンドの実行結果が表示される状態で開きます。 UnrealCVのコマンドをいくつか実行して、動作していることを確認します(コマンドのリストはここにあります)。
バーチャル空間上の人間の3Dモデルに対するcubemos骨格検知の実行
自作のUE4プロジェクトにUnrealCVプラグインが導入できたので、バーチャル空間上に人間の3Dモデルを配置して、cubemosの骨格検知を実行してみます。 まず、アニメーションつきの人間の3Dモデルをバーチャル空間に配置します。
アニメーションつきの人間の3Dモデルの入手と配置
アニメーションつきの人間の3DモデルをMixamoから入手します。 利用には無料のユーザー登録が必要です。
ログインしたら、適当なキャラクターとアニメーションを選んで、"Download"をクリックします。 執筆した時は"Joe"というモデルにサンバダンスのアニメーションを組み合わせてみました。
ダウンロードセッティングは、デフォルトのままにしておきます。.fbx
形式で3Dモデルをダウンロードします。
ダウンロードした.fbx
ファイルをUE4プロジェクトに追加します。
まずエディタウィンドウの下部にあるコンテンツブラウザで適当な格納先を作成します。
執筆した時は"mixamo"フォルダを作りその下に"Joe"フォルダを作りました。
"Import"をクリックしてダウンロードした.fbx
を選択します。
"FBX Import Options"ウィンドウが表示されるので、"Import Animations"をチェックしてから"Import"をクリックします。
インポートが完了すると、コンテンツブラウザに.fbx
の中身が展開されます。"Message Log"にエラーっぽい内容が表示された場合、インポートに失敗しているかもしれません。
展開されたskeletal meshをレベルに配置して、アニメーションの動作確認をします。 一連の操作を動画にしました。
mixamoからダウンロードしたFBXファイルをUE4にインポートする時の注意事項
選択したキャラクター(とアニメーションの組み合わせ?)によっては、下のような感じでFBXをインポートしてskeletal meshをレベルに配置しただけではそのまま使えない状態になってしまいました。 解決の仕方がわかなかったため、こういう状態になってしまった時は素直に別のキャラクターでFBXをダウンロードし直しました。
UE4初心者なので、ちょっと油断すると異形の者が召喚されてしまう。。 pic.twitter.com/SbicgOAY5b
— Yosuke MIYAJIMA (@mayosuke) June 3, 2020
cubemos骨格検知の実行
プロジェクトの準備ができたので、cubemos骨格検知を実行してみます。 以下のような、UnrealCVでUE4に接続し、 RGBイメージ取得ー>cubemosの骨格検知実行ー>推論結果をRGBイメージ上に描画をループするだけのシンプルなPythonスクリプトを作成しました。コードはここに公開してあります。
cubemosやUnrealCVなどの必要なPythonパッケージはここやここを参考にして、事前にインストールを済ませてあるものとします。
import numpy as np import cv2 import PIL.Image as Image from io import BytesIO import cubemosutil as cm from unrealcv import client def color_frame(client): res = client.request('vget /camera/0/lit png') img = Image.open(BytesIO(res)) npy = np.asarray(img)[:,:,:3] return cv2.cvtColor(npy, cv2.COLOR_RGB2BGR) if __name__ == "__main__": try: api = cm.get_api() # Cubemos Skeleton Tracking API res = client.connect() # connect to UE4 via UnrealCV print(res) while True: img = color_frame(client) skeletons = api.estimate_keypoints(img, 192) cm.render_skeletons(skeletons, img) cm.render_joints(skeletons, img) cv2.imshow('cubemos', img) if cv2.waitKey(1) & 0xFF == ord('q'): break finally: client.disconnect() cv2.destroyAllWindows()
スクリプトを実行する前にUE4側で"Play"状態にして、UnrealCVの機能が使用できるようにしておきます。 実行結果は以下のようになりました。骨格検知はできていますが、コマ落ちしてカクカクした表示になってしまいました。
3Dモデルがぬるぬるアニメーションしているところを複数視点でcubmos骨格検知にかける
先程のスクリプトでは、カクカクした表示になってしまったので、少し工夫して、もっとぬるぬるさせてみます。 ついでに、複数の視点を同時に扱えるようにもしてみます。 先に実行結果を載せておきます。
複数視点の実現
UE4上でプロジェクトを"Play"状態にして、UE4コンソール上でUnrealCVコマンドを実行してカメラの位置と回転情報を取得します。 UE4コンソールはバッククオートキーを2回押下して、コマンドの実行結果が表示されるようにします。
カメラの位置と回転情報を取得するUnrealCVコマンドは以下のとおりです。
vget /camera/0/location vget /camera/0/rotation
カメラを操作して以下のような4つの視点にして、それぞれの視点で上記コマンドを実行してlocation
とrotation
を記録します。
top view
side view
front view
over the shoulder view
フレーム間の同期とフレームレートの向上
以下のような実装で、フレーム間の同期をとりつつ、フレームレートを向上させます。
# top view (location, rotation) cam1 = ('-2101.312 762.003 364.453', '288.216 270.963 0.000') # over the shoulder view cam2 = ('-1825.573 421.577 168.139', '340.495 136.159 0.000') # front view cam3 = ('-2094.729 955.863 159.293', '349.620 272.152 0.000') # side view cam4 = ('-1718.284 659.636 156.002', '342.875 181.467 0.000') fouorcc = cv2.VideoWriter_fourcc(*'mp4v') vid_writer = cv2.VideoWriter("out.mov", fouorcc, fps, (1280*2,720*2)) vid_writer_org = cv2.VideoWriter("org.mov", fouorcc, fps, (1280*2,720*2)) frame_count = fps * rec_len client.request(f'vrun slomo {slomo}') while frame_count > 0: client.request('vset /action/game/pause') client.request(f'vset /camera/0/location {cam1[0]}') # x y z client.request(f'vset /camera/0/rotation {cam1[1]}') # pitch yaw roll color1 = color_frame(client) # for waiting the camera location settle color1 = color_frame(client) client.request(f'vset /camera/0/location {cam2[0]}') # x y z client.request(f'vset /camera/0/rotation {cam2[1]}') # pitch yaw roll color2 = color_frame(client) # for waiting the camera location settle color2 = color_frame(client) client.request(f'vset /camera/0/location {cam3[0]}') # x y z client.request(f'vset /camera/0/rotation {cam3[1]}') # pitch yaw roll color3 = color_frame(client) # for waiting the camera location settle color3 = color_frame(client) client.request(f'vset /camera/0/location {cam4[0]}') # x y z client.request(f'vset /camera/0/rotation {cam4[1]}') # pitch yaw roll color4 = color_frame(client) # for waiting the camera location settle color4 = color_frame(client) client.request('vset /action/game/pause') h1 = np.hstack((color1, color2)) h2 = np.hstack((color3, color4)) images = np.vstack((h1, h2)) vid_writer_org.write(images) #perform inference skeletons = api.estimate_keypoints(color1, 192) cm.render_skeletons(skeletons, color1) cm.render_joints(skeletons, color1) skeletons = api.estimate_keypoints(color2, 192) cm.render_skeletons(skeletons, color2) cm.render_joints(skeletons, color2) skeletons = api.estimate_keypoints(color3, 192) cm.render_skeletons(skeletons, color3) cm.render_joints(skeletons, color3) skeletons = api.estimate_keypoints(color4, 192) cm.render_skeletons(skeletons, color4) cm.render_joints(skeletons, color4) h1 = np.hstack((color1, color2)) h2 = np.hstack((color3, color4)) images = np.vstack((h1, h2)) vid_writer.write(images)
まず、UnrealCVのvrun slomo
コマンドで1フレームあたりの進行時間を遅らます。
次に、vset /action/game/pause
コマンドでアニメーションを一時停止させてから、vset /camera/0/location
コマンドとvset /camera/0/rotation
コマンドでカメラの位置をセットして、RGBイメージを取得します。これを4つの視点分繰り返します。
ポーズ中にvset /action/game/pause
コマンドを再び実行すると、アニメーションが再開します。
完全なコードはここにあります。
課題
上記実装には、以下2つの課題を抱えています。
- ゲーム側のフレーム進行とは完全に同期できていない
UE4側には、アニメーション(ゲームの進行)をポーズ中に1フレーム単位でスキップさせる機能があるのですが、 これをUnrealCV呼び出すことができそうになかったため、アニメーションの進行をスローモーションにしてからポーズー>再開を繰り返す、 という対応になりました。
- 処理にめっちゃ時間がかかる
8th gen i7搭載ラップトップを使用して、3秒分のフレームを処理するのに8分くらいかかります。
まとめ
Unreal Engine 4で作ったバーチャル空間上で3Dモデルがぬるぬるアニメーションしているところを複数視点でcubemosの骨格検知にかけてみました。 出力結果はぬるぬるさせられたものの、数秒分のフレームを処理するのに分単位で時間がかかってしまうのが辛いので、でできれば改善したいところです。
参考
- Skeleton Tracking SDK – cubemos
- unrealcv/unrealcv: UnrealCV: Connecting Computer Vision to Unreal Engine
- UnrealCV
- UnrealCV | Proceedings of the 25th ACM international conference on Multimedia
- Intel RealSenseに待望のSkeleton Tracking SDKが出た(cubemos製)ので触ってみた[Windows編] | Developers.IO
- Skeleton Tracking SDKのPython版サンプルのコードを読んでみた[Windows編] | Developers.IO
- Cubemosの骨格検知SDKとRealSenseを使って人が棚に手を伸ばしたことを検知してみた | Developers.IO
- Unreal Engine 4をコンピュータビジョンのバーチャル実験室にするツールUnrealCVを使ってみた | Developers.IO
- UnrealCV-Experiments/test-cubemos-skeleton-tracking at master · mayosuke/UnrealCV-Experiments