face-api.jsを使ってクライアントサイドだけで顔検出

face-api.jsを使ってクライアントサイドだけで顔検出

ブラウザで顔検出したい

ブラウザで顔検出したいのでface-api.jsを使う。

face-api.jsは画像から顔検出ができるJavaScriptのAPI。
顔検出以外にも顔特徴点抽出、年齢、性別、感情の判別などできる。
詳細はface-api.jsのgithubに書いてある。

顔検出ライブラリは他にも多数あるが、中でもface-api.jsはJavaScriptでありクライアントサイドで動いてくれるため以下の点で良い。

  • ユーザ側で計算するためサーバに負担がかからない
  • ユーザが画像をサーバに送る必要がない
  • 開発環境に左右されない

1. デモ

画像ファイルを選択すると画像処理された結果が表示される。

  1. ファイルを選択
  2. 顔検出ボタンを押す
  3. 待つ

2. 実験

デモでは顔検出、特徴点抽出の2つの処理を行っている。
顔検出、特徴点抽出のそれぞれについて学習済みモデルをgithubからダウンロードして使う必要があり、通常のモデルに加えて性能が落ちる代わりにサイズの小さい軽量版が用意されている。

軽いほうが良いのでデモでは軽量版を用いているが一応性能の違いを確かめた。

2.1. 実験結果

元画像


顔検出(軽量版) + 特徴点抽出(軽量版)


顔検出(軽量版) + 特徴点抽出(通常版)


顔検出(通常版) + 特徴点抽出(軽量版)


顔検出(通常版) + 特徴点抽出(通常版)

2.2. 実験結果について

角度が厳しいのか右端の人はいずれの組み合わせもうまくいってない。
他の人は良い感じ。

顔検出(軽量版) + 特徴点抽出(通常版)と顔検出(通常版) + 特徴点抽出(軽量版)を比較すると、後者の方がうまくいっているように見える。
きれいに顔だけを検出することで後段の特徴点抽出にいい影響を与えているのかもしれない。

3. 実装

3.1. ファイルダウンロード

githubからface-api.min.jsをダウンロード。
それとweightsフォルダから、必要な学習済みモデルをダウンロード。

今回のデモで使用したのは、

  • 顔検出
    tiny_face_detector_model-shard1
    tiny_face_detector_model-weights_manifest.json

  • 特徴点抽出
    face_landmark_68_tiny_model-shard1
    face_landmark_68_tiny_model-weights_manifest.json

の4ファイル。

3.2. チュートリアル

face-api.jsのチュートリアルを読んで良い感じに作る。

少し改造したらすごくエラーが起きたが、Promise.all()でモデルを全て読み込んでから他の処理をするようにしたら動いた。
下のコード(短い)をみたらわかる。

3.3. コード

今回のデモで使用したコード。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// モデル読み込み
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/../models'),
faceapi.nets.faceLandmark68TinyNet.loadFromUri('/../models'),
]).then(initialize);

// Promise.all()の処理(モデル読み込み)全て成功後に実行
async function initialize(){
// 「ファイル選択」'selectedFile'と「顔検出」'button'に関数を割り当てる
const selectedFile = document.getElementById("selectedFile");
const button = document.getElementById('button');
button.addEventListener("click", facedetection, false);
selectedFile.addEventListener("change", uploadImage, false);

// 関数1 「ファイル選択」 選択された画像を読み込む
async function uploadImage() {
// 選択された画像を読み込む
const imgFile = selectedFile.files[0];
const img = await faceapi.bufferToImage(imgFile);
// 画像サイズを変更(長辺を800pixにする)
const MIN_SIZE = 800;
let canvas = await faceapi.createCanvasFromMedia(img);
let ctx = canvas.getContext('2d');
let ratio = 1;
if( Math.max(canvas.width, canvas.height) > MIN_SIZE) ratio = MIN_SIZE / Math.max(canvas.width, canvas.height);
canvas.width = canvas.width * ratio;
canvas.height = canvas.height * ratio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// 画像を'myImg'に送る
const imgSrc = canvas.toDataURL("image/jpeg");
document.getElementById('myImg').src = imgSrc;
}

// 関数2 「顔検出」 検出領域と特徴点を描きこんだ画像を出力
async function facedetection() {
// 'myImg'から画像取得 canvas作成
const img = document.getElementById('myImg');
if(img.src == img.baseURI) return;
const canvas = await faceapi.createCanvasFromMedia(img);
document.getElementById("faceDetecting").textContent = "\n顔検出中\n少し時間がかかります"
// canvasのスタイル変更 筆を赤色(255, 0, 0)にする
const ctx = canvas.getContext('2d');
ctx.strokeStyle = "rgb(255, 0, 0)";
// 顔検出 + 特徴点68点抽出
const detections = await faceapi.detectAllFaces(img, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks(true); // faceLandmark68'Tiny'Net のときtrue
// 検出した顔の数だけループ
for (const detection of detections){
// 検出領域を長方形で表示
const box = detection.detection.box;
ctx.strokeRect(box.x, box.y, box.width, box.height);
// 特徴点を中心とした3*3の正方形を68点プロット
for (const point of detection.landmarks.positions){
ctx.strokeRect(point.x, point.y, 3, 3);
}
}
// canvasを画像に変換
document.getElementById("faceDetecting").textContent = ""
const imgSrc = canvas.toDataURL("image/jpeg",0.9);
document.getElementById("newImg").src = imgSrc;
}
}

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×