Jetson Xavier NX/JetsonNANOでDNN_BACKEND_CUDAが使えるOpenCV4.3をDockerでビルド

#XavierNX/JetsonNANOでBACKEND_CUDAなOpenCV4.3
##はじめに
XavierNX開発キッとは、画像処理をするにあたって、安価にハードウェアアクセラレータが使える環境の一つです。
NvidiaはXavierNX開発キットでのDokcerのサポートを強化しています。[NGC](https://ngc.nvidia.com/catalog/all)
などから、イメージをダウンロードしコンテナとして実行が可能です。

#概要
OpenCV4.3で、DNN_CUDAの使えるコンテナを作成しました。

#手順
- dokcer build時に --runtime nvidia の指定  
 - [runtime=nvidiaを有効してdocker buildする](https://thr3a.hatenablog.com/entry/20190528/1559053901)
を参考に/etc/docker/daemon.json を変更
- OpenCVをビルドするDockerfileの作成  
 - [Jetson Nano に OpenCV 4.1.2 をインストールする](https://qiita.com/daisuzu_/items/8cc8de8ea8dc557a2aad)を参考に
[Machine Learning for Jetson/L4T](https://ngc.nvidia.com/catalog/containers/nvidia:l4t-ml) をベースとするDockerfileを作成
- BACKEND_CUDAをイネーブルに  
 - cmakeに-D OPENCV_DNN_CUDA=ONの追加
- Could NOT find CUDNN (missing: CUDNN_INCLUDE_DIR) (Required is at least version "7.5")
への対応  
 - includeをhostからコピー
- Could NOT find CUDNN: Found unsuitable version "..", but required is at least "7.5" (found /usr/lib/aarch64-linux-gnu/libcudnn.so)への対応
 - cudnn.hではなく、cudnn_version.hを参照するように変更

#BACKEND_CUDAをイネーブルに
 cmakeに-D OPENCV_DNN_CUDA=ONの追加

XavierではJetpackとよばれる開発環境をNvidiaが提供しています。
これを使うと、OpenCVをそのまま利用することが可能です。
問題は、OpenCV4.2より前のバージョンではDNNモジュールが使えない点にあります。
OpenCVでDNNをつかうには、4.2以上をビルドする必要がありますが、Nvidiaはこれに関する情報を”いまのところ”公開していません。ってーか、OpenCVには非常につめたいです。
DNNモジュールのイネーブルには、cmakeに-D OPENCV_DNN_CUDA=ON　を追加する必要があります。

#Could NOT find CUDNN (missing: CUDNN_INCLUDE_DIR) (Required is at least version "7.5") へ対応
-D OPENCV_DNN_CUDA=ONを追加し、docker build すると今度は、
そもそもNvidiaが提供するイメージには、cudnn.h　が入ってないので、そのままだとdockerでビルドすることが出来ません。
仕方ないので、Dockerfile内で/usr/include/*とアーキテクチャ毎に振り分けてる/etc/alternativeの中身をcontainer内に全コピー
なんでヘッダだけいれてないのか、、きっとなんか崇高な理由があるはずですが、~~よっぱらってて面倒くさいので力業で解決することにします。~~ 5/28 /etc/alternativeを全部コピーしてたんですが、怪しげなのでcudnn関連のリンクをコピーするようにしました。これで少なくともalternativeは既存のファイルは上書きされません。/usr/includeはhostからコピーなんでままいいかなと。。

#Could NOT find CUDNN: Found unsuitable version "..", but required is at least "7.5" (found /usr/lib/aarch64-linux-gnu/libcudnn.so) へ対応
JetPack4.4ではCUDNNのバージョンは8.0で
バージョン番号は、cudnn_version.hにかかれています。
一方で、opencv4.3のCMAKEでは

```
  file(READ "${CUDNN_INCLUDE_DIR}/cudnn.h" CUDNN_H_CONTENTS)

  string(REGEX MATCH "define CUDNN_MAJOR ([0-9]+)" _ "${CUDNN_H_CONTENTS}")
  set(CUDNN_MAJOR_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "")
  string(REGEX MATCH "define CUDNN_MINOR ([0-9]+)" _ "${CUDNN_H_CONTENTS}")
  set(CUDNN_MINOR_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "")
  string(REGEX MATCH "define CUDNN_PATCHLEVEL ([0-9]+)" _ "${CUDNN_H_CONTENTS}")
  set(CUDNN_PATCH_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "")
```
なんて書いてあって、cudnn.hにバージョンがかかれていることを期待しています。
仕方ないので、Dockerfile内でパッチをあてることにしました。

```
 sed -i 's/{CUDNN_INCLUDE_DIR}\/cudnn.h/{CUDNN_INCLUDE_DIR}\/cudnn_version.h/g' cmake/FindCUDNN.cmake
```
>5/27 Update
>[nano_build_opencv](https://github.com/mdegans/nano_build_opencv)を確認したら、こんなことしなくてもCUDNN_VERSION='8.0'を足すと動くらしい。。。
そもそもnano_build_opencvでCUDNNがイネーブルになるならDockerの中でnano_build_opencvを実行した方がはやい気がします。
>>5/28 Update
>>動作するのを確認したので、DockerfileをCUDNN_VERSION='8.0'を使うようにアップデートしてsedでのパッチ当てをなくしました。

# 成果物
Python2のサポートはいれてません。。
> 5/28
> githubにコードをアップロードしました。
> git clone https://github.com/sowd0726/Jetson_docker.git
> こっちはスクリプト実行でNANO用もつくれるようにしてあります。


```Docker:Dockerfile
FROM nvcr.io/nvidia/l4t-base:r32.4.2
COPY include /usr/include
COPY alternatives /etc/alternatives
RUN mkdir /root/opencv430
RUN user="nvidia"
RUN passwd="nvidia"
RUN echo "** Remove other OpenCV first"
RUN cd /root
RUN echo "** Install requirement"
RUN sudo apt-get update
RUN sudo apt-get install -y zip
RUN sudo apt-get install -y build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
RUN sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
RUN sudo apt-get install -y python3.6-dev python3-numpy
RUN sudo apt-get install -y libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev
RUN sudo apt-get install -y libv4l-dev v4l-utils qv4l2 v4l2ucp
RUN sudo apt-get install -y curl
RUN sudo apt-get update
RUN echo "** Download opencv-4.3.0"
RUN cd /root/opencv430 && \
 curl -L https://github.com/opencv/opencv/archive/4.3.0.zip -o opencv-4.3.0.zip && \
 curl -L https://github.com/opencv/opencv_contrib/archive/4.3.0.zip -o opencv_contrib-4.3.0.zip
RUN cd /root/opencv430 && \
 unzip opencv-4.3.0.zip && \
 unzip opencv_contrib-4.3.0.zip && \
 cd opencv-4.3.0 && \
 echo "** Apply patch" && \
# sed -i 's/{CUDNN_INCLUDE_DIR}\/cudnn.h/{CUDNN_INCLUDE_DIR}\/cudnn_version.h/g' cmake/FindCUDNN.cmake && \
 mkdir release
RUN cd /root/opencv430/opencv-4.3.0/release && \
 cmake -D CUDNN_VERSION='8.0' -D WITH_CUDA=ON -D CUDA_FAST_MATH=ON -D WITH_CUBLAS=ON -D WITH_CUDNN=ON -D CUDA_ARCH_BIN=7.2 -D CUDA_ARCH_PTX=7.2 -D CUDA_ARCH_PTX="" -D OPENCV_DNN_CUDA=ON -D OPENCV_GENERATE_PKGCONFIG=ON -D CUDNN_INCLUDE_DIR=/usr/include -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.3.0/modules -D WITH_GSTREAMER=ON -D WITH_LIBV4L=ON -D BUILD_opencv_python3=ON -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local .. ; exit 0
RUN bash
RUN cd /root/opencv430/opencv-4.3.0/release && \
 make -j4 && \
 sudo make install && \
 echo 'export PYTHONPATH=$PYTHONPATH:'$PWD'/python_loader/' >> ~/.bashrc
RUN echo "** Install opencv-4.3 successfully" && \
 echo "** Bye :)"
CMD ["/bin/bash"]
```
>5/27 Update
Darknetをビルドしようとしたら
Package opencv was not found in the pkg-config search path
っていわれした。
pkgconfigをつくっとかないといけないらしく、
-D OPENCV_GENERATE_PKGCONFIG=ON
を追記しました。
>>5/28 Update
>> Jetson NANOで動かすには、GPUのARCHITECTUREが7.2になっている部分を5.3にするだけでOKです。# 確認ずみ github にはいれてあります。


```Docker:docker_build
mkdir include
cp -apd /usr/include/*cudnn* include
mkdir include/aarch64-linux-gnu
cp -apd /usr/include/aarch64-linux-gnu/*cudnn* include/aarch64-linux-gnu
mkdir alternatives
cp -apd /etc/alternatives/*cudnn* alternatives
sudo docker build -t opencv430:104 .

```
>5/28 Update
> 上記でイメージがopencv430:103というタグで作成されます。
> /etc/alternativesから盛大にコピーしたのを比較的Gentleに変更しました。必要なリンクだけになった予定です。
> git 上では/etc/nv_tegra_releaseでJetson NANOとXavierNXを区別してます。
>5/23-2
> git上のスクリプトのハードウェア判定がバグってたのでなおしました。
> なんかpip3 install v4l2captureするとうまくいかないので、余計なfileの上書きをしないようにしました。タグがopencv430:104になりました。依然としてv4l2captureはうまく入らないですが、あきらめよう。。

dockerはあんまりつかったことないので、無茶してますが、とりあえずこれでDNNがイネーブルのOpenCV4.3をDockerで使えました
これでOpenCVのバージョンがあげ放題。。。の予定