👏

[Next.js] tensorflow.js + WebCameraで顔の頂点情報を取得

2022/11/27に公開

はじめに

顔検出を実装する機会があったのシェアさせていただきます。

環境
electron + next.js + typescript
node-version 16.0.0

tensorflow.js
https://www.tensorflow.org/js

import

yarn add @tensorflow/tfjs
yarn add @tensorflow-models/face-landmarks-detection

https://www.npmjs.com/package/@tensorflow/tfjs
https://www.npmjs.com/package/@tensorflow-models/face-landmarks-detection

code

FaceTrack.ts

import React, { useRef, useEffect } from "react";
import * as tf from "@tensorflow/tfjs";
import * as faceLandmarksDetection from '@tensorflow-models/face-landmarks-detection';

export default function FaceTrack() {
	const videoRef = useRef(null)
	let video;
	let detector;

	useEffect(() => {
	    init();
	}, []);

	const init = async () => {
		const width = 640
		const height = 360

		video = videoRef.current
		video.id       = 'video';
		video.width    = width;
		video.height   = height;
		video.autoplay = true;

		let media = navigator.mediaDevices.getUserMedia({
			audio: false,
			video: {
				width: { ideal: width },
				height: { ideal: height }
			}
		}).then(async(stream) =>{
			video.srcObject = stream;
			const model = faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh;
			const detectorConfig = {
				runtime: 'tfjs', // or 'tfjs'
				refineLandmarks: true,
				solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh',
			}
			detector = await faceLandmarksDetection.createDetector(model, detectorConfig as faceLandmarksDetection.MediaPipeFaceMeshMediaPipeModelConfig);
			tf.setBackend("webgl")
			update()
		});
	};

	const update = async () => {
		const faces = await detector.estimateFaces(video);
		console.log(faces) // 顔の頂点情報
		window.requestAnimationFrame(update)
	}

	return (
		<div>
			<video ref={videoRef}/>
		</div>
	);
}

※簡略化のためにupdateという名前の関数でasync,awaitしていますが、他に更新する処理がある場合、別で処理を走らせて更新処理に影響が出ないようにしてください。

webカメラ映像をvideoタグに反映

ビデオタグの各種設定

video = videoRef.current
video.id       = 'video';
video.width    = width;
video.height   = height;
video.autoplay = true;

カメラ映像を、ビデオタグに反映

let media = navigator.mediaDevices.getUserMedia({
	audio: false,
	video: {
		width: { ideal: width },
		height: { ideal: height }
	}
}).then(async(stream) =>{
	// videoタグに反映
	video.srcObject = stream;
});

カメラが複数ある場合、解像度がidealから最も近いものが選択されます。
今回はカメラの解像度と表示するサイズが近いものになるように設定してます。

顔の頂点を取得

ライブラリをimport

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

モデルをロード

const model = faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh;
const detectorConfig = {
	runtime: 'tfjs',
	refineLandmarks: true,
	solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh',
}
detector = await faceLandmarksDetection.createDetector(model, detectorConfig as faceLandmarksDetection.MediaPipeFaceMeshMediaPipeModelConfig);
tf.setBackend("webgl")

webglが選択されないときがないように、明示的に選択

頂点を取得

const update = async () => {
	const faces = await detector.estimateFaces(video);
	console.log(faces)
	window.requestAnimationFrame(update)
}

detector.estimateFacesにソースを渡すことで、ユーザーごとの顔の頂点情報を配列で返却してくれます。
返却される数値はピクセルになります。
(1000px, 500pxの動画を読み込ませたときに、ちょうど中心の位置が返却された場合
[x: 500, y: 250, z: ? ]が返却されます。)

consoleで取得できる情報

window.requestAnimationFrameで次の再線画タイミングで、再度頂点を取得するように要求します。
フレッシュレートの高い画面では速く実行されるため、端末によって更新タイミングが異なります。
https://developer.mozilla.org/ja/docs/Web/API/Window/requestAnimationFrame

それぞれの頂点がどの位置を指しているかはgithubから参照してください。
https://github.com/google/mediapipe/blob/master/mediapipe/modules/face_geometry/data/canonical_face_model_uv_visualization.png

参考

https://qiita.com/chelcat3/items/02c77b55d080d770530a

最後に

使ってみるとすごい便利...
爆速ですね。
なにかあれば、コメントいただけますと幸いです。

Discussion