OpenSeeFaceとGodot4を繋ぐ
OpenSeeFace1で顔のランドマークを検出して、結果をソケット通信(UDP)で受け取ったときのメモです。
c#で受け取る例を示します。ついでに、pythonをGodotから実行する方法も載せておきます。
環境
Godot: v4.0.2.stable.official
Python: 3.9.9
OpenSeeFace: 9e07f6e1993022e7679f2e5ee3daec0ff617fac1
OpenSeeFaceとは
OpenSeeFaceは、MobileNetV3に基づいた顔のランドマーク検出モデルを実装したトラッキングライブラリです。 顔の特徴点(ランドマーク)を検出し、それを基に顔の動きを追跡することができます。
Webカメラ入力またはビデオファイルからの顔のトラッキングを行い、そのトラッキングデータをUDP経由で送信します。
この送信されたデータは3Dモデル、またはLive2Dモデルのアニメーションで利用できます。
このライブラリでは、複数のモデルを提供しており、ユーザーの環境に合わせて変更できます。
データの送信側
OpenSeeFaceではfacetracker.py
2を実行して起動します。
facetracker.py
にてsendtoによりトラッキングデータが送信されています。
デフォルトではipがlocalhost(127.0.0.1)、portは11573です。実行時の引数として与えることもできます。
packet
はbytearray()
によって作成され、packet.extend(bytearray(struct.pack("d", now)))
のようにデータを追加しています。
何のデータが出力されているかはlogファイルを確認するのが良いでしょう。少し多すぎるのでここでの記載は省略しますが、各フィードのメモをおまけとして追加しておきます。
Godotで受け取る
c#では、UDPのサーバーとしてUDPServer
3を使用できます。
公式ドキュメントを参考に、以下のようにOpenSeeFaceからデータを取得することができます。
extends Node
var server := UDPServer.func :
server.
func :
server.
if server. :
var peer = server.
var packet = peer.
取得したデータ(packet
)はbyte形式ですので、こちらを変換する必要があります。
そこで一旦、変換用の関数を定義しておきます。(もしかするともう少しいい変換方法があるかもです。)
func
var double: float = 0.0
if bytes. == 8:
var f64 = bytes.
double =
return double
func :
var integer: int = 0
if bytes. == 4:
var i32 = bytes.
integer =
return integer
func :
var float_number: float = 0.0
if bytes. == 4:
var f32 = bytes.
float_number =
return float_number
あとはいい感じに受け取ったデータを区切って、辞書型に保存する関数を定義します。
これでdata
を取得することで特徴データを使用できます。
func
var data = {}
var index = 0
var timestamp_bytes = packet.
var timestamp =
data = timestamp
index += 8
var face_id_bytes = packet.
var face_id =
data = face_id
index += 4
var width_bytes = packet.
var width =
data = width
index += 4
var height_bytes = packet.
var height =
data = height
index += 4
data =
data.
index += 4
data.
index += 4
var success_bytes = packet.
var success =
data = success
index += 1
var pnp_error_bytes = packet.
var pnp_error =
data = pnp_error
index += 4
var quaternions =
for i in :
quaternions.
data = quaternions
var eulers =
for i in :
eulers.
data = eulers
index += 12
var translations =
for i in :
translations.
data = translations
index += 12
index += 66 * 3 * 4 # Landmark
index += 66 * 3 * 4 # Point3D
index += 18 * 4 # ???
var features =
data = {}
for i in :
data =
index += 4
return data
pythonをGodotから呼ぶ
いちいち、facetracker.py
を起動して、Godotでパペットを起動してとするのは非常に手間がかかります。
そこでmainのスクリプトなどで最初に実行してしまうことにします。
OS.execute
4を使うと、OpenSeeFaceが停止するまで、それ以降の処理が実行されません。
そのため、OS.create_process
5を使用して、新しいプロセスを作成し、実行します。
const python_cmd = "<path to python>"
func :
var output =
var args =
var pid = OS.
return pid
これで実行はされます。しかし、これだとGodotが終了してもプロセスが実行され続けてしまいます。
_notification
関数内でNOTIFICATION_WM_CLOSE_REQUEST
を使用してウィンドウが閉じられたことを検出します。。
あとは、実行時に取得したpidを使ってOS.kill
によってkillします。
func
if what == NOTIFICATION_WM_CLOSE_REQUEST:
.
OS.
.
おまけ: パペット風の出力例
上記スクリプトを使って、ためしにパペットを作ってみました。
取得した特徴データのうち目の開閉と口の開閉、加えて顔の傾きを使用して出力しています。
顔トラッキング60fpsで動作確認した👀👄#GODOT4 #OpenSeeFace pic.twitter.com/C7iFZoYZJH
— kosh (@llbxg) May 8, 2023
おまけ: 特徴データのメモ
各フィードのメモです。
- Frame, Time: 映像フレームのIDとそのタイムスタンプ。
- Width, Height: 映像の幅と高さ。
- FPS: 映像のフレームレート(フレーム・パー・セカンド)。
- Face, FaceID: 顔の検出とその識別子。
- RightOpen, LeftOpen: 右目と左目が開いているかどうかの指標。
- AverageConfidence: 検出された顔の信頼度の平均。
- Success3D, PnPError: 3D顔再構成の成功とそのエラー。
- RotationQuat.X, Y, Z, W: 顔の回転を表すクォータニオン。
- Euler.X, Y, Z: オイラー角による顔の回転。
- RVec.X, Y, Z, TVec.X, Y, Z: 顔の位置と姿勢を表すロドリゲスベクトルと平行移動ベクトル。
- Landmark[i].X, Y, Confidence: i番目の顔のランドマーク(特徴点)の位置とその信頼度。
- Point3D[i].X, Y, Z: i番目の3Dランドマークの位置。
- eye_l, eye_r: 左目と右目の開き具合。
- eyebrow_steepness_l, eyebrow_updown_l, eyebrow_quirk_l, eyebrow_steepness_r, eyebrow_updown_r, eyebrow_quirk_r: 左右の眉の特性(傾き、上下動、特異な動き)。
- mouth_corner_updown_l, mouth_corner_inout_l, mouth_corner_updown_r, mouth_corner_inout_r: 口角の上下動と内外動。
- mouth_open, mouth_wide: 口の開き具合と幅。