ml5.js の Handpose でカメラ画像のサイズを変えたら描画がズレたので検証・対処してみる（PoseNet、Facemesh との比較も）

タイトルの通りの内容です。
（仕様を勘違いしている、とかあるかな...）

[ml5.js の Handpose](https://learn.ml5js.org/#/reference/handpose) の公式サンプルの中で、[「p5.js Web Editor ＋ Webカメラ」](https://editor.p5js.org/ml5/sketches/Handpose_Webcam)のサンプルの組み合わせを試した際、カメラ画像のサイズ変更を行ったら描画がズレるということが起こったので検証などをしてみました。

以下の画像は、サンプルをそのまま動かした時のものです。
カメラ画像の横x縦の大きさは 640 x 480 です。
![デフォルトの設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/fe90b803-bb83-9a3d-fae9-f0b96d84d3bc.jpeg)

以下の画像は、ビデオの横x縦の大きさを 480 x 360 にして、キャンバス上への描画時のサイズもそれを使った時のものです。
![ビデオのサイズを縮小.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/94236bcd-1a70-8153-d826-355387bbb376.jpeg)

キーポイントの描画にズレが生じています。

## Handpose のサンプル
### 元のコード
[ml5.js の Handpose](https://learn.ml5js.org/#/reference/handpose) の公式サンプルの、[「p5.js Web Editor ＋ Webカメラ」](https://editor.p5js.org/ml5/sketches/Handpose_Webcam)のサンプルは、コメントを除くと以下となります。

```javascript
let handpose;
let video;
let predictions = [];

function setup() {
  createCanvas(640, 480);
  video = createCapture(VIDEO);
  video.size(width, height);

  handpose = ml5.handpose(video, modelReady);

  handpose.on("predict", results => {
    predictions = results;
  });

  video.hide();
}

function modelReady() {
  console.log("Model ready!");
}

function draw() {
  image(video, 0, 0, width, height);

  drawKeypoints();
}

function drawKeypoints() {
  for (let i = 0; i < predictions.length; i += 1) {
    const prediction = predictions[i];
    for (let j = 0; j < prediction.landmarks.length; j += 1) {
      const keypoint = prediction.landmarks[j];
      fill(0, 255, 0);
      noStroke();
      ellipse(keypoint[0], keypoint[1], 10, 10);
    }
  }
}
```

カメラ画像のサイズは `video.size(width, height)` となっているので、キャンバスのサイズに合うように設定されています。
また、キャンバスに描画する処理が `image(video, 0, 0, width, height)` となっているので、キャンバスいっぱいにカメラ画像が表示されるようになっています。

### カメラ画像のサイズを変更したもの
このカメラ画像のサイズ・カメラ画像をキャンバスへ描画するサイズを以下のようにしたところ、冒頭の画像のようなズレが生じました。

- カメラ画像のサイズ設定：　video.size(480, 360)
- キャンバスへの描画処理：　image(video, 0, 0, 480, 360)

これを解決した対処法は、 `ellipse(keypoint[0], keypoint[1], 10, 10)` というキーポイントを描画している部分の、最初の 2つの変数（描画する x座標と y座標を指定する部分）の値がとる範囲を変更するというものでした。

```javascript
ellipse(map(keypoint[0], 0, 640, 0, 480), map(keypoint[1], 0, 480, 0, 360), 10, 10)
```

キーポイントの座標の値を map() を用いて、以下のようにしました。

- x座標：　0-640 の範囲を 0-480 の範囲にマッピング
- x座標：　0-480 の範囲を 0-360 の範囲にマッピング

その表示結果は以下のとおりです。
![調整後.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/b3034a34-72e1-7fcd-f950-248e84fb59da.jpeg)


ソースコードの中まではチェックできてないですが、カメラ画像が 640 x 480 のサイズとなるのが前提という感じでしょうか。

## PoseNet・Facemesh の場合も見てみる
他に、カメラ画像から人の姿勢・顔のキーポイントを検出するもの（「[PoseNet](https://learn.ml5js.org/#/reference/posenet)」と「[Facemesh](https://learn.ml5js.org/#/reference/facemesh)」）もあるので、それらでもカメラ画像のサイズ変更の影響を見てみます。

「p5.js Web Editor ＋ Webカメラ」のサンプルは、それぞれ以下になります。

- [PoseNet_webcam](https://editor.p5js.org/ml5/sketches/PoseNet_webcam)
- [Facemesh_Webcam](https://editor.p5js.org/ml5/sketches/Facemesh_Webcam)

試してみた結果、PoseNet のほうはキーポイントの座標に何も処理を加えなくても、表示位置のズレは出ませんでした。
![PoseNetで試した場合.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/88c1bd3a-3ff9-d689-80b6-371cdd0393c0.jpeg)

Facemesh のほうはキーポイントの座標に Handpose で行ったような処理を加えないと、表示位置がズレました（以下は、調整用の処理を加えた後の表示結果）。
![Facemeshで試した場合.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/83f2d41f-18cb-40db-d28c-c74c13755dd0.jpeg)

このあたり、仕様詳細を見て、違いが出る理由を探してみたいところです。