2
4

NVIDIA Vision Programming Interface (VPI)入門 C API編

Last updated at Posted at 2023-09-27

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を参照ください。

以降、これらのプリミティブなデータ構造の使い方の例を示します。

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
2
4
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
2
4