Raspberry Pi と Coral USB TPU Accelerator(EdgeTPU) で姿勢検出を行う(PoseNet)

## 【内容】
ここ数日、かなり活性化している「[Coral - Github](https://github.com/google-coral)」ですが、その中の一つでPoseNetが利用できるようになっていました。
今回は、これを動かしてみました。

## 【姿勢検出とは】
姿勢推定は、肘、肩、足が画像のどこにあるのか検出できるように、画像中の人物を検出するコンピュータビジョン(技術です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454012/2994851d-faec-ad4c-0071-3784304d372c.png)

PoseNetは身体の主要な関節がどこにあるかを推定します。
目、鼻、耳、肩、肘、手首、腰、膝、足首を左右それぞれ検出してくれるようです。


## 【インストール】
下記コマンドを実行し、リポジトリのクローンを行い、インストールスクリプトを実行します。
EdgeTPUが動く環境であれば、特に新たにインストールされるものはないと思います。

```bash:リポジトリのクローン
cd ~/
git clone https://github.com/google-coral/project-posenet
cd project-posenet
sudo sh install_requirements.sh
```

## 【サンプルプログラムの実行】
とにかく動かしてみます。

```bash:simple_pose
python3 simple_pose.py
```
「couple.jpg」(下記画像)がダウンロードされ、姿勢検出が行われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454012/2eea8a6e-7aef-2ae2-cacb-1565288fa927.png)

```bash:出力結果
Inference time: 102ms

Pose Score:  0.61855656
 right shoulder       x=160  y=173  score=1.0
 right eye            x=199  y=137  score=1.0
 left hip             x=324  y=179  score=0.2
 left ear             x=244  y=135  score=1.0
 left eye             x=224  y=138  score=1.0
 left ankle           x=316  y=175  score=0.1
 left knee            x=344  y=86   score=0.8
 right ankle          x=166  y=307  score=0.0
 left elbow           x=282  y=255  score=0.6
 right knee           x=170  y=294  score=0.0
 right elbow          x=154  y=256  score=0.9
 left shoulder        x=268  y=168  score=0.8
 right wrist          x=162  y=299  score=0.6
 nose                 x=210  y=152  score=1.0
 right ear            x=182  y=129  score=0.8
 left wrist           x=236  y=333  score=0.8
 right hip            x=203  y=233  score=0.0

Pose Score:  0.5867945
 right shoulder       x=362  y=151  score=0.8
 right eye            x=382  y=127  score=1.0
 left hip             x=525  y=106  score=0.0
 left ear             x=457  y=110  score=0.9
 left eye             x=416  y=128  score=1.0
 left ankle           x=504  y=178  score=0.0
 left knee            x=501  y=90   score=0.6
 right ankle          x=306  y=176  score=0.1
 left elbow           x=465  y=290  score=0.9
 right knee           x=350  y=167  score=0.1
 right elbow          x=328  y=246  score=0.8
 left shoulder        x=491  y=169  score=0.9
 right wrist          x=233  y=331  score=0.5
 nose                 x=398  y=145  score=1.0
 right ear            x=370  y=120  score=0.2
 left wrist           x=340  y=303  score=0.9
 right hip            x=361  y=161  score=0.3
```
102msで推論が完了し、推論した各関節位置が表示されます。
ただ、これだけだとよくわかりませんね…


## 【カメラキャプチャのサンプル実行】
Webカメラで動画をキャプチャしながら姿勢検出を行います。


```bash:pose_camera
python3 pose_camera.py
```
```bash:出力結果
Loading model:  models/posenet_mobilenet_v1_075_481_641_quant_decoder_edgetpu.tflite
INFO: Initialized TensorFlow Lite runtime.
Gstreamer pipeline:  v4l2src device=/dev/video0 ! video/x-raw,width=640,height=480,framerate=30/1 ! queue max-size-buffers=1 leaky=downstream  ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480 ! tee name=t
               t. ! queue max-size-buffers=1 leaky=downstream ! appsink name=appsink sync=false emit-signals=true max-buffers=1 drop=true
               t. ! queue max-size-buffers=1 leaky=downstream ! videoflip video-direction=horiz ! videoconvert
                  ! rsvgoverlay name=overlay ! videoconvert ! autovideosink

PoseNet: 163.9ms Frame IO: 31.66ms TrueFPS: 0.05 Nposes 0
PoseNet: 136.0ms Frame IO: 22.88ms TrueFPS: 3.82 Nposes 0
PoseNet: 123.3ms Frame IO: 19.96ms TrueFPS: 5.31 Nposes 0
PoseNet: 116.0ms Frame IO: 24.42ms TrueFPS: 5.74 Nposes 0
Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(2834): gst_base_sink_is_too_late (): /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstGLImageSinkBin:autovideosink0-actual-sink-glimage/GstGLImageSink:sink:
There may be a timestamping problem, or this computer is too slow.
Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(2834): gst_base_sink_is_too_late (): /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstGLImageSinkBin:autovideosink0-actual-sink-glimage/GstGLImageSink:sink:
There may be a timestamping problem, or this computer is too slow.
PoseNet: 111.8ms Frame IO: 23.93ms TrueFPS: 6.19 Nposes 0
PoseNet: 109.2ms Frame IO: 22.59ms TrueFPS: 6.51 Nposes 0
PoseNet: 109.3ms Frame IO: 21.05ms TrueFPS: 6.62 Nposes 0
PoseNet: 108.1ms Frame IO: 22.61ms TrueFPS: 6.66 Nposes 0
PoseNet: 106.7ms Frame IO: 23.68ms TrueFPS: 6.73 Nposes 0
PoseNet: 105.8ms Frame IO: 22.47ms TrueFPS: 6.81 Nposes 0
Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(2834): gst_base_sink_is_too_late (): /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstGLImageSinkBin:autovideosink0-actual-sink-glimage/GstGLImageSink:sink:
There may be a timestamping problem, or this computer is too slow.
Warning: gst-core-error-quark: A lot of buffers are being dropped. (13): gstbasesink.c(2834): gst_base_sink_is_too_late (): /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstGLImageSinkBin:autovideosink0-actual-sink-glimage/GstGLImageSink:sink:
There may be a timestamping problem, or this computer is too slow.
PoseNet: 105.8ms Frame IO: 22.39ms TrueFPS: 6.86 Nposes 0
PoseNet: 106.8ms Frame IO: 21.54ms TrueFPS: 6.89 Nposes 0
PoseNet: 109.0ms Frame IO: 21.84ms TrueFPS: 6.81 Nposes 0
PoseNet: 109.2ms Frame IO: 21.14ms TrueFPS: 6.80 Nposes 0
PoseNet: 110.1ms Frame IO: 21.50ms TrueFPS: 6.73 Nposes 0
PoseNet: 109.5ms Frame IO: 20.89ms TrueFPS: 6.78 Nposes 0
PoseNet: 108.7ms Frame IO: 20.96ms TrueFPS: 6.81 Nposes 0
PoseNet: 109.3ms Frame IO: 20.64ms TrueFPS: 6.80 Nposes 0
```
100ms強で推論して、7FPS弱出ているようです。
ただし、表示されているワーニングのせいなのか、1秒ごとにしか画面が更新されないのでカクカクして微妙です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454012/4fb77ff4-1f03-5874-381b-66013a853c96.png)
オプションで鏡状態にしたり、解像度を指定できます。

```bash:pose_camera_鏡モード
python3 pose_camera.py --mirror
```
```bash:pose_camera_鏡モード_解像度指定
python3 pose_camera.py --mirror --res 480x360
```



## 【anonymizer.py】
プライバシーを保護を目的とし、対象者の映像は残さず姿勢のみを推定します。
やってることは背景画像を固定して表示しているだけです。

```bash:anonymizer.pyの実行
python3 anonymizer.py
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454012/d8b09b10-e8c1-914c-231c-333912fad628.png)



## 【トラブルシュート】
私の環境ではプログラム実行時に以下のエラーが表示されました。

```bash:実行時エラー
Traceback (most recent call last):
  File "simple_pose.py", line 18, in <module>
    from pose_engine import PoseEngine
  File "/home/pi/project-posenet/pose_engine.py", line 22, in <module>
    'This demo requires Edge TPU version >= 2.11.1'
AssertionError: This demo requires Edge TPU version >= 2.11.1
```
どうやらEdgeTPUのライブラリが古かったようです。
下記コマンドを実行し、最新バージョンをインストールし直しました。

```bash:EdgeTPUモジュール最新化
cd ~/
wget https://dl.google.com/coral/edgetpu_api/edgetpu_api_latest.tar.gz -O edgetpu_api.tar.gz --trust-server-names
rm -Rf edgetpu_api
tar xzf edgetpu_api.tar.gz
cd edgetpu_api
sudo bash ./install.sh
```
これで動くようになりました。


## 【最後に】
EdgeTPU + PoseNetでとても簡単に姿勢検出ができるようになりました。
何に使うかはアイデア次第ですが、非常に面白いものが作れそうです。
今回は動かしませんでしたが、付属のプログラム「synthesizer.py」では3人の体で音楽を奏でることができるようです。
これは夢が広がります。

ただし、今回実行したサンプルプログラムのパフォーマンスが悪くてイマイチでした。
さらに、温度警告が表示されるほどCPUを酷使しています。
せっかくEdgeTPUを使っているのに勘弁してほしいところです。
改善の余地はありそうでしたので時間があったら修正してみたいと思います。

それから、同じプログラムをJetson Nanoで動かそうと試みましたがエラーで動きませんでした。
修正するときにはJetson Nanoでも動くようにして、パフォーマンスの比較をしてみようと思います。