この記事はOpenCV Advent Calendar 2016の20日目の記事です.
はじめに
この記事ではWeb上で語られることが少ないcudacodecモジュールについて紹介します.
cudacodecモジュールとは
OpenCVのcudacodecモジュールはGPUを用いて動画のエンコード/デコードを行うための機能を提供しています.また,内部的にはNVIDIA VIDEO CODEC SDKを使って実装されています.
このモジュールのAPIについては公式ドキュメントを参照下さい.
NVIDIA VIDEO CODEC SDKとは
NVIDIA VIDEO CODEC SDKでは動画のエンコード/デコード処理をGPUで行うためのAPIが提供されています.以下にNVIDIA VIDEO CODEC SDK公式サイトにある図を引用します.
この図からもわかるようにNVDEC,NVENCにてGPUを用いて動画のデコード処理,エンコード処理を行います.また,GPUのアーキテクチャによってサポートする機能,コーデック等が異なる点に注意が必要です.詳細は下記ページを参照ください.
- https://developer.nvidia.com/nvidia-video-codec-sdk#NVENCFeatures
- https://developer.nvidia.com/nvidia-video-codec-sdk#NVDECFeatures
cudacodecモジュールを有効にする方法
ただし,OpenCV 3.1時点のcudacodecモジュールにはバグがあるため,そのままではこれらの機能を有効にすることができません.
そのため,cudacodecモジュールを有効にするためには以下の変更(筆者のプルリク)を適用して下さい.この変更はmasterブランチにマージされているためOpenCV 3.2以降では特にこの変更なしで有効にできるはずです.
また,cudacodecモジュールとは直接関係ありませんが,今回の開発環境(VS2015,CUDA8.0)でOpenCV 3.1を動作させるために以下の変更も適用しています.
- changed to find TBB library(VS2015) #5868:VS2015版のTBBを見つけるための対応
- GraphCut deprecated in NPP 7.5 and removed in 8.0 #5672:CUDA8でビルドできない問題の対応
さらにこちらを読むとわかるように今回用いたGeForce GTX 1060ではハードウェアでH.265を処理できるため,cv::cudacodec::detail::VideoDecoder::create関数に
cudaVideoCodec_HEVC == _codec ||
という条件式を追加し,H.265のファイルも処理できるようにしています(この条件式を追加しないとASSERTが起きてしまう).
CMakeオプション
cudacodecモジュールを有効にするには以下のCMakeオプションをONにしてOpenCVライブラリをビルドする必要があります.
- BUILD_opencv_cudacodec
- BUILD_opencv_cudev
- WITH_CUDA
- WITH_NVCUVID
実装手順
ここまでの手順でライブラリ生成ができるようになったので実装手順に入ります.
cv::cudacodec::VideoReaderクラスを使った実装手順は以下の通りです.
- cv::cudacodec::VideoReaderクラスのインスタンスを生成する
- cv::cudacodec::VideoReaderクラスのnextFrameメソッドで1フレームデコードする
以下にこの手順に従ったサンプルコードを示します.
// cv::cudacodec::VideoReaderクラスのインスタンスを生成する
cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader("hoge.mpg");
cv::cuda::GpuMat d_frame;
// 1フレームデコードする
d_reader->nextFrame(d_frame)
パフォーマンス計測
以下のURLにあるテストデータを用いて1フレーム当たりのデコード時間を計測しました.
- https://github.com/opencv/opencv_extra/tree/3.1.0/testdata/gpu/video
- http://www.jell.yfish.us/
- http://www3.jvckenwood.com/pro/video/prohd/
また,処理時間を計測するために用いたテストプログラムはこちらを参照ください.各ファイルに対するCPU処理(cv::VideoCapture
)、GPU処理(cudacodec::createVideoReader
)のデコード時間は以下の通りです.
ファイル名 | コーデック | 画像サイズ | cv::VideoCapture | cv::cudacodec::createVideoReader |
---|---|---|---|---|
1920x1080.avi | MPEG-4 | 1920x1080 | 3.99ms | 0.82ms |
jellyfish-10-mbps-hd-h264.mkv | H.264 | 1920x1080 | 3.90ms | 0.56ms |
jellyfish-10-mbps-hd-hevc.mkv | H.265 | 1920x1080 | 4.03ms | 0.56ms |
jellyfish-50-mbps-hd-h264.mkv | H.264 | 1920x1080 | 4.10ms | 0.56ms |
jellyfish-50-mbps-hd-hevc.mkv | H.265 | 1920x1080 | 4.86ms | 0.56ms |
jellyfish-100-mbps-hd-h264.mkv | H.264 | 1920x1080 | 4.48ms | 0.57ms |
jellyfish-100-mbps-hd-hevc.mkv | H.265 | 1920x1080 | 6.35ms | 0.56ms |
TRM10098_01.MOV | H.264 | 1920x1080 | 6.97ms | 1.51ms |
TRM10115_01.MOV | H.264 | 1920x1080 | 6.50ms | 1.49ms |
上記の結果からもcudacodec::createVideoReader
を使うことで高速にデコード処理を行うことができていることがわかります(お使いのGPUによって速度は変わると思います).
ただし,http://www.jell.yfish.us/にあるMain10プロファイルのものは2016/12/18時点で正しく処理できませんでした.これはおそらくhttps://developer.nvidia.com/nvidia-video-codec-sdk#NVDECFeaturesにある
* HEVC/VP9 10/12 bit decoding SW support coming in Video Codec SDK 8.0
という制約のためだと考えられます.また,コーデックやプロファイルによってはデコード結果が正しくない場合もあるため,使用する前に確認しておくとよいでしょう.
TODO
また,現時点でのcudacodecモジュールに関する積み残しは以下の通りです.
ご興味ある方は対応してPRを投げてみてください!
- cudacodecモジュールの非Windows対応
- CMakeのFindCUDAでCUDA_nvcuvid_LIBRARYを見つけられるのは現状Windowsのみなので,Linux側の処理を書いてあげる必要あり
-
cv::cudacodec::detail::VideoDecoder::create関数のHEVC対応
- GPUのアーキテクチャによって対応コーデックが異なるのでその辺をちゃんとハンドリングしながらチェックする必要があるはず
- cudacodecモジュールのエンコード処理対応
- NVEncode APIを使った実装が必要
おわりに
この記事ではWeb上で語られることが少ないcudacodecモジュールについて紹介しました.正しくデコードできるかの動作確認は必要になるものの,動画のデコード処理時間を短縮できる魅力的な機能なので利用を検討してみてもよいかもしれません.
備考
筆者は以下の環境で動作確認しました.
- CPU:Intel Core i7-6700HQ
- メモリ:64GB
- GPU:NVIDIA GeForce GTX 1060 / 6GB
- Windows 10 Pro(64bit)
- OpenCV 3.1.0
- CUDA 8.0
- Visual Studio 2015 Professional