NVIDIAのJetson Nanoで「ディープ・ラーニングによる画像分類」をやってみた
Contents
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コンテナの実行)をクリックする。
基本的にはこちらのページの手順に従って事前準備をしてゆく。
プロジェクトのコピー
git cloneコマンドでGithubからプロジェクトをコピーする。
git clone --recursive https://github.com/dusty-nv/jetson-inference
jetson-inferenceディレクトリがコピーされる。
尚、recursiveは必要に応じてサブモジュールもcloneしてくれるオプション。
Dockerコンテナの実行
jetson-inferenceにディレクトリを移動してDockerコンテナを実行する。
cd jetson-inference
docker/run.sh
初回は必要なファイルのダウンロード&展開で時間がかかるので注意。
モデルのダウンロード
また途中でダウンロードするモデルを選択するメニューが表示される。
デフォルトで、GoogleNetとResNet-18等にチェックが入っているが必要に応じてその他のモデルをチェックする。
尚、選択可能なモデルは以下の通り。
- Image Recognition – all models(2.2G):画像認識の全てのモデル
- AlexNet(244MB)
- GoogleNet(54MB)
- GoogleNet-12(42MB)
- ResNet-18(47MB)
- ResNet-50(102MB)
- ResNet-101(179MB)
- ResNet-152(242MB)
- VGG-16(554MB)
- VGG-19(575MB)
- Inception-v4(172MB)
- Object Detection – all models(395MB):物体検知の全てのモデル
- SSD-Mobilenet-v1(27MB)
- SSD-Mobilenet-v2(68MB)
- SSD-Inception-v2(100MB)
- PedNet(30MB)
- MultiPed(24MB)
- FaceNet(24MB)
- DetectNet-COCO-Dog(29」MB)
- DetectNet-COCO-Bottle(29MB)
- DetectNet-COCO-Chair(29MB)
- DetectNet-COCO-Airplane(29MB)
- Semantic Segmentation – all(518MB):セマンティックセグメンテーションの全てのモデル
- FCN-ResNet18-Cityscapes-512×256(47MB)
- FCN-ResNet18-Cityscapes-1024×512(47MB)
- FCN-ResNet18-Cityscapes-2048×1024(47MB)
- FCN-ResNet18-DeepScene-576×320(47MB)
- FCN-ResNet18-DeepScene-864×480(47MB)
- FCN-ResNet18-MHP-512×320(47MB)
- FCN-ResNet18-MHP-640×360(47MB)
- FCN-ResNet18-Pascal-VOC-320×320(47MB)
- FCN-ResNet18-Pascal-VOC-512×320(47MB)
- FCN-ResNet18-SUN-RGBD-512×400(47MB)
- FCN-ResNet18-SUN-RGBD-640×512(47MB)
- Semantic Segmentation – legacy(1.4GB):セマンティックセグメンテーションレガシーの全てのモデル
- FCN-Alexnet-Cityscapes-SD(235MB)
- FCN-Alexnet-Cityscapes-HD(235MB)
- FCN-Alexnet-Aerial-FPV(7MB)
- FCN-Alexnet-Pascal-VOC(235MB)
- FCN-Alexnet-Synthia-CVPR(235MB)
- FCN-Alexnet-Synthia-Summer-SD(235MB)
- FCN-Alexnet-Synthia-Summer-HD(235MB)
- Image Processing – all models(138MB):画像処理の全てのモデル
- Deep-Homography-COCO(137MB)
- 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が使用される。 使用可能なモデルと指定するパラメーターとの関係は以下の通り。
|
プログラム
ソースコード
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等と予測されていた事が分かる。
他のモデル
最後に同じ画像を複数のネットワーク(モデル)で試してみた。
GoogleNet
下記の滑走路上の戦闘機の手前にスタッフ(人間)が3人写っている写真では43.21%の信頼度でwarplane(戦闘機)と分類された。
手前に人が写っている分、信頼度が多少落ちているのだと推測される。
ResNet-18
43.04%の信頼度でaircraft carrier(空母)と分類された。
確かに滑走路の部分が空母の甲板に見えなくもない。
AlexNet
20.37%の信頼度でwarplane(戦闘機)と分類された。
GoogleNetより信頼度が落ちている。
VGG-16
29.35%の信頼度でwarplane(戦闘機)と分類された。
Alexnetよりからは高いがやはりGoogleNetよりかは信頼度が落ちている。
上記のように同じ画像であっても選択するネットワークによって分類される画像や信頼度が異なる結果が得られら。
パラメーターを変更するだけで簡単に複数のネットワークを試すことが出来るので是非試してみて欲しい。
以上で今回の記事は終了とする。
この記事が何処かで誰かの役に立つことを願っている。
尚、当記事中の商品へのリンクはAmazonアソシエイトへのリンクが含まれています。Amazonのアソシエイトとして、当メディアは適格販売により収入を得ていますのでご了承ください。
1件の返信
[…] NVIDIAのJetson Nanoで「ディープ・ラーニングによる画像分類」をやってみた Hello AI World […]