はじめに
これまでの記事では、Media Foundationで取得したUSBカメラの映像を、D3DImageを利用して高速に表示する方法を紹介してきました。
また、GPUのVideoProcessorを利用することで、NV12からRGBへの色変換もGPU上で完結できるようになりました。
今回はその続きとして、カメラから入力した映像を表示用に利用しつつ、OpenVINOへも入力し、リアルタイムで物体検出を行ってみます。
OpenVINOのサンプルはPythonで紹介されることが多いですが、今回はC++からDirect3DのテクスチャをそのままOpenVINOへ渡して推論する方法を紹介します。
OpenVINOの開発環境設定
oneAPIをインストール
まずはIntel oneAPIをインストールします。
今回は 2026.1.0 を使用しました。
OpenVINO を展開
続いてOpenVINO をアーカイブ版で展開します。
以下の記事を参考にしました。
今回は
C:\Program Files (x86)\Intel\openvino_2026.2.1
へ展開しています。
OpenCLヘッダを追加
GPU版OpenVINOを利用する場合、OpenCLのヘッダが必要になります。
以下の2つのリポジトリからヘッダを取得します。
取得したヘッダを
C:\Program Files (x86)\Intel\openvino_2026.2.1\runtime\include\CL
へコピーしてください。
本来であればOpenCLのヘッダは別の場所に配置すべきだと思います。
今回は「たまたまこのフォルダにインクルードパスが通っていた」という理由だけで、ここへコピーしています(笑)
これでサンプルをビルドできるようになります。
サンプルのビルド
サンプルソースはGitHubへ公開しています。
現在は#ifdefでYOLOを無効化しているため、
USE_YOLO
を定義してビルドしてください。
サンプルを実行するには
OpenVINOのDLLをコピー
ビルドした実行ファイルと同じフォルダへ、以下のフォルダにあるDLLをコピーします。
C:\Program Files (x86)\Intel\openvino_2026.2.1\runtime\3rdparty\tbb\bin
および
C:\Program Files (x86)\Intel\openvino_2026.2.1\runtime\bin\intel64\Debug
または
C:\Program Files (x86)\Intel\openvino_2026.2.1\runtime\bin\intel64\Release
YOLOモデルを配置
今回は以下のリポジトリにある
yolov8n.onnx
を使用しました。
こちらも実行ファイルと同じフォルダへコピーしてください。
これで実行できると思います。
プログラムの流れ
今回の処理は以下のような流れになります。
Media Foundation
│
▼
USBカメラ映像(NV12)
│
▼
GPUへ転送
│
├────────────────────────────┐
│ │
▼ ▼
VideoProcessorで表示用の OpenVINOへ入力
NV12→RGB変換 │
▼
テンソル化
NV12 → RGB(float)へ変換
(OpenVINOが自動で実施)
│
▼
AI推論実行
│
▼
検出結果取得
│ │
└──────────────┬─────────────┘
▼
Direct2Dで検出枠を描画して表示
▼
D3DImageへ表示
今回のポイントは、GPU上に存在するNV12テクスチャをそのままOpenVINOへ渡していることです。
色変換やリサイズ、テンソル化といった前処理はOpenVINO側で行われます。また、検出結果の描画もDirect2DでGPU上に重ねています。
そのため、映像データは一度GPUへ転送すれば、その後はCPUへ読み戻すことなく、表示からAI推論、描画までをGPU上で完結させることができます。
今回のサンプルはOpenVINOのGPUリモートテンソル機能を利用しているため、OpenVINO GPUプラグインが利用できるIntel GPUが必要です。CPU推論へ切り替えるには入力テンソルの作成方法を変更する必要があります。
OpenVINOの初期化
まずはモデルをロードします。
モデルをキャッシュするよう設定しているため、2回目以降の起動はかなり高速になります。
// モデルをビルドした結果をキャッシュする(2回目以降初期化早くなる)
m_core.set_property(
ov::cache_dir("openvino_cache")
);
// モデル読み込む
auto model = m_core.read_model(modelPath);
続いてPrePostProcessorを利用して、入力画像の形式を指定します。
ov::preprocess::PrePostProcessor ppp(model);
ppp.input()
.tensor()
.set_spatial_static_shape(
height,
width)
.set_element_type(
ov::element::u8)
.set_color_format(
ov::preprocess::ColorFormat::NV12_TWO_PLANES,
{"y", "uv"})
.set_memory_type(
ov::intel_gpu::memory_type::surface);
ここでは、
- 入力画像サイズ
- NV12フォーマット
- GPUメモリ上のテクスチャ
であることをOpenVINOへ伝えています。
続いて前処理を設定します。
ppp.input()
.preprocess()
.convert_color(
ov::preprocess::ColorFormat::RGB)
.resize(
ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR)
.convert_element_type(
ov::element::f32)
.scale(255.0);
ppp.input()
.model()
.set_layout("NCHW");
ここで
- NV12→RGB
- 640×640へリサイズ
- floatへ変換
- 正規化
までをOpenVINOが自動で行います。
最後にモデルをビルドします。
m_model = ppp.build();
GPUデバイスと関連付けを行い、
m_gpu_context =
std::make_shared<ov::intel_gpu::ocl::D3DContext>(
m_core,
device
);
推論用オブジェクトを生成します。
m_compiledModel =
m_core.compile_model(
m_model,
*m_gpu_context
);
m_request =
m_compiledModel.create_infer_request();
これで初期化は完了です。
推論を実行する
毎フレーム取得したNV12テクスチャから、そのまま入力テンソルを生成します。
auto nv12_blob =
m_gpu_context->create_tensor_nv12(
(uint64_t)m_height,
(uint64_t)m_width,
texture
);
生成したテンソルを入力へ設定します。
auto inputs = m_compiledModel.inputs();
m_request.set_tensor(
inputs[0],
nv12_blob.first);
m_request.set_tensor(
inputs[1],
nv12_blob.second);
推論はたった1行です。
m_request.infer();
結果を取得します。
auto output =
m_request.get_output_tensor();
取得できるデータはYOLOv8でおなじみの
1 × 84 × 8400
のテンソルです。
今回はこのデータを解析し、検出した矩形やラベルをDirect2Dで描画しています。
実行結果
スクリーンショットはこんな感じです。
無事にリアルタイムで物体検出できました。
ただし、このままでは検出精度はあまり良くありません。
もっとも、実験台が本物の人物ではなく、おもちゃの人形という時点で少々条件が厳しい気もします(笑)
実用レベルまで持っていくには、パラメータ調整やモデル選択など、さらにノウハウを蓄積していく必要がありそうです。
まとめ
今回紹介した方法を利用すれば、
- Media Foundation
- Direct3D
- D3DImage
- Direct2D
- OpenVINO
を組み合わせて、Windowsネイティブアプリケーション上でリアルタイムAI推論を実現できます。
今回で、Media Foundationによる映像入力から、GPUでの画像処理、D3DImageによる表示、そしてOpenVINOによるAI推論まで、一通りGPUを活用した映像処理パイプラインを構築できました。
AIというとPythonを使う例が非常に多いですが、実際に製品や業務アプリへ組み込もうとすると、C++やC#から直接利用できるメリットは非常に大きいと感じます。
もちろん、AIを実用レベルで使いこなすにはモデル選択やパラメータ調整など、まだまだノウハウが必要です。
しかし今回のサンプルだけでも、GPU上の映像をそのままAIへ渡し、検出結果をリアルタイムに画面へ重ねて表示する基盤は完成しています。
ここから先は、用途に応じたモデルへ置き換えたり、トラッキングを追加したりと、さまざまな応用ができるでしょう。
なお、今回はWPF版のサンプルを紹介しましたが、OpenVINOやMedia Foundation、Direct3DまわりのコードはWinUI 3でもほぼそのまま利用できます。
違いは映像の表示先が
D3DImageなのかSwapChainPanelなのかという程度で、AI推論の部分はほとんど変更する必要はありません。そのため、WPFだけでなくWinUI 3向けのアプリケーションでも同じ構成を採用できます。
