Coral DevBoardでOpenCVをビルドしてインストールする

## 【内容】
[前回の記事](https://qiita.com/rhene/items/7c9e82d875aa1ba8aec5)でCoral DevBoardをセットアップして使えるようになりました。
早速過去のプログラムを使ってパフォーマンスを比較したかったのですが、Webカメラの入力や結果画像の出力にOpenCVを使っているためそのまま利用することは出来ません。
手っ取り早くインストールを試みましたが `pip3` も `apt` も該当モジュールが存在しません。

ということでCoral DevBoardでOpenCVをソースからビルドしてインストールします。


## 【注意点】
DevBoardでOpenCVをビルドするには2つ注意点があります。
まず1つ目はメモリ容量が足らない。
2つ目は内蔵ストレージ(eMMC)の容量が足らないです。

1つ目の解決方法としてはスワップ領域を設定します。 
実際にビルドしたところ、最大で1GB以上のスワップ領域を使いましたので、本手順では安全を見て2GBで設定しています。

2つ目の解決方法としてはSDカードをマウントして、ソースの取得およびビルドをSDカード上で行います。
こちらは最終的に11GB以上の容量を必要としましたので、16GBのSDカードを使いました。


## 【概要】
実行した手順は概ね以下の手順になります。

1. スワップ設定
2. 依存モジュールのインストール
3. SDカードのマウント
4. ソースの取得
5. cmakeの実行
6. build & install
7. 動作確認


## 【0.DevBoardへの接続】
まずはDevBoardに接続してください。
[前回の記事](https://qiita.com/rhene/items/7c9e82d875aa1ba8aec5)を参考にSerial Consoleまたはsshで繋いでください。

なお、今回作業後に気がついたのですが、DevBoardのHDMI出力にTVをつなぐと、左上のアイコンからターミナルを起動できるのですね。
恐らくこちらで作業しても良いと思います。



## 【1.スワップ領域の設定】
まずはスワップ領域を設定します。
下記のコマンドを実行することでスワップ領域を設定できます。

```bash:スワップ領域の設定
# スワップ領域の設定 (2GB)
sudo dd if=/dev/zero of=/swapfile bs=100M count=20
sudo mkswap /swapfile
sudo swapon /swapfile
```

最初1GBでビルドを行ったところ最後の最後で溢れて固まってしまいました。
ですので安全を見て2GBを指定しています。


## 【2. 依存モジュールのインストール】
ビルドに必要なモジュールをインストールします。
下記コマンドを実行してください。

```bash:依存モジュールのインストール
# 依存モジュールのインストール
sudo apt-get install build-essential cmake unzip pkg-config
sudo apt-get install libjpeg-dev libpng-dev libtiff-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev
sudo apt-get install libgtk-3-dev
sudo apt-get install libcanberra-gtk*
sudo apt-get install libatlas-base-dev gfortran
sudo apt-get install python3-dev
```


## 【3. SDカードのマウント】
OpenCVを内蔵ストレージ上でビルドするには容量が足りないため、SDカードをマウントしてその上で作業します。
SDカードは16GBのものを利用しました。

### SDカードの挿入とデバイスの確認
まずはSDカードをカードスロットに挿入します。
シリアルコンソールに繋いでいる場合はその時点で以下のようなメッセージが表示されます。
sshで繋いでいる場合は `dmesg` の最後の方に表示されます。

```bash:SDカード挿入ログ
[ 6250.755801] mmc1: host does not support reading read-only switch, assuming write-enable
[ 6250.917963] mmc1: new ultra high speed SDR104 SDHC card at address aaaa
[ 6250.925514] mmcblk1: mmc1:aaaa SL16G 14.8 GiB
[ 6250.938402]  mmcblk1: p1
```

この表示からSDカードのデバイス名を取得します。
最終行の `mmcblk1` が今回挿入されたSDカードのデバイス名です。
このあと、SDカードにアクセスする場合は `/dev/mmcblk1` でアクセスします。


### パティションの削除
SDカード挿入ログの最終行に表示された `mmcblk1: p1` の `p1` はパティションを表しています。
今回挿入したSDカードにはすでにパティションが切られていたようです。

事前にこのパティションを削除します。
`fdisk` コマンドでSDカードにアクセスし、 `p` コマンドでパティション情報を取得します。
その後、 `d` コマンドでパティションを削除し、 `w` コマンドでパティション情報を書き込んでいます。

```bash:パティションの削除
# fdisk
sudo fdisk /dev/mmcblk1

Welcome to fdisk (util-linux 2.29.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): p
Disk /dev/mmcblk1: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x7b12e7a8

Device         Boot Start      End  Sectors  Size Id Type
/dev/mmcblk1p1       8192 31116287 31108096 14.9G  c W95 FAT32 (LBA)

Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Command (m for help): p
Disk /dev/mmcblk1: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x7b12e7a8

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
```



### SDカードのフォーマット
SDカードをフォーマットします。
本来は新たにパティションを作ってフォーマットすべきでしょうが、今回は単純に作業用なのでSDカードごとフォーマットします。
フォーマットタイプはext4としています。

```bash:SDカードのフォーマット
# format
sudo mkfs.ext4 /dev/mmcblk1

mke2fs 1.43.4 (31-Jan-2017)
Found a dos partition table in /dev/mmcblk1
Proceed anyway? (y,N) y
Discarding device blocks: done
Creating filesystem with 3889536 4k blocks and 972944 inodes
Filesystem UUID: 2dd25c14-ce4e-4fda-ad71-12aa7643b64e
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
```

### SDカードのマウント
SDカードのフォーマットが完了したら、SDカードをマウントします。
事前にマウントポイントとして `/mnt/sdcard` を作成した上で、SDカードをマウントしています。

```bash:SDカードのマウント
# マウントポイントの作成
sudo mkdir /mnt/sdcard

# SDカードのマウント
sudo mount /dev/mmcblk1 /mnt/sdcard/
```


## 【4. ソースの取得】
GithubからOpenCVのソースを取得します。
今回は最新のリリースバージョンである `4.1.1` をダウンロードしました。

```bash:ソースの取得
# 事前にSDカードのマウントポイントに移動
cd /mnt/sdcard/

# 作業フォルダの作成と移動
sudo mkdir opencv
cd opencv

# Release version 4.1.1 をダウンロード
sudo wget -O opencv.zip https://github.com/opencv/opencv/archive/4.1.1.zip
sudo wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.1.1.zip

# ファイルの解凍
sudo unzip opencv.zip
sudo unzip opencv_contrib.zip

# フォルダ名の変更
sudo mv opencv-4.1.1 opencv
sudo mv opencv_contrib-4.1.1 opencv_contrib
```

最新のソースを使いたい場合は上記コマンドの代わりにgitからcloneしてください。

```bash:git_cloneの例
# git から最新版をcloneする
git clone https://github.com/opencv/opencv
git clone https://github.com/opencv/opencv_contrib
```

## 【5. cmakeの実行】
cmakeを実行してビルドの準備をします。
必要に応じてビルドオプションを変更してください。
ここでエラーが出る場合は依存ファイルが足りないなど、環境に問題があります。

```
# makeファイルを作成する
cd opencv
sudo mkdir build
cd build
sudo cmake -D CMAKE_BUILD_TYPE=RELEASE \
 -D CMAKE_INSTALL_PREFIX=/usr/local \
 -D INSTALL_PYTHON_EXAMPLES=ON \
 -D INSTALL_C_EXAMPLES=OFF \
 -D OPENCV_ENABLE_NONFREE=ON \
 -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
 -D BUILD_opencv_python2=OFF \
 -D BUILD_opencv_python3=ON \
 -D PYTHON_EXECUTABLE=/usr/bin/python3 \
 -D ENABLE_FAST_MATH=1 \
 -D ENABLE_NEON=ON -D WITH_LIBV4L=ON \
 -D WITH_V4L=ON \
 -D BUILD_EXAMPLES=OFF \
 ../
 ```

最終的に以下のように表示されればOKです。
Python用のライブラリを使う場合はログ内 `Python 3` の項目がきちんと埋まっているかを確認してください。

```bash:cmake結果
-- General configuration for OpenCV 4.1.1 =====================================
--   Version control:               unknown
--
--   Extra modules:
--     Location (extra):            /home/mendel/opencv/opencv_contrib/modules
--     Version control (extra):     unknown
--
--   Platform:
--     Timestamp:                   2019-08-27T01:25:57Z
--     Host:                        Linux 4.9.51-imx aarch64
--     CMake:                       3.7.2
--     CMake generator:             Unix Makefiles
--     CMake build tool:            /usr/bin/make
--     Configuration:               RELEASE
--
--   CPU/HW features:
--     Baseline:                    NEON FP16
--       required:                  NEON
--       disabled:                  VFPV3
--
--   C/C++:
--     Built as dynamic libs?:      YES
--     C++ Compiler:                /usr/bin/c++  (ver 6.3.0)
--     C++ flags (Release):         -fsigned-char -ffast-math -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG  -DNDEBUG
--     C++ flags (Debug):           -fsigned-char -ffast-math -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -fvisibility-inlines-hidden -g  -O0 -DDEBUG -D_DEBUG
--     C Compiler:                  /usr/bin/cc
--     C flags (Release):           -fsigned-char -ffast-math -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -O3 -DNDEBUG  -DNDEBUG
--     C flags (Debug):             -fsigned-char -ffast-math -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -g  -O0 -DDEBUG -D_DEBUG
--     Linker flags (Release):      -Wl,--gc-sections
--     Linker flags (Debug):        -Wl,--gc-sections
--     ccache:                      NO
--     Precompiled headers:         YES
--     Extra dependencies:          dl m pthread rt
--     3rdparty dependencies:
--
--   OpenCV modules:
--     To be built:                 aruco bgsegm bioinspired calib3d ccalib core datasets dnn dnn_objdetect dpm face features2d flann freetype fuzzy gapi hfs highgui img_hash imgcodecs imgproc line_descriptor ml objdetect optflow phase_unwrapping photo plot python3 quality reg rgbd saliency shape stereo stitching structured_light superres surface_matching text tracking ts video videoio videostab xfeatures2d ximgproc xobjdetect xphoto
--     Disabled:                    world
--     Disabled by dependency:      -
--     Unavailable:                 cnn_3dobj cudaarithm cudabgsegm cudacodec cudafeatures2d cudafilters cudaimgproc cudalegacy cudaobjdetect cudaoptflow cudastereo cudawarping cudev cvv hdf java js matlab ovis python2 sfm viz
--     Applications:                tests perf_tests apps
--     Documentation:               NO
--     Non-free algorithms:         YES
--
--   GUI:
--     GTK+:                        YES (ver 3.22.11)
--       GThread :                  YES (ver 2.50.3)
--       GtkGlExt:                  NO
--     VTK support:                 NO
--
--   Media I/O:
--     ZLib:                        /usr/lib/aarch64-linux-gnu/libz.so (ver 1.2.8)
--     JPEG:                        /usr/lib/aarch64-linux-gnu/libjpeg.so (ver 62)
--     WEBP:                        build (ver encoder: 0x020e)
--     PNG:                         /usr/lib/aarch64-linux-gnu/libpng.so (ver 1.6.28)
--     TIFF:                        /usr/lib/aarch64-linux-gnu/libtiff.so (ver 42 / 4.0.8)
--     JPEG 2000:                   build (ver 1.900.1)
--     OpenEXR:                     build (ver 2.3.0)
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES
--
--   Video I/O:
--     DC1394:                      NO
--     FFMPEG:                      YES
--       avcodec:                   YES (57.64.101)
--       avformat:                  YES (57.56.101)
--       avutil:                    YES (55.34.101)
--       swscale:                   YES (4.2.100)
--       avresample:                NO
--     GStreamer:                   NO
--     v4l/v4l2:                    YES (linux/videodev2.h)
--
--   Parallel framework:            pthreads
--
--   Trace:                         YES (with Intel ITT)
--
--   Other third-party libraries:
--     Lapack:                      NO
--     Eigen:                       NO
--     Custom HAL:                  YES (carotene (ver 0.0.1))
--     Protobuf:                    build (3.5.1)
--
--   OpenCL:                        YES (no extra features)
--     Include path:                /home/mendel/opencv/opencv/3rdparty/include/opencl/1.2
--     Link libraries:              Dynamic load
--
--   Python 3:
--     Interpreter:                 /usr/bin/python3 (ver 3.5.3)
--     Libraries:                   /usr/lib/aarch64-linux-gnu/libpython3.5m.so (ver 3.5.3)
--     numpy:                       /usr/local/lib/python3.5/dist-packages/numpy/core/include (ver 1.17.0)
--     install path:                lib/python3.5/dist-packages/cv2/python-3.5
--
--   Python (for build):            /usr/bin/python3
--
--   Java:
--     ant:                         NO
--     JNI:                         NO
--     Java wrappers:               NO
--     Java tests:                  NO
--
--   Install to:                    /usr/local
-- -----------------------------------------------------------------
--
-- Configuring done
-- Generating done
-- Build files have been written to: /home/mendel/opencv/opencv/build
```


## 【6. build & install】
cmakeコマンドが正常に完了したら、ビルド及びインストールを行います。

### OpenCVのビルド
実際に `make` コマンドでビルドを実行します。
今回は4プロセス並列でビルドし、処理時間を測定してみました。

```bash:OpenCVのビルド
time sudo make -j4
```
```bash:ビルド時間結果
real    275m9.931s
user    277m17.536s
sys     37m11.680s
```
今回は約4時間半程かかりました。
途中失敗したり条件を変えたため、この手順を5回行いました…

### OpenCVのインストール
正常にビルドが完了したら、下記コマンドでインストールします。

```bash:OpenCVのインストール
sudo make install
sudo ldconfig
```

以上で、OpenCVのインストールは完了です。


## 【7. 動作確認】
正常にインストールできているか確認します。
下記コマンドを実行して、エラーなくバージョンが表示されれば正常にインストールできています。

```bash:動作確認
python3 -c "import cv2;print(cv2.__version__);"
```
```bash:出力結果
4.1.1
```

## 【片付け】
必要に応じてSDカードのアンマウントとスワップ領域を削除します。

```bash:片付け
# SDカードのアンマウント
sudo umount /mnt/sdcard

# swap領域の削除
sudo swapoff /swapfile
sudo rm /swapfile
```

swap領域を削除することで内蔵ストレージ(eMMC)の容量をあけることが出来ます。


## 【最後に】
これで過去のプログラムを実行する環境が整いました。
実際に動作することも確認できましたので、このあとパフォーマンスを比較したいと思います。

なお、今回の作業を行ったあとの各パティションの空き容量は以下のとおりです。

```bash:空き容量の確認
df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.0G  2.1G  4.6G  32% /
devtmpfs        332M     0  332M   0% /dev
tmpfs           492M     0  492M   0% /dev/shm
tmpfs           492M   17M  476M   4% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           492M     0  492M   0% /sys/fs/cgroup
tmpfs           492M  492K  492M   1% /var/log
/dev/mmcblk0p1  124M   29M   89M  25% /boot
tmpfs            99M   13M   86M  13% /run/user/1000
/dev/mmcblk1     15G   11G  2.9G  80% /mnt/sdcard
```
内蔵ストレージは4.6GB空いています。
もう少しカスタマイズ可能かな。

なお、今回作業で使ったSDカードは16GBですが、最終的に11GB以上消費してしまいました。
以前はもっと少ない容量でもビルドできていたので、初めは内蔵ストレージだけでビルドできるかと試してみたのですが、まったく足りなかったですね…
Contrib moduleなどを削ればビルド行けるのでしょうか?
ちょっと今は試す気になれません。