21
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenCVAdvent Calendar 2024

Day 3

NVIDIA製ライブラリ関連のOpenCV CMakeオプションパーフェクトガイド

Last updated at Posted at 2024-12-03

この記事はOpenCV Advent Calendar 2024の3日目の記事です。

はじめに

OpenCVの一部モジュールはNVIDIA製ライブラリを用いることができ、CMakeオプションによって有効・無効などを制御することができます。一応、OpenCV公式ドキュメントにも情報があるのですが、一部の情報しか載っていません。そのため、本記事ではOpenCVのNVIDIA製ライブラリ関連CMakeオプションについて詳しく解説します。
※本記事の内容はOpenCV 4.10.0時点のソースコードをもとに書いています。

NVIDIA製ライブラリ関連CMakeオプション

OpenCVにおけるNVIDIA製ライブラリ関連CMakeオプションを下表にまとめます。以降でこのうちいくつかピックアップして詳しく解説します。

CMakeオプション 意味
BUILD_CUDA_STUBS OpenCVのcudaモジュールをCUDA SDKなしの環境でビルドするためのスタブをビルドするか
ENABLE_CUDA_FIRST_CLASS_LANGUAGE CUDA Toolkit検出時にCMakeのenable_language(CUDA)を使うかどうか
OPENCV_DNN_CUDA OpenCVのdnnモジュールでCUDA実装を有効にするか
OPENCV_CMAKE_CUDA_DEBUG OpenCVにおけるCUDA関連のCMake処理のログを詳細に表示する(OpenCV開発者向け)
WITH_CUDA OpenCVでCUDA実装を有効にするか
WITH_CUDNN OpenCVでcuDNN実装を有効にする
WITH_CUFFT OpenCVでcuFFT実装を有効にするか
WITH_CUBLAS OpenCVでcuBLAS実装を有効にするか
WITH_NVCUVID OpenCVでNVIDIA Video Decoder(NVCUVID)実装を有効にするか
WITH_NVCUVENC OpenCVでNVIDIA Video Codec SDK実装を有効にするか
CUDA_GENERATION どのGPU世代向けのバイナリを生成するか
CUDA_ARCH_PTX ターゲットのPTXアーキテクチャを指定
CUDA_ARCH_BIN ターゲットのGPUアーキテクチャを指定
CUDA_ARCH_FEATURES CUDA featureを指定
CMAKE_CUDA_ARCHITECTURES どのNVIDIA GPUアーキテクチャ向けにビルドするかを指定

ENABLE_CUDA_FIRST_CLASS_LANGUAGE

ENABLE_CUDA_FIRST_CLASS_LANGUAGE=ONの場合、OpenCVビルド時にCUDAを参照する際にenable_language(CUDA)が使われるようになります。CMakeのenable_language(CUDA)について詳細を知りたい方はhttps://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9444-build-systems-exploring-modern-cmake-cuda-v2.pdfを読むとよいでしょう。

このオプションが出来た経緯

このオプションができた経緯について少し補足します。CMakeドキュメントのhttps://cmake.org/cmake/help/v3.31/module/FindCUDA.htmlに以下の記述があります。

It is no longer necessary to use this module or call find_package(CUDA) for compiling CUDA code. Instead, list CUDA among the languages named in the top-level call to the project() command, or call the enable_language() command with CUDA. Then one can add CUDA (.cu) sources directly to targets similar to other languages.

Added in version 3.17: To find and use the CUDA toolkit libraries manually, use the FindCUDAToolkit module instead. It works regardless of the CUDA language being enabled.

従来、CMakeでCUDA Toolkitを検出する際、find_package(CUDA)が使われていましたが、

Deprecated since version 3.10: Do not use this module in new code.

とあり、この記法はCMake 3.10以降で推奨されていません。そのため、https://cliutils.gitlab.io/modern-cmake/chapters/packages/CUDA.htmlにあるように

enable_language(CUDA)

などに移行することが推奨されています。このような背景からENABLE_CUDA_FIRST_CLASS_LANGUAGEが追加されています。

ENABLE_CUDA_FIRST_CLASS_LANGUAGEについてもう少し掘り下げる

OpenCV内部でどうなっているかを知るためにhttps://github.com/opencv/opencv/blob/4.10.0/cmake/OpenCVFindLibsPerf.cmake#L41-L54を読んでみます。

# --- CUDA ---
if(WITH_CUDA)
  if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
    include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDALanguage.cmake")
  else()
    include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake")
  endif()
  if(NOT HAVE_CUDA)
    message(WARNING "OpenCV is not able to find/configure CUDA SDK (required by WITH_CUDA).
CUDA support will be disabled in OpenCV build.
To eliminate this warning remove WITH_CUDA=ON CMake configuration option.
")
  endif()
endif(WITH_CUDA)

上記ファイルを読むとわかるようにENABLE_CUDA_FIRST_CLASS_LANGUAGEの値に応じてCMakeファイルを読み分けていることがわかります。

条件 CMakeファイル
ENABLE_CUDA_FIRST_CLASS_LANGUAGE=OFF cmake/OpenCVDetectCUDA.cmake
ENABLE_CUDA_FIRST_CLASS_LANGUAGE=ON cmake/OpenCVDetectCUDALanguage.cmake

CUDA_GENERATION

CUDA_GENERATIONオプションによってどの世代のNVIDIA GPU向けにビルドするかを指定することができます。例えば、筆者が持っているGeforce RTX 2080 Ti(Turing世代)だと以下のような指定になります。

-D CUDA_GENERATION=Turing

CUDA_GENERATIONについてもう少し掘り下げる

CUDA_GENERATIONを指定した時のOpenCVのCMake周りの処理をもう少し追ってみましょう。opencv/cmake/OpenCVDetectCUDAUtils.cmakeに以下の記述があります。

macro(ocv_set_cuda_arch_bin_and_ptx nvcc_executable)
  ocv_initialize_nvidia_device_generations()
  set(__cuda_arch_ptx ${CUDA_ARCH_PTX})
  # 中略
  elseif(CUDA_GENERATION STREQUAL "Turing")
    set(__cuda_arch_bin ${_arch_turing})
  # 中略

例えば、CUDA_GENERATION=Turingの場合、set(__cuda_arch_bin ${_arch_turing})が実行されます。opencv/cmake/OpenCVDetectCUDAUtils.cmakeでは_arch_turingの定義は以下のようになっており、結果として__cuda_arch_binに7.5が設定されます。

set(_arch_turing   "7.5")

また、CUDA_GENERATIONにはAutoを設定することもできます。この場合、どのような挙動になるのでしょうか?挙動を理解するためにopencv/cmake/OpenCVDetectCUDAUtils.cmakeを読んでいきましょう。Autoの場合、ocv_detect_native_cuda_archマクロが呼ばれます。

elseif(CUDA_GENERATION STREQUAL "Auto")
  ocv_detect_native_cuda_arch(${nvcc_executable} _nvcc_res _nvcc_out)
  if(NOT _nvcc_res EQUAL 0)
    message(STATUS "CUDA: Automatic detection of CUDA generation failed. Going to build for all known architectures")
  else()
    string(REGEX MATCHALL "[0-9]+\\.[0-9]" __cuda_arch_bin "${_nvcc_out}")
  endif()

ocv_detect_native_cuda_archマクロの中身をさらに追っていきます。

macro(ocv_detect_native_cuda_arch nvcc_executable status output)
  set(OPENCV_CUDA_DETECT_ARCHS_COMMAND "${nvcc_executable}" ${OPENCV_CUDA_DETECTION_NVCC_FLAGS} "${OpenCV_SOURCE_DIR}/cmake/checks/OpenCVDetectCudaArch.cu" "--run")
  set(__cache_key_check "${OPENCV_CUDA_DETECT_ARCHS_COMMAND}")
  if(DEFINED OPENCV_CACHE_CUDA_ACTIVE_CC AND OPENCV_CACHE_CUDA_ACTIVE_CC_check STREQUAL __cache_key_check)
    set(${output} "${OPENCV_CACHE_CUDA_ACTIVE_CC}")
    set(${status} 0)
  else()
    execute_process(
        COMMAND ${OPENCV_CUDA_DETECT_ARCHS_COMMAND}
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/"
        RESULT_VARIABLE ${status}
        OUTPUT_VARIABLE _nvcc_out
        ERROR_VARIABLE _nvcc_err
        ERROR_QUIET
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    if(OPENCV_CMAKE_CUDA_DEBUG)
      message(WARNING "COMMAND: ${OPENCV_CUDA_DETECT_ARCHS_COMMAND}")
      message(STATUS "Result: ${${status}}")
      message(STATUS "Out: ${_nvcc_out}")
      message(STATUS "Err: ${_nvcc_err}")
    endif()
    string(REGEX REPLACE ".*\n" "" ${output} "${_nvcc_out}") #Strip leading warning messages, if any

    if(${status} EQUAL 0)
      # cache detected values
      set(OPENCV_CACHE_CUDA_ACTIVE_CC ${${output}} CACHE INTERNAL "")
      set(OPENCV_CACHE_CUDA_ACTIVE_CC_check "${__cache_key_check}" CACHE INTERNAL "")
    endif()
  endif()
endmacro()

おおまかには以下のことを行っています。

  1. nvccでopencv/cmake/checks/OpenCVDetectCudaArch.cuをビルドする
  2. execute_process()によってビルドしたプログラムを実行する
  3. プログラムの実行結果をもとに実行環境のGPU情報を取得する

さらに掘り下げるためにopencv/cmake/checks/OpenCVDetectCudaArch.cuを読んでみましょう。

#include <iostream>
#include <sstream>
#include <list>

int main()
{
    std::ostringstream arch;
    std::list<std::string> archs;

    int count = 0;
    if (cudaSuccess != cudaGetDeviceCount(&count)){ return -1; }
    if (count == 0) { return -1; }
    for (int device = 0; device < count; ++device)
    {
        cudaDeviceProp prop;
        if (cudaSuccess != cudaGetDeviceProperties(&prop, device)){ continue; }
        arch << prop.major << "." << prop.minor;
        archs.push_back(arch.str());
        arch.str("");
    }
    archs.unique(); // Some devices might have the same arch
    for (std::list<std::string>::iterator it=archs.begin(); it!=archs.end(); ++it)
        std::cout << *it << " ";
    return 0;
}

このプログラムではおおまかには以下のようなことをやっています。

  1. cudaGetDeviceCount()で実行環境のNVIDIA GPU台数をチェックする
  2. 実行環境のNVIDIA GPUごとにcudaGetDeviceProperties()でプロパティを取得する
  3. プロパティからアーキテクチャのバージョン情報を取り出す

CUDA_ARCH_PTX、CUDA_ARCH_BIN

  • CUDA_ARCH_PTXオプションでターゲットのPTXアーキテクチャを指定することができます
  • CUDA_ARCH_BINオプションでターゲットのGPUアーキテクチャを指定することができます

Compute Capabilityについてはhttps://zenn.dev/eduidl/articles/cuda-comparabilityを参照ください。例えば、筆者が持っているGeforce RTX 2080 Tiを例に挙げると、このGPUのCompute Capabilityは7.5になっています(https://developer.nvidia.com/cuda-gpus参照)。例えば、ターゲットのPTXアーキテクチャ、GPUアーキテクチャともにCompute Capabilityを7.5に指定する場合、以下のようなCMakeオプション指定になります。

-D CUDA_ARCH_PTX=7.5 -D CUDA_ARCH_BIN=7.5

CUDA_ARCH_FEATURES

CUDA_ARCH_FEATURESオプションでCUDA featureを指定することができます。指定できる値はhttps://cmake.org/cmake/help/v3.31/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.html#prop_gbl:CMAKE_CUDA_KNOWN_FEATURESを参照してください。CUDA/C++17の場合、以下のようなCMakeオプション指定になります。

-D CMAKE_CUDA_COMPILE_FEATURES=cuda_std_17

CMakeにおけるCUDA_ARCH_FEATURESについて詳細を知りたい方はあわせてこちらを読むとよいでしょう。

OPENCV_CMAKE_FORCE_CUDA

コンパイル時にOpenCVにおけるCUDA実装のコンパイルを無効化するオプションになっています(FORCE_CUDAという名前から受ける印象とは逆の挙動になっているのが気になります・・・)。

if((NOT UNIX AND CV_CLANG) OR OPENCV_CMAKE_FORCE_CUDA)
  message(STATUS "CUDA: Compilation is disabled (due to Clang unsupported on your platform).")
  return()
endif()

具体的には以下のCMakeファイルで使用されます。

条件 CMakeファイル
ENABLE_CUDA_FIRST_CLASS_LANGUAGE=OFF cmake/OpenCVDetectCUDA.cmake
ENABLE_CUDA_FIRST_CLASS_LANGUAGE=ON cmake/OpenCVDetectCUDALanguage.cmake

CMAKE_CUDA_ARCHITECTURES

CMAKE_CUDA_ARCHITECTURESはCMake本体で定義されているもので、https://cmake.org/cmake/help/v3.31/variable/CMAKE_CUDA_ARCHITECTURES.htmlにあるようにどのNVIDIA GPUアーキテクチャ向けにビルドするかを指定できます。例えば、筆者が持っているGeforce RTX 2080 TiのGPUのCompute Capabilityは7.5になっています(https://developer.nvidia.com/cuda-gpus参照)。このデバイス(Compute Capability 7.5)向けにビルドする場合の指定方法は以下の通りです。この場合、7.5ではなく、75となる点に注意が必要です。

-D CMAKE_CUDA_ARCHITECTURES=75

その他

NVCC_FLAGS_EXTRA

NVCC_FLAGS_EXTRAによってNVCCによるコンパイル時に使用するフラグ追加することができます。

デフォルトだとNVCC_FLAGS_EXTRAの中身は空になっています。

# NVCC flags to be set
set(NVCC_FLAGS_EXTRA "")

例えば、以下のように変更してOpenCVをビルドすることでCUDAカーネルのデバッグができるようになります。

# NVCC flags to be set
set(NVCC_FLAGS_EXTRA "-G -g")

各種ライブラリはOpenCVのどのモジュールで使われているのか?

OpenCVでcuDNN、cuFFT、cuBLASが使われることは知っていても具体的にどのモジュールで使われているかまでは知らないという方が多いのではないでしょうか?ここではどのモジュールのどの処理で使われているかをまとめました。

cuDNN

cuFFT

cuBLAS

NVCUVID

NVCUVENC

NVIDIA Optical Flow SDK

注意点

https://github.com/opencv/opencv/blob/4.10.0/cmake/OpenCVDetectCUDAUtils.cmake#L61に以下の説明があります。

Use CMAKE_CUDA_ARCHITECTURES if provided: order of preference CMAKE_CUDA_ARCHITECTURES > CUDA_GENERATION > CUDA_ARCH_BIN and/or CUDA_ARCH_PTX

ただし、この優先順位が守られないバグがhttps://github.com/opencv/opencv/issues/25920で報告されており、https://github.com/opencv/opencv/pull/25941で修正されているようです。

宣伝

関連情報として、OpenCVでのCUDA(GpuMat)活用については筆者のスライドhttps://www.docswell.com/s/fixstars/ZRXQ72-20220805もあわせて参照ください。

おわりに

本記事ではOpenCVのNVIDIA製ライブラリ関連CMakeオプションについて解説しました。
今回の解説でOpenCVをより使いこなしてくれる方が増えると幸いです。
明日は,@ASAKA-219 さんのOpenCVとROS 2で学ぶ画像処理入門です!

21
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?