Jetson NanoのDockerでOpenCV 4.1.0をコンパイルする

# はじめに

本記事はJetson NanoでOpenCV 4.1.0をインストールした記録である。Jetson NanoのベースイメージであるJetPack 4.2にはOpenCV 3.3.1が含まれているがOpenCV 4系列を使いたいのでインストールを行った。

コンパイルにあたり「[Jetson Nano へ OpenCV 4.1.0 をインストールする](https://qiita.com/asahi0301/items/3c287190348552f19415)」を全面的に参考にした。感謝。

# コンパイルにDockerを使う意味

OpenCVのコンパイルは重量級でありaptでかなり多くのパッケージをインストールする必要がある。このことは開発環境と実行環境の2つを兼ねるJetson nanoでは敷居が高い。そのためコンパイルには`Docker`を用いることにする。幸いにして128GBのSDカードを購入したので容量としては申し分ない。

コンパイルに使ったDockerfile他は下記URLに置いた。
https://github.com/yamamo-to/jetson_nano/tree/master/opencv4_compile

# OpenCV 4.1.0の起動

重量級のコンパイルにもかかわらず、上記の参考URLのスクリプトを使うとトラブルなくOpenCV 4.1.0がインストールされる。コンパイル時間はおよそ~~2時間30分~~3時間[^1]。素晴らしい！

[^1]: aptでhdf5やblasを追加したらコンパイル時間が延びた。

```bash:Dockerコンテナ内
foo@0a8f8ac6951a:~$ python3
Python 3.6.8 (default, Jan 14 2019, 11:02:34) 
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.0'
```

# 特徴抽出 SIFT/SURF を使う

## ソースコードの作成

以下のようなスクリプトを書いて2つの画像の特徴抽出&マッチングを試す。
コマンドライン引数からアルゴリズムを選択できるようにしている。

```python:test_xfeature2d.py
import numpy as np
import cv2
import argparse


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('algo')
    parser.add_argument('image1')
    parser.add_argument('image2')
    parser.add_argument('output')
    args = parser.parse_args()

    print('OpenCV version: {}'.format(cv2.__version__))

    im1 = cv2.imread(args.image1)
    im2 = cv2.imread(args.image2)

    gray1 = cv2.cvtColor(im1,cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(im2,cv2.COLOR_BGR2GRAY)

    descriptor = eval('cv2.xfeatures2d.{}_create()'.format(args.algo))
    kp1, desc1 = descriptor.detectAndCompute(gray1, None)
    kp2, desc2 = descriptor.detectAndCompute(gray2, None)

    bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
    matches = bf.match(desc1, desc2)

    n_matches = 100
    im3 = cv2.drawMatches(
        im1, kp1,
        im2, kp2,
        matches[:n_matches], im2.copy(), flags=0)

    cv2.imwrite(args.output, im3)


if __name__ == '__main__':
    main()
```

## 実行

入力画像として似た画像が必要であるが、ちょうどいい感じで似た画像があった(悲)[^2]。

[^2]: 詳細は「[Jetson Nanoのファン購入に気をつけろ！](https://qiita.com/yamamo-to/items/bf77048b6f650aad346f)」参照。

![fan_12v_mini.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/c7955684-f2d1-c622-6c4d-c4f3c24c2bff.jpeg)![fan_5v_mini.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/baa987f9-832e-6659-f86e-3b2bbf6d3ae1.jpeg)

では実行。

```bash:Dockerコンテナ内
foo@0a8f8ac6951a:~$ python3 test_xfeature2d.py SIFT data/fan_12v_mini.jpg data/fan_5v_mini.jpg features.jpg
OpenCV version: 4.1.0
Traceback (most recent call last):
  File "test_xfeature2d.py", line 39, in <module>
    main()
  File "test_xfeature2d.py", line 22, in main
    exec('descriptor = cv2.xfeatures2d.{}_create()'.format(args.algo))
  File "<string>", line 1, in <module>
cv2.error: OpenCV(4.1.0) /tmp/opencv_contrib-4.1.0/modules/xfeatures2d/src/sift.cpp:1207: error: (-213:The function/feature is not implemented) This algorithm is
 patented and is excluded in this configuration; Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library in function 'create'
```

・・・。2時間30分かけてコンパイルした結果がエラー吐いた。つまり

```bash
(-213:The function/feature is not implemented)
This algorithm is patented and is excluded in this configuration
```

OPENCV_ENABLE_NONFREEオプションを有効にしないとSIFTやSURFは使えないらしい・・・。

```
...
    -D OPENCV_ENABLE_NONFREE=ON \ ←追加
    -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.1.0/modules \
...
```

コンパイルは続く・・・。

## 結果

3時間後、コンパイルは無事終了。上記を実行した結果を示す。

![features_sift.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/4c669d21-d860-67f7-066f-296ea14e0cc8.jpeg)
SIFTの結果

![features_surf.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/2c356160-4e44-dee1-469e-3fa34669fa04.jpeg)
SURFの結果

何とも微妙な結果になってしまった。画像が難しすぎたかも知れない。

# おまけ

そもそもNONFREEって何だろうということで調べた。下記はSIFTの例で米国特許らしい。

```
"Method and apparatus for identifying scale invariant features
in an image and use of same for locating an object in an image,"
David G. Lowe, US Patent 6,711,293 (March 23, 2004). 
```

例えばSIFTの場合、下記に詳細が書かれており。

https://github.com/opencv/opencv_contrib/blob/master/modules/xfeatures2d/src/sift.cpp

オリジナルのソースコード(リンク先のウェブサイトはすでにない)を使うなら「著作権法」と「特許法」の両方に従えと書いてある。さらにバイナリ配布をするなら

- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

と書かれている。必要な表記が行われたファイルを一緒に配布すれば可能そうだけども・・・。