こんにちは、@d_etteiu8383です。この記事は夏のブログリレー 2022年 28日目の記事です。
概要
本記事ではMediaPipeのFaceMeshとKalidoKitを利用して、ブラウザ上でリアルタイムに顔認識を行う方法について説明します。
イントロ
突然ですが本日9月6日が何の日だかご存じでしょうか?そうです、真中のんさんの誕生日ですね。
彼女はアニメ『プリパラ』の登場人物の一人ですが、このアニメは3DCGを利用したライブシーンが特徴的ですよね。3Dと言えば<!-- ここにこじつけ理由を書く -->というわけで本記事ではMediaPipeのFaceMeshとKalidoKitを利用した、ブラウザ上でのリアルタイム顔認識について説明します。
MediaPipe Face Mesh
overview: https://google.github.io/mediapipe/solutions/face_mesh.html
GitHub: https://github.com/google/mediapipe
MediaPipe Face Mesh is a solution that estimates 468 3D face landmarks in real-time even on mobile devices. It employs machine learning (ML) to infer the 3D facial surface, requiring only a single camera input without the need for a dedicated depth sensor. Utilizing lightweight model architectures together with GPU acceleration throughout the pipeline, the solution delivers real-time performance critical for live experiences.
https://google.github.io/mediapipe/solutions/face_mesh.html
MediaPipe Face Meshは、機械学習を用いてカメラ入力からリアルタイムに3D顔表面位置を推定することができるライブラリです。
↓公式ページで紹介されているデモ動画(https://google.github.io/mediapipe/solutions/face_mesh#face-detection-model)↓
このデモでは、眉や虹彩、口の位置など計468個の"ランドマーク(目印となる点, 特徴点)"の位置を推定し、推定結果を描画しています。
↓MediaPipe Face Meshで得られる推定データの例↓
{
"image": {
中略
},
"multiFaceGeometry": [],
"multiFaceLandmarks": [
[
{
"x": 0.5204985737800598,
"y": 0.6729813814163208,
"z": -0.05491212010383606
},
{
"x": 0.5279504656791687,
"y": 0.589737594127655,
"z": -0.10225235670804977
},
中略
{
"x": 0.6175460815429688,
"y": 0.4767884314060211,
"z": 0.014705833978950977
}
]
]
}
KalidoKit
GitHub:https://github.com/yeemachine/kalidokit
Kalidokit is a blendshape and kinematics solver for Mediapipe/Tensorflow.js face, eyes, pose, and hand tracking models, compatible with Facemesh, Blazepose, Handpose, and Holistic. It takes predicted 3D landmarks and calculates simple euler rotations and blendshape face values.
https://github.com/yeemachine/kalidokit#face-pose-and-hand-tracking-calculator
MediaPipe Face Meshによる推定で得られるのは顔表面のランドマークの位置(="空間上の座標"の配列)であり、そのままでは扱いづらい形式となっています。顔認識を利用したい場面で求められるのは、たいていの場合"口角がどの程度上がっているか"や"目をどれぐらい閉じているか"、"どこを向いているか"といった情報です。このような情報は、ランドマーク間の位置関係から"うまく計算して"求める必要があります。今回、この計算をKalidoKitで行います。
KalidoKitは3DのVRMモデルとLive2Dアバターを動かすために必要なデータを得ることに特化して設計されており、いわゆる"Vtuber"的なことがしたいときに用いることが想定されています。Live2DやVRMを利用するサンプルコードがGlitchで公開されています。
MediaPipe Face Meshで推定したランドマーク位置をKalidoKitのソルバ("上手く計算して"くれる子)に渡してあげると、以下のような結果が得られます(https://github.com/yeemachine/kalidokit#outputs)。
// Kalidokit.Face.solve()
// Head rotations in radians
// Degrees and normalized rotations also available
{
eye: {l: 1,r: 1},
mouth: {
x: 0,
y: 0,
shape: {A:0, E:0, I:0, O:0, U:0}
},
head: {
x: 0,
y: 0,
z: 0,
width: 0.3,
height: 0.6,
position: {x: 0.5, y: 0.5, z: 0}
},
brow: 0,
pupil: {x: 0, y: 0}
}
x
やy
は座標や回転、mouth.shape
は口の形がどの母音の状態に近いかのパラメータ、eye.l
やeye.r
は目が明いているか閉じているか、などを表しています。
このように、ランドマークの座標データから、扱いやすいパラメータに変換することができます。
最終的に作れるもの
上手くいろいろすると下記ツイートのようなものが作れます。
ブラウザで顔認識してアイコン動かすテスト pic.twitter.com/GYo1vTvzZ2
— でっていう (@d_etteiu8383) July 20, 2022
上記のデモはhttps://you.eyemono.moeでお使いのブラウザから試すことができます。
手順
今回はVite + TypeScriptを利用した最低限のデモを作成します。
最低限のコードでの例であり、VueやReactなどのフレームワーク・ライブラリは利用していません。基本的には公式のドキュメントに従っているので前述した公式ページを見たほうがわかりやすいかも。
環境
- Windows10
- Node.js 16.15.1
- npm 8.17.0
- TypeScript 4.6.4
- vite 3.0.7
- mediapipe/camera_utils 0.3.1640029074
- mediapipe/drawing_utils 0.3.1620248257
- mediapipe/face_mesh 0.4.1633559619
- kalidokit 1.1.5
準備
viteの準備です(f7ca8f2)。
npm create vite@latest face-detect-example -- --template vanilla-ts
cd face-detect-example
npm install
npm run dev
準備できたら一旦不要部分を全部削除しましょう(75c2b6a)。
MediaPipe Face Mesh
インストール
本体である@mediapipe/face_mesh
に加え、カメラを簡単に扱うための@mediapipe/camera_utils
と、推定結果をきれいに描画するための@mediapipe/drawing_utils
も同時にインストールします。
npm i @mediapipe/face_mesh @mediapipe/camera_utils @mediapipe/drawing_utils
カメラの起動 + 顔認識・推定 + 推定結果描画
JSでの使用例が公式で紹介されている(https://google.github.io/mediapipe/solutions/face_mesh#javascript-solution-api)ため、これをまるまるTSで書きなおしました(d3a8226)。
簡単のため1ファイルにまとめています。
これだけで、下の動画のようにランドマークの検出ができます。便利~
内容を簡単に説明すると、
- video要素とcanvas要素の準備(21-27行目)
- faceMeshインスタンスの作成(67-72行目)
- faceMeshの設定(74-81行目)
- faceMeshによる推定が完了したときに実行するコールバック関数(
onResults
)の登録(84行目)onResults
内ではcanvasに検出結果を描画する処理を記述している(31-64行目)
- cameraインスタンスの作成(86-93行目)
- カメラスタート(95行目)
といった感じです。
このデモではvideo要素にカメラ映像を表示し、canvasに推定結果を描画しています。CSSでvideoとcanvasが重なるようにし、videoにはぼかしを入れています(https://github.com/detteiu8383/face-detect-example/blob/d3a82261c200844178dfb31cadac4c944f939abc/src/style.css)。
Kalidokit
インストール
npm i kalidokit
ソルバでの計算
READMEにある通り、Kalidokit.Face.solve()
にMediaPipe Face Meshの推定結果を渡すだけです(0c597a3)。簡単すぎ~
↓出力の例↓
{
"head": {
"y": -0.11274997287705743,
"x": -0.2229431972868542,
"z": 0.07574746454444863,
"width": 160.17577586460894,
"height": 120.18492581986624,
"position": {
"x": 208.53217542171478,
"y": 224.71441328525543,
"z": 48.97199869155884
},
"normalized": {
"y": -0.035889431033721636,
"x": -0.07096502375382895,
"z": 0.024111166817854163
},
"degrees": {
"y": -6.460097586069894,
"x": -12.77370427568921,
"z": 4.340010027213749
}
},
"eye": {
"l": 1,
"r": 1
},
"brow": 0,
"pupil": {
"x": -0.25158695791085445,
"y": -0.4272811294357963
},
"mouth": {
"x": -0.6,
"y": 0,
"shape": {
"A": 0,
"E": 0,
"I": 0,
"O": 0,
"U": 0
}
}
}
この例では計算結果をそのままコンソールに表示していますが、実際に使用する場合はこの値を用いてコンテンツを動的に変化させましょう。
応用例
数字を算出しただけでは何も面白くないので、何か動くモノを作ってみましょう。
で、作ったのが冒頭にも挙げたhttps://you.eyemono.moeです(GitHub:https://github.com/detteiu8383/svg-face)。
このページでは、Kalidokitで算出したパラメータを用いてSVGをアニメーションさせています。
https://github.com/detteiu8383/svg-face/blob/main/src/components/face/Face.tsxやhttps://github.com/detteiu8383/svg-face/blob/main/src/utils/animateFace.tsを見るとわかるのですが、SVGのtransform属性を用いたり、複数のパスの頂点や制御点を線形補完したりして無理やり動かしてます...
head.degrees.z
の値を使って顔を傾けたり、shape.eye.l
/shape.eye.r
の値(目の開き具合)の値を使って"閉じた状態の目"と"開いた状態の目"の制御点位置をブレンドしたりしています。
まとめ
以上、MediaPipeとKalidoKitを利用したリアルタイム顔認識方法の紹介でした。
キャラクターイラスト/3Dモデルを動かすことに縛られず、柔軟な発想でいろいろやってみたいですね。覗き込むように顔を動かさないと見えない文章とか、目を閉じてるときにだけこっそり表示される画像とか。皆さんもぜひ活用してみてはいかがでしょうか?
最後までお読みいただきありがとうございました。夏のブログリレー 明日の担当者は@inutamago_dogeggさんです!楽しみ~