Help us understand the problem. What is going on with this article?

OpenCVから参照される環境変数

はじめに

これは、OpenCV Advent Calendar 2018 3日目の記事です。関連記事は目次にまとめられています。なお、本記事は筆者個人の意見であり、筆者の所属組織とは無関係です。

OpenCVの挙動を変える

OpenCVの挙動は基本的にビルド時に決定されます。挙動というのはCUDA、OpenCL、Eigen、IPPやPLCなどの外部連携ライブラリを使う/使わない、SIMD命令を使う/使わない、と言った類の挙動です。
またビルド時に連携するライブラリのパスも、場合によっては一々指定する必要があります。

その時に、毎回手動でパスを指定したり、挙動を変えるためにビルドをやり直すのも、なかなかイケてない状況です。そこで、一部限られた挙動に限り、環境変数で挙動を変えることが可能です。

具体例も交えて紹介していきます。

実行時の挙動を変える

実行時の挙動の一つとして、OpenCLのデバイスが挙げられます。OpenCLはご存知のようにアクセラレータで動作するカーネルを、単一の言語で記述することができます。ここで言う「アクセラレータ」はGeforeceやRadeonなどのGPUが一般的ですが、Intel HD GraphicsなどのCPU内蔵のGPU、FPGAやCPU自身でも実行することが1できます。

例えば、筆者の私物のラップトップは、以下のような構成です

Device
CPU Intel Core i7 8650U
iGPU Intel HD Graphics 620
dGPU NVIDIA Geforce GTX 1060

ここに記述されているCPU、iGPU、dGPUはそれぞれOpenCLに対応しているので、OpenCLでカーネルを記述すると、勝手にベクタライズ/並列化されたプログラムをそれぞれのプラットフォームで走らせることができます。ただし、基本的にはOpenCLが列挙した1番目のデバイスが選ばれます。他のプラットフォームで走らせたい場合はどうすれば良いのでしょうか?

ここで、環境変数OPENCV_OPENCL_DEVICEを指定すると、任意のプラットフォームを指定できます

環境変数の指定方法はコントロールパネルからしたり、Visual Studioでデバッグしてる場合は、Debugの項目からも指定できます。Visual Studioでデバッグ実行する際に環境変数を指定する方法は結構便利なので、皆さん覚えてくださいね。

Screenshot 2018-11-27 05.03.42.png

便利は便利なのですが、本記事ではGit bashコマンドラインで直接指定して、OpenCVのテストプログラムを実行してみます。

以下が実行したときのログです

$ ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
CTEST_FULL_OUTPUT
OpenCV version: 4.0.0
OpenCV VCS version: 4.0.0
Build type: N/A
WARNING: build value differs from runtime: Release
Compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe  (ver 19.0.24215.1)
Parallel framework: ms-concurrency
CPU features: SSE SSE2 SSE3 *SSE4.1 *SSE4.2 *FP16 *AVX *AVX2
Intel(R) IPP version: ippIP AVX2 (l9) 2017.0.3 (-) Jul 31 2017
OpenCL Platforms:
    NVIDIA CUDA
        dGPU: GeForce GTX 1060 (OpenCL 1.2 CUDA)
    Intel(R) OpenCL
        iGPU: Intel(R) UHD Graphics 620 (OpenCL 2.1 )
        CPU: Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz (OpenCL 2.1 (Build 10))
Current OpenCL device:
    Type = dGPU
    Name = GeForce GTX 1060
    Version = OpenCL 1.2 CUDA
    Driver version = 411.63
    Address bits = 64
    Compute units = 10
    Max work group size = 1024
    Local memory size = 48 KB
    Max memory allocation size = 1 GB 512 MB
    Double support = Yes
    Host unified memory = No
    Device extensions:
        cl_khr_global_int32_base_atomics
        cl_khr_global_int32_extended_atomics
        cl_khr_local_int32_base_atomics
        cl_khr_local_int32_extended_atomics
        cl_khr_fp64
        cl_khr_byte_addressable_store
        cl_khr_icd
        cl_khr_gl_sharing
        cl_nv_compiler_options
        cl_nv_device_attribute_query
        cl_nv_pragma_unroll
        cl_nv_d3d10_sharing
        cl_khr_d3d10_sharing
        cl_nv_d3d11_sharing
        cl_nv_copy_opts
        cl_nv_create_buffer
    Has AMD Blas = No
    Has AMD Fft = No
    Preferred vector width char = 1
    Preferred vector width short = 1
    Preferred vector width int = 1
    Preferred vector width long = 1
    Preferred vector width float = 1
    Preferred vector width double = 1
Note: Google Test filter = *CvtColor.BGR2YCrCb/0

解説

この出力を全部読むと日が暮れるので、かいつまんで説明すると、OpenCVをロードした時点でデバイスの列挙が行われます

OpenCL Platforms:
    NVIDIA CUDA
        dGPU: GeForce GTX 1060 (OpenCL 1.2 CUDA)
    Intel(R) OpenCL
        iGPU: Intel(R) UHD Graphics 620 (OpenCL 2.1 )
        CPU: Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz (OpenCL 2.1 (Build 10))

前述の通り、GeForce GTX 1060、Intel UHD Graphics 620とIntel Core i7-8650Uの3種類が列挙されています。その後読み解くと、以下のような出力があります。

Current OpenCL device:
    Type = dGPU
    Name = GeForce GTX 1060
    Version = OpenCL 1.2 CUDA

dGPUとは"Discrete GPU"の略で、いわゆるGPUのことです。広義のGPUには、CPU内蔵のIntel HD Graphicsも含みます。明示的に区別するためにCPU内蔵のGPUをiGPU、独立したGPUをdGPUと指定します。

これがデフォルトの挙動です。では、環境変数を指定してデバイスを切り替えてみましょう。

iGPU (Intel HD Graphics 620)を使う

起動時に変数名=値というペアをコマンドの前に指定すると、環境変数を上書きできます。以下では:igpuを環境変数に指定しています。(コロンを忘れないように)

$ OPENCV_OPENCL_DEVICE=:igpu ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
(中略)
Current OpenCL device:
    Type = iGPU
    Name = Intel(R) UHD Graphics 620
    Version = OpenCL 2.1

先程と出力が代わり、iGPUを使用するように切り替わっていることがわかります。OpenCLの対応バージョンも変わっています。この様に、OpenCLのカーネルを実行するデバイスを、実行時(プログラム起動時)に切り替えられていることがわかります。

CPU (Intel Core i7 8650U)を使う

最近のCore i7 シリーズは、CPUでもOpenCLを実行できます。並列化したプログラムを、わざわざ書き換えなくても、一度書いたOpenCLのカーネルを再利用できます。

$ OPENCV_OPENCL_DEVICE=:cpu ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
(中略)
Current OpenCL device:
    Type = CPU
    Name = Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz
    Version = OpenCL 2.1 (Build 10)

今度はちゃんとCPU上で実行されました。

その他実行時に参照される環境変数

書きなぐるだけなのも何なので、筆者が使ってる変数を抜粋した上で解説を書き加えておきます

OPENCV_CPU_DISABLE

  • OpenCV 3.3 から導入されたDispatchの機能を使うことで、一部のCPUの特殊命令を使用せずに画像処理を行います。例えば、デフォルトのコンフィグだとSSE4、AVX、AVX2あたりの命令は実行時にチェックがおこなれます。このとき、OPENCV_CPU_DISABLE=AVXと設定することでAVX命令を使う部分を迂回して画像処理が行われます。これにより、実行結果が遅くなりますが、この差から、AVX版のパフォーマンス向上がどれ位なのか知ることができます。実行時に命令無効化が行われるので、ビルドし直す必要がありません。
  • 当然ながらDispatchを利用せず、Baselineだけで指定した場合はこの環境変数は無視されます。

OPENCV_DUMP_CONFIG

  • OpenCVのデバッグ情報を出力します。OpenCVをロードした時点で、DLL内部にあるビル度情報が吐き出されます。
OpenCV build configuration is:

General configuration for OpenCV 4.0.0 =====================================
  Version control:               4.0.0

  Platform:
    Timestamp:                   2018-11-20T14:48:49Z
    Host:                        Windows 10.0.17134 AMD64
    CMake:                       3.11.0
    CMake generator:             Visual Studio 14 2015 Win64
    CMake build tool:            C:/Program Files (x86)/MSBuild/14.0/bin/MSBuild.exe
    MSVC:                        1900

  CPU/HW features:
    Baseline:                    SSE SSE2 SSE3
      requested:                 SSE3
    Dispatched code generation:  SSE4_1 SSE4_2 FP16 AVX AVX2
      requested:                 SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX
      SSE4_1 (5 files):          + SSSE3 SSE4_1
      SSE4_2 (2 files):          + SSSE3 SSE4_1 POPCNT SSE4_2
      FP16 (1 files):            + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 AVX
      AVX (6 files):             + SSSE3 SSE4_1 POPCNT SSE4_2 AVX
      AVX2 (11 files):           + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 FMA3 AVX AVX2
(中略)
  Install to:                    C:/work/opencv-fork/build/install
-----------------------------------------------------------------

  • 内部ではgetBuildInformation()を呼んでるだけです。空文字以外を指定するとビルド情報が出力されます2
system.cpp
        if (getenv("OPENCV_DUMP_CONFIG"))
        {
            fprintf(stderr, "\nOpenCV build configuration is:\n%s\n",
                cv::getBuildInformation().c_str());
        }

OPENCV_FOR_THREADS_NUM

  • CPU上で並列実行の並列数を指定します。imgprocモジュールに代表されるような、全画素をなめるようなループをCPU実行する場合、OpenCVは内部でparallell_forで実行します。
  • デフォルトではCPUの論理スレッド数が指定されます。(ただし、Android版では2スレッドがデフォルト値)この値を変える事によって、コア数の違いでパフォーマンスがスケールするか実験できます。また、OpenCV内部をデバッグする場合、並列に処理される部分はトレースが著しく面倒くさいのですが、この変数を1に設定することで並列化を一時的に無効化できます。OpenCVビルド職人がデバッグする際に重宝する環境変数です。

  • 無指定の場合

$ opencv_perf_imgproc --gtest_filter=Size_CvtMode_cvtColor8u.cvtColor8u/123
Time compensation is 0
CTEST_FULL_OUTPUT
(中略)
[ RUN      ] Size_CvtMode_cvtColor8u.cvtColor8u/123, where GetParam() = (640x480, COLOR_BGR2GRAY)
[ PERFSTAT ]    (samples=100   mean=0.20   median=0.19   min=0.19   stddev=0.01 (4.4%))
[       OK ] Size_CvtMode_cvtColor8u.cvtColor8u/123 (106 ms)
  • スレッド数を1に固定した場合
$ OPENCV_FOR_THREADS_NUM=1 opencv_perf_imgproc --gtest_filter=Size_CvtMode_cvtColor8u.cvtColor8u/123
Time compensation is 0
CTEST_FULL_OUTPUT
(中略)
[ RUN      ] Size_CvtMode_cvtColor8u.cvtColor8u/123, where GetParam() = (640x480, COLOR_BGR2GRAY)
[ PERFSTAT ]    (samples=100   mean=0.42   median=0.40   min=0.38   stddev=0.04 (8.7%))
[       OK ] Size_CvtMode_cvtColor8u.cvtColor8u/123 (223 ms)
  • 100回計測した中央値がmedianに表示されてますが、倍遅くなってることが分かります3

OPENCV_IPP

  • IPPライブラリが実行するモジュールを実行時に選択できる
OPENCV_IPP=disabled|sse42|avx2|avx512
  • disabledにするとOpenCV独自の実装が呼ばれる。またavx512は64bit環境下でのみ選べる。
  • 複数の項目は指定できないし、一つ選択すると、下位の命令セットも自動的に有効になる。(avx2を指定するとsse42も有効になる)
    • IPP 自体の設定はもっと細かいのだが、OpenCVでまとめられて、指定できるのは前述の3つ+disabledのみ

OPENCV_LEGACY_WAITKEY

  • 特殊キーを拾う挙動が3.2から変わりました4。その挙動を3.2より前に戻すためにはこの環境変数を使用します。空文字以外を指定すると3.2より前の挙動に戻ります。

OPENCV_OPENCL_DEVICE

  • 前述の通り
OPENCV_OPENCL_DEVICE=disabled|platform:deviceTypes:deviceName
  • disabled は問答無用に無効化します。
  • deviceTypesは現状以下の中から選びます
    • gpu
    • dgpu
    • igpu
    • cpu
    • accelerator
    • all
  • deviceNameは
    • 1桁の数字の場合は列挙されたn番目のデバイスを選ぶ
    • 2桁以上の場合は名前の中に含まれた数字にマッチするかどうか
      • ex: 1080 -> Geforce GTX1080
      • ex: 580 -> Radeon RX 580, Geforce GTX 580
      • ex: 620 -> Intel(R) UHD Graphics 620

OPENCV_OPENCL_RUNTIME

  • OpenCL.dllもしくはlibOpenCL.soをロードするためのパスを環境変数で指定できる
  • disabledを指定すると、libOpenCL.soのロードを諦める(OPENCV_OPENCL_DEVICE=disabledと等価)

OPENCV_TEST_DATA_PATH

  • opencv_extraリポジトリのパス。テスト実行時にこのパスが参照され、リポジトリ内にあるテストデータやリファレンスの結果が参照され、テストが実行される。

OPENCV_TRACE

  • TRACEに関してはdandelion1124先生に聞いて下さい。

OPENCV_VIDEOCAPTURE_DEBUG

  • OpenCVのビデオ読み込み周りのバックグラウンドエンジンからのデバッグ情報を表示する。

ビルド時に参照される環境変数一覧

特定のライブラリと、OpenCVを連携することができます。このとき、連携させるライブラリがどこにインストールされているか、環境変数に登録するライブラリが多いです。5

この環境変数が設定されており、かつ環境変数が示す先に実体が存在するとき、OpenCVは指定されたライブラリと連携したビルドを試します。

  • ANT_DIR
  • Atlas_ROOT_DIR
  • DESTDIR
  • EIGEN_ROOT
  • GSTREAMER_DIR
  • HOME
  • IPP_ASYNC_ROOT
  • MFX_HOME
  • MKLROOT
  • NVSDKCOMPUTE_ROOT
  • OPEN_NI_INSTALL_PATH
  • OPEN_NI_INSTALL_PATH64
  • OPEN_NI_LIB
  • OPEN_NI_LIB64
  • OpenBLAS_HOME
  • OpenBLAS
  • OPENCV_DOWNLOAD_PATH
  • OPENCV_IPP_PATH
  • OPENNI2_INCLUDE
  • OPENNI2_INCLUDE64
  • OPENNI2_LIB
  • OPENNI2_LIB64
  • OPENNI2_REDIST
  • PCSDK_DIR
  • ProgramFiles
  • SystemDrive
  • TBBROOT
  • VA_INTEL_IOCL_ROOT
  • VA_INTEL_MSDK_ROOT

終わりに

OpenCV を再ビルドするの、疲れますよね。(疲れないけれど)

筆者は以下の環境で確認しました

  • OpenCV 4.0.0
  • Windows 10 64bit
  • Visual Studio Community 2015 (Update 3)
  • CMake 3.11.0
  • CPU Intel Core i7 8650U
  • iGPU Intel HD Graphics 620
  • dGPU NVIDIA Geforce GTX 1060

明日も私の予定で、なんかネタを考えます。


  1. デバイスが対応していれば 

  2. なのでOPENCV_DUMP_CONFIG=OFFを指定してもgetBuildInformationがcall/出力されます。 

  3. でも、本当は8倍差が出てほしいのだが? 

  4. 厳密には3.2.0-rcから 

  5. Windowsでは、レジストリに書き込むライブラリもある 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away