NVIDIAのJetson Nanoで「ディープ・ラーニングによる画像分類」をやってみた | そう備忘録

NVIDIAのJetson Nanoで「ディープ・ラーニングによる画像分類」をやってみた

Hello AI World

NVIDIAのJetson Nano 2GB 開発者キットを購入したのでGithubで公開されているHello AI WorldのDeploying Deep Learningをやってみた時の記事。

Deploying Deep Learning(ディープラーニングの構築)はJetson Nanoを使ってのDeep Learningネットワークの構築について書かれた記事で主に以下の章がある。

Inference(推論)

  • Classifying Images with ImageNet:ImageNetによる画像分類
  • Locating Objects with DetectNet:DetectNetによる物体の検出
  • Semantic Segmentation with SegNet:SegNetによるセマンティックセグメンテーション

Training(学習)

  • Transfer Learning with PyTorch:PyTorchによる転移学習
  • Classification/Recognition (ResNet-18):ResNet-18による分類と認識
  • Object Detection (SSD-Mobilenet):SSD-Mobilenetによる物体の検出

今回の記事では一番最初のClassifying Images with ImageNet(ImageNetの画像分類)の記事とする。

尚、Jetson Nano 2GB 開発者キットのセットアップはこちら、入門コース(Getting Started with AI on Jetson Nano!)をやってみた時の記事はこちらを参照して欲しい。

環境

今回の実行環境は以下の通り。

本体

NVIDIA Jetson Nano 2GB 開発者キット

OS

Ubuntu18.04.5 LTS

JetPack SDK

JetPack 4.4.1

事前準備

Jetson Nano 2GB開発者キットのOSや必要なミドルソフトウェアのセットアップは終了している前提とする。

NVIDIAのHello AIのページにアクセスして”Running the Docker Container”(Dockerコンテナの実行)をクリックする。

Hello AI World事前準備

基本的にはこちらのページの手順に従って事前準備をしてゆく。

Dockerコンテナの実行

プロジェクトのコピー

git cloneコマンドでGithubからプロジェクトをコピーする。

git clone --recursive https://github.com/dusty-nv/jetson-inference

jetson-inferenceディレクトリがコピーされる。

尚、recursiveは必要に応じてサブモジュールもcloneしてくれるオプション。

jetson-inferenceディレクトリがコピーされる

Dockerコンテナの実行

jetson-inferenceにディレクトリを移動してDockerコンテナを実行する。

cd jetson-inference
docker/run.sh

初回は必要なファイルのダウンロード&展開で時間がかかるので注意。

モデルのダウンロード

また途中でダウンロードするモデルを選択するメニューが表示される。

デフォルトで、GoogleNetとResNet-18等にチェックが入っているが必要に応じてその他のモデルをチェックする。

ダウンロードするモデルの選択

尚、選択可能なモデルは以下の通り。

  1. Image Recognition – all models(2.2G):画像認識の全てのモデル
  2. AlexNet(244MB)
  3. GoogleNet(54MB)
  4. GoogleNet-12(42MB)
  5. ResNet-18(47MB)
  6. ResNet-50(102MB)
  7. ResNet-101(179MB)
  8. ResNet-152(242MB)
  9. VGG-16(554MB)
  10. VGG-19(575MB)
  11. Inception-v4(172MB)
  12. Object Detection – all models(395MB):物体検知の全てのモデル
  13. SSD-Mobilenet-v1(27MB)
  14. SSD-Mobilenet-v2(68MB)
  15. SSD-Inception-v2(100MB)
  16. PedNet(30MB)
  17. MultiPed(24MB)
  18. FaceNet(24MB)
  19. DetectNet-COCO-Dog(29」MB)
  20. DetectNet-COCO-Bottle(29MB)
  21. DetectNet-COCO-Chair(29MB)
  22. DetectNet-COCO-Airplane(29MB)
  23. Semantic Segmentation – all(518MB):セマンティックセグメンテーションの全てのモデル
  24. FCN-ResNet18-Cityscapes-512×256(47MB)
  25. FCN-ResNet18-Cityscapes-1024×512(47MB)
  26. FCN-ResNet18-Cityscapes-2048×1024(47MB)
  27. FCN-ResNet18-DeepScene-576×320(47MB)
  28. FCN-ResNet18-DeepScene-864×480(47MB)
  29. FCN-ResNet18-MHP-512×320(47MB)
  30. FCN-ResNet18-MHP-640×360(47MB)
  31. FCN-ResNet18-Pascal-VOC-320×320(47MB)
  32. FCN-ResNet18-Pascal-VOC-512×320(47MB)
  33. FCN-ResNet18-SUN-RGBD-512×400(47MB)
  34. FCN-ResNet18-SUN-RGBD-640×512(47MB)
  35. Semantic Segmentation – legacy(1.4GB):セマンティックセグメンテーションレガシーの全てのモデル
  36. FCN-Alexnet-Cityscapes-SD(235MB)
  37. FCN-Alexnet-Cityscapes-HD(235MB)
  38. FCN-Alexnet-Aerial-FPV(7MB)
  39. FCN-Alexnet-Pascal-VOC(235MB)
  40. FCN-Alexnet-Synthia-CVPR(235MB)
  41. FCN-Alexnet-Synthia-Summer-SD(235MB)
  42. FCN-Alexnet-Synthia-Summer-HD(235MB)
  43. Image Processing – all models(138MB):画像処理の全てのモデル
  44. Deep-Homography-COCO(137MB)
  45. Super-Resolution-BSD500(1MB)

モデルの追加

尚、モデルは後から以下のコマンドで追加することもできる。

cd jetson-inference/tools
./download-models.sh

ドライブのマウント

コンテナが実行されると下記のドライブがマウントされる。

  • jetson-inference/data
  • jetson-inference/python/training/classification/data
  • jetson-inference/python/training/classification/models
  • jetson-inference/python/training/detection/ssd/data
  • jetson-inference/python/training/detection/ssd/models

上記のドライブにコンテナ実行中に書き込んだファイルはコンテナを終了した後も保存されるが、それ以外のディレクトリに書き込んだファイルはコンテナ終了と同時に消えてしまう。

上記以外のドライブをマウントしたいのであれば–volumeオプションを付けてコンテナを実行する。

docker/run.sh --volume /my/host/path:/my/container/path

プロジェクトのビルド

尚、今回は試していないがDockerコンテナを使用せずにJetson Nanoに直接プロジェクトをビルドインすることも可能だ。

詳細はこちらのページを確認して欲しい。

プロジェクトのソースからのビルド

ImageNetの画像分類

写真に何が写っているのかを推測するプロジェクト。

ダウンロードしている複数のモデルで試すことが可能で、写真に複数の物体が写っている時は信頼度の高い物体の結果を表示する。

※バックグラウンドでは2位以下の信頼度の物体も分類しているがこのプログラムの出力結果としては1位の結果を写真上にオーバーレイ表示している。

作業ディレクトリに移動

作業ディレクトリに移動する。

cd build/aarch64/bin

分類プログラムの実行

下記のコマンドでPythonプログラムを実行する。

./imagenet.py images/orange_0.jpg images/test/orange_0_output.jpg

images/orange_0.jpg

下記のオレンジとミツバチの写真をInputファイルとした。

オレンジとミツバチの写真

images/test/orange_0_output.jpg

出力ファイル。

左上に96.68%でorange(オレンジ)の写真と分類されている。

出力ファイル

–network

起動時にモデルを指定する事が可能で未指定時はGoogleNetが使用される。

使用可能なモデルと指定するパラメーターとの関係は以下の通り。

モデル

パラメーター

AlexNet

alexnet

GoogleNet

googlenet

GoogleNet-12

googlenet-12 

ResNet-18

resnet-18

ResNet-50

resnet-50

ResNet-101

resnet-101

ResNet-152

resnet-152

VGG-16

vgg-16

VGG-19

vgg-19

Inception-v4

inception-v4

プログラム

ソースコード

NVIDIAのサンプルプログラム(imagenet.py)は以下の通り。

上部のコメント部分に「NVIDIAの著作権表示を行う条件で制限なしで使用する権利、コピー、変更、マージ、公開、配布、サブライセンス、およびソフトウェアのコピーの販売、無制限でソフトウェアを扱える(意訳)」とあったので、このブログに掲載させて貰っている。

尚、ソースコード中に日本語でコメントを追記している。

#!/usr/bin/python3
#
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#

import jetson.inference
import jetson.utils

import argparse # パラメター
import sys


# parse the command line
parser = argparse.ArgumentParser(description="Classify a live camera stream using an image recognition DNN.", 
                                 formatter_class=argparse.RawTextHelpFormatter, epilog=jetson.inference.imageNet.Usage() +
                                 jetson.utils.videoSource.Usage() + jetson.utils.videoOutput.Usage() + jetson.utils.logUsage())

parser.add_argument("input_URI", type=str, default="", nargs='?', help="URI of the input stream")
parser.add_argument("output_URI", type=str, default="", nargs='?', help="URI of the output stream")
parser.add_argument("--network", type=str, default="googlenet", help="pre-trained model to load (see below for options)")
parser.add_argument("--camera", type=str, default="0", help="index of the MIPI CSI camera to use (e.g. CSI camera 0)\nor for VL42 cameras, the /dev/video device to use.\nby default, MIPI CSI camera 0 will be used.")
parser.add_argument("--width", type=int, default=1280, help="desired width of camera stream (default is 1280 pixels)")
parser.add_argument("--height", type=int, default=720, help="desired height of camera stream (default is 720 pixels)")
parser.add_argument('--headless', action='store_true', default=(), help="run without display")

is_headless = ["--headless"] if sys.argv[0].find('console.py') != -1 else [""]

try:
	opt = parser.parse_known_args()[0] # パラメーターの読み込み
except:
	print("")
	parser.print_help()
	sys.exit(0)


# load the recognition network ネットワーク(モデル)の読み込み
net = jetson.inference.imageNet(opt.network, sys.argv)

# create video sources & outputs
input = jetson.utils.videoSource(opt.input_URI, argv=sys.argv) # videoSourceは動画や複数の画像を同時に扱えるAPI
output = jetson.utils.videoOutput(opt.output_URI, argv=sys.argv+is_headless)
font = jetson.utils.cudaFont()

# process frames until the user exits
while True:
	# 入力ビデオソースをイメージにキャプチャー
	img = input.Capture()

	# 画像分類 Class IDとconfidence(信頼値)を取得する
	class_id, confidence = net.Classify(img)

	# Class IDからディスクリプションを取得する
	class_desc = net.GetClassDesc(class_id)

	# イメージに信頼値やディスクリプションを上書きする
	font.OverlayText(img, img.width, img.height, "{:05.2f}% {:s}".format(confidence * 100, class_desc), 5, 5, font.White, font.Gray40)
	
	# イメージを出力する
	output.Render(img)

	# ネットワーク名、FPSを表示する
	output.SetStatus("{:s} | Network {:.0f} FPS".format(net.GetNetworkName(), net.GetNetworkFPS()))

	# パフォーマンス情報を表示する
	net.PrintProfilerTimes()

	# インプットまたはアウトプットののストリーミングが終了するまで繰り返す
	if not input.IsStreaming() or not output.IsStreaming():
		break

補足説明

プログラム中に日本語でコメントを入れているのでおよその処理は分かると思うので使用しているNVIDIAのライブラリーとクラスについて補足しておく。

jetson.inference

jetson.inferenceはNVIDIAが提供するjetsonの推論用のライブラリー。

55行目の net = jetson.inference.imageNet(opt.network, sys.argv) で指定したネットワークを読み込んでいる。

imageNetクラス

imageNetはDNN(Deep Neural Network)による画像分類を行うクラス。

主なメソッドは以下の通り。

Classify(…)

RGBA画像を分類してオブジェクトのクラスと信頼度を返す。

GetClassDesc(…)

指定されたオブジェクトクラスの説明を返す。

GetClassSynset(…)

指定されたクラスのsynsetデータカテゴリ文字列を返す。

synsetは通常クラストレーニングデータフォルダーにマップされる。

GetNetworkName(…)

モデルで使用されているネットワークの名前を返す。

GetNumClasses(…)

このネットワークモデルが分類できるオブジェクトクラスの数を返す。

__init__(…)

初期化

jetson.utils

jetson.utilsはNVIDIAが提供するjetsonのユーティリティのライブラリー。

videoSourceクラス

58行目の input = jetson.utils.videoSource(opt.input_URI, argv=sys.argv) のvideoSourceはカメラ、ビデオストリーム、および画像のインターフェイスのクラス。

主なメソッドは以下の通り。

Capture(…)

フレームをキャプチャしてcudaImageを返す。

CUDA(Compute Unified Device Architecture)はNVIDIAが開発・提供している、GPU向けの汎用並列コンピューティングプラットフォームおよびプログラミングモデルの事。

Close(…)

ビデオフレームのストリーミングの停止。

GetFrameRate(…)

ビデオソースの1秒あたりのフレーム数を返す。

GetHeight(…)

ビデオソースの高さをピクセル単位で返す。

GetWidth(…)

ビデオソースの幅をピクセル単位で返す。

IsStreaming(…)

ストリームがオープンの時はtrueを返し、クローズの時はfalseを返す。

Open(…)

ストリーミングフレームのビデオソースを開く。

__init__(…)

初期化

videoOutputクラス

59行目の output = jetson.utils.videoOutput(opt.output_URI, argv=sys.argv+is_headless) のvideoOutputはビデオと画像をストリーミングするためのクラス。

主なメソッドは以下の通り。

Close(…)

ビデオフレームのストリーミングの停止。

GetFrameRate(…)

ビデオアウトプットの1秒あたりのフレーム数を返す。

GetHeight(…)

ビデオアウトプットの高さをピクセル単位で返す。

GetWidth(…)

ビデオアウトプットの幅をピクセル単位で返す。

IsStreaming(…)

ストリームがオープンの時はtrueを返し、クローズの時はfalseを返す。

Open(…)

ストリーミングフレームのビデオアウトプットを開く。

Render(…)

フレームを描画する。

SetStatus(…)

ステータス文字列(ウィンドウタイトルバーのテキストなど)を設定する。

__init__(…)

初期化

cudaFontクラス

CUDAを使用したビットマップフォントを上に重ねて(オーバーレイ)描画するクラス。

主なメソッドは以下の通り。

OverlayText(…)

特定のテキスト文字列を上に重ねて描画する。

__init__(…)

初期化

カメラからの映像の分類

imagenet.pyは画像の他にJetson Nanoに接続されたカメラの映像をキャプチャして分類したり、ファイル(mp4)に出力することができる。

videoSourceクラスやvideoOutputクラスがビデオストリームにも対応している為だ。

尚、その際はCLI(Command Line Interface)では無くてデスクトップ環境でLXTerminalから下記のコマンドを実行すればビデオストリームの出力結果がデスクトップ上に表示される。

  • ./imagenet.py /dev/video0 # USB接続のカメラ
  • ./imagenet.py /dev/video0 output.mp4 # 出力結果をmp4ファイルに保存
  • ./imagenet.py csi: //0 # CSI-2コネクタに接続したカメラ

実行結果

Jetson Nanoに接続されたUSBカメラでウィルキンソンの炭酸水を写した所、75.24%の信頼度でwater bottleと分類された。

実行結果

尚、LXTerminal上ではリアルタイムで推測されている様子が分かる。

water bottleの他にはpop bottle、hair spray等と予測されていた事が分かる。

LXTermの画面

他のモデル

最後に同じ画像を複数のネットワーク(モデル)で試してみた。

GoogleNet

下記の滑走路上の戦闘機の手前にスタッフ(人間)が3人写っている写真では43.21%の信頼度でwarplane(戦闘機)と分類された。

手前に人が写っている分、信頼度が多少落ちているのだと推測される。

GoogleNetでのClassification

ResNet-18

43.04%の信頼度でaircraft carrier(空母)と分類された。

確かに滑走路の部分が空母の甲板に見えなくもない。

ResNet-18

AlexNet

20.37%の信頼度でwarplane(戦闘機)と分類された。

GoogleNetより信頼度が落ちている。

AlexNet

VGG-16

29.35%の信頼度でwarplane(戦闘機)と分類された。

Alexnetよりからは高いがやはりGoogleNetよりかは信頼度が落ちている。

VGG-16

上記のように同じ画像であっても選択するネットワークによって分類される画像や信頼度が異なる結果が得られら。

パラメーターを変更するだけで簡単に複数のネットワークを試すことが出来るので是非試してみて欲しい。

以上で今回の記事は終了とする。

最後に

この記事が何処かで誰かの役に立つことを願っている。

尚、当記事中の商品へのリンクはAmazonアソシエイトへのリンクが含まれています。Amazonのアソシエイトとして、当メディアは適格販売により収入を得ていますのでご了承ください。

souichirou

やった事を忘れない為の備忘録 同じような事をやりたい人の参考になればと思ってブログにしてます。 主にレゴ、AWS(Amazon Web Services)、WordPress、Deep Learning、RaspberryPiに関するブログを書いています。 仕事では工場に協働ロボットの導入や中小企業へのAI/IoT導入のアドバイザーをやっています。 2019年7月にJDLA(一般社団法人 日本デイープラーニング協会)Deep Learning for GENERALに合格しました。 質問は記事一番下にあるコメントかメニュー上部の問い合わせからお願いします。

おすすめ

1件の返信

  1. 2023年9月8日

    […] NVIDIAのJetson Nanoで「ディープ・ラーニングによる画像分類」をやってみた Hello AI World […]

質問やコメントや励ましの言葉などを残す

名前、メール、サイト欄は任意です。
またメールアドレスは公開されません。