face-api.jsで複数の顔を検出する

# はじめに
先日face-api.jsを使ってみました。
前回の記事↓

https://qiita.com/s_u/items/a72c374e4d27f4b1a431

今回はface-api.jsについてもう少し調べてみたいと思います。


# face-api.jsとは？
繰り返しになりますが、face-api.jsはブラウザで顔検出（face detection：人の顔を自動的に見つける）と顔認識（face recognition：個人を識別する）ができるJavaScript APIです
機械学習用のライブラリTensorFlow.jsが利用されています
[face-api.js](https://justadudewhohacks.github.io/face-api.js/docs/index.html)

# face-api.jsを使う準備
前回参照。
まだNode.jsは勉強中なので
今回も自分のプロジェクトにスクリプトを含める方法です。

# 今回試すもの
「トラニナル」はとにかく動くものを短時間で作る。という
マッチョな目標があったので読み飛ばしましたが
[face-api.js](https://justadudewhohacks.github.io/face-api.js/docs/index.html#usage-loading-models)の内容をもう少し読んでいきます。

⇒読んだ結果、複数顔の検出を試しました！

## Available Models
### Face Detection Models
顔検出モデルについて書かれています。
#### SSD Mobilenet V1
画像内の各顔の位置を計算し、
各顔の確率とともにバウンディングボックスを返します。

face-api.js内で一番標準的な（？）顔検出のモデルだと思われます。
よく見かけるコードではこちらが使われていることが多いので、
私も「トラニナル」では思考停止状態でこちらを使用しました。

#### Tiny Face Detector
パフォーマンスの高いリアルタイムの顔検出器。
SSD Mobilenet V1顔検出器と比較して、高速・小型・リソース消費が少なく、
パフォーマンスはわずかに低下します。
モバイルデバイスやクライアントのリソースが限られているときはこちらを使用すべき。

「トラニナル」は使用者から「重い」という声を聞きました。
というか、自分でもテストして「重いなぁ」と思ったんですが
その時点で12/27だったので諦めました。
そんな問題を解決してくれそうな話がさっそく書いてあります！
これは使ってみたい。

#### MTCNN
MTCNN：マルチタスクカスケード畳み込みニューラルネットワーク。
（Google翻訳頼みですが合ってるのかな？）
一般に、他の顔検出器の方がパフォーマンスが優れている。

実験のためにリポジトリに入っている顔検出器のようです。
試すのはもちろんOK。

顔検出ついては上記3つについて書かれていました。

### 68 Point Face Landmark Detection Models
68点面顔ランドマーク検出モデル。
デフォルトのモデル（face_landmark_68_model）は350KB
小さなモデル（face_landmark_68_tiny_model）は80KB

顔ランドマーク検出の検出器にも小さいものがあることが分かりました！
これまた朗報ですね。

### Face Recognition Model
顔認識のモデル。あらゆる人物の顔認識に使用できます。
ResNet-34のようなアーキテクチャが実装され、
人物の顔の特性を記述するために
任意の顔画像から顔記述子（face descriptor：128個の値を持つ特徴ベクトル）が計算されます。
 2つの任意の顔の類似性は、それらの顔記述子を比較することによって
（たとえばユークリッド距離を計算することによって）決定できます。

ResNetとはニューラルネットワークのモデルのようですね。
難しいところは理解できなかったのですが
このモデルを使えば特定人物のお顔を認識出来るようです。

### Face Expression Recognition Model
顔の表情認識モデル。
軽量、高速で、適度な精度を提供。
眼鏡をかけると、予測結果の精度が低下する可能性がある。

表情認識も気になりますよね～
笑顔の瞬間で撮影してくれたら面白いなぁ。

# Detecting Faces
「トラニナル」で使ったのは
detectSingleFaceと言うメソッドでした。

ここも他の方法について調べる時間が無かっただけなので、
今回もう少し調べてみます。

**画像内で最も信頼度の高い顔を検出する**
```JavaScript
const detection = await faceapi.detectSingleFace(input)
```
 FaceDetection が返ってきます。

**画像内のすべての顔を検出する**
```JavaScript
const detections = await faceapi.detectAllFaces(input)
```
Array < FaceDetection >が返ってきます。

detectAllFacesとdetectSingleFaceはデフォルトではSSD Mobilenet V1顔検出モデルを利用します。
対応するオプションオブジェクトを渡すことで、顔検出器を指定することができます。

```JavaScript
const detections1 = await faceapi.detectAllFaces(input, new faceapi.SsdMobilenetv1Options())
const detections2 = await faceapi.detectAllFaces(input, new faceapi.TinyFaceDetectorOptions())
const detections3 = await faceapi.detectAllFaces(input, new faceapi.MtcnnOptions())
```

# 顔のランドマーク検出

**「画像内で最も信頼度の高い顔を検出」し
「その顔の68点顔ランドマークを計算」**
```JavaScript
const detectionWithLandmarks = await faceapi.detectSingleFace(input).withFaceLandmarks()
```

**「画像内のすべての顔を検出」し、
「検出された顔ごとに68点顔ランドマークを計算」**
```JavaScript
const detectionsWithLandmarks = await faceapi.detectAllFaces(input).withFaceLandmarks()
```
**detectSingleFace**を使うか**detectAllFaces**を使うか、と言うことですね。


デフォルトのモデルではなく小さなモデルを使うには
```JavaScript
const useTinyModel = true
const detectionsWithLandmarks = await faceapi.detectAllFaces(input).withFaceLandmarks(useTinyModel)
```
と、withFaceLandmarks()の引数に**true**を与えればよさそうです。

# 複数顔検出をやってみる
ということで、色々試してみたくはなったのですが、
今回は「複数の顔を検出する」ことにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/7ecf2abf-302d-445b-6e40-4d9e9d144273.png)
早速結果です。

やったことは
**detectSingleFace** → **detectAllFaces**
だけです！
あとは返ってきた**FaceDetection**のデータが配列に入っているので
複数人分の描画を行うように書き換えました。

今回はココまで。

# おわりに
用意されている顔モデルについて確認し、
複数人の顔検出・顔ランドマーク検出が出来ました。

小さいサイズのモデルも使ってみたいですし、
顔認識や表情検出も気になります。
次の機会に試してみたいと思います。