NodeJSで、サーバ上にある画像をPosenetする

ブラウザに表示される画像や、PCカメラからブラウザに映る人間の姿を姿勢推定する「TensorflowJS Posenet」。
[ブラウザで動くもの](https://qiita.com/Chishiki/items/ab5266f7fd007337be70)を、なぜわざわざNodeJSで動作させたいのか、というと、クライアント側の処理負荷を抑えたい、ということがあります。あと、PHPやMySQLなどのWEB環境と連動しやすくしたいこともあります。

まず、Macを使用し、特定のフォルダに置いてある画像で、NodeJSで姿勢推定が出来るか、試してみました。

## 必要なライブラリなど
すみません、細かい解説は割愛します。
###node-canvas
node-canvas を動かすためのライブラリのインストール

```console
$ brew install pkg-config cairo pango libpng jpeg giflib
```
node-canvasインストール

```console
$ npm install --save canvas botkit
```
###TensorFlowJS NodeJS
npmでインストールします。

```console
$ npm install --save rollup
$ npm install --save @tensorflow/tfjs@^1.1.0
$ npm install --save @tensorflow-models/posenet
$ npm install --save @tensorflow/tfjs-node

GPUの場合
$ npm install --save @tensorflow/tfjs-node-gpu
```

##注意点
1.tfjsではなく、tfjs-nodeを使うこと。

```Javascript
//const tf = require('@tensorflow/tfjs');
const tf = require('@tensorflow/tfjs-node');
```

2.画像はCanvasに投影後、テンソル型にして、estimateSinglePose()を実行すること。
NodeJSをバッチで走らすとDOMは無いですが、「node-canvas」でCanvasタグをDOMのように扱えます。

```Javascript
var imageScaleFactor = 0.5;
var outputStride = 16;
var flipHorizontal = false;

// モデルのロード
var net = await posenet.load(0.75);

const img = new Image();
img.src = './hoge.jpg';

const canvas = createCanvas(img.width, img.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);

// Tensor型に変換
const input = tf.browser.fromPixels(canvas);

// 姿勢JSON取得
const pose = await net.estimateSinglePose(input, imageScaleFactor, flipHorizontal, outputStride);

console.log(pose);

```

##結果

```js
{
  score: 0.8253553825266221,
  keypoints: [
    { score: 0.9589502215385437, part: 'nose', position: [Object] },
    { score: 0.6782095432281494, part: 'leftEye', position: [Object] },
    { score: 0.9743408560752869, part: 'rightEye', position: [Object] },
    { score: 0.3834344744682312, part: 'leftEar', position: [Object] },
    { score: 0.9573087692260742, part: 'rightEar', position: [Object] },
    {
      score: 0.9608330726623535,
      part: 'leftShoulder',
      position: [Object]
    },
    {
      score: 0.970580518245697,
      part: 'rightShoulder',
      position: [Object]
    },
    {
      score: 0.5824703574180603,
      part: 'leftElbow',
      position: [Object]
    },
    {
      score: 0.8478972911834717,
      part: 'rightElbow',
      position: [Object]
    },
    {
      score: 0.4102041721343994,
      part: 'leftWrist',
      position: [Object]
    },
    {
      score: 0.43626463413238525,
      part: 'rightWrist',
      position: [Object]
    },
    { score: 0.9976338148117065, part: 'leftHip', position: [Object] },
    { score: 0.9926899671554565, part: 'rightHip', position: [Object] },
    { score: 0.9901325702667236, part: 'leftKnee', position: [Object] },
    {
      score: 0.9896235466003418,
      part: 'rightKnee',
      position: [Object]
    },
    {
      score: 0.9450682401657104,
      part: 'leftAnkle',
      position: [Object]
    },
    {
      score: 0.9553994536399841,
      part: 'rightAnkle',
      position: [Object]
    }
  ]
}
```
あとは、この座標に準じて、キーポイントをcanvasに描画して、Canvasごとfs.writeFile()でアウトプットするだけです。

サーバにアップロードされた複数の画像に、姿勢推定ラインが付けられるようになりますね。

ffmpegでmp４スロー動画を1秒１フレームで切り出し、40枚の画像を処理するのに３分ほど。。この辺りはやはりGPUじゃないと厳しいのかな。

こうして生成したキーポイントとボーンの付いた画像から、さらにffmpegでキーポイント・ボーン付き動画を作ることは出来ますね。JSONファイルに書き出しや、canvasでキーポイント・ボーンだけの動画を生成することも可能。サーバ上のTensorFlow・Posenetで、Openposeに引けを取らない仕様に出来そうです。処理速度は早くないと思いますが。。

CentOSやUbuntuでも同じように出来ると思います。

![tiger1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74852/42d9aefd-6079-0676-9597-c72968dc86e8.jpeg)
![tiger2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74852/ca9ec351-35a1-e126-13a6-0d80b9dad516.jpeg)