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.2/architecture.htmlにあるVPIアーキテクチャの図を以下に引用します。
より詳細な情報を知りたい方はhttps://www.youtube.com/watch?v=gCf1wQLfYCMにあるNVIDIA Japanによる解説動画を観るとよいでしょう。
動作環境
VPIがサポートしている動作環境は以下の通りです。詳細はhttps://docs.nvidia.com/vpi/2.2/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.2/installation.htmlを参照ください。
バックエンド
https://docs.nvidia.com/vpi/2.2/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.2/architecture.html#arch_backendを参照ください。
データ構造
VPIにおけるプリミティブなデータ構造について紹介します。VPIはC API、Python APIを提供していますが、以降の説明ではPython APIを用いた例を述べます。詳細はhttps://docs.nvidia.com/vpi/2.2/basic_concepts.html#basic_buffersを参照ください。
- 2D Images
- Python APIだと
vpi.Image
- https://docs.nvidia.com/vpi/2.2/python/build/vpi.Image.html#vpi.Image
- Python APIだと
- 1D Arrays
- Python APIだと
vpi.Array
- https://docs.nvidia.com/vpi/2.2/python/build/vpi.Array.html
- Python APIだと
- 2D Image Pyramids
- Python APIだと
vpi.Pyramid
- https://docs.nvidia.com/vpi/2.2/python/build/vpi.Pyramid.html
- Python APIだと
以降、これらのプリミティブなデータ構造の使い方の例を示します。
vpi.Image
vpi.Image
の使用例は以下の通りです。ここでは8bit unsigned integer型の画素値を持つ1チャンネル画像を扱っているので画像フォーマットはvpi.Format.U8
と指定しています。画像フォーマットの詳細はhttps://docs.nvidia.com/vpi/2.2/python/build/vpi.Format.htmlを参照ください。
import vpi
width = 640
height = 480
format = vpi.Format.U8
# vpi.Image
img = vpi.Image((width, height), format)
print(f"img.format = {img.format}")
print(f"img.width = {img.width}")
print(f"img.height = {img.height}")
print(f"img.size = {img.size}")
このスクリプトの実行結果は以下の通りです。
img.format = Format.U8
img.width = 640
img.height = 480
img.size = (640, 480)
vpi.Array
vpi.Array
の使用例は以下の通りです。
import vpi
capacity = 10
format = vpi.Format.U8
# vpi.Array
arr = vpi.Array(capacity, format)
print(f"arr.capacity = {arr.capacity}")
print(f"arr.size = {arr.size}")
このスクリプトの実行結果は以下の通りです。
arr.capacity = 10
arr.size = 0
対応アルゴリズム
VPIが提供するコンピュータビジョン、画像処理アルゴリズムはhttps://docs.nvidia.com/vpi/2.2/algorithms.htmlにあります。ただし、バックエンドによってはサポートしていないアルゴリズムがあったり、利用できる画像サイズ、パラメータに制約があるためアルゴリズムのページを確認ください。
シンプルなパイプライン
https://docs.nvidia.com/vpi/2.2/architecture.html#arch_simple_pipelineにあるシンプルな画像処理パイプラインの例を用いて説明します。この例ではおおまかに以下の処理を行っています。
- 読み込んだ画像をndarrayに格納
- ndarrayをvpi.Imageに変換
- CUDA backendでbox filter適用
import cv2
import vpi
img_cv = cv2.imread("kodim08.png", cv2.IMREAD_GRAYSCALE)
# ndarrayをvpi.Imageに変換
img = vpi.asimage(img_cv)
# CUDA backendでbox filter適用
with vpi.Backend.CUDA:
output = img.box_filter(ksize=7)
前述のコードで
with vpi.Backend.CUDA:
output = img.box_filter(ksize=7)
となっている箇所は、box_filterメソッドにてバックエンドを指定するように
output = img.box_filter(ksize=7, backend=vpi.Backend.CUDA)
と書くこともできます。
データ変換
VPIには各種データ変換機能が用意されており、他のライブラリと連携ができるようになっています。
ここではPython APIでよく使うと思われる以下の変換について紹介します。
- ndarray->vpi.Image
- vpi.Image->ndarray
- torch.Tensor->vpi.Image
また、Jetsonデバイスのみではありますが、EGLImage、NvBufferとの相互変換もサポートしています。詳細はhttps://docs.nvidia.com/vpi/2.2/python/interoperabilities.htmlを参照ください。
ndarray->vpi.Image
vpi.asimageでndarrayからvpi.Imageへ変換することができます。
import cv2
import vpi
img_cv = cv2.imread("kodim08.png", cv2.IMREAD_GRAYSCALE)
# ndarrayをvpi.Imageに変換
img = vpi.asimage(img_cv)
vpi.Image->ndarray
以下のメソッドでvpi.Imageからndarrayへ変換することができます。
- cpuメソッド:https://docs.nvidia.com/vpi/2.2/python/build/vpi.Container.cpu.html
- toメソッド:https://docs.nvidia.com/vpi/2.2/python/build/vpi.Container.to.html
import vpi
width = 640
height = 480
format = vpi.Format.U8
img = vpi.Image((width, height), format)
# cpuメソッド
buffer1 = input.cpu()
# toメソッド
buffer2 = input.to(vpi.MemType.CPU)
torch.Tensor->vpi.Image
vpi.asimage
でtorch.Tensorからvpi.Imageへ変換することができます。
import cv2
import torch
import vpi
img_cv = cv2.imread("kodim08.png", cv2.IMREAD_GRAYSCALE)
torch_image = torch.asarray(np_image).cuda()
vpi_image = vpi.asimage(torch_image)
詳細はhttps://developer.nvidia.com/blog/improved-interoperability-between-vpi-and-pytorch/を参照ください。
VPIディレクトリ構成
https://docs.nvidia.com/vpi/2.2/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.1
- Python 3.8.10
- VPI 2.2.7