nvidia-docker 2.x上でGPU対応版のOpenCV 2.xをビルド、インストールする

## 概要

本記事では、[nvidia-docker](https://github.com/NVIDIA/nvidia-docker) 2.x上に[OpenCV](https://opencv.org/)の2.x系をGPU（CUDA）オプションを有効化した状態でビルド、インストールする方法についてざっと解説します。

## デフォルトランタイムの変更

GPU版のOpenCVをビルドする際、`docker build`時に共有ライブラリ（例えば`libnvcuvid.so`）を参照する必要があるため、デフォルトランタイムを`runc`から`nvidia`に変更する必要があります。
別途Qiitaに記事を書きましたので、詳しくはそちらの記事を参照ください。

[nvidia-dockerのbuild時にGPU、関連ライブラリを有効にする - Qiita](https://qiita.com/yuyakato/items/640ba7a6264f1c21aea7)

なお、デフォルトランタイムを`nvidia`に切り替えない場合でもビルド自体は成功しますが、`cmake`時に以下のような警告が出力されます。

```
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
CUDA_CUDA_LIBRARY (ADVANCED)
    linked by target "opencv_gpu" in directory /tmp/opencv/opencv-2.4.13.6/modules/gpu
    linked by target "opencv_gpu" in directory /tmp/opencv/opencv-2.4.13.6/modules/gpu
```

## `Dockerfile`完成版

まずは完成した`Dockerfile`をどうぞ。なお、今回ビルドしたのはOpenCV のバージョンは2.4.13.6です。

```Dockerfile
FROM nvidia/cuda:8.0-devel-ubuntu16.04

ENV NVIDIA_DRIVER_CAPABILITIES all

RUN apt-get update \
  && apt-get install --yes --no-install-recommends \
    build-essential \
    ca-certificates \
    cmake \
    ffmpeg \
    libavcodec-dev \
    libavformat-dev \
    libavresample-dev \
    libjpeg-dev \
    libpng-dev \
    libswscale-dev \
    libtbb-dev \
    libtiff-dev \
    libv4l-dev \
    pkg-config \
    unzip \
    wget \
  && rm -rf /var/lib/apt/lists/*

RUN OPENCV_VERSION="2.4.13.6" \
  && mkdir -p /tmp/opencv \
  && cd /tmp/opencv/ \
  && wget https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip -q \
  && unzip ${OPENCV_VERSION}.zip -d . \
  && mkdir /tmp/opencv/opencv-${OPENCV_VERSION}/build \
  && cd /tmp/opencv/opencv-${OPENCV_VERSION}/build/ \
  && cmake \
    -D BUILD_TESTS=OFF \
    -D BUILD_PERF_TESTS=OFF \
    -D WITH_FFMPEG=ON \
    -D WITH_TBB=ON \
    -D WITH_CUDA=ON \
    -D WITH_CUBLAS=ON \
    -D WITH_NVCUVID=ON \
    -D ENABLE_FAST_MATH=ON \
    -D CUDA_FAST_MATH=ON \
    -D CUDA_ARCH_BIN="6.1" \
    -D CUDA_ARCH_PTX="6.1" \
    -D CUDA_nvcuvid_LIBRARY=/usr/lib/x86_64-linux-gnu/libnvcuvid.so.1 \
    .. \
    | tee /tmp/opencv_cmake.log \
  && make -j "$(nproc)" \
    | tee /tmp/opencv_build.log \
  && make install \
    | tee /tmp/opencv_install.log \
  && rm -rf /tmp/opencv
```

上記`Dockerfile`をビルドした時の構成の結果は以下の通りです。（`/tmp/opencv_cmake.log`より抜粋）

```txt
-- General configuration for OpenCV 2.4.13.6 =====================================
--   Version control:               unknown
--
--   Platform:
--     Timestamp:                   2018-04-18T08:01:17Z
--     Host:                        Linux 4.4.0-83-generic x86_64
--     CMake:                       3.5.1
--     CMake generator:             Unix Makefiles
--     CMake build tool:            /usr/bin/make
--     Configuration:               Release
--
--   C/C++:
--     Built as dynamic libs?:      YES
--     C++ Compiler:                /usr/bin/c++  (ver 5.4.0)
--     C++ flags (Release):         -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG
--     C++ flags (Debug):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG
--     C Compiler:                  /usr/bin/cc
--     C flags (Release):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG
--     C flags (Debug):             -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG
--     Linker flags (Release):
--     Linker flags (Debug):
--     ccache:                      NO
--     Precompiled headers:         YES
--
--   OpenCV modules:
--     To be built:                 core flann imgproc highgui features2d calib3d ml video legacy objdetect photo gpu ocl nonfree contrib stitching superres ts videostab
--     Disabled:                    world
--     Disabled by dependency:      -
--     Unavailable:                 androidcamera dynamicuda java python viz
--
--   GUI:
--     QT:                          NO
--     GTK+ 2.x:                    NO
--     GThread :                    NO
--     GtkGlExt:                    NO
--     OpenGL support:              NO
--     VTK support:                 NO
--
--   Media I/O:
--     ZLib:                        /usr/lib/x86_64-linux-gnu/libz.so (ver 1.2.8)
--     JPEG:                        /usr/lib/x86_64-linux-gnu/libjpeg.so (ver )
--     PNG:                         /usr/lib/x86_64-linux-gnu/libpng.so (ver 1.2.54)
--     TIFF:                        /usr/lib/x86_64-linux-gnu/libtiff.so (ver 42 - 4.0.6)
--     JPEG 2000:                   build (ver 1.900.1)
--     OpenEXR:                     build (ver 1.7.1)
--
--   Video I/O:
--     DC1394 1.x:                  NO
--     DC1394 2.x:                  NO
--     FFMPEG:                      YES
--       avcodec:                   YES (ver 56.60.100)
--       avformat:                  YES (ver 56.40.101)
--       avutil:                    YES (ver 54.31.100)
--       swscale:                   YES (ver 3.1.101)
--       avresample:                YES (ver 2.1.0)
--     GStreamer:                   NO
--     OpenNI:                      NO
--     OpenNI PrimeSensor Modules:  NO
--     PvAPI:                       NO
--     GigEVisionSDK:               NO
--     UniCap:                      NO
--     UniCap ucil:                 NO
--     V4L/V4L2:                    Using libv4l1 (ver 1.10.0) / libv4l2 (ver 1.10.0)
--     XIMEA:                       NO
--     Xine:                        NO
--
--   Other third-party libraries:
--     Use IPP:                     NO
--     Use Eigen:                   NO
--     Use TBB:                     YES (ver 4.4 interface 9002)
--     Use OpenMP:                  NO
--     Use GCD                      NO
--     Use Concurrency              NO
--     Use C=:                      NO
--     Use Cuda:                    YES (ver 8.0)
--     Use OpenCL:                  YES
--
--   NVIDIA CUDA
--     Use CUFFT:                   YES
--     Use CUBLAS:                  YES
--     USE NVCUVID:                 YES
--     NVIDIA GPU arch:             61
--     NVIDIA PTX archs:            61
--     Use fast math:               YES
--     Tiny gpu module:             NO
--
--   OpenCL:
--     Version:                     dynamic
--     Include path:                /tmp/opencv/opencv-2.4.13.6/3rdparty/include/opencl/1.2
--     Use AMD FFT:                 NO
--     Use AMD BLAS:                NO
--
--   Python:
--     Interpreter:                 NO
--
--   Java:
--     ant:                         NO
--     JNI:                         NO
--     Java tests:                  NO
--
--   Documentation:
--     Build Documentation:         NO
--     Sphinx:                      NO
--     PdfLaTeX compiler:           NO
--     Doxygen:                     NO
--
--   Tests and samples:
--     Tests:                       NO
--     Performance tests:           NO
--     C/C++ Examples:              NO
--
--   Install path:                  /usr/local
--
--   cvconfig.h is in:              /tmp/opencv/opencv-2.4.13.6/build
-- -----------------------------------------------------------------
```

## 解説

### ベースイメージ

今回はベースイメージとして、CUDA 8.0がインストールされたUbuntu 16.04 LTSを使用しました。おそらく他の環境でも似たようなものだと思います。

```Dockerfile
FROM nvidia/cuda:8.0-devel-ubuntu16.04
```

### `NVIDIA_DRIVER_CAPABILITIES`環境変数

今回は`libnvcuvid.so`を使いたかったため、`NVIDIA_DRIVER_CAPABILITIES`環境変数に`all`を指定しました。
値として`video`を含んでいれば問題ないとは思いますが、今回は広めに設定しています。ちなみにデフォルト値は`utility`です。

```Dockerfile
ENV NVIDIA_DRIVER_CAPABILITIES all
```

`NVIDIA_DRIVER_CAPABILITIES`環境変数については以下のページを参照ください。

[NVIDIA/nvidia-container-runtime: NVIDIA container runtime](https://github.com/NVIDIA/nvidia-container-runtime)

### ビルド環境

以下のUbuntuパッケージは、ソースパッケージの取得（ダウンロード）、展開のために使用しています。

```sh
apt-get install \
  ca-certificates \
  unzip \
  wget
```

また、以下のUbuntuパッケージは、ビルドのために使用しています。

```sh
apt-get install \
  build-essential \
  cmake \
  pkg-config
```

### メディア

以下のUbuntuパッケージは、各種メディアの読み書きのために使用しています。

```sh
apt-get install \
  libjpeg-dev \
  libpng-dev \
  libtiff-dev
```

`cmake`の結果では、以下の箇所が該当します。

```txt
--   Media I/O:
--     ZLib:                        /usr/lib/x86_64-linux-gnu/libz.so (ver 1.2.8)
--     JPEG:                        /usr/lib/x86_64-linux-gnu/libjpeg.so (ver )
--     PNG:                         /usr/lib/x86_64-linux-gnu/libpng.so (ver 1.2.54)
--     TIFF:                        /usr/lib/x86_64-linux-gnu/libtiff.so (ver 42 - 4.0.6)
```

### FFmpeg

以下のUbuntuパッケージ、`cmake`オプションは、FFmpegを有効化するために使用しています。

```sh
apt-get install \
  ffmpeg \
  libavcodec-dev \
  libavformat-dev \
  libavresample-dev \
  libswscale-dev
cmake \
  -D WITH_FFMPEG=ON
```

`cmake`の結果では、以下の箇所が該当します。

```txt
--   Video I/O:
--     FFMPEG:                      YES
--       avcodec:                   YES (ver 56.60.100)
--       avformat:                  YES (ver 56.40.101)
--       avutil:                    YES (ver 54.31.100)
--       swscale:                   YES (ver 3.1.101)
--       avresample:                YES (ver 2.1.0)
```


### V4L（Video for Linux）

以下のUbuntuパッケージは、V4L（Video for Linux）を有効化するために使用しています。

```sh
apt-get install \
  libv4l-dev
```

`cmake`の結果では、以下の箇所が該当します。

```txt
--   Video I/O:
--     V4L/V4L2:                    Using libv4l1 (ver 1.10.0) / libv4l2 (ver 1.10.0)
```

### TBB（Threading Building Blocks）

以下のUbuntuパッケージ、`cmake`オプションは、TBB（Threading Building Blocks）を有効化するために使用しています。

```sh
apt-get install \
  libtbb-dev
cmake \
  -D WITH_TBB=ON
```

`cmake`の結果では、以下の箇所が該当します。

```
--   Other third-party libraries:
--     Use TBB:                     YES (ver 4.4 interface 9002)
```

### CUDA

以下の`cmake`オプションは、CUDAを有効化するために使用しています。
なお、GPUアーキテクチャ（Compute Capability）としてGeForce 10シリーズ（Pascal世代）向けの「6.1」を指定しています。

```
cmake \
  -D WITH_CUDA=ON \
  -D WITH_CUBLAS=ON \
  -D WITH_NVCUVID=ON \
  -D ENABLE_FAST_MATH=ON \
  -D CUDA_FAST_MATH=ON \
  -D CUDA_ARCH_BIN="6.1" \
  -D CUDA_ARCH_PTX="6.1" \
  -D CUDA_nvcuvid_LIBRARY=/usr/lib/x86_64-linux-gnu/libnvcuvid.so.1
```

`cmake`の結果では、以下の箇所が該当します。

```
--   Other third-party libraries:
--     Use Cuda:                    YES (ver 8.0)
--
--   NVIDIA CUDA
--     Use CUFFT:                   YES
--     Use CUBLAS:                  YES
--     USE NVCUVID:                 YES
--     NVIDIA GPU arch:             61
--     NVIDIA PTX archs:            61
--     Use fast math:               YES
--     Tiny gpu module:             NO
```

### その他

その他、テストを無効化するために以下の`cmake`オプションを指定しています。

```
cmake \
  -D BUILD_TESTS=OFF \
  -D BUILD_PERF_TESTS=OFF
```

`cmake`の結果では、以下の箇所が該当します。

```
--   Tests and samples:
--     Tests:                       NO
--     Performance tests:           NO
```

ざっくりした解説は以上です。

## 参考

* [NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs](https://github.com/NVIDIA/nvidia-docker)
    * nvidia-dockerの本家サイト。
* [NVIDIA/nvidia-container-runtime: NVIDIA container runtime](https://github.com/NVIDIA/nvidia-container-runtime)
    * nvidia-dockerのランタイム。
* [OpenCV library](https://opencv.org/)
    * OpenCVの本家サイト。
* [nvidia-dockerのbuild時にGPU、関連ライブラリを有効にする - Qiita](https://qiita.com/yuyakato/items/640ba7a6264f1c21aea7)
* [Dockerfile Tips - Qiita](https://qiita.com/yuyakato/items/4e8616ad83d67e35bc5a)