RaspberryPi
ROS
TensorFlow
YOLOv2
Movidius

Raspbian Stretch+NCS(Neural Compute Stick)+YoloV2+Webカメラ+ROSによるリアルタイム複数動体検知環境の構築

I wrote it in English in the comment section.

◆ はじめに

Raspberry Pi 3 model B+ へ、タイトル記載のディープラーニング(DeepLearning)環境をインストール・構築する。
OSを導入するところからのクリーンな状態での作業を前提とし、初期状態から着手すれば、ほぼコピー&ペーストだけでつまづくことなく完了するはず。

ごちゃごちゃするのは好きではないため、pyenv, virtualenv は使用しない。
パーミッションの設定は適当とし、セキュリティは特に考慮しない。
同じパッケージを何度も導入しようとしているところ、無駄なパッケージをインストールしようとしているところはご愛敬で。
ノーミス・ノンストップで走っても半日以上かかるのでかなりの覚悟が必要。
ちなみに、Intel が公開しているNCS導入スクリプトはそのままではopenCVがまともに入らないので注意。
Stretchは初回起動時にSDのパーティション拡張を自動で実行してくれるため、Jessieより若干手順が楽。
SSHで作業している場合は、時間の掛かるスクリプト部分でターミナルがタイムアウトしてしまうことが多いため、Raspberry Pi→HDMI→ディスプレイ、の直接接続構成での作業をお勧めする。
ezgif-3-e1cfe20787.gif

次回は、Neural Compute Stickの3本挿し(Multi Stick構成)を試そうと思う。
結果だけ言うと1本でも次回の実装のほうが遥かに動作が速い。
https://qiita.com/PINTO/items/7f13fcb7c894c27691b2

次次回の実装は次回の実装より更に2倍速く動作する。
https://qiita.com/PINTO/items/db3ab44a3e2bcd87f2d8

そして更に上記のどれよりも速く高精度なMobileNet-SSDによる実装。
HW構成次第ではRaspberryPiで10FPS以上を達成。
https://qiita.com/PINTO/items/b97b3334ed452cb555e2

Raspbian JessieにてNCS無しの類似構成でSingle Shot MultiBox Detector環境を構築した前回の記事はこちら。
https://qiita.com/PINTO/items/bbf5ff907966f213fbdf

◆ 環境 [2017.12.11時点]

・Windows 10 Pro [作業用PC] + TeraTerm
・Raspberry Pi 3 model B+
・Intel Movidius Neural Compute Stick  https://developer.movidius.com/
・Raspbian Stretch 2017-11-29
・NCS SDK v1.11.00.04 → [2018.01.10 VerUp] v1.12.00
・ROS kinetic 1.12.11
・samba 4.2.14
・Python 2.7.13、3.5.3(OS標準導入済)
・Tensorflow 1.4.0
・Keras 2.1.2(今回は使わないのでお好みで)
・OpenCV 3.3.1 → [2018.01.12 VerUp] 3.4.0
・Jupyter notebook(今回は使わないのでお好みで)
・MicroSD Card class10 SDHC 32GB
・USBキーボード、USBマウス、液晶テレビ、HDMIケーブル
・インターネット接続可能な有線LAN 又は Wi-Fi環境

◆ Raspbian stretch のインストール

1.下記をダウンロード
http://ftp.jaist.ac.jp/pub/raspberrypi/raspbian/images/raspbian-2017-12-01/2017-11-29-raspbian-stretch.zip
2.ダウンロードされたzipを解凍
3.Win32DiskImagerをダウンロードしてインストール https://ja.osdn.net/projects/sfnet_win32diskimager/downloads/Archive/win32diskimager-1.0.0-install.exe/
4.SDカードをホストPCに挿入/接続
5.Win32DiskImagerを起動し、2017-11-29-raspbian-stretch.imgを指定して書き込み
6.Raspberry Pi 3 へSDカードを挿入して電源ON
7.スタートメニュー→設定→RaspberryPiの設定→インタフェース→必要なオプションを「有効」にする
8.スタートメニュー→設定→RaspberryPiの設定→ローカライゼーション
 (1)ロケールの設定「ja」「JP」「UTF-8」
 (2)タイムゾーン「Asia」「Tokyo」
 (3)キーボードの設定「日本」「日本語」
 (4)無線LANの国「JP Japan」
9.Wi-Fiの省電力モード無効化

$ sudo iwconfig wlan0 power off

10.rootユーザのパスワード設定

$ sudo passwd root ※好きなパスワードを登録

◆ ROS [kinetic]のインストール

1.下記のコマンドを実行

$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
$ wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -O - | sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get -y upgrade
$ sudo apt-get install -y python-rosdep python-rosinstall-generator python-wstool python-rosinstall build-essential cmake
$ sudo apt-get install -y python-pip python-setuptools python-yaml python-distribute python-docutils python-dateutil python-six
$ sudo rosdep init
$ rosdep update
$ mkdir -p ~/ros_catkin_ws
$ pushd ~/ros_catkin_ws
$ rosinstall_generator ros_comm common_msgs tf --rosdistro kinetic --deps --wet-only --tar > kinetic-ros_comm-wet.rosinstall
$ wstool init src kinetic-ros_comm-wet.rosinstall
$ rosdep install -y --from-paths src --ignore-src --rosdistro kinetic -r --os=debian:stretch
$ sudo ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release --install-space /opt/ros/kinetic -j2
$ popd
$ echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
$ source ~/.bashrc
$ sudo reboot

◆ 環境変数の追加

1.下記のコマンドを実行

$ export ROS_HOSTNAME=raspberrypi.local
$ export ROS_IP=`hostname -I`
$ export ROS_MASTER_URI=http://192.168.xxx.xxx:11311

※`hostname -I`の部分はPCのホスト名を入力するのではなく、hostnameとそのままの文脈で入力する
※RaspberryPi側でマスターを起動する場合は、export ROS_MASTER_URI=http://`hostname -I`:11311
※上記の環境変数を削除する場合は、unset ROS_MASTER_URI
※RaspberryPi以外の端末をマスターとする場合はIP部分を読み替える

 例)リモートのUbuntuをマスターとする場合は、Ubuntu側で下記を追加
   PC起動のたびに下記コマンドを打つ必要があるため、~/.bashrcに書いておくと良い

   $ export ROS_HOSTNAME=ubuntu.local
   $ export ROS_IP=`hostname -I`
   $ export ROS_MASTER_URI=http://`hostname -I`:11311

◆ Sambaのインストール

1.下記のコマンドを実行

$ sudo apt-get install -y samba
$ sudo nano /etc/samba/smb.conf

2.設定ファイル(smb.conf)に下記を追記して保存

[pi]
path = /home/pi
read only = No
guest ok = Yes
force user = pi

[etc]
path = /etc
read only = No
guest ok = Yes
force user = root

[usr]
path = /usr
read only = No
guest ok = Yes
force user = root

[tmp]
path = /tmp
read only = No
guest ok = Yes
force user = root

[opt]
path = /opt
read only = No
guest ok = Yes
force user = root

3.sambaのデーモンを再起動する

$ sudo service smbd restart

◆ TMP領域の拡張とログファイルのRAMDISK化

1.下記のコマンドを実行

$ cd /etc
$ sudo cp fstab fstab_org
$ sudo nano /etc/fstab

2.ファイルの末尾に下記を追記して保存

# TMP領域の拡張
tmpfs /tmp tmpfs defaults,size=512m,noatime,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=128m,noatime,mode=1777 0 0
# /var/log をRAMディスクにマウント
tmpfs /var/log tmpfs defaults,size=32m,noatime,mode=0755 0 0
# ~/.ros/log をRAMディスクにマウント
tmpfs /home/pi/.ros/log tmpfs defaults,size=32m,noatime,mode=1777 0 0

3.Raspberry Piを再起動

$ sudo reboot

◆ 無用なシステムログの無効化

1.下記コマンドを実行

$ cd /etc
$ sudo cp rsyslog.conf rsyslog.conf_org
$ sudo nano rsyslog.conf

2.下記のように書き換えて保存する

###############
#### RULES ####
###############

#
# First some standard log files.  Log by facility.
#
auth,authpriv.*                 /var/log/auth.log
*.*;auth,authpriv.none          -/var/log/syslog
#cron.*                         /var/log/cron.log
daemon.*                        -/var/log/daemon.log
#kern.*                         -/var/log/kern.log
#lpr.*                          -/var/log/lpr.log
#mail.*                         -/var/log/mail.log
#user.*                         -/var/log/user.log

#
# Logging for the mail system.  Split it up so that
# it is easy to write scripts to parse these files.
#
#mail.info                      -/var/log/mail.info
#mail.warn                      -/var/log/mail.warn
#mail.err                       /var/log/mail.err

◆ Raspberry Pi起動時にログ出力先フォルダを自動生成するよう変更

1.下記コマンドを実行

$ cd /etc
$ sudo cp rc.local rc.local_org
$ sudo nano rc.local

2.既に入力済みの部分を下記のように書き換えて保存する

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

# 
# RAMディスクに自動的にフォルダを追加
# 
mkdir -p /var/log/ConsoleKit
mkdir -p /var/log/samba
mkdir -p /var/log/fsck
mkdir -p /var/log/apt
mkdir -p /var/log/ntpstats
#chown root.ntp /var/log/ntpstats
chown root.adm /var/log/samba
touch /var/log/lastlog
touch /var/log/wtmp
touch /var/log/btmp
chown root.utmp /var/log/lastlog
chown root.utmp /var/log/wtmp
chown root.utmp /var/log/btmp

exit 0

◆SWAP領域の一時的な拡張 2018/03/17追記

これを実施しておかないとOpenCVのビルド時にスワップが大量に発生してフリーズする、かつ SWAP領域が不足してビルド中に異常終了する、 かつ ビルドが1日経っても終わらない。ビルドのシーケンスとdf -hコマンド、free -hコマンドの状況を張り付いて見ていたところ、OpenCVビルド時にはMEM:1GB + SWAP:1GB以上消費するようだ。単位はMB。

$ sudo nano /etc/dphys-swapfile
CONF_SWAPSIZE=2048

$ sudo /etc/init.d/dphys-swapfile restart swapon -s
$ free -h

◆ 必要なパッケージの一部分インストール

1.下記のコマンドを実行する

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install -y build-essential libc6-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev
$ sudo apt-get install -y libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev
$ sudo apt-get install -y cmake git pkg-config unzip qtbase5-dev python-dev python3-dev python-numpy python3-numpy
$ sudo apt-get install -y cmake-qt-gui mesa-utils libgl1-mesa-dri libprotobuf-dev protobuf-compiler libvtk6-dev
$ sudo apt-get install -y libvtk6-qt-dev python-vtk6 tcl-vtk6 libopencv-dev libgtk-3-dev libdc1394-22 libdc1394-22-dev
$ sudo apt-get install -y libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev libavcodec-dev libavformat-dev
$ sudo apt-get install -y libswscale-dev libxine2-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libv4l-dev
$ sudo apt-get install -y libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev
$ sudo apt-get install -y libxvidcore-dev v4l-utils gfortran python-opencv libgtk2.0-dev libx264-dev libqt5core5a
$ sudo apt-get install -y libqtgui4 libqt4-test libqt4-opengl-dev libatlas-base-dev libeigen3-dev libtesseract-dev
$ sudo apt-get install -y libleptonica-dev tesseract-ocr tesseract-ocr-jpn tesseract-ocr-osd libblas-dev liblapack-dev
$ sudo apt-get install -y python-setuptools python3-decorator python3-scipy python3-pandas python3-h5py libhdf5-dev libpq5 libpq-dev apt-show-versions ffmpeg
$ sudo reboot
$ sudo pip3 install h5py;sudo pip3 install imageio;sudo apt-get update;sudo apt-get upgrade;sudo apt-get clean;
$ sudo apt-get clean;sudo pip3 install --upgrade pillow
$ sudo apt-get clean;sudo pip3 install --upgrade matplotlib
$ sudo apt-get update;sudo apt-get upgrade
$ sudo reboot

◆ Python3.x版 Tensorflow1.4.0のインストール

1.下記のコマンドを実行

$ cd ~
$ mkdir tensorflow1.4.0;cd tensorflow1.4.0
$ wget https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.4.0/tensorflow-1.4.0-cp35-none-linux_armv7l.whl
$ sudo pip3 install tensorflow-1.4.0-cp35-none-linux_armv7l.whl
$ sudo apt-get update;sudo apt-get upgrade
$ python3
>>> import tensorflow
>>> exit()

◆TBB (Intel Threading Building Blocks)の導入 2018/03/17追記

毎度ビルドするのが面倒なため、お手軽導入用パッケージを作った。
OpenCVの並列処理対応用。
あえて自分でビルドしたい物好きな人はこちらの手順
https://github.com/PINTO0309/TBBonARMv7/blob/master/TBB_BuildProcedure_2018U2_arm.txt

【導入による効果】
OpenCVの並列化クラスParallelLoopBodyとparallel_for_ - Qiita
C++17時代の並列ソート - Qiita

1.TBB(2018 U2)インストール用debファイルのダウンロード

$ cd ~
$ wget https://github.com/PINTO0309/TBBonARMv7/raw/master/libtbb-dev_2018U2_armhf.deb

2.下記のコマンドを実行してインストール

$ sudo dpkg -i ~/libtbb-dev_2018U2_armhf.deb
$ sudo ldconfig

◆ Neural Compute Stick SDKのインストール

1.Neural Compute Stick をRaspberry PiのUSBポートへ挿入する
※Raspberry PiのUSBポート残りの3個のうち1個は、Neural Compute Stickの横幅が太いため挿せなくなる。
※常時4ポート使用したい場合はUSBハブの追加を検討する。
※このタイミングでNeural Compute Stickを挿しておかないと後続のビルドに失敗する、ように見える。
※処理途中に表示されるWarningは無視してもかまわない。

2.Neural Compute Stick SDKのインストール
【参考】https://movidius.github.io/ncsdk/install.html

$ cd ~
$ git clone https://github.com/movidius/ncsdk.git
$ cd ~/ncsdk
$ nano ./install-opencv.sh

3.下記のように、ファイル中盤あたりに記載されているopenCV周りの記述を変更して保存
※バックスラッシュ「\」は「半角の¥」に読み替える。
※この部分はOpenCVを単体でインストールしたい時のシーケンスとしてもそのまま使える。

                cd ~
                wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.4.0.zip
                unzip opencv.zip
                wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.4.0.zip
                unzip opencv_contrib.zip
                cd ~/opencv-3.4.0/
                mkdir build
                cd build
                cmake -D CMAKE_CXX_FLAGS="-DTBB_USE_GCC_BUILTINS=1 -D__TBB_64BIT_ATOMICS=0" \
                      -D CMAKE_BUILD_TYPE=RELEASE \
                      -D CMAKE_INSTALL_PREFIX=/usr/local \
                      -D INSTALL_PYTHON_EXAMPLES=OFF \
                      -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.4.0/modules \
                      -D BUILD_EXAMPLES=OFF \
                      -D PYTHON_DEFAULT_EXECUTABLE=$(which python3) \
                      -D INSTALL_PYTHON_EXAMPLES=OFF \
                      -D BUILD_opencv_python2=ON \
                      -D BUILD_opencv_python3=ON \
                      -D WITH_OPENCL=OFF \
                      -D WITH_OPENGL=OFF \
                      -D WITH_TBB=ON \
                      -D BUILD_TBB=OFF \
                      -D WITH_CUDA=OFF \
                      -D ENABLE_NEON:BOOL=ON \
                      -D WITH_QT=OFF \
                      -D BUILD_opencv_dnn_modern=OFF ..
                make -j $(($(nproc) + 1))
                sudo make install
                sudo ldconfig

4.下記のコマンドを実行
make examples で openCV3.4.0/opencv_contrib3.4.0 が自動でフルインストールされる、ワーニングが4~5件表示されるが無視してよし

$ make install
$ make examples
$ sudo /bin/bash -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/opencv.conf'
$ sudo /bin/bash -c 'echo "/usr/lib" >> /etc/ld.so.conf.d/opencv.conf'
$ sudo ldconfig
$ sudo apt-get update
$ cd ~;sudo rm opencv.zip;sudo rm opencv_contrib.zip
$ python3
>>> import cv2
>>> exit()

$ sudo reboot

◆ Keras2.1.2インストール

1.下記のコマンドを実行

 $ sudo pip3 install keras

2.実行確認、importしてエラーが出なければ正常

$ python3
>>> import keras
Using TensorFlow backend.
>>> exit()

◆ Jupyter notebookのインストール

1.下記のコマンドを実行

$ cd ~
$ git clone https://github.com/kleinee/jns.git
$ cd jns
$ sudo chmod +x *.sh
$ sudo pip3 install jupyter;sudo pip3 install readline;sudo pip3 install ipyparallel
$ ./configure_jupyter.sh
$ sudo ./install_tex.sh;sudo ./install_stack.sh;

2.Jupyterのインストール先パス確認
※ 表示されたパスは後続の作業で使用 (1)

$ which jupyter

3.Jupyter notebookの自動起動設定

$ sudo nano /etc/systemd/system/jupyter.service

※空のファイルが開かれるので下記のとおり入力して保存(notebookの左側のパスを(1)で置き換え)

[Unit]
Description=Jupyter notebook

[Service]
Type=simple
PIDFile=/var/run/jupyter-notebook.pid
ExecStart=/usr/local/bin/jupyter notebook  ←(1)
User=pi
Group=pi
WorkingDirectory=/home/pi/notebooks
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

4.下記のコマンドを実行

$ sudo systemctl start jupyter;sudo systemctl enable jupyter;sudo systemctl status jupyter
$ sudo reboot

◆ Jupyter notebook動作確認

1.操作用PCのブラウザから「http://(Raspberry PiのIPアドレス):8888/」にアクセス
2.ログインパスワード:jns

◆ 後始末

1.下記コマンドを実行

$ sudo apt-get autoremove
$ sudo apt-get clean

◆ Webカメラ + YoloV2 での複数動体検出環境の導入と味見

1.下記のコマンドを実行

$ cd ~
$ git clone https://github.com/duangenquan/YoloV2NCS.git
$ cd YoloV2NCS
$ nano ~/YoloV2NCS/src/PythonWrapper.cpp
※下記のとおり編集して保存。
    //long buflen;
    int buflen;
$ make
$ mvNCCompile ./models/caffemodels/yoloV2Tiny20.prototxt -w ./models/caffemodels/yoloV2Tiny20.caffemodel -s 12
$ cd detectionExample
$ mkdir utils
$ nano app_utils.py

2.下記のソースを全コピペ

app_utils.py
# From http://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/

import struct
import six
import collections
import cv2
import datetime
from threading import Thread
from matplotlib import colors


class FPS:
    def __init__(self):
        # store the start time, end time, and total number of frames
        # that were examined between the start and end intervals
        self._start = None
        self._end = None
        self._numFrames = 0

    def start(self):
        # start the timer
        self._start = datetime.datetime.now()
        return self

    def stop(self):
        # stop the timer
        self._end = datetime.datetime.now()

    def update(self):
        # increment the total number of frames examined during the
        # start and end intervals
        self._numFrames += 1

    def elapsed(self):
        # return the total number of seconds between the start and
        # end interval
        return (self._end - self._start).total_seconds()

    def fps(self):
        # compute the (approximate) frames per second
        return self._numFrames / self.elapsed()


class WebcamVideoStream:
    def __init__(self, src, width, height):
        # initialize the video camera stream and read the first frame
        # from the stream
        #print(src)
        self.stream = cv2.VideoCapture(src)
        self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, width)
        self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
        (self.grabbed, self.frame) = self.stream.read()

        # initialize the variable used to indicate if the thread should
        # be stopped
        self.stopped = False

    def start(self):
        # start the thread to read frames from the video stream
        Thread(target=self.update, args=()).start()
        return self

    def update(self):
        # keep looping infinitely until the thread is stopped
        while True:
            # if the thread indicator variable is set, stop the thread
            if self.stopped:
                return

            # otherwise, read the next frame from the stream
            (self.grabbed, self.frame) = self.stream.read()

    def read(self):
        # return the frame most recently read
        return self.frame

    def stop(self):
        # indicate that the thread should be stopped
        self.stopped = True


def standard_colors():
    colors = [
        'AliceBlue', 'Chartreuse', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque',
        'BlanchedAlmond', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite',
        'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan',
        'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange',
        'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet',
        'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite',
        'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod',
        'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki',
        'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue',
        'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey',
        'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
        'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime',
        'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid',
        'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
        'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin',
        'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed',
        'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed',
        'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple',
        'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown',
        'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
        'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow',
        'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
        'WhiteSmoke', 'Yellow', 'YellowGreen'
    ]
    return colors


def color_name_to_rgb():
    colors_rgb = []
    for key, value in colors.cnames.items():
        colors_rgb.append((key, struct.unpack('BBB', bytes.fromhex(value.replace('#', '')))))
    return dict(colors_rgb)


def draw_boxes_and_labels(
        boxes,
        classes,
        scores,
        category_index,
        instance_masks=None,
        keypoints=None,
        max_boxes_to_draw=20,
        min_score_thresh=.5,
        agnostic_mode=False):
    """Returns boxes coordinates, class names and colors

    Args:
      boxes: a numpy array of shape [N, 4]
      classes: a numpy array of shape [N]
      scores: a numpy array of shape [N] or None.  If scores=None, then
        this function assumes that the boxes to be plotted are groundtruth
        boxes and plot all boxes as black with no classes or scores.
      category_index: a dict containing category dictionaries (each holding
        category index `id` and category name `name`) keyed by category indices.
      instance_masks: a numpy array of shape [N, image_height, image_width], can
        be None
      keypoints: a numpy array of shape [N, num_keypoints, 2], can
        be None
      max_boxes_to_draw: maximum number of boxes to visualize.  If None, draw
        all boxes.
      min_score_thresh: minimum score threshold for a box to be visualized
      agnostic_mode: boolean (default: False) controlling whether to evaluate in
        class-agnostic mode or not.  This mode will display scores but ignore
        classes.
    """
    # Create a display string (and color) for every box location, group any boxes
    # that correspond to the same location.
    box_to_display_str_map = collections.defaultdict(list)
    box_to_color_map = collections.defaultdict(str)
    box_to_instance_masks_map = {}
    box_to_keypoints_map = collections.defaultdict(list)
    if not max_boxes_to_draw:
        max_boxes_to_draw = boxes.shape[0]
    for i in range(min(max_boxes_to_draw, boxes.shape[0])):
        if scores is None or scores[i] > min_score_thresh:
            box = tuple(boxes[i].tolist())
            if instance_masks is not None:
                box_to_instance_masks_map[box] = instance_masks[i]
            if keypoints is not None:
                box_to_keypoints_map[box].extend(keypoints[i])
            if scores is None:
                box_to_color_map[box] = 'black'
            else:
                if not agnostic_mode:
                    if classes[i] in category_index.keys():
                        class_name = category_index[classes[i]]['name']
                    else:
                        class_name = 'N/A'
                    display_str = '{}: {}%'.format(
                        class_name,
                        int(100 * scores[i]))
                else:
                    display_str = 'score: {}%'.format(int(100 * scores[i]))
                box_to_display_str_map[box].append(display_str)
                if agnostic_mode:
                    box_to_color_map[box] = 'DarkOrange'
                else:
                    box_to_color_map[box] = standard_colors()[
                        classes[i] % len(standard_colors())]

    # Store all the coordinates of the boxes, class names and colors
    color_rgb = color_name_to_rgb()
    rect_points = []
    class_names = []
    class_colors = []
    for box, color in six.iteritems(box_to_color_map):
        ymin, xmin, ymax, xmax = box
        rect_points.append(dict(ymin=ymin, xmin=xmin, ymax=ymax, xmax=xmax))
        class_names.append(box_to_display_str_map[box])
        class_colors.append(color_rgb[color.lower()])
    return rect_points, class_names, class_colors

3../detectionExample/Main.py をWebカメラ対応のロジックに変更

$ cd ..
$ cp ./detectionExample/Main.py ./detectionExample/Main_BK.py
$ nano ./detectionExample/Main.py

4.下記のソースを全コピペ

Main.py
import sys,os,time,csv,getopt,cv2,argparse
import numpy as np
from datetime import datetime

from ObjectWrapper import *
from Visualize import *

from utils.app_utils import FPS, WebcamVideoStream

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--graph', dest='graph', type=str,
                        default='graph', help='MVNC graphs.')
    parser.add_argument('-src', '--source', dest='video_source', type=int,
                        default=0, help='Device index of the camera.')
    parser.add_argument('-wd', '--width', dest='width', type=int,
                        default=400, help='Width of the frames in the video stream.')
    parser.add_argument('-ht', '--height', dest='height', type=int,
                        default=300, help='Height of the frames in the video stream.')

    args = parser.parse_args()

    network_blob=args.graph

    detector = ObjectWrapper(network_blob)

    video_capture = WebcamVideoStream(src=args.video_source,
                                      width=args.width,
                                      height=args.height).start()

    # image preprocess
    img = video_capture.read()

    while True:
        results = detector.Detect(img)

        imdraw = Visualize(img, results)
        cv2.imshow('Demo',imdraw)
        #print(datetime.now())

        key = cv2.waitKey(1)
        if key == ord('q'):
            break
        img = video_capture.read()

video_capture.stop()
cv2.destroyAllWindows()

5.WEBカメラ (UVC [USB Video Class] 対応のもの) をRaspberry PiのUSBポートへ接続

6.動体検知の実行テスト、下記コマンドを実行

$ startx   ← CLIモードで作業中のときのみ実行、GUIモードへ切り替え、SSHで作業していないときは無視
$ cd ~/YoloV2NCS   ← GUI側(Desktop側)の「端末」アプリで実行
$ python3 ./detectionExample/Main.py   ← GUI側(Desktop側)の「端末」アプリで実行

◆SWAP領域の縮小

$ sudo nano /etc/dphys-swapfile
CONF_SWAPSIZE=100

$ sudo /etc/init.d/dphys-swapfile restart swapon -s

◆ TMP領域の縮小

1.下記のコマンドを実行

$ cd /etc
$ sudo nano /etc/fstab

2.ファイルの下記の箇所を変更して保存

tmpfs /tmp tmpfs defaults,size=32m,noatime,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=32m,noatime,mode=1777 0 0

3.下記のコマンドを実行

$ sudo reboot

◆ おまけ

(1) 実行時にSWAP領域が不足した場合は下記実施

$ sudo nano /etc/dphys-swapfile
CONF_SWAPSIZE=1024

$ sudo /etc/init.d/dphys-swapfile restart swapon -s

(2) SDカード長寿命化のためSWAPを無効化

1.下記のコマンドを実行

$ free
$ sudo swapoff --all
$ sudo apt-get remove dphys-swapfile
$ sudo reboot

(3) Python3 コマンドを Python コマンドへ誘導する設定

1.下記のコマンドを実行

$ sudo nano ~/.bashrc

2.下記一行をファイルの一番下に追記

alias python=python3

3.下記コマンドを実行

$ source ~/.bashrc

4.下記コマンドを実行、PythonコマンドでPython3が起動するようになる

$ python
>>>

(4) Ubuntu 16.04 amd64,x86/x64用 TBB(2018 U2) debパッケージ

deb: https://drive.google.com/open?id=1MyaI9dgTO47_18HeVjmycC2zZeE46rdo
ビルドシーケンス: https://drive.google.com/open?id=10FbuthIF2EEDilN1j0RoK-InmPKNYb_A