Mediapipeのフェイスメッシュを使って他人の顔画像に自分の顔の動きを反映させる
はじめに
MediapipeにはFaceMeshの機能があり、顔の画像からフェイスメッシュを作成することができます。じゃあこのまま画像通りのテクスチャをメッシュに張り付け、自分の顔の動きをメッシュに反映させることができればおもしろいのでは?と考えました。
また、今回はMediapipeはPythonで動かし、Python側とUnity側で通信を行うようにしました。
FaceMeshについて
大体こんな機能です。見づらいですが・・・。(青のメッシュがあります)
使う画像
こちらからお借りしました
処理の流れ
以下のように行います。
画像からFaceMeshのランドマークを取得する。
ランドマークの沿ってUnityでMeshを作成(頂点を設置、uvの設定を行う)。
自分の顔の動きをFaceMeshで推定し、その情報をMeshの頂点情報に変換する。
画像からFaceMeshのランドマークを取得する
割愛します。
こちらの記事と大体同じように実行しています。
画像から取得できた情報は
{"Index": 0, "Point": {"x": 0.4969289302825928, "y": 0.7072110176086426, "z": -0.04640699923038483}},
といった形で、正規化されているものを出力するようにします。ここの出力はファイルに出力しておきます。(以降、頂点情報のファイルと呼ぶことにします)
この形の出力にする理由としてはUnity側で用意したクラスに合うようにしています。なので、特にこれに縛られる必要はありません。
さらに追加で画像の四隅分の4点を追加しています。
一応結果の一部を載せます
[{"Index": 0, "Point": {"x": 0.4969289302825928, "y": 0.7072110176086426, "z": -0.04640699923038483}}, {"Index": 1, "Point": {"x": 0.5046584606170654, "y": 0.6278799772262573, "z": -0.10028451681137085}}, {"Index": 2, "Point": {"x": 0.500106692314148, "y": 0.6438664793968201, "z": -0.04985152184963226}}, {"Index": 3, "Point": {"x": 0.4852482080459595, "y": 0.555286705493927, "z": -0.08173280954360962}}...]
ランドマークの沿ってUnityでMeshを作成
動的なメッシュの作成についてはこちらで
各頂点についてはMediapipeから得られた値をそのまま使用しました。元画像が1:1なので特に見ために変化はありませんでした。
SetTrianglesに指定する値についてはこちらのファイルを参照しました。
自分のUnity上のにおいてある状態だとメッシュの向きが逆になったため、配列を反転して使用しています。
これは顔の周りだけの情報になりますので、さらに顔の外側の頂点と画像の四隅を繋ぐような組み合わせも足しておきます。
追加分
234, 127, 478, 127, 162, 478, 162, 21, 478, 21, 54, 478, 54, 103, 478, 103, 67, 478, 67, 109, 478, 109, 10, 478,
10, 338, 481, 338, 297, 481, 297, 332, 481, 332, 284, 481, 284, 251, 481, 251, 389, 481, 389, 356, 481, 356, 454, 481,
454, 323, 480, 323, 361, 480, 361, 288, 480, 288, 397, 480, 397, 365, 480, 365, 379, 480, 379, 378, 480, 378, 400, 480, 400, 377, 480, 377, 152, 480,
152, 148, 479, 148, 176, 479, 176, 149, 479, 149, 150, 479, 150, 136, 479, 136, 172, 479, 172, 58, 479, 58, 132, 479, 132, 93, 479, 93, 234, 479,
10, 481, 478, 454, 480, 481, 152, 479, 480, 234, 478, 479
uvの値についてもMediapipeから得られた値をそのまま使用しました。uvに渡す値は正規化したものが必要になりますが、Mediapipeから得られた値は正規化されているため、値をそのまま入れることができます。
自分の顔の動きをFaceMeshで推定し、その情報をMeshの頂点情報に変換する
ここでもやっていることは上記のものとほぼ変わりありません。
Mediapipeから得られた値でverticesの再設定を行うのみです。
結果
顔以外の部分についてはSetTrianglesの設定によってかなり角刈りっぽくなってしまいました・・・。ただ顔だけ見てみると左を向いたときはそれっぽく見えてますね。元画像が左右で光の当たり方が違うので違和感があります。
顎などのランドマークからの相対位置を使って頂点を移動させるともう少し良くなりそうですね。