face-api.jsのリサイズを理解する【技術メモ】【小ネタ】

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/b6d31427-3ef7-8a1a-d68e-896254c9be43.png)

# はじめに
株式会社マイスター・ギルド新規事業部のウサギーです。
弊社新規事業部では、新規サービスの立ち上げを目指して
日々、アイディアの検証やプロトタイプの作成を行っています。

今回も、face-api.jsネタです。

# リサイズを使いこなしたい
[face-api.js](https://github.com/justadudewhohacks/face-api.js)を気に入ってよく使っています。

特によく使っているのが、
・顔検出
・68点顔ランドマーク検出
の2つの機能。

face-api.jsには標準で検出のリサイズ方法があるのですが、
実はあんまり使ったことが無く…
今回はここへの理解を深めたいと思います。

# リサイズが必要な理由
検出に使ったソースの解像度と、画面に表示するキャンバスのサイズが一致するとは限りません。

例えば、検出ソースは640×480、表示は1280×960のような場合、
結果は「640×480の画像に対してどの位置に顔があるのか」を示すので、
そのまま表示してしまうと大きさも位置も一致しません。

イメージとしてはこんな感じ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/64b8550e-02d7-3990-f50e-b604560613a0.png)
そのため、検出結果を「1280×960で表示した場合」に変換する必要があります。


# リサイズについて調べる

### リサイズの使い方
早速悩んだのですが、
ググるととリサイズの使い方が2つ出てきました。

https://justadudewhohacks.github.io/face-api.js/docs/index.html

↑ページの __Displaying Detection Results__
>Drawing the detected faces into a canvas:
```JavaScript:ひとつめのやり方
const detections = await faceapi.detectAllFaces(input)

// resize the detected boxes in case your displayed image has a different size then the original
const detectionsForSize = faceapi.resizeResults(detections, { width: input.width, height: input.height })
// draw them into a canvas
const canvas = document.getElementById('overlay')
canvas.width = input.width
canvas.height = input.height
faceapi.drawDetection(canvas, detectionsForSize, { withScore: true })
```

https://github.com/justadudewhohacks/face-api.js#displaying-detection-results
↑ページの __Displaying Detection Results__

>Preparing the overlay canvas:
```JavaScript:ふたつめのやり方
const displaySize = { width: input.width, height: input.height }
// resize the overlay canvas to the input dimensions
const canvas = document.getElementById('overlay')
faceapi.matchDimensions(canvas, displaySize)
```

>Preparing the overlay canvas:face-api.js predefines some highlevel drawing functions, which you can utilize:
```JavaScript
/* Display detected face bounding boxes */
const detections = await faceapi.detectAllFaces(input)
// resize the detected boxes in case your displayed image has a different size than the original
const resizedDetections = faceapi.resizeResults(detections, displaySize)
// draw detections into the canvas
faceapi.draw.drawDetections(canvas, resizedDetections)
```
:::note info
さくっと結論を述べると、
後者の __matchDimensions__ と __resizeResults__ を使う方法が◎です。
前者の手法は古いようです。
:::
一つずつ見ていきます。

### 顔検出BOXをリサイズして表示
結果を表示するオーバーレイキャンバスを準備します。
```JavaScript
const displaySize = { width: input.width, height: input.height };
const canvas = document.getElementById('overlay')
faceapi.matchDimensions(canvas, displaySize)
```
まず、表示サイズとして、検出ソースと同じwidth,heightを定義。
肝になるのは __faceapi.matchDimensions(canvas, displaySize)__ で、
オーバーレイキャンバスを、表示サイズ（入力ソースサイズ）に変換します。


顔検出と結果の描画。
```JavaScript
const detections = await faceapi.detectAllFaces(input)
const resizedDetections = faceapi.resizeResults(detections, displaySize)
faceapi.draw.drawDetections(canvas, resizedDetections)
```
1行目は顔検出を実施していますが、リサイズのためにオプションをつけたりというのは特になく、実際にリサイズにかかわる処理は2行目。
__faceapi.resizeResults(detections, displaySize)__
ここでリサイズを行い、3行目の __draw.drawDetections__ はcanvasに描画しているのみ。

:::note info
整理すると
__matchDimensions__ で結果表示するキャンバスのサイズをリサイズ
__resizeResults__ で検出結果をリサイズ
できるということです。
:::

### 68点顔ランドマークをリサイズして表示
68点顔ランドマークの場合も見ておきます。

```JavaScript
const detectionsWithLandmarks = await faceapi
  .detectAllFaces(input)
  .withFaceLandmarks()

const resizedResults = faceapi.resizeResults(detectionsWithLandmarks, displaySize)
faceapi.draw.drawDetections(canvas, resizedResults)
faceapi.draw.drawFaceLandmarks(canvas, resizedResults)
```
リサイズはBOXの場合と同じく __resizeResults__ 
表示に __drawFaceLandmarks__ を使うだけですね。

### resizeResultsは何が違うのか？
検出結果を表示するだけなら用意してある描画関数で十分ですが、
顔検出、特に顔ランドマークを利用する場合は座標値を使ったりするので、
リサイズによって何が変わっているのか、
どんなデータが入っているのか見ておこうと思います。

リサイズ前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/c1e074b9-30bd-fa87-907f-068dcc916975.png)

リサイズ後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/8769ada6-d72d-8711-7ede-60605d083af8.png)

:::note
データ形式は同じ
:::

リサイズ前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/f2cb753f-17cf-7999-3b52-ff1dbe8d3fa0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/e9fe4f83-feca-9cee-23c2-d05ddd82da5d.png)



リサイズ後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/76912476-3633-7775-b749-bdd311265818.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/5ca2de23-406e-b03d-161b-728047a4de8f.png)


:::note
顔検出のリサイズを行うと
alignedRectとdetectionの __boxの座標値__ と、imageDimsが変化
:::

リサイズ前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/473fd59a-9622-398f-ea1b-3d83744ba340.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/9c129128-2dce-d67c-d9f2-55a8a8fab437.png)


リサイズ後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/278ea2b7-3ca6-8144-5e6b-7777e779a2c3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913784/f353d0e0-4f4c-17a7-b84a-2749bc16c5a9.png)

:::note
68点顔ランドマークのデータ（landmarksとunshiftedLandmarks）の
Dimensions、positionsの各座標、imageHeight,imgWidth、shiftの座標がリサイズされる
:::

各データが何を表しているか？は未学習なのでスルーしますが、
例えば、顔ランドマークの各点を利用して何か描画したい！
というときは、リサイズ後のpositionsを利用すれば大丈夫そうです。

# おわりに
これまでリサイズの使い方がわからなくて、入力データと表示データのサイズが異なる場合に挑むのに不安を感じることが多かったのですが、これでリサイズも使っていけそうです🐰