AI

TensorFlow.jsでフェイストラッキングを実装する手順|478点ランドマークと旧APIからの移行

ブラウザのカメラ映像から顔の位置と表情の動きを取り出す処理は、TensorFlow.jsと公式配布モデルの組み合わせでサーバーを介さずに実装できます。ただしWeb上に残る実装例の多くは2021年前後の facemesh パッケージを前提にしており、現行の face-landmarks-detection とはAPIも戻り値も違います。ここでは2026年8月時点の実パッケージ構成に沿って、モデルの選び分け、カメラ入力から推論までの最小実装、旧APIからの移行、処理落ちの切り分けまでを扱います。TensorFlow.js自体の導入方法やテンソル操作の基礎はTensorFlow.jsとは?ブラウザとNode.jsで動かすJavaScript機械学習の導入と使い方にまとめてあります。

まとめ:TensorFlow.jsフェイストラッキング実装の要点

顔の枠だけが必要なら @tensorflow-models/face-detection(6点)、表情や視線まで扱うなら @tensorflow-models/face-landmarks-detection(478点)を選びます。両者とも createDetector() でモデルを作り estimateFaces() で推論する共通インターフェースで、旧 facemesh.load()scaledMesh は現行APIには存在しません。runtime には mediapipetfjs の2択があり、CDN資産を追加で読み込めるなら前者が高速です。フレーム落ちが出た場合は解像度を下げる前に、まずバックエンドがWebGLに乗っているかを tf.getBackend() で確認します。なお tfjs系の顔モデルは2024年10月を最後に更新が止まっているため、新規案件では後継のMediaPipe Tasksを先に検討する価値があります。以下、判断基準と実装を順に見ていきます。

顔検出とランドマーク検出の使い分け(6点と478点の判断基準)

TensorFlow.jsから使える顔モデルは、公式のtfjs-modelsリポジトリで2種類に整理されています。用途を取り違えるとモデルサイズと処理時間を無駄に払うことになるため、最初にどちらが要るのかを決めます。

パッケージ 最新版 モデル keypoints 主な用途
@tensorflow-models/face-detection 1.0.3 MediaPipeFaceDetector 6 顔の枠取り・人数カウント・モザイク
@tensorflow-models/face-landmarks-detection 1.0.6 MediaPipeFaceMesh 478 表情・顔の向き・視線・ARフィルター
@tensorflow-models/blazeface 0.1.0 BlazeFace(旧単体) 6 新規採用は非推奨

face-detectionが返す6点は右目・左目・鼻・口・右耳・左耳の位置で、顔がどこにあるかは分かっても、まぶたの開閉や口角の動きは取れません。表情に反応するエフェクトや視線推定を実装するなら478点のFaceMeshが必要です。478点の内訳は顔メッシュ468点に虹彩10点を加えたもので、公式READMEにも「MediaPipeFaceMesh returns 478 keypoints」と明記されています。旧来の資料が「468点」と書いているのは虹彩モデルを含まない世代の数値です。

BlazeFaceの単体パッケージ @tensorflow-models/blazeface は2023年8月の0.1.0で止まっており、同じモデルは face-detection の MediaPipeFaceDetector から使えます。新規に書くコードで単体パッケージを選ぶ理由はありません。

パッケージ構成と導入手順(tfjs 4.22.0系)

CDN読み込みとnpmインストールの選択

ビルド環境を持たない検証ページならCDN、アプリケーションに組み込むならnpmです。tfjs本体とモデルパッケージはバージョン系列が別なので、まとめて上げようとしないでください。

npm install @tensorflow/[email protected] \
            @tensorflow-models/[email protected] \
            @mediapipe/face_mesh

@mediapipe/face_mesh は次に述べる runtime: 'mediapipe' を選んだときだけ必要になるモデル資産です。このパッケージ自体は0.4.1633559619(2021-10-06)で更新が止まっていますが、solutionPathから読む静的資産なので、バージョンが古いこと自体は動作の障害になりません。

runtime指定:mediapipeとtfjsの判断基準

createDetectorの detectorConfig.runtime には 'mediapipe''tfjs' を指定できます。mediapipeはWebAssembly実装のMediaPipeソリューションを直接呼ぶ経路で、solutionPath(CDNまたは自ホスト)から追加資産を取得します。tfjsはTensorFlow.jsのバックエンド上でモデルを走らせる経路で、追加の外部資産が不要な代わりに、tfjsのバックエンド初期化に依存します。

社内ネットワークから外部CDNへ出られない、あるいは配信資産を自社ドメインに寄せたい要件があるなら 'tfjs' を選びます。それ以外は 'mediapipe' で構いません。

import * as faceLandmarksDetection from '@tensorflow-models/face-landmarks-detection';

const detector = await faceLandmarksDetection.createDetector(
  faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh,
  {
    runtime: 'mediapipe',
    solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh',
    refineLandmarks: true,
  }
);

refineLandmarks をtrueにすると虹彩の10点まで推定され、keypointsが478点になります。falseのままだと468点に留まるため、視線方向を扱う実装ではtrueが前提です。

カメラ映像から推論までの最小実装

getUserMediaの実行条件とHTTPS制約

navigator.mediaDevices.getUserMedia() はセキュアコンテキストでのみ利用できます。具体的にはHTTPS、または http://localhost です。開発機のIPアドレス(http://192.168.x.x など)で他端末から開くと navigator.mediaDevices がundefinedになり、カメラ権限のダイアログすら出ません。スマートフォン実機で確認する段階でここに引っかかる例が多いので、検証用の証明書か、localhostへのポートフォワードを先に用意しておきます。

const video = document.querySelector('video');
const stream = await navigator.mediaDevices.getUserMedia({
  video: { width: 640, height: 480, facingMode: 'user' },
});
video.srcObject = stream;
await video.play();

estimateFacesの戻り値とkeypointsの読み取り

推論は detector.estimateFaces(video) にvideo要素をそのまま渡せます。テンソルへの変換を自前で書く必要はありません。戻り値は検出した顔ごとの配列で、各要素が box(xMin/xMax/yMin/yMax/width/height)と keypoints を持ちます。

async function renderLoop() {
  const faces = await detector.estimateFaces(video, { flipHorizontal: false });
  for (const face of faces) {
    for (const kp of face.keypoints) {
      // kp.x, kp.y はピクセル座標、kp.z は深度、kp.name は主要点のみ付与
      ctx.fillRect(kp.x, kp.y, 2, 2);
    }
  }
  requestAnimationFrame(renderLoop);
}
renderLoop();

keypointsの name は全478点に付くわけではなく、rightEyeやlipsUpperInnerなど主要な点にのみ設定されます。特定の部位だけを追う実装では、名前ではなくインデックスで参照する方が安定します。ループは setInterval ではなく requestAnimationFrame を使い、推論が終わってから次のフレームを要求する形にします。推論時間より短い間隔で呼ぶと、処理待ちのフレームが積み上がって遅延が伸び続けます。

旧facemeshパッケージからの移行手順

2021年前後の記事やサンプルは @tensorflow-models/facemesh を前提にしています。このパッケージは非推奨で、face-landmarks-detectionに統合されました。移行で書き換えが必要な箇所は3つです。

旧API 現行API
facemesh.load() faceLandmarksDetection.createDetector(SupportedModels.MediaPipeFaceMesh, config)
model.estimateFaces(video) detector.estimateFaces(video)(引数の形が変更)
prediction.scaledMesh([x,y,z]の配列) face.keypoints({x, y, z, name}のオブジェクト配列)

移行で最も事故が多いのが3行目です。旧 scaledMesh[x, y, z] の数値配列だったため point[0]point[1] でアクセスするコードが書かれています。現行の keypoints はオブジェクトなので、同じ添字で読むとundefinedになり、描画が無反応になるだけでエラーは出ません。boundingBox.topLeft も現行では box.xMinbox.yMin に変わっています。

インデックス番号そのものは旧 scaledMesh と現行 keypoints で共通のFaceMeshトポロジに従うため、部位を指すインデックス定数はそのまま流用できます。書き換えるのは取得方法だけです。

バックエンド選択とフレーム落ちの切り分け(WebGL・WebGPU・WASM)

フレーム落ちが出たとき、解像度やモデルを触る前に確認するのはバックエンドです。runtime: 'tfjs' の場合、TensorFlow.jsは利用可能なバックエンドを自動選択しますが、WebGLコンテキストの取得に失敗すると黙ってCPUへ落ちます。CPUバックエンドでのFaceMesh推論は実用速度になりません。

await tf.ready();
console.log(tf.getBackend()); // 'webgl' が出るか確認する

WebGPUを使う場合は @tensorflow/tfjs-backend-webgpu 4.22.0 を追加で読み込み、await tf.setBackend('webgpu') を明示します。WebGLの詳細はWebGLとは?仕組み・できること・対応ブラウザとUnityビルドまで解説で扱っています。

バックエンドがWebGLで、それでも重い場合の効き目は、入力解像度の削減が最も大きく出ます。640×480を320×240に落とすと処理量はおよそ4分の1になります。検出する顔の数を maxFaces で1に固定するのも有効です。既定値は複数顔を想定しているため、単一ユーザー向けのアプリでは無駄が生じます。

TensorFlow.jsでの顔追跡を採用すべきでない場面

新規案件では、tfjs経由の顔モデルを第一候補にしないことを勧めます。理由は更新の停止です。@tensorflow-models/face-landmarks-detectionface-detection はいずれも2024年10月10日を最後に公開が止まり、TensorFlow.js本体(4.22.0)も2024年10月21日で更新されていません。一方で同じモデルを提供するMediaPipe側の @mediapipe/tasks-vision は1.0.1が2026年7月31日に公開されており、こちらが実質的な現行系です。既存のtfjsアプリに顔検出を足すのでなければ、MediaPipe Tasksを直接使う構成を先に評価してください。MediaPipeそのものの位置づけはMediaPipe(メディアパイプ)とは?Google製AIライブラリの機能・読み方・商用利用をわかりやすく解説にまとめてあります。

もう1つ、本人確認やなりすまし防止の用途にこれらのモデルを流用してはいけません。FaceMeshが返すのは顔の形状の点群で、写真やディスプレイに表示した顔と実在の人物を区別する仕組みを持ちません。印刷した顔写真をカメラに向けても478点は問題なく取れます。生体検知が要件に入る場合は、その目的の専用サービスを使う設計にします(FaceLivenessDetectorコンポーネントの利用となりすまし防止の実践方法)。

プライバシー面では、ブラウザ内で推論が完結し映像が外部に出ないことが、この構成の実務上いちばんの利点です。撮影映像をサーバーへ送らない設計は、同意取得や保管期間の設計負担を減らします。この考え方はエッジAIとは?クラウドAIとの違い・仕組み・実装の判断基準を解説で扱う判断軸と同じです。

よくある質問(FAQ)

TensorFlow.jsのフェイストラッキングは無料で使えますか?

TensorFlow.js本体とtfjs-modelsの顔モデルはApache License 2.0で公開されており、商用利用も可能です。利用料は発生せず、推論はすべて利用者の端末上で動くためサーバー費用もかかりません。ただしライセンス表記の保持義務があるため、配布物のライセンス一覧には含めてください。

FaceMeshの特徴点は468点ですか、478点ですか?

現行の @tensorflow-models/face-landmarks-detection 1.0.6 の公式READMEでは478点です。内訳は顔メッシュ468点と虹彩10点で、虹彩の10点は refineLandmarks: true を指定したときに加わります。468点という記述は虹彩モデルを含まない旧世代の情報です。

スマートフォンのブラウザでも動きますか?

iOS SafariとAndroid Chromeのいずれでも動作しますが、前提としてページがHTTPSで配信されている必要があります。getUserMedia はセキュアコンテキスト限定のため、開発機のIPアドレス直打ちでは権限ダイアログが出ません。実機確認の前にHTTPS環境を用意してください。

旧facemeshのサンプルコードがそのまま動かないのはなぜですか?

facemesh.load()prediction.scaledMesh は現行パッケージには存在しないためです。createDetector()face.keypoints に置き換えます。scaledMeshは数値配列、keypointsはオブジェクト配列と型も違うので、point[0] のような添字アクセスはプロパティ名アクセスへ書き換える必要があります。

顔検出だけで表情は不要な場合、どちらのパッケージを使いますか?

@tensorflow-models/face-detection 1.0.3 を使います。返るkeypointsは6点で、モデルサイズも推論時間もFaceMeshより小さく済みます。顔にモザイクをかける、人数を数えるといった用途はこれで足ります。

関連記事

資料請求

RELATED POSTS 関連記事