技術実装
KDDI のデベロッパーは、Mediapipe の強力で効率的な Python パッケージを使用することで、パフォーマーの顔の特徴を検出し、52 個のブレンドシェイプをリアルタイムで抽出できました。
import mediapipe as mp
from mediapipe.tasks import python as mp_python
MP_TASK_FILE = "face_landmarker_with_blendshapes.task"
class FaceMeshDetector:
def __init__(self):
with open(MP_TASK_FILE, mode="rb") as f:
f_buffer = f.read()
base_options = mp_python.BaseOptions(model_asset_buffer=f_buffer)
options = mp_python.vision.FaceLandmarkerOptions(
base_options=base_options,
output_face_blendshapes=True,
output_facial_transformation_matrixes=True,
running_mode=mp.tasks.vision.RunningMode.LIVE_STREAM,
num_faces=1,
result_callback=self.mp_callback)
self.model = mp_python.vision.FaceLandmarker.create_from_options(
options)
self.landmarks = None
self.blendshapes = None
self.latest_time_ms = 0
def mp_callback(self, mp_result, output_image, timestamp_ms: int):
if len(mp_result.face_landmarks) >= 1 and len(
mp_result.face_blendshapes) >= 1:
self.landmarks = mp_result.face_landmarks[0]
self.blendshapes = [b.score for b in mp_result.face_blendshapes[0]]
def update(self, frame):
t_ms = int(time.time() * 1000)
if t_ms <= self.latest_time_ms:
return
frame_mp = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame)
self.model.detect_async(frame_mp, t_ms)
self.latest_time_ms = t_ms
def get_results(self):
return self.landmarks, self.blendshapes
Firebase Realtime Database に、52 個のブレンドシェイプに関する float 値のコレクションが格納されます。各行は、順番にリストされたブレンドシェイプに対応しています。
_neutral,
browDownLeft,
browDownRight,
browInnerUp,
browOuterUpLeft,
...
これらのブレンドシェイプ値は、カメラが起動して FaceMesh モデルが実行されている間、リアルタイムで継続的に更新されます。データベースは各フレームで最新のブレンドシェイプ値を反映し、FaceMesh モデルによって検出された顔の表情の動的な変化をキャプチャします。
ブレンドシェイプ データを抽出したら、次のステップではそのデータを Firebase Realtime Database に送信します。この高度なデータベース システムを活用することで、リアルタイム データがシームレスにクライアントに転送され、サーバーのスケーラビリティに関する懸念が解消されます。そのため KDDI は、ユーザー エクスペリエンスを効率化することに集中できます。
import concurrent.futures
import time
import cv2
import firebase_admin
import mediapipe as mp
import numpy as np
from firebase_admin import credentials, db
pool = concurrent.futures.ThreadPoolExecutor(max_workers=4)
cred = credentials.Certificate('your-certificate.json')
firebase_admin.initialize_app(
cred, {
'databaseURL': 'https://your-project.firebasedatabase.app/'
})
ref = db.reference('projects/1234/blendshapes')
def main():
facemesh_detector = FaceMeshDetector()
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
facemesh_detector.update(frame)
landmarks, blendshapes = facemesh_detector.get_results()
if (landmarks is None) or (blendshapes is None):
continue
blendshapes_dict = {k: v for k, v in enumerate(blendshapes)}
exe = pool.submit(ref.set, blendshapes_dict)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
exit()
さらに、デベロッパーは、ブレンドシェイプ データを Firebase Realtime Database から Google Cloud の Immersive Stream for XR インスタンスにリアルタイムでシームレスに送信します。Google Cloud の Immersive Stream for XR は、クラウド側で Unreal Engine プロジェクトを実行し、写真のようにリアルで没入感のある 3D や拡張現実(AR)エクスペリエンスをリアルタイムでスマートフォンやブラウザにストリーミングしてレンダリングするマネージド サービスです。
この統合により KDDI は、キャラクターのフェイシャル アニメーションを最小限のレイテンシでリアルタイムにストリーミングし、没入型のユーザー エクスペリエンスを確実に提供できるようになりました。
Immersive Stream for XR によって実行される Unreal Engine 側では、Firebase C++ SDK を使用して Firebase からデータをシームレスに受信します。データベース リスナーを確立することにより、Firebase Realtime Database のテーブルで更新が発生したらすぐにブレンドシェイプ値を取得できます。この統合により、最新のブレンドシェイプ データにリアルタイムでアクセスできるようになるため、Unreal Engine プロジェクトにおいて動的で応答性の高いフェイシャル アニメーションを実現できます。
Firebase SDK からブレンドシェイプ値を取得したら、アニメーション ブループリントの『Modify Curve』ノードを使用して、Unreal Engine でフェイシャル アニメーションを実施します。各ブレンドシェイプ値はフレームごとにキャラクターに割り当てられるため、キャラクターの表情をリアルタイムで正確に制御できます。
Unreal Engine にリアルタイム データベース リスナーを効果的に実装するには、代替のシングルトン パターンとして GameInstance サブシステムを利用します。これにより、データベース接続、認証、バックグラウンドでの継続的なデータ受信の処理を担う専用の BlendshapesReceiver インスタンスを作成できます。
GameInstance サブシステムを利用すると、BlendshapesReceiver インスタンスを作成して、ゲーム セッションの存続期間全体で維持できます。これにより、受信したブレンドシェイプ データを使用してアニメーション ブループリントがフェイシャル アニメーションを読み取って実施している間、永続的なデータベース接続が確保されます。
KDDI は、MediaPipe を実行するローカル PC 1 台だけで、実際のパフォーマーの表情と動きをキャプチャし、質の高い 3D リターゲット アニメーションをリアルタイムで作成することに成功しました。