Ubuntu 20.04 on Raspberry PI 4 model BでCoral USB Accelerater(TensorFlow Lite)を動かしてみる

Vitis AIと比較を行うために、Webカメラでの物体検知を行いたい。

# Raspberry PIにUbuntu20.04をインストール

[Raspberry PI Imager](https://www.raspberrypi.org/downloads/)を使ってSDカードにUbuntuを焼く。

初回はユーザ名`ubuntu`、パスワード`ubuntu`でログインし、パスワードを設定、パッケージを更新しておく。(結構時間がかかる)

```sh
sudo apt update
sudo apt full-upgrade -y
```

## LANの設定

`/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`を作って編集

```
network={
    ssid="SSID名"
    scan_ssid=1
    key_mgmt=WPA-PSK
    psk="パスワード"
}
```

`systemctl enable wpa_supplicant@wlan0`を実行
`/etc/systemd/network/25-wlan.network`を作って編集

```ini
[Match]
Name=wl*

[Network]
DHCP=true
MulticastDNS=true
```

有線の分も作成。無線だと画面転送が遅い。
`/etc/systemd/network/20-eth.network`を作って編集

```ini
[Match]
Name=eth*

[Network]
DHCP=true
```

`A start job is running for wait for network to be configured.`で起動が遅いので以下を実施しておく。
`/etc/netplan/70-netcfg.yaml`に、以下の内容を書いておく。

```yaml
network:
    ethernets:
        wlan0:
            optional: true
    version: 2
```

有効化

```sh
sudo netplan apply
```


## 最小限のGUI構築(うまくできない？)

[Debian / Ubuntu上のwaylandとwestonで最小限のGUIを構築する](https://qiita.com/kakinaguru_zo/items/db69cdbcb565c50f060a)を参考にする。

まず、不要なパッケージを削除

```sh
sudo apt --purge remove nplan ifupdown avahi-autoipd
```

日本語ロケール化

```
sudo dpkg-reconfigure locales
# ja_JP.UTF-8を選択
```

必要そうなパッケージをインストール

```sh
sudo apt install libnss-resolve libnss-systemd dbus-user-session policykit-1
```

Wayland/westonの最小限パッケージをインストール

```sh
sudo apt install weston xwayland xfonts-base fonts-vlgothic fonts-ipafont poppler-data
```

`.weston.ini`を書いて、設定

```ini
[core]
xwayland=true
modules=systemd-notify.so
```

起動方法(起動できない)

```sh
weston --log=weston.log --config=.weston.ini
```

### Ubuntu 20.04でPython3.7.8を使う(pyenv)

TensorFlow LiteがPython3.7までの対応で、Ubuntu 20.04はPython3.8なので、pyenvを使用する。
refer: https://www.kkaneko.jp/tools/ubuntu/ubuntu_pyenv.html

```sh
# 必要パッケージをインストール
sudo apt -yV install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

# pyenvをgitからダウンロード
git clone https://github.com/pyenv/pyenv.git ~/.pyenv

# 設定
echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.bashrc
echo 'if [ -d "${PYENV_ROOT}" ]; then' >> ~/.bashrc
echo '    export PATH=${PYENV_ROOT}/bin:$PATH' >> ~/.bashrc
echo '    eval "$(pyenv init -)"' >> ~/.bashrc
echo 'fi' >> ~/.bashrc
exec $SHELL -l
source ~/.bashrc
```

Python3.7.8をインストールして適用

```sh
pyenv install 3.7.8
pyenv shell 3.7.8
```

## pyenv上のPython3.7.8にopencvをインストール

WebカメラとTensorFlow Liteを簡単に扱いたいのでopencvを使うことにする。

まず、必要なパッケージをインストール

```sh
sudo apt install libgtk2.0-dev pkg-config libusb-1.0-0-dev
```

そんで、ソースからビルド。VideoCaptureで動画ファイルを読む場合は、`-DWITH_FFMPEG=ON`が必要。
refer: https://zv-louis.hatenablog.com/entry/2018/05/08/063000

```sh
wget https://github.com/opencv/opencv/archive/4.4.0.zip -O opencv-4.4.0.zip
unzip opencv-4.4.0.zip
wget https://github.com/opencv/opencv_contrib/archive/4.4.0.zip -O opencv_contrib-4.4.0.zip
unzip opencv_contrib-4.4.0.zip

cd opencv-4.4.0
mkdir build
cd build
cmake .. \
-DCMAKE_BUILD_TYPE=RELEASE \
-DCMAKE_INSTALL_PREFIX=~/.pyenv/versions/3.7.8/usr/local/ \
-DINSTALL_C_EXAMPLES=OFF \
-DWITH_FFMPEG=ON \
-DBUILD_NEW_PYTHON_SUPPORT=ON \
-DBUILD_opencv_python3=ON \
-DBUILD_opencv_legacy=OFF \
-DINSTALL_PYTHON_EXAMPLES=ON \
-DBUILD_EXAMPLES=ON \
-DPYTHON_EXECUTABLE=~/.pyenv/versions/3.7.8/bin/python \
-DPYTHON_LIBRARY=~/.pyenv/versions/3.7.8/lib/libpython3.7m.a \
-DPYTHON_INCLUDE_DIR=~/.pyenv/versions/3.7.8/include/python3.7m \
-DPYTHON_INCLUDE_DIRS=~/.pyenv/versions/3.7.8/include/python3.7m \
-DPYTHON_INCLUDE_DIRS2=~/.pyenv/versions/3.7.8/include/python3.7m \
-DINCLUDE_DIRS=~/.pyenv/versions/3.7.8/include/python3.7m \
-DINCLUDE_DIRS2=~/.pyenv/versions/3.7.8/include/python3.7m \
-DPYTHON_PACKAGES_PATH=~/.pyenv/versions/3.7.8/lib/python3.7/site-packages \
-DPYTHON_NUMPY_INCLUDE_DIR=~/.pyenv/versions/3.7.8/lib/python3.7/site-packages/numpy/core/include \
-DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.4.0/modules

# raspiは4コアだが、4コアすべて使うとスワップアウトしたりしてやっかい
make -j 3; make install

# cv2でimportできるようにシンボリックリンクを張る
 ln -s ~/.pyenv/versions/3.7.8/usr/local/lib/python3.7/site-packages/cv2/python-3.7/cv2.cpython-37m-aarch64-linux-gnu.so ~/.pyenv/versions/3.7.8/lib/python3.7/site-packages/
```

swapが足りない(メモリがどうのこうののエラーになる)場合は以下の対応をする。
refer: https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-20-04

```sh
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```

# TensorFlow Liteのもろもろをインストール

[Get started with the USB Accelerator](https://coral.ai/docs/accelerator/get-started/)を参考に、環境をインストール。

## Install the Edge TPU runtime

```sh
echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-get update

# ファンとかで冷却できる場合はlibedgetpu1-max、安全に使いたい場合はlibedgetpu1-std
# sudo apt-get install libedgetpu1-std
sudo apt-get install libedgetpu1-max
```

## tflite_runtimeのインストール

python3.8系統にはまだ対応していないので、python3.7系統が必要。

```sh
pip3 install https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_aarch64.whl
```

## デモ(動作確認)

```sh
git clone https://github.com/google-coral/tflite.git ./google-coral_tflite
cd google-coral_tflite
cd python/examples/classification
bash install_requirements.sh

# 実行
python3 classify_image.py \
--model models/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
--labels models/inat_bird_labels.txt \
--input images/parrot.jpg
```

# USB Webカメラのデモ

tfliteのサンプルを動かしてみる。

以下で準備。

```sh
git clone https://github.com/google-coral/examples-camera google-coral_examples-camera
cd google-coral_examples-camera

# モデルダウンロード
bash download_models.sh

# opnecvのサンプルを動かしてみる
cd opencv
# 必要パッケージ
sudo apt-get -y install libhdf5-1* libatlas-base-dev
```

実行(結局X11 Forwardingした)

```sh
python3 detect.py --camera_idx 0
```

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">カメラによっては20fps超えるぞ。 <a href="https://t.co/KcQuI53eKM">pic.twitter.com/KcQuI53eKM</a></p>&mdash; nv-h (@saido_nv) <a href="https://twitter.com/saido_nv/status/1286234573278523394?ref_src=twsrc%5Etfw">July 23, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

表示しながらでも20fps超なので結構高速だが、複数カメラに対して複数アプリ起動はできない。(TPUが死ぬ)
CPU使用率はそこそこ少ない。(Raspberry PIは高速なはずだから多いのか？)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139130/955fa8c7-0b94-d18d-a025-d9c6a1a0f8cc.png)

# C++アプリ(詳細は未確認)

後述のgo-tfliteが良さそうなので、詳細確認を保留している。

ホストでクロスコンパイル
https://github.com/google-coral/examples-camera/tree/master/nativeapp

```sh
sudo apt-get install libglib2.0-dev libgstreamer1.0-dev libedgetpu-dev

cd ~/sources
git clone https://github.com/tensorflow/tensorflow
cd tensorflow
# git checkout d855adfc5a0195788bf5f92c3c7352e638aa1109
# https://github.com/tensorflow/tensorflow/issues/36184#issuecomment-579274152
git checkout -b v2.2.0 refs/tags/v2.2.0 # r2.2ブランチでもよさそう
./tensorflow/lite/tools/make/download_dependencies.sh
./tensorflow/lite/tools/make/build_aarch64_lib.sh
cd ..
tar -cvzf tensorflow.tar.gz tensorflow
# sftpとかでtensorflow.tar.gzを転送
```

Raspberry PI上で以下のサンプルをビルドしてみる。
https://github.com/mattn/webcam-detect-tflite

以下から`mplus-TESTFLIGHT-*.tar.xz`をダウンロードして`mplus-1c-thin.ttf`を取り出す。
https://osdn.net/projects/mplus-fonts/releases/62344

```sh
sudo apt install libopencv-dev
cd ~/sources

git clone https://github.com/mattn/webcam-detect-tflite
cd webcam-detect-tflite
cp <どこか>/mplus-1c-thin.ttf . # フォントが必要
```

MakefileがWindwos向けなので、以下のように書き換える。

```Makefile
CXXFLAGS ?= \
	-I$(HOME)/sources/tensorflow \
	-I$(HOME)/sources/tensorflow/tensorflow/lite/tools/make/downloads/flatbuffers/include \
	-I$(HOME)/sources/tensorflow/tensorflow/lite/tools/make/downloads/absl \
	-I/usr/include/freetype2/ \
	-I/usr/include/opencv4

LDFLAGS ?= \
	-L$(HOME)/sources/tensorflow/tensorflow/lite/tools/make/gen/linux_aarch64/lib/

.PHONY: all clean

all: webcam-detector

webcam-detector: main.o
	gcc -O3 -o webcam-detector main.o \
	$(LDFLAGS) \
	-ltensorflow-lite \
	-lstdc++ -lpthread -ldl -lm \
	-lopencv_videoio -lopencv_core -lopencv_highgui -lopencv_imgproc\
	-lfreetype

main.o : main.cxx
	g++ -c --std=c++11 main.cxx -O3 $(CXXFLAGS)

clean:
	rm -f webcam-detector
```

実行してみると、結構高速に動いている模様。


# Go binding for TensorFlow Lite

マルチスレッドとかをGoで関単に書きたいので、これを使いたい。
https://github.com/mattn/go-tflite

まずは、tfliteのC APIをインストールする必要がある。
tfliteのC APIをビルドするためにはbazelなるものが必要で、これはarm64向けのaptパッケージがないようなので、バイナリをインストールする。

CPUだけ使う場合(EdgeTPUを使わない)は、最新のタグ`v2.2.0`で良いが、EdgeTPUも使いたい場合は`d855adfc5a0195788bf5f92c3c7352e638aa1109`のコミットのものを使用する必要がある。
(@mattn コメントありがとうございます！)

```sh
sudo apt install g++ unzip zip openjdk-11-jdk

cd ~/Downloads
wget https://github.com/bazelbuild/bazel/releases/download/3.4.1/bazel-3.4.1-linux-arm64

cd ~/sources
git clone https://github.com/tensorflow/tensorflow
cd tensorflow

# EdgeTPU版はこのコミットのものが必要(動作確認は後述)
git checkout d855adfc5a0195788bf5f92c3c7352e638aa1109
https://github.com/tensorflow/tensorflow/issues/36184#issuecomment-579274152

# CPU版だけでいい場合はこれでも大丈夫
# git checkout -b v2.2.0 rafs/tags/v2.2.0 # r2.2ブランチでもよさそう

cd ~/sources/tensorflow
pyenv global 3.7.8 # "pyenv shell 3.7.8" ではダメ

# C APIをビルド
~/Downloads/bazel-3.4.1-linux-arm64 build -c opt --config monolithic //tensorflow/lite/c:tensorflowlite_c
```

上記でC APIがビルドできたので、go-tfliteをビルドする。

```sh
# golangが必要
sudo apt install golang-go

cd ~/sources
git clone https://github.com/mattn/go-tflite
cd go-tflite
# C APIのパス設定
export CGO_CFLAGS=-I/home/ubuntu/sources/tensorflow
export CGO_LDFLAGS=-L/home/ubuntu/sources/tensorflow/bazel-out/aarch64-opt/bin/tensorflow/lite/c
export LD_LIBRARY_PATH=/home/ubuntu/sources/tensorflow/bazel-out/aarch64-opt/bin/tensorflow/lite/c
```

`label_image_edgetpu`を動かしてみる。

```sh
cd _example/label_image_edgetpu
./fetch_testfiles.sh
go run main.go
```

以下の表示になる

```log
EdgeTPU Version: BuildLabel(COMPILER=6.3.0 20170516,DATE=redacted,TIME=redacted,CL_NUMBER=317268237), RuntimeVersion(13)
01: 1  bicycle: 0.643137
02: 2  car: 0.533333
00: 0  person: 0.250980
03: 3  motorcycle: 0.235294
```

CPU版`label_image`も問題なく動く。

```sh
cd ../label_image
go run main.go
# -> 85: peacock: 0.976471
```

同様に、`ssd`と`ssd_edgetpu`も動くが、`ssd_edgetpu`のコンソール表示がうるさい。

```log
ubuntu@ubuntu:~/sources/go-tflite/_example/ssd_edgetpu$ go run main.go
[ WARN:0] global ../modules/videoio/src/cap_gstreamer.cpp (480) isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
EdgeTPU Version: BuildLabel(COMPILER=6.3.0 20170516,DATE=redacted,TIME=redacted,CL_NUMBER=317268237), RuntimeVersion(13)
I :319] e-fuse programming revision: 2
2020/07/27 11:45:33 width: 300, height: 300, type: UInt8, scale: 0.0078125, zeropoint: 128
2020/07/27 11:45:33 input tensor count: 1, output tensor count: 4
I :1137] bulk in 1024 bytes from buffer index [0]
I :1137] bulk in 1024 bytes from buffer index [1]
I :1137] bulk in 1024 bytes from buffer index [2]
I :1137] bulk in 1024 bytes from buffer index [3]
I :1137] bulk in 1024 bytes from buffer index [4]
I :1137] bulk in 1024 bytes from buffer index [5]
I :1137] bulk in 1024 bytes from buffer index [6]
I :1137] bulk in 504 bytes from buffer index [7]
I :1137] bulk in 1024 bytes from buffer index [8]
I :1137] bulk in 1024 bytes from buffer index [9]
I :1137] bulk in 1024 bytes from buffer index [10]
I :1137] bulk in 1024 bytes from buffer index [11]
I :1137] bulk in 1024 bytes from buffer index [12]
I :1137] bulk in 1024 bytes from buffer index [13]
I :1137] bulk in 1024 bytes from buffer index [14]
I :1137] bulk in 1024 bytes from buffer index [15]
I :1137] bulk in 1024 bytes from buffer index [16]
I :1137] bulk in 1024 bytes from buffer index [17]
I :1137] bulk in 1024 bytes from buffer index [18]
I :1137] bulk in 1024 bytes from buffer index [19]
I :1137] bulk in 1024 bytes from buffer index [20]
I :1137] bulk in 1024 bytes from buffer index [21]
I :1137] bulk in 1024 bytes from buffer index [22]
I :1137] bulk in 1024 bytes from buffer index [23]
I :1137] bulk in 1024 bytes from buffer index [24]
I :1137] bulk in 1024 bytes from buffer index [25]
I :1137] bulk in 1024 bytes from buffer index [26]
I :1137] bulk in 1024 bytes from buffer index [27]
I :1137] bulk in 1024 bytes from buffer index [28]
I :1137] bulk in 1024 bytes from buffer index [29]
I :1137] bulk in 1024 bytes from buffer index [30]
I :1137] bulk in 1024 bytes from buffer index [31]
I :1137] bulk in 1024 bytes from buffer index [0]
I :1137] bulk in 1024 bytes from buffer index [1]
I :1137] bulk in 1024 bytes from buffer index [2]

...繰り返しっぽい
```

CPU版のコンソール表示は以下のようにすっきりしている。

```log
ubuntu@ubuntu:~/sources/go-tflite/_example/ssd$ go run main.go
[ WARN:0] global ../modules/videoio/src/cap_gstreamer.cpp (480) isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
2020/07/27 11:37:35 width: 300, height: 300, type: UInt8, scale: 0.0078125, zeropoint: 128
2020/07/27 11:37:35 input tensor count: 1, output tensor count: 4
```

CPU版が15fpsで、EdgeTPU版が20fpsであまり差がない。カメラのfps設定は関係なさそうなので、シリアル処理していて表示がボトルネックになってる可能性が高いように思う。