VPIとは
VPI(https://docs.nvidia.com/vpi/)は、NVIDIA Vision Programming Interfaceのことで、Jetsonデバイス、NVIDIA GPU搭載のx86_64マシン向けにコンピュータビジョン、画像処理アルゴリズムの機能を提供するライブラリです。
また、各種アルゴリズムは、CPUだけでなくNVIDIA GPU、PVAなどの異なるアクセラレータ向けの実装がなされており、特定アクセラレータに処理をオフロードし、連携できるようになっています。https://docs.nvidia.com/vpi/2.3/architecture.htmlにあるVPIアーキテクチャの図を以下に引用します。
より詳細な情報を知りたい方はhttps://www.youtube.com/watch?v=gCf1wQLfYCMにあるNVIDIA Japanによる解説動画を観るとよいでしょう。
動作環境
VPIがサポートしている動作環境は以下の通りです。詳細はhttps://docs.nvidia.com/vpi/2.3/architecture.html#arch_supported_platformsを参照ください。
- Jetson AGX Xavier、Jetson Xavier NX
- Jetson AGX Orin、Jetson AGX Orin NX
- NVIDIA GPU搭載(Maxwell世代以降)のLinux x86_64マシン
インストール方法はhttps://docs.nvidia.com/vpi/2.3/installation.htmlを参照ください。
バックエンド
https://docs.nvidia.com/vpi/2.3/basic_concepts.html#basic_backendにある表を以下に示します。この表にはVPIが提供するバックエンドと対応デバイス、アーキテクチャが書かれています。
Backend | Device/platform |
---|---|
CPU | x86_64アーキテクチャ/Ubuntuマシン、Jetsonデバイスで利用可能 |
CUDA | x86_64アーキテクチャのNVIDIA GPU搭載Ubuntuマシン、Jetsonデバイスで利用可能 |
PVA (Programmable Vision Accelerator) |
Jetson AGX Xavier、Jetson Xavier NXシリーズ以降のデバイスのみで利用可能(NanoシリーズはPVA非搭載) |
VIC (Video Image Compositor) |
Jetsonデバイスのみ利用可能 |
NVENC (NVIDIA Encoder Engine) |
Jetsonデバイスのみ利用可能。ただし、dense optical flowはJetson AGX Xavierシリーズ以降のJetsonデバイスのみ |
OFA (NVIDIA Optical Flow Accelerator) |
Jetson AGX OrinシリーズのJetsonデバイスのみ利用可能 |
各バックエンドの詳細はhttps://docs.nvidia.com/vpi/2.3/architecture.html#arch_backendを参照ください。
データ構造
VPIにおけるプリミティブなデータ構造について紹介します。VPIはC API、Python APIを提供していますが、以降の説明ではC APIを用いた例を述べます。詳細はhttps://docs.nvidia.com/vpi/2.3/basic_concepts.html#basic_buffersを参照ください。
- 2D Images
- C APIだと
VPIImage
- https://docs.nvidia.com/vpi/2.3/group__VPI__Image.html
- C APIだと
- 1D Arrays
- C APIだと
VPIArray
- https://docs.nvidia.com/vpi/2.3/group__VPI__Array.html
- C APIだと
- 2D Image Pyramids
- C APIだと
VPIPyramid
- https://docs.nvidia.com/vpi/2.3/group__VPI__Pyramid.html
- C APIだと
以降、これらのプリミティブなデータ構造の使い方の例を示します。
VPIImage
// VPIImageを使うために要インクルード
#include <vpi/Image.h>
int main(int argc, char *argv[])
{
int32_t width = 512;
int32_t height = 256;
VPIImageFormat fmt = VPI_IMAGE_FORMAT_U8;
// VPIImage生成
VPIImage image;
vpiImageCreate(width, height, fmt, 0, &image);
// VPIImage解放
vpiImageDestroy(image);
return 0;
}
VPIArray
// VPIArrayを使うために要インクルード
#include <vpi/Array.h>
int main(int argc, char *argv[])
{
int32_t capacity = 10;
VPIArrayType type = VPI_ARRAY_TYPE_U8;
// VPIArray生成
VPIArray arr;
vpiArrayCreate(capacity, type, 0, &arr);
// VPIArray解放
vpiArrayDestroy(arr);
return 0;
}
VPIPyramid
// VPIPyramidを使うために要インクルード
#include <vpi/Pyramid.h>
int main(int argc, char *argv[])
{
int32_t width = 512;
int32_t height = 256;
VPIImageFormat fmt = VPI_IMAGE_FORMAT_U8;
int32_t numLevels = 3;
// VPIPyramid生成
VPIPyramid pyr;
vpiPyramidCreate(width, height, fmt, numLevels, 0.5f, 0, &pyr);
// VPIPyramid解放
vpiPyramidDestroy(pyr);
return 0;
}
データ変換
VPIには各種データ変換機能が用意されており、他のライブラリと連携ができるようになっています。ここではC APIでよく使うと思われるOpenCVのcv::Mat
クラスとの相互変換について紹介します。
- Mat->VPIImage
- VPIImage->Mat
Mat->VPIImage
// 要インクルード
#include <vpi/OpenCVInterop.hpp>
cv::Mat cvSrc = cv::Mat(cv::Size(512, 256), CV_8UC3, cv::Scalar(0, 0, 255));
// Mat->VPIImage
VPIImage src;
vpiImageCreateWrapperOpenCVMat(cvSrc, 0, &src);
ここではvpiImageCreateWrapperOpenCVMat
の例を示していますが、それ以外の方法もあります。https://docs.nvidia.com/vpi/2.3/group__VPI__OpenCVInterop.htmlを参照ください。
VPIImage->Mat
// 要インクルード
#include <vpi/OpenCVInterop.hpp>
VPIImage dst;
// dstの初期化はこのコードでは省略している
// VPIImageのロック
// この例ではREADのみであるためVPI_LOCK_READを指定
// この例ではCPU backendを使うためVPI_IMAGE_BUFFER_HOST_PITCH_LINEARを指定
// 詳細は https://docs.nvidia.com/vpi/2.3/group__VPI__Image.html#ga6024548d5ee11f88fab7341830262e2d 参照
VPIImageData dstData;
vpiImageLockData(dst, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &dstData);
// VPIImageData->Mat
cv::Mat cvDst;
vpiImageDataExportOpenCVMat(dstData, &cvDst);
// VPIImageのアンロック
vpiImageUnlock(dst);
対応アルゴリズム
VPIが提供するコンピュータビジョン、画像処理アルゴリズムはhttps://docs.nvidia.com/vpi/2.3/algorithms.htmlにあります。ただし、バックエンドによってはサポートしていないアルゴリズムがあったり、利用できる画像サイズ、パラメータに制約があるためアルゴリズムのページを確認ください。
シンプルなパイプライン
https://docs.nvidia.com/vpi/2.3/architecture.html#arch_simple_pipelineにある画像を入力としてBox Filterを適用し、出力するシンプルなパイプラインの実装例を示します。下図はパイプラインを図示したものです。
#include <vpi/Image.h>
#include <vpi/Stream.h>
// 今回、BoxFilterを使うためインクルード
#include <vpi/algo/BoxFilter.h>
#include <vpi/OpenCVInterop.hpp>
#include <opencv2/core.hpp>
#include <iostream>
int main(int argc, char *argv[])
{
VPIImage src;
cv::Mat cvSrc = cv::imread("kodim08.png", cv::IMREAD_GRAYSCALE);
vpiImageCreateWrapperOpenCVMat(cvSrc, 0, &src);
int32_t width;
int32_t height;
vpiImageGetSize(src, &width, &height);
VPIImageFormat type;
vpiImageGetFormat(src, &type);
// srcと同じ画像サイズ、フォーマットでVPIImageを生成
VPIImage dst;
vpiImageCreate(width, height, type, 0, &dst);
// ストリームの生成
VPIStream stream;
vpiStreamCreate(0, &stream);
// タスクのサブミット(このサンプルではVPI_BACKEND_CUDAを指定している)
vpiSubmitBoxFilter(stream, VPI_BACKEND_CUDA, src, dst, 5, 5, VPI_BORDER_ZERO);
// ストリームの同期
vpiStreamSync(stream);
// 解放処理
vpiStreamDestroy(stream);
vpiImageDestroy(src);
vpiImageDestroy(dst);
return 0;
}
CMakeLists.txt
CMakeLists.txtのサンプルを以下に示します。
cmake_minimum_required(VERSION 3.5)
project(vpi_sample)
# VPIパッケージを検索
find_package(vpi REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
# VPIライブラリのリンク設定
target_link_libraries(${PROJECT_NAME} vpi)
エラー処理
ステータスコード
VPIはVPIStatusでステータスコードを定義しています。詳細はhttps://docs.nvidia.com/vpi/2.3/group__VPI__Status.html#ga00590fb7e3bc6c02d207f4d280c27056を参照ください。
ステータスコード | 意味 |
---|---|
VPI_SUCCESS | Operation completed successfully. |
VPI_ERROR_NOT_IMPLEMENTED | Operation isn't implemented. |
VPI_ERROR_INVALID_ARGUMENT | Invalid argument, either wrong range or value not accepted. |
VPI_ERROR_INVALID_IMAGE_FORMAT | Image type not accepted. |
VPI_ERROR_INVALID_ARRAY_TYPE | Array type not accepted. |
VPI_ERROR_INVALID_PAYLOAD_TYPE | Payload not created for this algorithm. |
VPI_ERROR_INVALID_OPERATION | Operation isn't valid in this context. |
VPI_ERROR_INVALID_CONTEXT | Context is invalid or is already destroyed. |
VPI_ERROR_DEVICE | Device backend error. |
VPI_ERROR_NOT_READY | Operation not completed yet, try again later. |
VPI_ERROR_BUFFER_LOCKED | Invalid operation on a locked buffer. |
VPI_ERROR_OUT_OF_MEMORY | Not enough free memory to allocate object. |
VPI_ERROR_INTERNAL | Internal, non specific error. |
VPI APIによってはステータスコードを返すものがあるので、エラー処理やデバッグの際に活用するとよいでしょう。
VPIStatus status = vpiStreamCreate(0, &stream);
ステータスチェックマクロ
公式サンプルコードでは以下のようなステータスチェックマクロを実装しています。
#define CHECK_STATUS(STMT) \
do \
{ \
VPIStatus status = (STMT); \
if (status != VPI_SUCCESS) \
{ \
char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
std::ostringstream ss; \
ss << vpiStatusGetName(status) << ": " << buffer; \
throw std::runtime_error(ss.str()); \
} \
} while (0);
CHECK_STATUS
マクロの使用例は以下の通りです。
VPIStream stream;
CHECK_STATUS(vpiStreamCreate(0, &stream));
VPIディレクトリ構成
https://docs.nvidia.com/vpi/2.3/installation.htmlに説明がある通り、VPIのディレクトリ構成は下表の通りです。
ファイルパス | |
---|---|
/opt/nvidia/vpi2 |
VPIルートディレクトリ |
/opt/nvidia/vpi2/bin |
デモプログラムなどの実行バイナリが格納されたディレクトリ |
/opt/nvidia/vpi2/include |
インクルードディレクトリ |
/opt/nvidia/vpi2/lib/<arch> |
VPIライブラリが格納されたディレクトリ |
/opt/nvidia/vpi2/samples |
VPIサンプルプログラム(C++、Python)が格納されたディレクトリ |
動作確認環境
- reComputer J4012(Jetson Orin NX 16GB)
- JetPack 5.1.2
- VPI 2.3.9