ネットワークカメラとJetson Nanoを使って姿勢推定を試してみた

# はじめに

本記事は、カメラとJetson Nanoにて姿勢推定(pose estimation)を試してみた記録です。姿勢推定とは、ディープラーニングを学ぼうとすると良く出てくる要素の一つではないかと思います。画像処理AIの分野で言うと、他にも画像分類(Image Classification)、物体検出(Object Detection)、領域分類(Semantic Segmentation)などが挙げられますが、本記事では触れていません。同じコンテナを使い、下記の画像に含まれていることを試すことができます。

![VisionAI_sample](https://github.com/dusty-nv/jetson-inference/raw/master/docs/images/deep-vision-primitives.jpg)

上の図ですと、姿勢推定(Pose Estimation)は左下に示されています。人体の骨格の状態を推定して、人の動きを検出している状態です。

# 利用したデバイスとスペック

- Jetson Nano Developer Kit (945-13450-0000-000)
	- Carrier board A02 revision
	- 128 core Maxwell GPU
	- 4 core ARM A57
	- 4GB Memory
	- 1 camera connector
	- 472 GFLOPS
	- Raspberry Pi Camera v2.1
	- JetPack v4.6.1 (これが最新リリースのようです)
	- ディスプレイ、キーボード、マウス
- iPhone 14
	- アプリLive-Reporter (RTSP対応ネットワークカメラ相当)
- Macbook Pro M1 Ventura 13.5.2
	- VLC Media Player v3.0.18

# Jetsonの準備

## OSイメージのダウンロード

本記事では、Jetson Nano 4GBモデルを使用しています。[Jetson Download Center](https://developer.nvidia.com/embedded/downloads)にて、ご利用のモデルにあったJetPackをご利用下さい。Jetson Nanoは2GBモデルと4GBモデルがあります。[Jetson Nano 4GBモデルのOSイメージはこちらからダウンロード](https://developer.nvidia.com/embedded/l4t/r32_release_v7.1/jp_4.6.1_b110_sd_card/jeston_nano/jetson-nano-jp461-sd-card-image.zip)頂けます。

## OSイメージをSDカードに書き込み

ダウンロードしたOSイメージをSDカードに書き込みます。32GB以上のSDカードを用意し、balenaEtcherなどを使ってOSイメージを書き込みます。左からOSイメージの指定、ドライブ(SDカード)の指定、実行ボタン、となります。画像はRaspberry Piのイメージを指定していますが、先にダウンロードしたJetPackのイメージを指定して下さい。

![balenaEtcher](https://camo.qiitausercontent.com/a1d87187c53dce38bcd2683fe14aa171fd31431e/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313535373532392f38343566303464332d376430382d323463612d643138652d6361393062616462303637622e706e67)

## 初回起動と初期設定

手順に沿って、NVIDIA Jetson software EULAの確認など、適宜設定を進めて下さい。詳しい手順の確認が必要な場合は、英語ですが[NVIDIA DEVELOPER / Get Started with Jetson Nano Developer Kit](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit#intro)を参照下さい。

## カメラの動作

Terminalを起動し、下記コマンドでカメラを認識していることを確認して下さい。lsの結果に何も表示されていない場合、カメラが認識されていません。RTSP対応ネットワークカメラで対応する場合は、この項目は無視して下さい。

```sh
ls -la /dev/video*
```

カメラが認識されている場合は`nvgstcapture`コマンドで動作を確認してみます。画像が表示されれば問題ありません。終了する際は`Ctrl+c`です。

# コンテナのセットアップ

こちらの[Running the Docker Container](https://github.com/dusty-nv/jetson-inference/blob/master/docs/aux-docker.md)を参考に一通りセットアップされたdockerコンテナを起動します。必要な情報とサンプルデータなどをGitHubからcloneします。その後、`docker/run.sh`にてpullおよび基本的な設定を行います。Jetson Nanoでは、少し時間がかかります。

```sh
git clone --recursive --depth=1 https://github.com/dusty-nv/jetson-inference
cd jetson-inference
docker/run.sh
```

この作業により、必要なモジュール等が読み込まれたコンテナの土台が完成します。事前学習モデルについては、初回実行の際にダウンロードされます。また、画像や映像は`images/`にサンプルが保存されていますが、実行に必要な画像は都度ダウンロードすることになります。出力結果の保存は`images/test/`が推奨と書いてあります。これはホストの`jetson-inference/data/images`をコンテナの`images`にマウントされていることが理由です。これによりホストとコンテナのファイルのやり取りが容易になります。

## コンテナに関する参考情報

上記で使っているコンテナはNVIDIA GPUを搭載したx86-64のLinux環境でも動作する、と記載があります。NVIDA driversのインストール手順も含めて、[こちら](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)に情報があります。

さらにWindows環境にて[WSL2+GPU+Container-Toolkit](https://qiita.com/nabion/items/4c4d4d4119c8586cbd9e)として動作させた例もあるようです。

# 姿勢推定(Pose Estimation)を試す

コンテナ内にて、以下のコマンドを実行すると、images配下にある人物が映った画像の姿勢推定の結果が`test/`に出力されます。

```
posenet.py "images/humans_*.jpg" images/test/pose_humans_%i.jpg
```

ムービーをresnet18で処理した結果をサンプルとして紹介します。
![dance.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/fb6d1775-28e2-9a7d-6fcb-1f5261064959.gif)

次にJetsonに接続済みのRaspberry Pi カメラの映像を使って同じことを試してみます。

```
posenet.py csi://0
```

次にハンドのモデルを使ってみます。学習済みモデルをダウンロードし、実行してくれます。

```
posenet.py --network=resnet18-hand csi://0
```

こちらは同じくresnet18-handで処理した結果をサンプルとして紹介します。
![music.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/a54d0cde-c941-f84a-3fb3-31154369081f.gif)

## ネットワークカメラを使って姿勢推定を行う

今度はネットワーク上のカメラ映像を使って試してみました。ここではRTSP配信に対応した[Live-Reporter](https://apps.apple.com/jp/app/live-reporter-スマートフォンをライブカメラに/id996017825)というiOSアプリを使って動作を確認しました。接続先IPアドレスはアプリ側にて確認できますので、都度確認してください。また、当然ながらJetsonとiPhoneがネットワーク的に疎通できる必要があります。これは他のネットワークカメラを使う場合でも同様です。
Live-Reporterのデフォルトのフレームレートが30 fpsとなっていましたが、Jetson nanoでは処理が少々多すぎるようでしたので、15 fpsに設定変更して対応しました。
下記コマンドは接続先RTSPサーバーのIPアドレスとポート番号を指定しています。

```
posenet.py --network=resnet18-hand rtsp://192.168.100.101:554
```

上の設定にさらにプラスしてJetson nano上での映像表示ではなく、ネットワーク接続しているMacbook Proの上で姿勢推論済みの映像を確認してみました。`--headless`オプションはJetsonのGUI上に映像を表示しない方法です。

ネットワーク構成
![nw_diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1557529/96cde44a-6def-933a-2ef1-bd213cd41d85.png)


```
posenet.py --headless rtsp://192.168.100.101:554 rtsp://@:554/myoutput
```

VLCメディアプレイヤーのメニュー「ファイル」-> 「ネットワークを開く」->「URL」に`rtsp://{jetsonのIPアドレス}/myoutput`と指定すると、姿勢推定の結果が表示されます。

## カメラの指定に関する情報

今回はRaspberry Pi カメラ v2.1、すなわちCSIカメラとなります。なので上記に記載した通り`posenet.py csi://0`のような指定となります。RTSPの場合は`rtsp://user:pass@ip:1234`という形式になるようです。先に試した構成は、ユーザー名とパスワードが設定されていない例ですが、市販されているネットワーク監視カメラの場合は、ユーザー名とパスワードが設定されているはずです。カメラ側の設定をご確認下さい。V4L2カメラの場合は`/dev/video0`となります。その他オプションについては[こちらに説明](https://github.com/dusty-nv/jetson-inference/blob/master/docs/aux-streaming.md)があります。この指定を間違うと`gstCamera -- didn't discover any v4l2 devices`という感じでエラーが出力されてExceptionが発生します。

# 参考情報

- [NVIDIA Jetson HELLO AI WORLD / Pose Estimation](https://github.com/dusty-nv/jetson-inference/blob/master/docs/posenet.md)
- [NVIDIA Jetson HELLO AI WORLD / Deploying Deep Learning](https://github.com/dusty-nv/jetson-inference/tree/master)
- [NVIDIA DEVELOPER / Get Started with Jetson Nano Developer Kit](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit#intro)