この記事はMDCアドベントカレンダー22日目の記事となる。
はじめに
- 表題のJetson nanoとはNVIDIA製の128-core GPU、Quod-core ARM CPUを持つシングルボードコンピュータ (ref: Wikipedia link)。
- 公式 製品概要:最新の Jetson 製品の購入 – NVIDIA Developer
- 公式 Jetson nano 2GB 仕様:Jetson Nano 2GB Developer Kit | NVIDIA Developer
- 本記事では2020年10月5日に発売発表されたJetson nano 2GBとUSBカメラを用いてWeb動画ストリーミング機能をPython Flaskで構築するためのソースコード等を紹介するもの。
機能概要
- Webブラウザ経由でUSBカメラでキャプチャした動画(連続画像)を表示
- 音声なし、動画の同時表示クライアント数は1つ
実行時のイメージ
紹介するソースコードの実行時のイメージは以下の通り。自身が想定した以上にヌルヌル動くので驚きだった。
実行環境
本体(+ソフトウェアのバージョン)と付属部品を紹介する。
Jetson nano 2GB 本体
NVIDIA Jetson Nano 2GB 開発者キット JETSON NANO 2GB DEV KIT
初期設定時はモニタ・キーボード・マウス等ケーブルに囚われるイメージだが、VNC, ssh周りの設定が完了すれば2枚目の画像の通り多少はスッキリする。
OSバージョン
$ cat /etc/nv_tegra_release # R32 (release), REVISION: 4.4, GCID: 23942405, BOARD: t210ref, EABI: aarch64, DATE: Fri Oct 16 19:44:43 UTC 2020 $ dpkg-query --show nvidia-l4t-core nvidia-l4t-core 32.4.4-20201016124427
Python、パッケージバージョン
主に使用するのはOpenCV、Flask、uWSGIとなる。
$ python3 -V Python 3.6.9 $ pip -V pip 20.3.3 from /usr/local/lib/python3.6/dist-packages/pip (python 3.6) $ pip freeze | grep -e Flask -e uWSGI Flask==1.1.2 uWSGI==2.0.19.1 $ python Python 2.7.17 (default, Sep 30 2020, 13:38:04) [GCC 7.5.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>> print(cv2.__version__) 4.5.0
USBカメラ
最近発売されて価格と性能が釣り合っているロジクールC505を使用。赤ちゃんモニター用途だとより広角が欲しくなるかもだが、トライアルであれば十分な仕様。
その他部品
microSDカードと電源ケーブル。欲を言えばケースも欲しかったが、2020年12月時点2GBモデルに対応する適当なケースが無く裸で使用している。
- Smraza Raspberry Pi 4 USB-C (Type C)電源、5V 3A ラズベリーACアダプター RPi 4b Model B 1GB / 2GB / 4GB/ 8GB適用 PSE取得
- Samsung EVO Plus 128GB microSDXC UHS-I U3 100MB/s Full HD & 4K UHD Nintendo Switch 動作確認済 MB-MC128GA/ECO 国内正規保証品
ソースコード
構成としてはFlask Webアプリ内でUSBカメラのキャプチャ画像を60fpsで出力する構成となる。
ファイル・フォルダ構成
実際には下記以外に開発環境であるVS code用の設定ファイル、Web公開の為のBasic認証用のNginxコンテナ周りの設定ファイルやDockerfile、docker-compose.ymlファイル等があるが、表題とズレる為割愛する。
~/code/jetson-web-stream$ tree ├── app.pid # uwsgi経由での起動時に作成自動作成される ├── app.py ├── templates │ └── index.html ├── uwsgi.ini
app.py – Flaskアプリ本体
Interface 2021年1月号にbottleフレームワーク(FW)版の記載があるが、私が得意なFWはDjangoかFlaskの為、メインの処理部分を参考にしつつFlask-nizeしたもの。
ページ構成としてはルートディレクトリ’/’にアクセス時に呼び出されるindex.htmlテンプレート内のimgソースとしてvideo_recvメソッドが呼び出される。当該ページ(画像)はサーバのプッシュ通信で描画内容を更新させる為mimetype=’multipart/x-mixed-replace;boundary=frame’指定とし、__main()処理内のyield句で生成されるjpg画像が60fpsで切り替わる流れ。
from flask import Flask, render_template, Response import cv2 import time app = Flask(__name__) def __main(): cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 1280 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360) # 720 if not cap.isOpened(): # ビデオキャプチャー可能か判断 print("Not Opened Video Camera") exit() while True: ret, img = cap.read() if not ret: # キャプチャ失敗時に終了 print("Video Capture Err") break # キャプチャ画像を出力 result, jpgImg = cv2.imencode('.jpg', img=img, params=[int(cv2.IMWRITE_JPEG_QUALITY), 80]) # 0 - 100 yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + bytearray(jpgImg) + b'\r\n\r\n' time.sleep(1 / 60) cap.release() cv2.destroyAllWindows() return 0 @app.route('/') def index(): return render_template('index.html') @app.route('/video_recv') def video_recv(): return Response(__main(), mimetype='multipart/x-mixed-replace;boundary=frame') if __name__ == '__main__': print(cv2.__version__) app.run(host='0.0.0.0', port=8080, debug=True)
index.html – Flask アプリのテンプレート
<!DOCTYPE html> <html> <head> <title>Ba!</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> <!-- #ID_01{ text-align : center; } #ID_TITLE{ font-size : 10pt; text-align : center; } --> </style> </head> <body> <div id="ID_TITLE">Web Streaming by Single board computer "Jetson nano 2GB"</div> <div id="ID_01"> </div> </body> </html>
uwsgi.ini – Flask用APサーバー
開発環境でのテスト実行であればif name == ‘main‘:句内を直接実行で構わないが、本番運用時は以下のWARNINGの通り推奨されない。
$ python3 app.py 4.5.0 * Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. <攻略>
Development Server — Flask Documentation (2.0.x)
uwsgi.ini記述は以下の通り。
[uwsgi] master = true http=0.0.0.0:8080 wsgi-file = app.py callable = app processes = 1 threads = 1 pidfile=./app.pid
実行方法
# uwsgiサーバーの起動 $ uwsgi uwsgi.ini &amp;amp; [1] 18387 # 起動プロセス番号を確認 $ cat app.pid 18387 # uwsgiサーバーの停止 $ uwsgi --stop app.pid
コマンド末尾に&を付与することでバックグラウンドでの実行が可能。尚、実行時のリソース使用状況は以下の通り。これまで動画像処理の経験があまりなく、必要リソースの肌感覚が掴めなかった為参考となった。配信のマルチスレッド化とアクセス数の増加時にどのように推移するかは気になる点である。
$ top top - 22:20:37 up 1 day, 13:11, 2 users, load average: 0.39, 0.19, 0.19 Tasks: 248 total, 1 running, 247 sleeping, 0 stopped, 0 zombie %Cpu(s): 5.7 us, 0.6 sy, 0.0 ni, 92.4 id, 0.0 wa, 1.1 hi, 0.2 si, 0.0 st KiB Mem : 2027368 total, 467944 free, 598448 used, 960976 buff/cache KiB Swap: 5207980 total, 4990740 free, 217240 used. 1352596 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 18975 xx 20 0 1351516 66968 25232 S 19.2 3.3 0:02.64 uwsgi 5343 root 20 0 6530024 44920 20744 S 0.7 2.2 56:16.21 Xorg 19528 xx 20 0 10148 3928 3192 R 0.7 0.2 0:00.12 top 4405 root -51 0 0 0 0 S 0.3 0.0 33:22.67 sugov:0 5676 xx 20 0 371972 19496 15068 S 0.3 1.0 11:05.43 vino-server 5713 xx 20 0 361252 12776 9312 S 0.3 0.6 61:26.18 clipit 5977 xx 20 0 499780 3108 2004 S 0.3 0.2 18:58.24 python3 15604 root 20 0 0 0 0 S 0.3 0.0 0:01.16 kworker/u8:2 18153 systemd+ 20 0 8732 3380 2060 S 0.3 0.2 0:01.83 nginx 18979 xx 20 0 988352 45204 1628 S 0.3 2.2 0:00.03 uwsgi 1 root 20 0 161208 6112 4264 S 0.0 0.3 0:09.15 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:35.86 kthreadd 3 root 20 0 0 0 0 S 0.0 0.0 0:03.62 ksoftirqd/0 5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H 7 root 20 0 0 0 0 S 0.0 0.0 1:33.45 rcu_preempt 8 root 20 0 0 0 0 S 0.0 0.0 0:03.07 rcu_sched 9 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh 10 root rt 0 0 0 0 S 0.0 0.0 0:00.02 migration/0 $ free -m total used free shared buff/cache available Mem: 1979 584 457 18 938 1321 Swap: 5085 212 4873
作成動機・背景
これまでシングルボードコンピュータを扱ったことがなく、書籍やWeb上の活用事例を見ながら漠然とした興味に留まっていたがJetson nano 2GBのお手頃価格が背中を押した感じ。
我が家はFamily budgetのみの為、妻とJetson nano 2GBの活用方法を相談している中で、表題のアプリはコロナ禍で英露から来日出来ない妻の両親が「何時でもお孫さんと一緒だよ」と感じられるツールにもなるとのことで、了解してもらった。
ただ、実際に設置してみると現時点は我々夫婦が専らのユーザーとなり生後4ヶ月の息子の寝返りチャレンジをチラ見する事となった。。(今の所寝返るが腕抜きが出来ず最後に泣いて両親駆けつけレスキュー)