今度はThreeDPoseTrackerではなくmediapipeのHolisticとHandsを使って、手や体の動きをトラッキングすることで、mediapipeとVSeeFace(Leap Motionなし)で、Webカメラだけでモーションキャプチャ―してVRMモデルを動かす実験をしました。
まだまだ試行錯誤している途中ですが、ここまでの記録をメモとして残しておきます。
※追記:今はこんなことをしなくても、ThreeDPoseTrackerだけで同等以上のことができます。
スポンサーサイト
やりたかったこと
Webカメラ1台の上半身の映像で、顔や手など上半身の動きをトラッキングして、VRMモデルを動かすことです。
要はバストアップのvtuberみたいなことを、Leap Motionや特別なVR機器なしで実現することです。
以前の記事では、腕の動きをThreeDPoseTrackerでトラッキングしましたが、本来ThreeDPoseTrackerは全身をトラッキングするものなので、上半身のトラッキングならもっと良いやり方がありそうだし、当時は手首や指の動きもトラッキングできなかったので、別のやり方を探していました。
そこでmediapipeを見つけたので、試してみることにしました。
今回はmediapipeの中でもHolisticとHands を使って実験しました。
mediapipeの説明は省略します。公式サイトを参照してください
Holistic - mediapipe
Hands - mediapipe
環境
・OS:Windows10
・Python:3.9.7
・mediapipe:0.8.9
・python-osc:1.8.0
基本方針
VSeeFaceは、機能や顔の動きの認識など、かなり優れているため、基本はこれを使わせていただいて、腕の動きをmediapipeのholisticで、手の動きをHandsで認識して、その情報をVSeeFaceにVMCプロトコルで送信してVRMモデルを動かす、という方針にしました。
一旦、下の図のような構成を想定してはじめました。
SplitCamとは、Webカメラの映像を複数のアプリケーションで利用できるソフトウェアです。
Live streaming software with multistreaming & free restreaming | SplitCam
VSeeFaceとmediapipeでWebカメラを使いたかったので、入れています。
今回は、この図の「何かプログラム」のところの説明です。
それ以外のところや、インストールの話は省略します。
説明の流れ
以降、
(1)mediapipeを呼び出す部分
(2)mediapipeから腕/手の座標を取得する部分
(3)取得した座標をVRMモデルの動きに変換する部分
(4)モデルの動きをVMCプロトコルで送信する部分
についての実験を簡単に説明します。
(1)mediapipeを呼び出す
mediapipeを呼び出す部分はPythonで作ることにしました。簡単そうだったので。
sample_holistic.py、sample_hand.pyというサンプルがあって、ほぼそのまま使わせて頂きました。
なので説明は省略します。
mediapipe-python-sample/
ちなみに、私はmodel_complexity=1で実行しています。
(2)mediapipeから腕/手の座標を取得する
(2-1)腕の座標の取得
Holisticの説明(Holistic - mediapipe)を見ると、POSE_WORLD_LANDMARKSというので、ワールド座標(x,y,z)を取得できそうです。
先ほどのサンプルを改変して、resultsのpose_world_landmarksで腕の座標を取得できました。
center = landmark_point[11][1]
child = landmark_point[13][1]
取得した値を見てみたところ、左右が逆でしたがそういうものっぽいです。
例えば、Pose - mediapipeによると、左肩(left_shoulder)は11番ですが、私の環境では12番でした。
(2-2)手の座標の取得
Holisticでは、手や指の座標は取得できないので、Handsで手の座標を取得します。
いつからかわかりませんが、HandsにもMULTI_HAND_WORLD_LANDMARKSという出力があって、これでワールド座標(x,y,z)を取得できようになっていました。
そこで、handsのサンプル mediapipe-python-sample/sample_hand.py at main ・ Kazuhito00/mediapipe-python-sample ・ GitHub を流用することにしました。
例えば、resultsにHandsの結果が格納されている場合、人差し指の付け根は、
for hand_landmarks, handedness in zip(results.multi_hand_world_landmarks, results.multi_handedness):という感じです。
if handedness.classification[0].label == "Left":
center = hand_landmarks.landmark[5]
child = hand_landmarks.landmark[6]
(3)取得した座標をVRMモデルの動きに変換する
このやり方がなかなかわからなくて、苦労しました。
pythonでVRMモデルを扱う方法が知らなかったのと、VRMモデルを動かすための「Quaternion」が全然わからなかったのと、さらにQuaternionをpythonで扱う方法も知らなかったためです。
そこで苦肉の策として、座標から動きへの変換はUnityで実現することにしました。
UnityだとVRMモデルを扱えるUniVRMが簡単に使えるし、Quaternionも扱いやすそうに見えたので。
この結果、下の図のような構成になりました。
オレンジ色の箇所ように、PythonとUnityの2つのプログラムに分けました。
ちょっと馬鹿っぽいですが、今回は実験なので良いことにします。
(3-1)PythonからUnityへのデータ送受信
やり方は、何でも良いと思います。
今回はあえて、VMCプロトコルの練習も兼ねて、VMCプロトコル(というか、Open Sound Control(osc)プロトコル)で送受信してみることにしました。
pythonでoscを使うには、pythonoscをpipでインストールして使いました。
送るデータは、送りたい座標とその子座標を送ってみました。
左肘のデータの場合、左肘が14番、子の左手首は16番です。
なんとなくこんな感じに。
center = landmark_point[14][1]通常のVMCプロトコルと区別するために、typeを"/VMC/Ext/Bone/Pos3d"にしました。
child = landmark_point[16][1]
client.send_message("/VMC/Ext/Bone/Pos3d", ["LeftLowerArm", center[0],center[1],center[2], child[0],child[1],child[2]])
メッセージは、一括して送った方が良いかなと思ったので、
msg = osc_message_builder.OscMessageBuilder(address="/VMC/Ext/Bone/Pos3d")と変更しました。
for val in ["LeftLowerArm", center[0],center[1],center[2], child[0],child[1],child[2]]:
msg.add_arg(val)
bundle.add_content(msg.build())
〜(他にもいろいろ送受信メッセージをbundleに追加)〜
bundle = bundle.build()
client.send(bundle)
unity側でVMCプロトコルを受信する部分は、VMC公式サンプルを流用しました。
VirtualMotionCaptureProtocol/SampleBonesReceive.cs at master ・ sh-akira/VirtualMotionCaptureProtocol ・ GitHub
これを、読み込んだVRMモデルに紐づけただけで、あっさり受信できました。
(3-2)座標からモデルを動かすところ
受信した座標からVRMモデルの動きに変換する部分はUnityで実現しました。
これが本当に難しかったです。いまだによくわかってません。
Quaternionを本質的にまだ理解しきれていないのですが、色々なサイトを読んで、それっぽく実装しました。
上記のSampleBonesReceive.csを改変しています。
まず、Start()のタイミングで、VRMモデルの各パーツの初期ベクトルを計算して保持します。
例えば、左ひじから手首の場合は、
target = animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);みたいな感じ。
child = animator.GetBoneTransform(HumanBodyBones.LeftHand);
fromDirection[HumanBodyBones.LeftLowerArm] = target.position - child.position;
次に、OnDataReceived()で、VRMモデルの各ボーンを回転させます。
mediapipeから取得した座標データで、Quaternion.FromToRotation()を使って回転させています。
Vector3 center = new Vector3((float)message.values[1], -(float)message.values[2], -(float)message.values[3]); // 中心みたいな感じの計算です。
Vector3 child = new Vector3((float)message.values[4], -(float)message.values[5], -(float)message.values[6]); // 子
HumanBodyBones bone;
if (Enum.TryParse((string)message.values[0], out bone))
{
Transform target = animator.GetBoneTransform(bone);
target.rotation = Quaternion.FromToRotation(fromDirection[bone], center - child);
}
これで大体の関節は動くようになりました。
ただ、手首の回転と親指の動きがうまくいかないので、まだ試行錯誤しています。
(4)モデルの動きをVMCプロトコルで送信する
UnityからVSeeFaceへのVMCプロトコルの送信部分も公式サンプルをそのまま使いました。
VirtualMotionCaptureProtocol/SampleBonesSendBundle.cs at master ・ sh-akira/VirtualMotionCaptureProtocol ・ GitHub
VSeeFace側でのVMCプロトコル受信は、以前にやったのと同じで良いと思います。
これと言って苦労はなかったです。
注意点は、pythonからUnityに送るときに使っているPortと違うPortを使うことくらいです。
結果
結果として、今のところこんな感じで動いています。
・腕や指の動きは、ある程度再現できた
・手首の動きがうまくできていない
・親指の動きも何か変な気がする。
・認識精度はまあまあ
・ポイントがカメラからはずれたときの動きが変
ほどほどの結果にはなったので、割と満足しています。
ただ、説明がわかりにくい部分も多々あると思います。もう諦めました・・・
ここまでやっておいて言うのもあれですが、素直にLeap Motion使った方が早い気もしています。私は使ったことないのでわかりませんが。
追記
ThreeDPoseTrackerがバージョンアップして、mediapipeでハンドトラッキングできるようになっていますので、ThreeDPoseTrackerを使うのも良いかなと思います。
追記2
私の書いたプログラムのソースコードの提供はお断りさせていただいております。
(再頒布に関する権利関係がよくわかっていないためです。)
参考にしたサイト様
・Holistic - mediapipe
・Hands - mediapipe
・Virtual Motion Capture Protocol (VMCProtocol 公式ページ)
・GitHub - Kazuhito00/mediapipe-python-sample
関連
・VSeeFace
・USBカメラモーションキャプチャーThreeDPoseTrackerの説明 - Qiita
以上
スポンサーサイト
タグ:mediapipe
もし可能でしたらこのメールアドレスに返事をください。よろしくお願いします
大変申し訳ないのですが、ソースコードやプロジェクトの提供は遠慮させてください。
今回のソースコードは、記事にも書いている通り、GitHub等にて公開されているソースコードを私が流用、改変したものです。
流用、改変したソースコードを再頒布する場合には、各流用元のライセンス規約に従う必要があるのですが、正直なところ、各ライセンスの確認や規約を遵守するのが面倒なため、私はソースコードを頒布しない方針にしております。
すみません。
お手数ですが、リンクしている流用元のコードを利用したり、私の記事を参考にしたりして、なんとかしていただければと思います。
よろしくお願いいたします。
>Hello worldさん
>
>僕は中学三年生で今media pipeと映像を用いた前後の運動の取得に人間の構造的な予測を組み合わせて、webカメラ一台で3Dのモーショントラッキングをしようと考えています。それの参考にしたいのでpythonプログラムとunityのプロジェクトをいただけませんでしょうか?
>もし可能でしたらこのメールアドレスに返事をください。よろしくお願いします